Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d5fb1404e3 | |||
| c0b33c2144 | |||
| d192a02cc5 | |||
| 558544e461 | |||
| 099908c303 |
@@ -1,5 +0,0 @@
|
|||||||
node_modules/
|
|
||||||
.env
|
|
||||||
*.log
|
|
||||||
npm-debug.log*
|
|
||||||
.DS_Store
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
# CHANGELOG
|
|
||||||
|
|
||||||
## 1.0.0 (2026-05-24)
|
|
||||||
|
|
||||||
- Eerste commit — Express API voor Padel Planner
|
|
||||||
- SQLite database met players, matches, scores
|
|
||||||
- REST endpoints: /players, /matches, /scores
|
|
||||||
- PIN authenticatie voor spelers
|
|
||||||
@@ -32,8 +32,10 @@ function migrateSchema() {
|
|||||||
hours INTEGER NOT NULL DEFAULT 0,
|
hours INTEGER NOT NULL DEFAULT 0,
|
||||||
wins INTEGER NOT NULL DEFAULT 0,
|
wins INTEGER NOT NULL DEFAULT 0,
|
||||||
games INTEGER NOT NULL DEFAULT 0,
|
games INTEGER NOT NULL DEFAULT 0,
|
||||||
avail_mode TEXT NOT NULL DEFAULT 'flex',
|
avail_mode TEXT NOT NULL DEFAULT 'flex',
|
||||||
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
availability_temp TEXT,
|
||||||
|
rejected_slots TEXT,
|
||||||
|
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS player_availability (
|
CREATE TABLE IF NOT EXISTS player_availability (
|
||||||
@@ -75,6 +77,9 @@ function migrateSchema() {
|
|||||||
CREATE INDEX IF NOT EXISTS idx_matchp_match ON match_players(match_id);
|
CREATE INDEX IF NOT EXISTS idx_matchp_match ON match_players(match_id);
|
||||||
CREATE INDEX IF NOT EXISTS idx_matchp_player ON match_players(player_id);
|
CREATE INDEX IF NOT EXISTS idx_matchp_player ON match_players(player_id);
|
||||||
|
|
||||||
|
-- Migration: match type kolom
|
||||||
|
ALTER TABLE matches ADD COLUMN match_type TEXT NOT NULL DEFAULT 'friendly';
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS sessions (
|
CREATE TABLE IF NOT EXISTS sessions (
|
||||||
id TEXT PRIMARY KEY,
|
id TEXT PRIMARY KEY,
|
||||||
player_id TEXT REFERENCES players(id) ON DELETE SET NULL,
|
player_id TEXT REFERENCES players(id) ON DELETE SET NULL,
|
||||||
@@ -96,6 +101,12 @@ function migrateSchema() {
|
|||||||
PRIMARY KEY (player_id, match_id)
|
PRIMARY KEY (player_id, match_id)
|
||||||
);
|
);
|
||||||
`);
|
`);
|
||||||
|
|
||||||
|
// Idempotent column migrations
|
||||||
|
try { db.exec("ALTER TABLE matches ADD COLUMN proposed_teams TEXT"); } catch(e) {}
|
||||||
|
try { db.exec("ALTER TABLE matches ADD COLUMN players_arr TEXT DEFAULT '[]'"); } catch(e) {}
|
||||||
|
try { db.exec("ALTER TABLE players ADD COLUMN availability_temp TEXT"); } catch(e) {}
|
||||||
|
try { db.exec("ALTER TABLE players ADD COLUMN rejected_slots TEXT"); } catch(e) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Players ───
|
// ─── Players ───
|
||||||
@@ -167,13 +178,13 @@ function updatePlayer(id, player) {
|
|||||||
const merged = { ...existing, ...player };
|
const merged = { ...existing, ...player };
|
||||||
db.prepare(`
|
db.prepare(`
|
||||||
UPDATE players SET name=?, level=?, position=?, telegram_id=?, pin=?,
|
UPDATE players SET name=?, level=?, position=?, telegram_id=?, pin=?,
|
||||||
sessions=?, hours=?, wins=?, games=?, avail_mode=?, availability_temp=?
|
sessions=?, hours=?, wins=?, games=?, avail_mode=?, availability_temp=?, rejected_slots=?
|
||||||
WHERE id=?
|
WHERE id=?
|
||||||
`).run(
|
`).run(
|
||||||
merged.name, merged.level, merged.position,
|
merged.name, merged.level, merged.position,
|
||||||
String(merged.telegram_id || ''), String(merged.pin || ''),
|
String(merged.telegram_id || ''), String(merged.pin || ''),
|
||||||
merged.sessions, merged.hours, merged.wins, merged.games,
|
merged.sessions, merged.hours, merged.wins, merged.games,
|
||||||
merged.avail_mode, merged.availability_temp || null, id
|
merged.avail_mode, merged.availability_temp || null, merged.rejected_slots || null, id
|
||||||
);
|
);
|
||||||
|
|
||||||
if (player.availability !== undefined) {
|
if (player.availability !== undefined) {
|
||||||
@@ -249,8 +260,8 @@ function addMatch(match) {
|
|||||||
const db = getDb();
|
const db = getDb();
|
||||||
const id = match.id || String(Date.now());
|
const id = match.id || String(Date.now());
|
||||||
const stmt = db.prepare(`
|
const stmt = db.prepare(`
|
||||||
INSERT INTO matches (id, status, responses, proposed_at, date, start, end, location, day, score, booker_id, booker_name)
|
INSERT INTO matches (id, status, responses, proposed_at, date, start, end, location, day, score, booker_id, booker_name, match_type)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
`);
|
`);
|
||||||
stmt.run(id, match.status || 'proposed',
|
stmt.run(id, match.status || 'proposed',
|
||||||
JSON.stringify(match.responses || {}),
|
JSON.stringify(match.responses || {}),
|
||||||
@@ -260,7 +271,8 @@ function addMatch(match) {
|
|||||||
match.day || null,
|
match.day || null,
|
||||||
typeof match.score === 'object' ? JSON.stringify(match.score) : (match.score || null),
|
typeof match.score === 'object' ? JSON.stringify(match.score) : (match.score || null),
|
||||||
match.booker_id || null,
|
match.booker_id || null,
|
||||||
match.booker_name || null);
|
match.booker_name || null,
|
||||||
|
match.match_type || 'friendly');
|
||||||
|
|
||||||
if (Array.isArray(match.players)) {
|
if (Array.isArray(match.players)) {
|
||||||
const pstmt = db.prepare(`INSERT INTO match_players (match_id, player_id, team, score) VALUES (?, ?, ?, ?)`);
|
const pstmt = db.prepare(`INSERT INTO match_players (match_id, player_id, team, score) VALUES (?, ?, ?, ?)`);
|
||||||
@@ -286,11 +298,11 @@ function updateMatch(id, match) {
|
|||||||
// Build SET dynamically to only update provided fields
|
// Build SET dynamically to only update provided fields
|
||||||
const fields = [];
|
const fields = [];
|
||||||
const values = [];
|
const values = [];
|
||||||
for (const key of ['status', 'responses', 'proposed_at', 'date', 'start', 'end', 'location', 'day', 'score', 'booker_id', 'booker_name']) {
|
for (const key of ['status', 'responses', 'proposed_at', 'date', 'start', 'end', 'location', 'day', 'score', 'booker_id', 'booker_name', 'proposed_teams', 'match_type']) {
|
||||||
if (match[key] !== undefined) {
|
if (match[key] !== undefined) {
|
||||||
fields.push(`${key}=?`);
|
fields.push(`${key}=?`);
|
||||||
let val = merged[key];
|
let val = merged[key];
|
||||||
if (key === 'responses' || (key === 'score' && typeof val === 'object' && val !== null)) {
|
if (key === 'responses' || key === 'score' || key === 'proposed_teams') {
|
||||||
val = JSON.stringify(val);
|
val = JSON.stringify(val);
|
||||||
}
|
}
|
||||||
values.push(key === 'booker_id' || key === 'booker_name' ? String(val) : val);
|
values.push(key === 'booker_id' || key === 'booker_name' ? String(val) : val);
|
||||||
@@ -446,6 +458,11 @@ function rowToMatch(r) {
|
|||||||
try { score = JSON.parse(r.score); } catch (e) { /* ignore */ }
|
try { score = JSON.parse(r.score); } catch (e) { /* ignore */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let proposed_teams = null;
|
||||||
|
if (r.proposed_teams) {
|
||||||
|
try { proposed_teams = JSON.parse(r.proposed_teams); } catch (e) { /* ignore */ }
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: r.id,
|
id: r.id,
|
||||||
status: r.status,
|
status: r.status,
|
||||||
@@ -457,6 +474,8 @@ function rowToMatch(r) {
|
|||||||
location: r.location,
|
location: r.location,
|
||||||
day: r.day,
|
day: r.day,
|
||||||
score: score,
|
score: score,
|
||||||
|
proposed_teams: proposed_teams,
|
||||||
|
match_type: r.match_type || 'friendly',
|
||||||
booker_id: r.booker_id || null,
|
booker_id: r.booker_id || null,
|
||||||
booker_name: r.booker_name || null,
|
booker_name: r.booker_name || null,
|
||||||
players: players
|
players: players
|
||||||
|
|||||||
-103
@@ -1,103 +0,0 @@
|
|||||||
// migrate.js — Migreer data.json naar SQLite
|
|
||||||
const path = require('path');
|
|
||||||
const fs = require('fs');
|
|
||||||
|
|
||||||
// Laad de db.js module — we moeten even opletten dat we in de juiste dir staan
|
|
||||||
process.env.DB_PATH = '/data/data.db';
|
|
||||||
|
|
||||||
// We importeren db.js met require, maar omdat we het nog niet in de container hebben
|
|
||||||
// zetten we de module in de juiste dir
|
|
||||||
console.log('📦 Data migratie: JSON → SQLite');
|
|
||||||
console.log('────────────────────────────────\n');
|
|
||||||
|
|
||||||
const DATA_PATH = process.env.DATA_PATH || '/data/data.json';
|
|
||||||
|
|
||||||
if (!fs.existsSync(DATA_PATH)) {
|
|
||||||
console.log('❌ Geen data.json gevonden op', DATA_PATH);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = JSON.parse(fs.readFileSync(DATA_PATH, 'utf-8'));
|
|
||||||
console.log(`📖 Ingelezen: ${data.players ? data.players.length : 0} spelers, ${data.matches ? data.matches.length : 0} matches, ${data.settings ? Object.keys(data.settings).length : 0} settings`);
|
|
||||||
|
|
||||||
// Laad db.js — gebruik een relatief pad zodat we ook via node /app/api/migrate.js kunnen draaien
|
|
||||||
const db = require('./db.cjs');
|
|
||||||
|
|
||||||
// ─── Players migreren ───
|
|
||||||
if (Array.isArray(data.players)) {
|
|
||||||
console.log(`\n👥 Spelers (${data.players.length}):`);
|
|
||||||
for (const p of data.players) {
|
|
||||||
try {
|
|
||||||
const existing = db.getPlayerByTelegram(p.telegram_id);
|
|
||||||
if (existing) {
|
|
||||||
console.log(` ⏭️ ${p.name} — bestaat al (telegram_id ${p.telegram_id})`);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
db.addPlayer(p);
|
|
||||||
console.log(` ✅ ${p.name} — level ${p.level}, ${p.position} (PIN: ${p.pin})`);
|
|
||||||
} catch (e) {
|
|
||||||
console.log(` ❌ ${p.name} — FOUT: ${e.message}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Matches migreren ───
|
|
||||||
if (Array.isArray(data.matches)) {
|
|
||||||
console.log(`\n🎾 Matches (${data.matches.length}):`);
|
|
||||||
for (const m of data.matches) {
|
|
||||||
try {
|
|
||||||
// Check of match al bestaat
|
|
||||||
const existing = db.getMatch(m.id);
|
|
||||||
if (existing) {
|
|
||||||
console.log(` ⏭️ Match ${m.id} — bestaat al`);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
db.addMatch(m);
|
|
||||||
console.log(` ✅ Match ${m.id} — status: ${m.status}, spelers: ${Array.isArray(m.players) ? m.players.length : 0}`);
|
|
||||||
} catch (e) {
|
|
||||||
console.log(` ❌ Match ${m.id} — FOUT: ${e.message}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Settings migreren ───
|
|
||||||
if (data.settings && typeof data.settings === 'object') {
|
|
||||||
console.log(`\n⚙️ Settings (${Object.keys(data.settings).length}):`);
|
|
||||||
for (const [key, value] of Object.entries(data.settings)) {
|
|
||||||
db.setSetting(key, value);
|
|
||||||
console.log(` ✅ ${key} = ${value}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Sessions migreren ───
|
|
||||||
if (Array.isArray(data.sessions)) {
|
|
||||||
console.log(`\n🔑 Sessions (${data.sessions.length}):`);
|
|
||||||
for (const s of data.sessions) {
|
|
||||||
try {
|
|
||||||
db.setSession(s.id, s.player_id || s.playerId, s.data || {});
|
|
||||||
console.log(` ✅ Session ${s.id}`);
|
|
||||||
} catch (e) {
|
|
||||||
console.log(` ❌ Session ${s.id} — FOUT: ${e.message}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Verificatie ───
|
|
||||||
console.log('\n📊 Verificatie:');
|
|
||||||
const players = db.getPlayers();
|
|
||||||
const matches = db.getMatches();
|
|
||||||
console.log(` 👥 Spelers in DB: ${players.length}`);
|
|
||||||
console.log(` 🎾 Matches in DB: ${matches.length}`);
|
|
||||||
console.log(` ⚙️ Settings in DB:`, JSON.stringify(db.getSettings()));
|
|
||||||
|
|
||||||
// Toon spelers
|
|
||||||
for (const p of players) {
|
|
||||||
const days = p.availability && !p.availability.isSet
|
|
||||||
? Object.keys(p.availability).filter(k => k !== 'isSet' && k !== 'days').length
|
|
||||||
: 0;
|
|
||||||
console.log(` 📍 ${p.name}: level ${p.level}, ${days} beschikbare dagen`);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('\n✅ Migratie voltooid!');
|
|
||||||
console.log(`📁 data.json: ${DATA_PATH}`);
|
|
||||||
console.log(`🗄️ SQLite: ${process.env.DB_PATH}`);
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "padel-planner",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"type": "module",
|
|
||||||
"scripts": {
|
|
||||||
"start": "node api/server.js"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"better-sqlite3": "^12.10.0",
|
|
||||||
"express": "^4.21.0",
|
|
||||||
"playwright": "^1.60.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
// Peakz Padel beschikbaarheid scraper
|
|
||||||
// Aanroep: node padel-peakz.mjs [datum] [locatie]
|
|
||||||
// Voorbeeld: node padel-peakz.mjs 2026-05-24 Atoomweg
|
|
||||||
|
|
||||||
const { chromium } = require('playwright');
|
|
||||||
|
|
||||||
const LOCATIONS = {
|
|
||||||
'Atoomweg': '6637dd8e-cd4b-4fee-af49-196f6828b7dc',
|
|
||||||
'Groningen': '6637dd8e-cd4b-4fee-af49-196f6828b7dc',
|
|
||||||
};
|
|
||||||
const RESERVATION_TYPE = '6';
|
|
||||||
const PLAYING_TIME = '90';
|
|
||||||
|
|
||||||
async function getAvailability(dateStr, locationName) {
|
|
||||||
const locationId = LOCATIONS[locationName] || LOCATIONS['Atoomweg'];
|
|
||||||
const browser = await chromium.launch({ headless: true });
|
|
||||||
const context = await browser.newContext({
|
|
||||||
userAgent: 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36'
|
|
||||||
});
|
|
||||||
const page = await context.newPage();
|
|
||||||
|
|
||||||
try {
|
|
||||||
const url = `https://www.peakzpadel.nl/reserveren/court-booking/reservation?daypart=---&date=${dateStr}&location=${encodeURIComponent(locationName)}&playingTimes=${PLAYING_TIME}&courtTypeIds=13`;
|
|
||||||
await page.goto(url, { waitUntil: 'networkidle', timeout: 30000 });
|
|
||||||
await page.waitForTimeout(3000);
|
|
||||||
|
|
||||||
const slots = await page.evaluate(() => {
|
|
||||||
const buttons = document.querySelectorAll('button');
|
|
||||||
const results = [];
|
|
||||||
for (const btn of buttons) {
|
|
||||||
const text = btn.textContent.trim();
|
|
||||||
if (text.match(/^\d{2}:\d{2}/)) {
|
|
||||||
const price = text.match(/€\s*([\d,]+)/);
|
|
||||||
results.push({
|
|
||||||
time: text.split(' ')[0],
|
|
||||||
price: price ? price[1] : null,
|
|
||||||
available: !btn.disabled
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
date: dateStr,
|
|
||||||
location: locationName,
|
|
||||||
slots,
|
|
||||||
available: slots.filter(s => s.available),
|
|
||||||
unavailable: slots.filter(s => !s.available)
|
|
||||||
};
|
|
||||||
} catch (err) {
|
|
||||||
return { error: err.message };
|
|
||||||
} finally {
|
|
||||||
await browser.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// CLI mode
|
|
||||||
const dateArg = process.argv[2] || new Date().toISOString().split('T')[0];
|
|
||||||
const locationArg = process.argv[3] || 'Atoomweg';
|
|
||||||
|
|
||||||
getAvailability(dateArg, locationArg).then(result => {
|
|
||||||
if (result.error) {
|
|
||||||
console.error('Fout:', result.error);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output als JSON voor machine-readable
|
|
||||||
if (process.argv.includes('--json')) {
|
|
||||||
console.log(JSON.stringify(result));
|
|
||||||
process.exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Human readable
|
|
||||||
const dayNames = ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'];
|
|
||||||
const d = new Date(result.date);
|
|
||||||
const dayLabel = dayNames[d.getDay()];
|
|
||||||
const dateLabel = `${dayLabel} ${d.getDate()}/${d.getMonth()+1}`;
|
|
||||||
|
|
||||||
console.log(`\n🏓 Peakz Padel — ${result.location}`);
|
|
||||||
console.log(`📅 ${dateLabel}`);
|
|
||||||
console.log(`🟢 ${result.available.length} banen vrij`);
|
|
||||||
console.log(`🔴 ${result.unavailable.length} banen bezet`);
|
|
||||||
console.log('');
|
|
||||||
|
|
||||||
if (result.available.length > 0) {
|
|
||||||
console.log('Vrije tijden:');
|
|
||||||
for (const s of result.available) {
|
|
||||||
const price = s.price ? ` — €${s.price}` : '';
|
|
||||||
console.log(` 🟢 ${s.time}${price}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('');
|
|
||||||
process.exit(0);
|
|
||||||
});
|
|
||||||
@@ -11,7 +11,7 @@ const PORT = 3000;
|
|||||||
|
|
||||||
// Load SQLite db layer (CommonJS module via createRequire)
|
// Load SQLite db layer (CommonJS module via createRequire)
|
||||||
const require = createRequire(import.meta.url);
|
const require = createRequire(import.meta.url);
|
||||||
const db = require("./db/db.cjs");
|
const db = require("./db.cjs");
|
||||||
|
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
app.use(express.static(path.join(__dirname, "..", "public")));
|
app.use(express.static(path.join(__dirname, "..", "public")));
|
||||||
@@ -363,6 +363,42 @@ app.delete("/api/matches/:id", (req, res) => {
|
|||||||
res.json({ ok: true });
|
res.json({ ok: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ─── TEAM INDELING (voorgesteld) ───
|
||||||
|
app.post("/api/matches/:id/teams", (req, res) => {
|
||||||
|
let m = db.getMatch(String(req.params.id));
|
||||||
|
if (!m) return res.status(404).json({ error: "match not found" });
|
||||||
|
const { team1, team2 } = req.body;
|
||||||
|
if (!Array.isArray(team1) || !Array.isArray(team2))
|
||||||
|
return res.status(400).json({ error: "team1 and team2 must be arrays" });
|
||||||
|
const allIds = [...team1, ...team2];
|
||||||
|
if (allIds.length !== 4)
|
||||||
|
return res.status(400).json({ error: "expected 4 players total" });
|
||||||
|
db.updateMatch(String(req.params.id), { proposed_teams: { team1, team2 } });
|
||||||
|
m = db.getMatch(String(req.params.id));
|
||||||
|
const playerIds = (m.players || []).map(p => p.id);
|
||||||
|
res.json({ ...m, players: playerIds, players_array: m.players });
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/api/matches/:id/teams", (req, res) => {
|
||||||
|
const m = db.getMatch(String(req.params.id));
|
||||||
|
if (!m) return res.status(404).json({ error: "match not found" });
|
||||||
|
const teams = m.proposed_teams || null;
|
||||||
|
res.json({ teams, match: { id: m.id, date: m.date, start: m.start, status: m.status, match_type: m.match_type || 'friendly' } });
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── MATCH TYPE (ranked/friendly) ───
|
||||||
|
app.post("/api/matches/:id/type", (req, res) => {
|
||||||
|
let m = db.getMatch(String(req.params.id));
|
||||||
|
if (!m) return res.status(404).json({ error: "match not found" });
|
||||||
|
const { match_type } = req.body;
|
||||||
|
if (match_type !== 'ranked' && match_type !== 'friendly')
|
||||||
|
return res.status(400).json({ error: "match_type must be 'ranked' or 'friendly'" });
|
||||||
|
db.updateMatch(String(req.params.id), { match_type });
|
||||||
|
m = db.getMatch(String(req.params.id));
|
||||||
|
const playerIds = (m.players || []).map(p => p.id);
|
||||||
|
res.json({ ...m, players: playerIds, players_array: m.players });
|
||||||
|
});
|
||||||
|
|
||||||
// ─── SCORES ───
|
// ─── SCORES ───
|
||||||
app.post("/api/matches/:id/score", (req, res) => {
|
app.post("/api/matches/:id/score", (req, res) => {
|
||||||
let m = db.getMatch(String(req.params.id));
|
let m = db.getMatch(String(req.params.id));
|
||||||
@@ -403,22 +439,25 @@ app.post("/api/matches/:id/verify", (req, res) => {
|
|||||||
score.confirmed_at = new Date().toISOString();
|
score.confirmed_at = new Date().toISOString();
|
||||||
|
|
||||||
// Update player wins/losses (idempotent via player_match_stats tabel)
|
// Update player wins/losses (idempotent via player_match_stats tabel)
|
||||||
for (const pid of score.team1) {
|
// Alleen voor ranked matches
|
||||||
const p = db.getPlayer(String(pid));
|
if (m.match_type === 'ranked') {
|
||||||
if (p && !db.hasMatchStat(String(pid), m.id)) {
|
for (const pid of score.team1) {
|
||||||
db.updatePlayer(String(pid), { wins: (p.wins || 0) + 1, games: (p.games || 0) + 1 });
|
const p = db.getPlayer(String(pid));
|
||||||
db.addMatchStat(String(pid), m.id);
|
if (p && !db.hasMatchStat(String(pid), m.id)) {
|
||||||
|
db.updatePlayer(String(pid), { wins: (p.wins || 0) + 1, games: (p.games || 0) + 1 });
|
||||||
|
db.addMatchStat(String(pid), m.id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
for (const pid of score.team2) {
|
||||||
for (const pid of score.team2) {
|
const p = db.getPlayer(String(pid));
|
||||||
const p = db.getPlayer(String(pid));
|
if (p && !db.hasMatchStat(String(pid), m.id)) {
|
||||||
if (p && !db.hasMatchStat(String(pid), m.id)) {
|
db.updatePlayer(String(pid), { games: (p.games || 0) + 1 });
|
||||||
db.updatePlayer(String(pid), { games: (p.games || 0) + 1 });
|
db.addMatchStat(String(pid), m.id);
|
||||||
db.addMatchStat(String(pid), m.id);
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
score.status = hasOpponentConfirm ? "confirmed" : "partial";
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
score.status = hasOpponentConfirm ? "confirmed" : "partial";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
db.updateMatch(String(req.params.id), { score });
|
db.updateMatch(String(req.params.id), { score });
|
||||||
@@ -522,10 +561,6 @@ app.listen(PORT, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// Graceful shutdown
|
|
||||||
process.on("SIGTERM", () => { console.log("Shutting down..."); db.close(); process.exit(0); });
|
|
||||||
process.on("SIGINT", () => { console.log("Shutting down..."); db.close(); process.exit(0); });
|
|
||||||
|
|
||||||
// Graceful shutdown
|
// Graceful shutdown
|
||||||
const closeSignal = (sig) => {
|
const closeSignal = (sig) => {
|
||||||
console.log("Shutting down (" + sig + ")...");
|
console.log("Shutting down (" + sig + ")...");
|
||||||
|
|||||||
Reference in New Issue
Block a user