'use strict'; const { Worker: ThreadWorker, isMainThread, parentPort, workerData } = require('worker_threads'); // ============ DATABASE WORKER THREAD ============ if (!isMainThread) { const Database = require('better-sqlite3'); const path = require('path'); const fs = require('fs'); const DATA_DIR = workerData.dataDir || "/data"; if (!fs.existsSync(DATA_DIR)) { fs.mkdirSync(DATA_DIR, { recursive: true }); } const dbPath = path.join(DATA_DIR, 'users.db'); const db = new Database(dbPath); db.pragma('journal_mode = WAL'); db.pragma('synchronous = OFF'); db.pragma('cache_size = -2000000'); db.pragma('temp_store = MEMORY'); db.pragma('mmap_size = 268435456'); db.exec(` CREATE TABLE IF NOT EXISTS users ( id TEXT PRIMARY KEY, username TEXT UNIQUE NOT NULL, password_hash TEXT NOT NULL, name TEXT NOT NULL, created_at TEXT NOT NULL, is_login INTEGER DEFAULT 0, bio TEXT DEFAULT '', pic_url TEXT DEFAULT '', date_of_birth TEXT DEFAULT '', is_profile_public TEXT DEFAULT 'true' ) `); db.exec(`CREATE INDEX IF NOT EXISTS idx_username ON users(username);`); const stmts = { getAllUsers: db.prepare('SELECT * FROM users'), getUserById: db.prepare('SELECT * FROM users WHERE id = ?'), getUserByUsername: db.prepare('SELECT * FROM users WHERE username = ?'), insertUser: db.prepare(` INSERT INTO users ( id, username, password_hash, name, created_at, is_login, bio, pic_url, date_of_birth, is_profile_public ) VALUES ( @id, @username, @password_hash, @name, @created_at, @is_login, @bio, @pic_url, @date_of_birth, @is_profile_public ) `), updateLogin: db.prepare('UPDATE users SET is_login = ? WHERE id = ?'), updateProfile: db.prepare(` UPDATE users SET username = COALESCE(NULLIF(@username, ''), username), name = COALESCE(NULLIF(@name, ''), name), bio = COALESCE(NULLIF(@bio, ''), bio), date_of_birth = COALESCE(NULLIF(@date_of_birth, ''), date_of_birth), is_profile_public = COALESCE(NULLIF(@is_profile_public, ''), is_profile_public) WHERE id = @id `), updatePhoto: db.prepare('UPDATE users SET pic_url = ? WHERE id = ?'), getCount: db.prepare('SELECT COUNT(*) as count FROM users') }; parentPort.on('message', async (message) => { const { id, action, data } = message; try { let result; switch (action) { case 'getAllUsers': result = stmts.getAllUsers.all(); break; case 'getUserById': result = stmts.getUserById.get(data.userId); break; case 'getUserByUsername': result = stmts.getUserByUsername.get(data.username); break; case 'insertUser': stmts.insertUser.run(data.user); result = { success: true }; break; case 'updateLogin': stmts.updateLogin.run(data.isLogin ? 1 : 0, data.userId); result = { success: true }; break; case 'updateProfile': stmts.updateProfile.run({ id: data.userId, username: data.username || '', name: data.name || '', bio: data.bio || '', date_of_birth: data.date_of_birth || '', is_profile_public: data.is_profile_public || 'true' }); result = { success: true }; break; case 'updatePhoto': stmts.updatePhoto.run(data.picUrl, data.userId); result = { success: true }; break; case 'batchOperation': result = processBatch(data.operations); break; case 'getCount': result = stmts.getCount.get().count; break; default: throw new Error(`Unknown action: ${action}`); } parentPort.postMessage({ id, success: true, result }); } catch (error) { parentPort.postMessage({ id, success: false, error: error.message }); } }); function processBatch(operations) { const transaction = db.transaction(() => { for (const op of operations) { switch (op.action) { case 'signup': try { stmts.insertUser.run(op.user); } catch (e) {} break; case 'login': stmts.updateLogin.run(1, op.userId); break; case 'logout': stmts.updateLogin.run(0, op.userId); break; case 'updateProfile': stmts.updateProfile.run({ id: op.data.id || op.data.userId, username: op.data.username || '', name: op.data.name || '', bio: op.data.bio || '', date_of_birth: op.data.date_of_birth || '', is_profile_public: op.data.is_profile_public || 'true' }); break; case 'updatePhoto': stmts.updatePhoto.run(op.picUrl, op.userId); break; } } }); transaction(); return { processed: operations.length }; } return; } // ============ MAIN THREAD ============ const fastify = require('fastify')({ logger: false, bodyLimit: 1048576, keepAliveTimeout: 61000, connectionTimeout: 61000, maxRequestsPerSocket: 0, requestTimeout: 30000 }); const cors = require('@fastify/cors'); const multipart = require('@fastify/multipart'); const fastifyStatic = require('@fastify/static'); const crypto = require('crypto'); const fs = require('fs'); const fsp = require('fs').promises; const path = require('path'); let sharp; try { sharp = require('sharp'); } catch (e) {} const MAX_BUCKET_SIZE = 60 * 1024 * 1024 * 1024; const MAX_USERS = 30000; const MAX_PHOTO_SIZE_KB = 20; const MAX_RPS = 15000; const BATCH_SIZE = 15000; const HASH = process.env.HASH || 'v3l0st-d3f4ult-h4sh-k3y-2024!@#$%^&*()'; const SPACE_NAME = process.env.SPACE_NAME || '2'; const MAX_USERNAME_LENGTH = 200; const MAX_NAME_LENGTH = 200; const MAX_PASSWORD_LENGTH = 200; const MAX_BIO_LENGTH = 1000; let totalUsers = 0; let currentRPS = 0; let rpsWindowStart = Date.now(); let isSpaceFull_RPS = false; let isSpaceFull_Users = false; const DATA_DIR = process.env.DATA_DIR || "/data"; const PHOTOS_DIR = path.join(DATA_DIR, "photos"); const STATUS_DIR = process.env.STATUS_DIR || "/status"; const FULL_FILE = path.join(STATUS_DIR, "full.txt"); const SIZE_FILE = path.join(STATUS_DIR, "size.txt"); [DATA_DIR, PHOTOS_DIR, STATUS_DIR].forEach(dir => { if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); }); if (!fs.existsSync(FULL_FILE)) fs.writeFileSync(FULL_FILE, '', 'utf8'); if (!fs.existsSync(SIZE_FILE)) fs.writeFileSync(SIZE_FILE, '', 'utf8'); async function getFullSpaces() { try { const content = await fsp.readFile(FULL_FILE, 'utf8'); return content.trim() ? content.trim().split('|') : []; } catch (e) { return []; } } async function addSpaceToFull() { try { let content = await fsp.readFile(FULL_FILE, 'utf8').catch(() => ''); content = content.trim(); let spaces = content ? content.split('|') : []; if (!spaces.includes(SPACE_NAME)) { spaces.push(SPACE_NAME); await fsp.writeFile(FULL_FILE, spaces.join('|'), 'utf8'); } } catch (e) {} } async function removeSpaceFromFull() { try { let content = await fsp.readFile(FULL_FILE, 'utf8').catch(() => ''); content = content.trim(); let spaces = content ? content.split('|') : []; const newSpaces = spaces.filter(s => s !== SPACE_NAME); if (newSpaces.length !== spaces.length) { await fsp.writeFile(FULL_FILE, newSpaces.join('|'), 'utf8'); } } catch (e) {} } async function addSpaceToSize() { try { let content = await fsp.readFile(SIZE_FILE, 'utf8').catch(() => ''); content = content.trim(); let spaces = content ? content.split('|') : []; if (!spaces.includes(SPACE_NAME)) { spaces.push(SPACE_NAME); await fsp.writeFile(SIZE_FILE, spaces.join('|'), 'utf8'); } } catch (e) {} } function checkRPSStatus() { const now = Date.now(); if (now - rpsWindowStart >= 1000) { if (currentRPS >= MAX_RPS * 0.9 && !isSpaceFull_RPS) { isSpaceFull_RPS = true; addSpaceToFull(); } else if (currentRPS < MAX_RPS * 0.7 && isSpaceFull_RPS) { isSpaceFull_RPS = false; removeSpaceFromFull(); } currentRPS = 0; rpsWindowStart = now; } } async function checkUserCountStatus() { try { const count = await dbRequest('getCount'); if (count >= MAX_USERS && !isSpaceFull_Users) { isSpaceFull_Users = true; await addSpaceToSize(); await addSpaceToFull(); } } catch (e) {} } let dbWorker; let dbRequestId = 0; const dbPromises = new Map(); function initDBWorker() { return new Promise((resolve, reject) => { dbWorker = new ThreadWorker(__filename, { workerData: { dataDir: DATA_DIR } }); dbWorker.on('message', (message) => { const { id, success, result, error } = message; const promise = dbPromises.get(id); if (promise) { dbPromises.delete(id); if (success) promise.resolve(result); else promise.reject(new Error(error)); } }); dbWorker.on('error', reject); dbWorker.on('online', resolve); }); } function dbRequest(action, data = {}) { return new Promise((resolve, reject) => { const id = ++dbRequestId; dbPromises.set(id, { resolve, reject }); dbWorker.postMessage({ id, action, data }); }); } class FastRateLimiter { constructor(maxRequests = 15000, windowMs = 1000) { this.maxRequests = maxRequests; this.windowMs = windowMs; this.requests = 0; this.windowStart = Date.now(); } isAllowed() { const now = Date.now(); if (now - this.windowStart >= this.windowMs) { this.requests = 0; this.windowStart = now; } if (this.requests >= this.maxRequests) return false; this.requests++; currentRPS++; return true; } } const rateLimiter = new FastRateLimiter(15000, 1000); function hashPassword(password) { return crypto.createHash('sha256').update(HASH + password + HASH).digest('hex'); } function verifyPassword(password, hash) { return hashPassword(password) === hash; } function generateId() { return crypto.randomBytes(16).toString('base64url').substring(0, 21); } function getBucketSize() { let totalSize = 0; try { const files = fs.readdirSync(PHOTOS_DIR); for (const file of files) { try { totalSize += fs.statSync(path.join(PHOTOS_DIR, file)).size; } catch (e) {} } try { totalSize += fs.statSync(path.join(DATA_DIR, 'users.db')).size; } catch (e) {} } catch (e) {} return totalSize; } async function compressToWebp(fileData, maxSizeKb = 20) { if (!sharp) return fileData; try { let image = sharp(fileData); const metadata = await image.metadata(); const currentSizeKb = fileData.length / 1024; if (metadata.format === 'webp' && currentSizeKb <= maxSizeKb) return fileData; let quality = 80; let outputBuffer; while (quality >= 10) { outputBuffer = await image .resize(800, 800, { fit: 'inside', withoutEnlargement: true }) .webp({ quality }) .toBuffer(); if (outputBuffer.length / 1024 <= maxSizeKb) break; quality -= 10; } if (outputBuffer.length / 1024 > maxSizeKb) { outputBuffer = await image .resize(400, 400, { fit: 'inside', withoutEnlargement: true }) .webp({ quality: 30 }) .toBuffer(); } return outputBuffer; } catch (e) { return fileData; } } async function setupFastify() { await fastify.register(cors, { origin: '*' }); await fastify.register(multipart, { limits: { fileSize: 5 * 1024 * 1024 } }); await fastify.register(fastifyStatic, { root: PHOTOS_DIR, prefix: '/photos/', maxAge: 86400000 }); fastify.addHook('onRequest', async (request, reply) => { if (!rateLimiter.isAllowed()) { reply.status(503).send({ error: 'rate_limit' }); return; } }); fastify.post('/signup', async (request, reply) => { const { username, password, name } = request.body || {}; if (!username || !password || !name) { reply.status(400).send({ detail: 'Missing fields' }); return; } const cleanUsername = username.trim().toLowerCase(); if (cleanUsername.length < 3 || cleanUsername.length > MAX_USERNAME_LENGTH) { reply.status(400).send({ detail: `Username must be 3-${MAX_USERNAME_LENGTH} chars` }); return; } if (password.length < 6 || password.length > MAX_PASSWORD_LENGTH) { reply.status(400).send({ detail: `Password must be 6-${MAX_PASSWORD_LENGTH} chars` }); return; } if (name.trim().length > MAX_NAME_LENGTH) { reply.status(400).send({ detail: `Name max ${MAX_NAME_LENGTH} chars` }); return; } const existingUser = await dbRequest('getUserByUsername', { username: cleanUsername }); if (existingUser) { reply.status(409).send({ detail: 'Username exists' }); return; } const count = await dbRequest('getCount'); if (count >= MAX_USERS) { reply.status(507).send({ detail: 'Server full' }); return; } const userId = generateId(); const newUser = { id: userId, username: cleanUsername, password_hash: hashPassword(password), name: name.trim(), created_at: new Date().toISOString(), is_login: 0, bio: '', pic_url: '', date_of_birth: '', is_profile_public: 'true' }; try { await dbRequest('insertUser', { user: newUser }); totalUsers = count + 1; await checkUserCountStatus(); reply.send({ success: true, user_id: userId, username: cleanUsername }); } catch (e) { reply.status(500).send({ detail: 'Signup failed' }); } }); fastify.post('/login', async (request, reply) => { const { username, password } = request.body || {}; if (!username || !password) { reply.status(400).send({ detail: 'Missing fields' }); return; } const cleanUsername = username.trim().toLowerCase(); const user = await dbRequest('getUserByUsername', { username: cleanUsername }); if (!user) { reply.status(401).send({ detail: 'Invalid credentials' }); return; } if (!verifyPassword(password, user.password_hash)) { reply.status(401).send({ detail: 'Invalid credentials' }); return; } await dbRequest('updateLogin', { userId: user.id, isLogin: true }); reply.send({ success: true, id: user.id, username: user.username, name: user.name, bio: user.bio || '', pic_url: user.pic_url || '', date_of_birth: user.date_of_birth || '', is_profile_public: user.is_profile_public || 'true', created_at: user.created_at }); }); fastify.post('/logout', async (request, reply) => { const { id } = request.body || {}; if (!id) { reply.status(400).send({ detail: 'Missing ID' }); return; } const user = await dbRequest('getUserById', { userId: id }); if (!user) { reply.status(404).send({ detail: 'Not found' }); return; } await dbRequest('updateLogin', { userId: id, isLogin: false }); reply.send({ success: true }); }); fastify.put('/updateprofiledetails', async (request, reply) => { const { id, username, name, bio, date_of_birth, is_profile_public } = request.body || {}; if (!id) { reply.status(400).send({ detail: 'Missing ID' }); return; } const user = await dbRequest('getUserById', { userId: id }); if (!user) { reply.status(404).send({ detail: 'Not found' }); return; } if (name && name.length > MAX_NAME_LENGTH) { reply.status(400).send({ detail: `Name max ${MAX_NAME_LENGTH} chars` }); return; } if (bio && bio.length > MAX_BIO_LENGTH) { reply.status(400).send({ detail: `Bio max ${MAX_BIO_LENGTH} chars` }); return; } let cleanUsername = ''; if (username && username.trim() !== '') { cleanUsername = username.trim().toLowerCase(); if (cleanUsername.length < 3 || cleanUsername.length > MAX_USERNAME_LENGTH) { reply.status(400).send({ detail: `Username must be 3-${MAX_USERNAME_LENGTH} chars` }); return; } if (cleanUsername !== user.username) { const existingUser = await dbRequest('getUserByUsername', { username: cleanUsername }); if (existingUser) { reply.status(409).send({ detail: 'Username exists' }); return; } } } await dbRequest('updateProfile', { userId: id, username: cleanUsername, name: name || '', bio: bio !== undefined ? bio : '', date_of_birth: date_of_birth || '', is_profile_public: is_profile_public || 'true' }); reply.send({ success: true }); }); fastify.post('/uploadphoto', async (request, reply) => { if (getBucketSize() >= MAX_BUCKET_SIZE) { reply.status(507).send({ detail: 'Storage full' }); return; } try { const data = await request.file(); if (!data || !data.mimetype.startsWith('image/')) { reply.status(400).send({ detail: 'Invalid image' }); return; } const fileBuffer = await data.toBuffer(); const id = (data.fields && data.fields.id) ? data.fields.id.value : null; const user = await dbRequest('getUserById', { userId: id }); if (!id || !user) { reply.status(404).send({ detail: 'Not found' }); return; } const compressed = await compressToWebp(fileBuffer, MAX_PHOTO_SIZE_KB); const filename = `${id}_${Math.floor(Date.now() / 1000)}.webp`; await fsp.writeFile(path.join(PHOTOS_DIR, filename), compressed); await dbRequest('updatePhoto', { userId: id, picUrl: `/photos/${filename}` }); reply.send({ success: true }); } catch (e) { reply.status(500).send({ detail: 'Failed' }); } }); fastify.get('/getuser', async (request, reply) => { const { id, username } = request.query || {}; let user; if (username) { user = await dbRequest('getUserByUsername', { username: username.toLowerCase() }); } else if (id) { user = await dbRequest('getUserById', { userId: id }); } else { reply.status(400).send({ detail: 'Missing params' }); return; } if (!user) { reply.status(404).send({ detail: 'Not found' }); return; } reply.send({ id: user.id, username: user.username, name: user.name, bio: user.bio || '', pic_url: user.pic_url || '', date_of_birth: user.date_of_birth || '', is_profile_public: user.is_profile_public || 'true', created_at: user.created_at, is_login: Boolean(user.is_login) }); }); fastify.get('/check', async (request, reply) => { const count = await dbRequest('getCount'); reply.send({ status: 'ok', count: count, maxUsers: MAX_USERS, isFull: count >= MAX_USERS, spaceName: SPACE_NAME }); }); return fastify; } async function start() { try { await initDBWorker(); totalUsers = await dbRequest('getCount'); await setupFastify(); setInterval(() => { checkRPSStatus(); }, 100); setInterval(async () => { await checkUserCountStatus(); }, 5000); const port = process.env.PORT || 7860; await fastify.listen({ port, host: '0.0.0.0' }); console.log(`Server running on port ${port}`); } catch (err) { console.error('Startup error:', err.message); process.exit(1); } } process.on('SIGTERM', async () => { await removeSpaceFromFull(); await fastify.close(); if (dbWorker) await dbWorker.terminate(); process.exit(0); }); process.on('SIGINT', async () => { await removeSpaceFromFull(); await fastify.close(); if (dbWorker) await dbWorker.terminate(); process.exit(0); }); start().catch(err => process.exit(1));