Files
captchalmpoc/server.js

97 lines
2.6 KiB
JavaScript

import express from 'express';
import { createExpressMiddleware } from 'captchalm';
import path from 'path';
import { fileURLToPath } from 'url';
import fs from 'fs/promises';
import { existsSync } from 'fs';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const DB_PATH = path.join(__dirname, 'posts.json');
const app = express();
app.use(express.json());
app.use(express.static(path.join(__dirname, 'public')));
// Initialize DB if not exists
if (!existsSync(DB_PATH)) {
await fs.writeFile(DB_PATH, JSON.stringify([], null, 2));
}
// Helper to read/write DB
async function getPosts() {
const data = await fs.readFile(DB_PATH, 'utf-8');
return JSON.parse(data);
}
async function savePost(post) {
const posts = await getPosts();
posts.unshift(post); // Add to beginning
await fs.writeFile(DB_PATH, JSON.stringify(posts, null, 2));
return posts;
}
// Custom Rate Limiting
const rateLimitMap = new Map();
const RATE_LIMIT_WINDOW = 30000; // 30 seconds
// CaptchaLM Middleware (Rate limit removed)
const { protect, challenge } = createExpressMiddleware({
secret: 'ai-twitter-secret-key-123',
difficulty: 'medium',
});
// --- API Endpoints ---
// 1. Get Challenges (for Agents)
app.get('/api/challenge', challenge);
// 2. Get Posts (Public)
app.get('/api/posts', async (req, res) => {
try {
const posts = await getPosts();
res.json(posts);
} catch (err) {
res.status(500).json({ error: 'Database error' });
}
});
// 3. Create Post (Protected - AI Only + Custom Rate Limit)
app.post('/api/posts', protect, async (req, res) => {
const ip = req.ip || req.socket.remoteAddress;
const lastPostTime = rateLimitMap.get(ip);
const now = Date.now();
if (lastPostTime && (now - lastPostTime < RATE_LIMIT_WINDOW)) {
const remaining = Math.ceil((RATE_LIMIT_WINDOW - (now - lastPostTime)) / 1000);
return res.status(429).json({
error: `Rate limited. Please wait ${remaining}s.`,
errorCode: 'RATE_LIMITED'
});
}
const { content, agentId = 'Unknown Agent' } = req.body;
if (!content) {
return res.status(400).json({ error: 'Content is required' });
}
const newPost = {
id: Date.now().toString(),
agentId,
content,
timestamp: new Date().toISOString()
};
await savePost(newPost);
rateLimitMap.set(ip, now); // Update rate limit timestamp
console.log(`[New Log] ${agentId}: ${content.substring(0, 50)}...`);
res.json({ success: true, post: newPost });
});
const PORT = 3000;
app.listen(PORT, () => {
console.log(`Server running at http://localhost:${PORT}`);
});