Files
SuperSunday/server.mjs
karim hassan e3620c7f42 Day:1
2025-08-18 19:30:47 +00:00

147 lines
6.7 KiB
JavaScript

import http from 'http';
import { readFile, writeFile, stat } from 'fs/promises';
import { createReadStream } from 'fs';
import path from 'path';
import url from 'url';
const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
const PORT = process.env.PORT || 8080;
const DATA_DIR = path.join(__dirname, 'data');
const DB_PATH = path.join(DATA_DIR, 'db.json');
const mime = { '.html':'text/html; charset=utf-8','.css':'text/css; charset=utf-8','.js':'application/javascript; charset=utf-8','.json':'application/json; charset=utf-8','.svg':'image/svg+xml','.png':'image/png','.ico':'image/x-icon','.webp':'image/webp' };
function send(res, code, body, headers={}){ res.writeHead(code, { 'content-type':'text/plain; charset=utf-8', ...headers }); res.end(body); }
async function ensureDb(){
try { await stat(DB_PATH); }
catch {
const base = new Date(Date.UTC(2025, 9, 5, 8, 0, 0)); // 5 Oct 2025 08:00 UTC
const events = [];
for (let i=0;i<8;i++){
const start = new Date(base.getTime() + i*14*24*3600*1000);
const end = new Date(start.getTime() + 3*3600*1000);
events.push({ id:`ss-winter-${i+1}`, kind:"Americano", name:`SuperSunday — Winter Edition #${i+1}`, date:start.toISOString().slice(0,19), end:end.toISOString().slice(0,19), location:"Sport City Woluwe" });
}
const now = new Date();
events.push(
{ id:"ss-live-now", kind:"Americano", name:"SuperSunday — LIVE (démo)", date: new Date(now.getTime()-30*60*1000).toISOString().slice(0,19), end: new Date(now.getTime()+2.5*3600*1000).toISOString().slice(0,19), location:"TC Églantiers" },
{ id:"ss-futur-1", kind:"Americano", name:"SuperSunday — Inscription ouverte", date: new Date(now.getTime()+10*24*3600*1000).toISOString().slice(0,19), end: new Date(now.getTime()+10*24*3600*1000+3*3600*1000).toISOString().slice(0,19), location:"Sport City Woluwe" }
);
const names = ["SmashQueen","PadelKing42","MrVibora","LaBandeja","MissChiquita","VoléeMagique"];
const regs = names.map((n,i)=>({
id:`reg_seed_${i+1}`, createdAt:new Date().toISOString(), eventId: i%2===0? "ss-live-now" : "ss-winter-1",
player:{ name:n, email:`${n.replace(/[^a-z0-9]/ig,'').toLowerCase()}@demo.local`, phone:`+3247${(1000000+i).toString().padStart(7,'0')}`, level: i%3===0? "Avancé": (i%3===1? "Intermédiaire":"Débutant") },
payment:i%3===0? "card": (i%3===1? "paypal":"onspot"), status: i%2===0? "confirmé":"en_attente", paymentStatus: i%2===0? "paid":"pending"
}));
await writeFile(DB_PATH, JSON.stringify({
config:{ siteName:"Padel24Play — V1 (8080)", currency:"EUR", priceEur:48, refundWindowHours:48 },
users:[
{ email:"admin@supersunday.com", password:"password123", role:"admin", name:"Admin"},
{ email:"player@supersunday.com", password:"password123", role:"user", name:"Player"}
],
events, registrations: regs
}, null, 2));
}
}
async function loadDb(){ await ensureDb(); return JSON.parse(await readFile(DB_PATH,'utf-8')); }
async function saveDb(d){ await writeFile(DB_PATH, JSON.stringify(d,null,2)); }
function sendJson(res, obj){ send(res, 200, JSON.stringify(obj), {'content-type':'application/json'}); }
async function parseBody(req){
return new Promise((resolve,reject)=>{
let data=''; req.on('data',c=> data+=c); req.on('end', ()=>{
try{
const ct = req.headers['content-type']||'';
if (!data) return resolve({});
if (ct.includes('application/json')) return resolve(JSON.parse(data));
if (ct.includes('application/x-www-form-urlencoded')){
const out={}; data.split('&').forEach(kv=>{ const [k,v] = kv.split('='); out[decodeURIComponent(k)] = decodeURIComponent((v||'').replace(/\\+/g,' ')); });
return resolve(out);
}
resolve({ raw:data });
}catch(e){ reject(e); }
});
});
}
async function serveStatic(req,res){
const publicDir = path.join(__dirname, 'public');
let pathname = url.parse(req.url).pathname || '/';
if (pathname === '/') pathname = '/index.html';
const filePath = path.join(publicDir, pathname);
try {
const st = await stat(filePath);
if (!st.isDirectory()){
const ext = path.extname(filePath).toLowerCase();
res.writeHead(200, {'content-type': mime[ext] || 'application/octet-stream'});
createReadStream(filePath).pipe(res);
return true;
}
}catch{}
return false;
}
function isoToV(s){ return s.replace(/[-:]/g,'').replace('T','')+'Z'; }
async function handleApi(req,res){
const { pathname, query } = url.parse(req.url, true);
if (pathname === '/api/config' && req.method==='GET'){
const d = await loadDb(); return sendJson(res, d.config);
}
if (pathname === '/api/events' && req.method==='GET'){
const d = await loadDb(); return sendJson(res, d.events);
}
if (pathname === '/api/registrations' && req.method==='GET'){
const d = await loadDb(); const list = query.eventId ? d.registrations.filter(r=> r.eventId===query.eventId) : d.registrations;
return sendJson(res, list);
}
if (pathname === '/api/registrations' && req.method==='POST'){
const d = await loadDb(); const body = await parseBody(req);
const id = 'reg_'+Math.random().toString(36).slice(2,10);
const rec = { id, createdAt:new Date().toISOString(), status:'en_attente', paymentStatus:'pending', ...body };
d.registrations.push(rec); await saveDb(d);
return sendJson(res, rec);
}
if (pathname.startsWith('/api/ical/') && req.method==='GET'){
const regId = pathname.split('/').pop();
const d = await loadDb(); const reg = d.registrations.find(r=> r.id===regId);
if (!reg) return send(res,404,'Not found');
const ev = d.events.find(e=> e.id===reg.eventId);
if (!ev) return send(res,404,'Event not found');
const ics = [
'BEGIN:VCALENDAR','VERSION:2.0','PRODID:-//P24P//SuperSunday//FR',
'BEGIN:VEVENT',
`UID:${reg.id}@p24p`,
`DTSTAMP:${isoToV(ev.date)}`,
`DTSTART:${isoToV(ev.date)}`,
`DTEND:${isoToV(ev.end)}`,
`SUMMARY:${ev.name}`,
`LOCATION:${ev.location}`,
`DESCRIPTION:Inscription ${reg.player?.name||''}`,
'END:VEVENT','END:VCALENDAR'
].join('\\r\\n');
return send(res,200,ics,{'content-type':'text/calendar; charset=utf-8','content-disposition':`attachment; filename="${reg.id}.ics"`});
}
return false;
}
const server = http.createServer(async (req,res)=>{
try{
if (req.url.startsWith('/api/')){
const ok = await handleApi(req,res);
if (ok===false) return send(res,404,'API not found');
return;
}
const ok = await serveStatic(req,res);
if (!ok) send(res,404,'Not found');
}catch(e){ console.error(e); send(res,500,'Server error'); }
});
server.listen(PORT, ()=> console.log(`✅ SuperSunday V1 (prod) http://localhost:${PORT}`));