147 lines
6.7 KiB
JavaScript
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}`));
|