/* Secret-Swap – minimal Express server with flat-file storage */ import express from 'express'; import fs from 'fs'; import path from 'path'; import crypto from 'crypto'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const USERS_FILE = path.join(__dirname, 'users.json'); const EXCH_FILE = path.join(__dirname, 'exchanges.json'); const PORT = process.env.PORT || 3000; /* ---------- helpers ---------- */ const readJSON = (f, d = {}) => (fs.existsSync(f) ? JSON.parse(fs.readFileSync(f)) : d); const writeJSON = (f, o) => fs.writeFileSync(f, JSON.stringify(o, null, 2)); const sessions = new Map(); // token → username const genToken = () => crypto.randomUUID(); function hashPass(pw, salt = crypto.randomBytes(16).toString('hex')) { const hash = crypto.scryptSync(pw, salt, 64).toString('hex'); return `${salt}:${hash}`; } function checkPass(pw, stored) { const [salt, ref] = stored.split(':'); const hash = crypto.scryptSync(pw, salt, 64).toString('hex'); return crypto.timingSafeEqual(Buffer.from(hash, 'hex'), Buffer.from(ref, 'hex')); } /* ---------- tiny templating ---------- */ const page = (title, body) => `
User exists.
')); users[username]=hashPass(password); writeJSON(USERS_FILE,users); res.redirect('/login'); }); app.get('/login',(req,res)=>res.send(page('Login',` `))); app.post('/login',(req,res)=>{ const {username,password}=req.body; const users=readJSON(USERS_FILE,{}); if(!users[username]||!checkPass(password,users[username])) return res.send(page('Error','Bad credentials.
')); const token=genToken(); sessions.set(token,username); res.setHeader('Set-Cookie',`token=${token}; HttpOnly; Path=/`); res.redirect('/dashboard'); }); app.get('/logout',(req,res)=>{ if(req.token) sessions.delete(req.token); res.setHeader('Set-Cookie','token=; Max-Age=0; Path=/'); res.redirect('/login'); }); /* ---------- dashboard ---------- */ app.get(['/','/dashboard'],needAuth,(req,res)=>{ const exchanges=readJSON(EXCH_FILE,{}); const list=Object.entries(exchanges) .filter(([id,x])=>x.owner===req.user||x.partner===req.user) .map(([id,x])=>{ const role = x.owner===req.user? 'owner':'partner'; return `Logged in as ${req.user} | Logout
Partner username not found.
')); const exchanges=readJSON(EXCH_FILE,{}); const id=crypto.randomUUID(); exchanges[id]={id,owner:req.user,partner,secret,responses:[]}; writeJSON(EXCH_FILE,exchanges); const host=req.headers.host; res.send(page('Swap created',`Send this link to ${partner}:
`)); }); /* ---------- respond ---------- */ app.get('/respond/:id',needAuth,(req,res)=>{ const ex=readJSON(EXCH_FILE,{})[req.params.id]; if(!ex) return res.send(page('Error','Swap not found.
')); if(req.user!==ex.partner && req.user!==ex.owner) return res.send(page('Forbidden','You are not part of this swap.
')); if(req.user===ex.owner) return res.redirect(`/view/${req.params.id}`); const done=ex.responses.some(r=>r.from===req.user); if(done) return res.redirect(`/view/${req.params.id}`); res.send(page('Respond to secret', `Original secret will be revealed after you submit yours.
`)); }); app.post('/respond/:id',needAuth,(req,res)=>{ const data=readJSON(EXCH_FILE,{}); const ex=data[req.params.id]; if(!ex||req.user!==ex.partner) return res.send(page('Error','Not allowed.
')); ex.responses.push({from:req.user,secret:req.body.response}); writeJSON(EXCH_FILE,data); res.send(page('Secret revealed',`Original secret from ${ex.owner}: ${ex.secret}
`)); }); /* ---------- view (owner) ---------- */ app.get('/view/:id',needAuth,(req,res)=>{ const ex=readJSON(EXCH_FILE,{})[req.params.id]; if(!ex||req.user!==ex.owner) return res.send(page('Error','Not allowed.
')); const list=ex.responses.map(r=>`Your secret: ${ex.secret}