48 lines
1.7 KiB
JavaScript
48 lines
1.7 KiB
JavaScript
import { createServer } from 'http';
|
|
import { appendFile, mkdir, readFile, stat } from 'fs/promises';
|
|
import { existsSync, createReadStream } from 'fs';
|
|
import { resolve, extname } from 'path';
|
|
|
|
const PORT = process.env.PORT || 3000;
|
|
const HOST = '0.0.0.0';
|
|
const PUBLIC_DIR = resolve('./public');
|
|
const DATA_DIR = resolve('./data');
|
|
const CSV = resolve(DATA_DIR, 'signups.csv');
|
|
const emailSet = new Set();
|
|
|
|
const mime = { '.html':'text/html','.js':'application/javascript','.css':'text/css' };
|
|
|
|
async function ensureFile() {
|
|
await mkdir(DATA_DIR, { recursive:true });
|
|
if(!existsSync(CSV)) await appendFile(CSV, 'timestamp,email\n');
|
|
}
|
|
|
|
function validEmail(e){return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e);}
|
|
|
|
async function serveFile(path, res){
|
|
try{
|
|
const st=await stat(path);
|
|
res.writeHead(200,{'Content-Type':mime[extname(path)]||'text/plain'});
|
|
createReadStream(path).pipe(res);
|
|
}catch{res.writeHead(404).end('Not found');}
|
|
}
|
|
|
|
createServer(async (req,res)=>{
|
|
if(req.url==='/api/subscribe' && req.method==='POST'){
|
|
let body='';for await(const c of req)body+=c;
|
|
try{
|
|
const {email}=JSON.parse(body);
|
|
if(!validEmail(email)){res.writeHead(400).end('Invalid');return;}
|
|
if(emailSet.has(email)){res.writeHead(200).end('OK');return;}
|
|
const line=`${new Date().toISOString()},${email}\n`;
|
|
await appendFile(CSV,line);emailSet.add(email);
|
|
res.writeHead(200).end('Saved');
|
|
}catch{res.writeHead(500).end('Error');}
|
|
return;
|
|
}
|
|
const path = req.url==='/'?resolve(PUBLIC_DIR,'index.html'):resolve(PUBLIC_DIR,req.url.slice(1));
|
|
serveFile(path,res);
|
|
}).listen(PORT,HOST,async()=>{
|
|
await ensureFile();
|
|
console.log(`Running on http://${HOST}:${PORT}`);
|
|
}); |