use prettier formatting

This commit is contained in:
2026-03-13 17:07:06 +01:00
parent 6a0bb9061d
commit d597c6545e
45 changed files with 1909 additions and 861 deletions

View File

@@ -25,10 +25,19 @@ const OSU_CLIENT_ID = process.env.OSU_CLIENT_ID!;
const OSU_CLIENT_SECRET = process.env.OSU_CLIENT_SECRET!;
for (const [k, v] of Object.entries({
R2_ACCOUNT_ID, R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY,
R2_BUCKET, R2_PUBLIC_URL, DATABASE_URL, OSU_CLIENT_ID, OSU_CLIENT_SECRET
R2_ACCOUNT_ID,
R2_ACCESS_KEY_ID,
R2_SECRET_ACCESS_KEY,
R2_BUCKET,
R2_PUBLIC_URL,
DATABASE_URL,
OSU_CLIENT_ID,
OSU_CLIENT_SECRET
})) {
if (!v) { console.error(`Missing env var: ${k}`); process.exit(1); }
if (!v) {
console.error(`Missing env var: ${k}`);
process.exit(1);
}
}
// ── R2 ─────────────────────────────────────────────────────────────────
@@ -76,7 +85,9 @@ function isOsuUrl(url: string | null): boolean {
}
}
function sleep(ms: number) { return new Promise(r => setTimeout(r, ms)); }
function sleep(ms: number) {
return new Promise((r) => setTimeout(r, ms));
}
// ── osu! API ───────────────────────────────────────────────────────────
@@ -96,7 +107,7 @@ async function getOsuToken(): Promise<string> {
})
});
if (!res.ok) throw new Error(`osu! token failed: ${res.status}`);
const data = await res.json() as { access_token: string; expires_in: number };
const data = (await res.json()) as { access_token: string; expires_in: number };
osuToken = data.access_token;
osuTokenExpiry = Date.now() + data.expires_in * 1000;
return osuToken;
@@ -121,7 +132,7 @@ async function getBeatmap(beatmapId: string): Promise<BeatmapData | null> {
headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' }
});
if (!res.ok) return null;
return await res.json() as BeatmapData;
return (await res.json()) as BeatmapData;
} catch {
return null;
}
@@ -137,14 +148,17 @@ const sql = new SQL(DATABASE_URL);
console.log('═══ 1/3: Backfilling mappool slot metadata + covers ═══\n');
const slots = await sql<{ id: string; beatmap_id: string; cover_url: string | null; title: string | null }[]>`
const slots = await sql<
{ id: string; beatmap_id: string; cover_url: string | null; title: string | null }[]
>`
SELECT id, beatmap_id, cover_url, title FROM mappool_slot
WHERE cover_url IS NULL OR title IS NULL
`;
console.log(`Found ${slots.length} slots missing metadata\n`);
let slotOk = 0, slotFail = 0;
let slotOk = 0,
slotFail = 0;
for (const slot of slots) {
process.stdout.write(` beatmap ${slot.beatmap_id} ... `);
const bm = await getBeatmap(slot.beatmap_id);
@@ -181,7 +195,9 @@ for (const slot of slots) {
console.log(`\nSlots: ${slotOk} updated, ${slotFail} failed\n`);
// Also update any slots that have osu! URLs instead of R2 URLs (partially migrated)
const osuUrlSlots = await sql<{ id: string; cover_url: string | null; list_cover_url: string | null }[]>`
const osuUrlSlots = await sql<
{ id: string; cover_url: string | null; list_cover_url: string | null }[]
>`
SELECT id, cover_url, list_cover_url FROM mappool_slot
WHERE (cover_url LIKE '%ppy.sh%' OR list_cover_url LIKE '%ppy.sh%')
`;
@@ -189,8 +205,12 @@ const osuUrlSlots = await sql<{ id: string; cover_url: string | null; list_cover
if (osuUrlSlots.length > 0) {
console.log(`Found ${osuUrlSlots.length} slots with osu! URLs (need R2 proxy)\n`);
for (const slot of osuUrlSlots) {
const newCover = slot.cover_url && isOsuUrl(slot.cover_url) ? await proxyToR2(slot.cover_url) : slot.cover_url;
const newList = slot.list_cover_url && isOsuUrl(slot.list_cover_url) ? await proxyToR2(slot.list_cover_url) : slot.list_cover_url;
const newCover =
slot.cover_url && isOsuUrl(slot.cover_url) ? await proxyToR2(slot.cover_url) : slot.cover_url;
const newList =
slot.list_cover_url && isOsuUrl(slot.list_cover_url)
? await proxyToR2(slot.list_cover_url)
: slot.list_cover_url;
await sql`UPDATE mappool_slot SET cover_url = ${newCover}, list_cover_url = ${newList} WHERE id = ${slot.id}`;
}
console.log('Done re-proxying slot covers\n');
@@ -206,7 +226,8 @@ const users = await sql<{ id: string; image: string | null }[]>`
SELECT id, image FROM "user" WHERE image IS NOT NULL
`;
let avatarOk = 0, avatarSkip = 0;
let avatarOk = 0,
avatarSkip = 0;
for (const u of users) {
if (!isOsuUrl(u.image)) {
avatarSkip++;
@@ -235,7 +256,8 @@ const teams = await sql<{ id: string; avatar_url: string | null }[]>`
SELECT id, avatar_url FROM team WHERE avatar_url IS NOT NULL
`;
let teamOk = 0, teamSkip = 0;
let teamOk = 0,
teamSkip = 0;
for (const t of teams) {
if (!isOsuUrl(t.avatar_url)) {
teamSkip++;

View File

@@ -25,8 +25,7 @@ async function count(query: string): Promise<number> {
async function exec(description: string, deleteSql: string) {
// Count first
const countSql = deleteSql
.replace(/^DELETE FROM/, 'SELECT COUNT(*) as count FROM');
const countSql = deleteSql.replace(/^DELETE FROM/, 'SELECT COUNT(*) as count FROM');
const n = await count(countSql);
if (n === 0) {
@@ -53,9 +52,7 @@ async function main() {
const realUsers = await count(
`SELECT COUNT(*) as count FROM "user" WHERE id NOT LIKE 'seed-user-%'`
);
const seedUsers = await count(
`SELECT COUNT(*) as count FROM "user" WHERE id LIKE 'seed-user-%'`
);
const seedUsers = await count(`SELECT COUNT(*) as count FROM "user" WHERE id LIKE 'seed-user-%'`);
console.log(` Real users: ${realUsers}`);
console.log(` Seed users: ${seedUsers}\n`);
@@ -168,37 +165,23 @@ async function main() {
// 13. Teams
console.log('Phase 13: Teams');
await exec(
'team (owned by seed users)',
`DELETE FROM team WHERE owner_id LIKE 'seed-user-%'`
);
await exec('team (owned by seed users)', `DELETE FROM team WHERE owner_id LIKE 'seed-user-%'`);
// 14. Sessions (cascade from user delete, but clean up explicitly)
console.log('Phase 14: Sessions');
await exec(
'session (from seed users)',
`DELETE FROM session WHERE user_id LIKE 'seed-user-%'`
);
await exec('session (from seed users)', `DELETE FROM session WHERE user_id LIKE 'seed-user-%'`);
// 15. Accounts
console.log('Phase 15: Accounts');
await exec(
'account (from seed users)',
`DELETE FROM account WHERE user_id LIKE 'seed-user-%'`
);
await exec('account (from seed users)', `DELETE FROM account WHERE user_id LIKE 'seed-user-%'`);
// 16. Users (the root)
console.log('Phase 16: Users');
await exec(
'user (seed users)',
`DELETE FROM "user" WHERE id LIKE 'seed-user-%'`
);
await exec('user (seed users)', `DELETE FROM "user" WHERE id LIKE 'seed-user-%'`);
// ── Verify ──
console.log('\n── Verification ──');
const remaining = await count(
`SELECT COUNT(*) as count FROM "user" WHERE id LIKE 'seed-user-%'`
);
const remaining = await count(`SELECT COUNT(*) as count FROM "user" WHERE id LIKE 'seed-user-%'`);
const remainingMatches = await count(
`SELECT COUNT(*) as count FROM match WHERE created_by LIKE 'seed-user-%'`
);

View File

@@ -38,7 +38,9 @@ async function main() {
}
if (flag === '--full') {
await db.execute(sql`DELETE FROM team_member WHERE team_id IN (SELECT id FROM team WHERE is_personal = false)`);
await db.execute(
sql`DELETE FROM team_member WHERE team_id IN (SELECT id FROM team WHERE is_personal = false)`
);
await db.execute(sql`DELETE FROM team WHERE is_personal = false`);
console.log(' Cleared: non-personal teams');

View File

@@ -42,9 +42,30 @@ const mappoolSlot = pgTable('mappool_slot', {
// Sample beatmap IDs (real osu! maps)
const SAMPLE_MAPS = [
{ id: '75', category: 'NM', title: 'Disco Prince', artist: 'Kenji Ninuma', sr: 2.5, bpm: 120 },
{ id: '163', category: 'NM', title: 'Crazy Noisy Bizarre Town', artist: 'The DU', sr: 3.2, bpm: 140 },
{ id: '250', category: 'HD', title: 'Sakura Kagetsu', artist: 'Mutsuhiko Izumi', sr: 4.1, bpm: 165 },
{ id: '306', category: 'HR', title: 'Airman ga Taosenai', artist: 'Team Nekokan', sr: 5.0, bpm: 200 },
{
id: '163',
category: 'NM',
title: 'Crazy Noisy Bizarre Town',
artist: 'The DU',
sr: 3.2,
bpm: 140
},
{
id: '250',
category: 'HD',
title: 'Sakura Kagetsu',
artist: 'Mutsuhiko Izumi',
sr: 4.1,
bpm: 165
},
{
id: '306',
category: 'HR',
title: 'Airman ga Taosenai',
artist: 'Team Nekokan',
sr: 5.0,
bpm: 200
},
{ id: '390', category: 'DT', title: 'Kira Kira Days', artist: 'MOSAIC.WAV', sr: 4.5, bpm: 175 },
{ id: '455', category: 'FM', title: 'FREEDOM DiVE', artist: 'xi', sr: 6.0, bpm: 222 },
{ id: '500', category: 'TB', title: 'Blue Zenith', artist: 'xi', sr: 6.5, bpm: 200 }
@@ -61,10 +82,13 @@ async function main() {
}
// Create a sample mappool
const [pool] = await db.insert(mappool).values({
name: 'Dev Test Pool',
verifiedAt: new Date()
}).returning();
const [pool] = await db
.insert(mappool)
.values({
name: 'Dev Test Pool',
verifiedAt: new Date()
})
.returning();
console.log(`Created mappool: ${pool.name} (${pool.id})`);
@@ -86,7 +110,9 @@ async function main() {
});
}
console.log(` Added ${SAMPLE_MAPS.length} maps (${[...new Set(SAMPLE_MAPS.map((m) => m.category))].join(', ')})`);
console.log(
` Added ${SAMPLE_MAPS.length} maps (${[...new Set(SAMPLE_MAPS.map((m) => m.category))].join(', ')})`
);
console.log('Done. Sign in via osu! OAuth to create a user and personal team.');
process.exit(0);
}

View File

@@ -258,8 +258,8 @@ function rankToElo(rank: number): number {
function generateSyntheticRank(index: number, total: number): number {
const p = index / total;
if (p < 0.05) return randInt(1000, 10000);
if (p < 0.20) return randInt(10000, 50000);
if (p < 0.60) return randInt(50000, 300000);
if (p < 0.2) return randInt(10000, 50000);
if (p < 0.6) return randInt(50000, 300000);
if (p < 0.85) return randInt(300000, 1000000);
return randInt(1000000, 5000000);
}
@@ -286,20 +286,108 @@ function timer() {
// ── Name generators ─────────────────────────────────────────────────────
const PREFIXES = [
'', '', '', 'xx', 'xX', 'ii', 'oO', 'Xx', '-', '_', 'Mr', 'Dr', 'DJ'
];
const PREFIXES = ['', '', '', 'xx', 'xX', 'ii', 'oO', 'Xx', '-', '_', 'Mr', 'Dr', 'DJ'];
const WORDS = [
'Ace', 'Arrow', 'Blaze', 'Bolt', 'Cat', 'Cloud', 'Comet', 'Crow', 'Crystal', 'Cyber',
'Dark', 'Dawn', 'Demon', 'Diamond', 'Dragon', 'Dream', 'Eagle', 'Echo', 'Edge', 'Elite',
'Ember', 'Fang', 'Fire', 'Flash', 'Fox', 'Frost', 'Ghost', 'Gold', 'Hawk', 'Haze',
'Hero', 'Hunt', 'Ice', 'Iron', 'Jade', 'Jet', 'King', 'Knight', 'Leo', 'Light',
'Luna', 'Lynx', 'Mist', 'Moon', 'Neo', 'Night', 'Nova', 'Omega', 'Onyx', 'Phantom',
'Phoenix', 'Pixel', 'Prime', 'Pulse', 'Rage', 'Rain', 'Raven', 'Rex', 'Rise', 'River',
'Rock', 'Ruby', 'Rush', 'Sage', 'Shade', 'Shadow', 'Silver', 'Sky', 'Snap', 'Snow',
'Solar', 'Soul', 'Spark', 'Star', 'Steel', 'Storm', 'Strike', 'Sun', 'Swift', 'Sword',
'Thunder', 'Tiger', 'Titan', 'Turbo', 'Venom', 'Viper', 'Void', 'Wave', 'Wild', 'Wind',
'Wolf', 'Wrath', 'Zen', 'Zero', 'Zeta', 'Zoom', 'Apex', 'Atlas', 'Aura', 'Blade'
'Ace',
'Arrow',
'Blaze',
'Bolt',
'Cat',
'Cloud',
'Comet',
'Crow',
'Crystal',
'Cyber',
'Dark',
'Dawn',
'Demon',
'Diamond',
'Dragon',
'Dream',
'Eagle',
'Echo',
'Edge',
'Elite',
'Ember',
'Fang',
'Fire',
'Flash',
'Fox',
'Frost',
'Ghost',
'Gold',
'Hawk',
'Haze',
'Hero',
'Hunt',
'Ice',
'Iron',
'Jade',
'Jet',
'King',
'Knight',
'Leo',
'Light',
'Luna',
'Lynx',
'Mist',
'Moon',
'Neo',
'Night',
'Nova',
'Omega',
'Onyx',
'Phantom',
'Phoenix',
'Pixel',
'Prime',
'Pulse',
'Rage',
'Rain',
'Raven',
'Rex',
'Rise',
'River',
'Rock',
'Ruby',
'Rush',
'Sage',
'Shade',
'Shadow',
'Silver',
'Sky',
'Snap',
'Snow',
'Solar',
'Soul',
'Spark',
'Star',
'Steel',
'Storm',
'Strike',
'Sun',
'Swift',
'Sword',
'Thunder',
'Tiger',
'Titan',
'Turbo',
'Venom',
'Viper',
'Void',
'Wave',
'Wild',
'Wind',
'Wolf',
'Wrath',
'Zen',
'Zero',
'Zeta',
'Zoom',
'Apex',
'Atlas',
'Aura',
'Blade'
];
const SUFFIXES = ['', '', '', '_', '-', 'HD', 'osu', 'pp', 'x', 'z', '69', '420', 'YT', 'TTV'];
@@ -313,54 +401,210 @@ function generateUsername(index: number): string {
}
const TEAM_NAMES = [
'Team Absolute', 'Velocity Gaming', 'Rising Phoenix', 'Dark Matter', 'Quantum Flux',
'Storm Chasers', 'Neon Knights', 'Solar Flare', 'Arctic Wolves', 'Thunder Strike',
'Crimson Tide', 'Iron Fortress', 'Shadow Realm', 'Crystal Edge', 'Blazing Stars',
'Night Owls', 'Golden Eagles', 'Frost Bite', 'Wild Cards', 'Steel Legion',
'Moon Riders', 'Star Dust', 'Fire Hawks', 'Ice Breakers', 'Wind Walkers',
'Dragon Scale', 'Raven Wing', 'Silver Bullet', 'Ghost Protocol', 'Prime Time',
'Echo Chamber', 'Volt Force', 'Sky Runners', 'Omega Squad', 'Alpha Pack',
'Zero Gravity', 'Dawn Patrol', 'Pixel Perfect', 'Cyber Monks', 'Neo Tokyo',
'Mist Walkers', 'Pulse Wave', 'Zenith', 'Apex Legends', 'Void Runners',
'Soul Reapers', 'Blade Dancers', 'Turbo Charged', 'Snap Dragons', 'Rush Hour',
'Phantom Force', 'Diamond Dogs', 'Cosmic Rays', 'Hyper Beam', 'Sonic Boom',
'Prism', 'Eclipse', 'Nebula', 'Supernova', 'Horizon',
'Catalyst', 'Entropy', 'Vanguard', 'Sentinel', 'Oracle',
'Maelstrom', 'Tempest', 'Cascade', 'Enigma', 'Paradox',
'Mirage', 'Nexus', 'Vertex', 'Cipher', 'Stratos',
'Helix', 'Spectrum', 'Polaris', 'Inferno', 'Tsunami',
'Rampage', 'Havoc', 'Fury', 'Blitz', 'Reign',
'Rapture', 'Genesis', 'Onslaught', 'Vendetta', 'Oblivion',
'Dominion', 'Ascension', 'Redemption', 'Revolution', 'Evolution',
'Dynasty', 'Empire', 'Republic', 'Alliance', 'Syndicate',
'Collective', 'Foundation', 'Initiative', 'Consortium', 'Federation'
'Team Absolute',
'Velocity Gaming',
'Rising Phoenix',
'Dark Matter',
'Quantum Flux',
'Storm Chasers',
'Neon Knights',
'Solar Flare',
'Arctic Wolves',
'Thunder Strike',
'Crimson Tide',
'Iron Fortress',
'Shadow Realm',
'Crystal Edge',
'Blazing Stars',
'Night Owls',
'Golden Eagles',
'Frost Bite',
'Wild Cards',
'Steel Legion',
'Moon Riders',
'Star Dust',
'Fire Hawks',
'Ice Breakers',
'Wind Walkers',
'Dragon Scale',
'Raven Wing',
'Silver Bullet',
'Ghost Protocol',
'Prime Time',
'Echo Chamber',
'Volt Force',
'Sky Runners',
'Omega Squad',
'Alpha Pack',
'Zero Gravity',
'Dawn Patrol',
'Pixel Perfect',
'Cyber Monks',
'Neo Tokyo',
'Mist Walkers',
'Pulse Wave',
'Zenith',
'Apex Legends',
'Void Runners',
'Soul Reapers',
'Blade Dancers',
'Turbo Charged',
'Snap Dragons',
'Rush Hour',
'Phantom Force',
'Diamond Dogs',
'Cosmic Rays',
'Hyper Beam',
'Sonic Boom',
'Prism',
'Eclipse',
'Nebula',
'Supernova',
'Horizon',
'Catalyst',
'Entropy',
'Vanguard',
'Sentinel',
'Oracle',
'Maelstrom',
'Tempest',
'Cascade',
'Enigma',
'Paradox',
'Mirage',
'Nexus',
'Vertex',
'Cipher',
'Stratos',
'Helix',
'Spectrum',
'Polaris',
'Inferno',
'Tsunami',
'Rampage',
'Havoc',
'Fury',
'Blitz',
'Reign',
'Rapture',
'Genesis',
'Onslaught',
'Vendetta',
'Oblivion',
'Dominion',
'Ascension',
'Redemption',
'Revolution',
'Evolution',
'Dynasty',
'Empire',
'Republic',
'Alliance',
'Syndicate',
'Collective',
'Foundation',
'Initiative',
'Consortium',
'Federation'
];
// Beatmap metadata templates — used to generate pool slots with varied data
const ARTISTS = [
'xi', 'DragonForce', 'UNDEAD CORPORATION', 'Imperial Circus Dead Decadence',
'Camellia', 'FELT', 'Halozy', 'Sound Horizon', 'Reol', 'DECO*27',
'ClariS', 'LiSA', 'Aimer', 'EGOIST', 'supercell', 'Linked Horizon',
'Foreground Eclipse', 'BABYMETAL', 'Hana', 'IOSYS',
'Mafumafu', 'Eve', 'Yorushika', 'YOASOBI', 'Kenshi Yonezu',
'Aqours', 'Roselia', 'Raise A Suilen', 'Pastel*Palettes', 'Poppin Party'
'xi',
'DragonForce',
'UNDEAD CORPORATION',
'Imperial Circus Dead Decadence',
'Camellia',
'FELT',
'Halozy',
'Sound Horizon',
'Reol',
'DECO*27',
'ClariS',
'LiSA',
'Aimer',
'EGOIST',
'supercell',
'Linked Horizon',
'Foreground Eclipse',
'BABYMETAL',
'Hana',
'IOSYS',
'Mafumafu',
'Eve',
'Yorushika',
'YOASOBI',
'Kenshi Yonezu',
'Aqours',
'Roselia',
'Raise A Suilen',
'Pastel*Palettes',
'Poppin Party'
];
const TITLES = [
'FREEDOM DiVE', 'Blue Zenith', 'Uta', 'Image Material', 'Brain Power',
'The Big Black', 'Airman ga Taosenai', 'Kira Kira Days', 'Senbonzakura', 'Harumachi Clover',
'Storytellers', 'Highscore', 'Louder than Steel', 'Snow Drive', 'Night of Knights',
'Yuki no Hana', 'Rainbow Road', 'Galaxy Collapse', 'Last Goodbye', 'Shinbatsu',
'Crystallize', 'Overkill', 'Plasma Gun', 'Time Freeze', 'Diamond',
'United', 'Alchemy', 'Scarlet Rose', 'Infinite Dream', 'Lost Umbrella',
'Through the Fire and Flames', 'Cry Thunder', 'Soldiers of the Wasteland', 'Valley of the Damned', 'Black Fire',
'Akasha', 'Ethereal', 'Chrono Trigger', 'Solar System', 'Phantom Rider'
'FREEDOM DiVE',
'Blue Zenith',
'Uta',
'Image Material',
'Brain Power',
'The Big Black',
'Airman ga Taosenai',
'Kira Kira Days',
'Senbonzakura',
'Harumachi Clover',
'Storytellers',
'Highscore',
'Louder than Steel',
'Snow Drive',
'Night of Knights',
'Yuki no Hana',
'Rainbow Road',
'Galaxy Collapse',
'Last Goodbye',
'Shinbatsu',
'Crystallize',
'Overkill',
'Plasma Gun',
'Time Freeze',
'Diamond',
'United',
'Alchemy',
'Scarlet Rose',
'Infinite Dream',
'Lost Umbrella',
'Through the Fire and Flames',
'Cry Thunder',
'Soldiers of the Wasteland',
'Valley of the Damned',
'Black Fire',
'Akasha',
'Ethereal',
'Chrono Trigger',
'Solar System',
'Phantom Rider'
];
const VERSIONS = [
'Normal', 'Hard', 'Insane', 'Expert', "Expert+", 'Lunatic',
"Someone's Extra", 'Another', 'Extreme', "Lasse's Insane",
'Reform', 'Deluge', 'Overdose', 'Rain', 'Platter',
"Monstrata's Expert", "Sotarks' Insane", "fieryrage's Extra", "Kroytz's Insane",
'Kalibe', 'Cataclysm'
'Normal',
'Hard',
'Insane',
'Expert',
'Expert+',
'Lunatic',
"Someone's Extra",
'Another',
'Extreme',
"Lasse's Insane",
'Reform',
'Deluge',
'Overdose',
'Rain',
'Platter',
"Monstrata's Expert",
"Sotarks' Insane",
"fieryrage's Extra",
"Kroytz's Insane",
'Kalibe',
'Cataclysm'
];
// ── PHASE FUNCTIONS ─────────────────────────────────────────────────────
@@ -431,7 +675,13 @@ async function seedUsers(): Promise<UserData[]> {
await batchInsert(account, accounts);
console.log(` Users + accounts: ${USER_COUNT} (${t()})`);
return users.map((u) => ({ id: u.id, name: u.name, email: u.email, role: u.role, createdAt: u.createdAt }));
return users.map((u) => ({
id: u.id,
name: u.name,
email: u.email,
role: u.role,
createdAt: u.createdAt
}));
}
async function seedPersonalTeams(users: UserData[]): Promise<TeamData[]> {
@@ -472,7 +722,9 @@ async function seedNonPersonalTeams(users: UserData[]): Promise<TeamData[]> {
for (let i = 0; i < TEAM_COUNT; i++) {
teams.push({
name: TEAM_NAMES[i % TEAM_NAMES.length] + (i >= TEAM_NAMES.length ? ` ${Math.floor(i / TEAM_NAMES.length) + 1}` : ''),
name:
TEAM_NAMES[i % TEAM_NAMES.length] +
(i >= TEAM_NAMES.length ? ` ${Math.floor(i / TEAM_NAMES.length) + 1}` : ''),
isPersonal: false,
ownerId: users[i].id
});
@@ -582,10 +834,15 @@ async function seedMappools(users: UserData[]): Promise<PoolData[]> {
// Generate slots for each pool
const SLOT_TEMPLATE = [
{ cat: 'NM', order: 1 }, { cat: 'NM', order: 2 }, { cat: 'NM', order: 3 },
{ cat: 'HD', order: 1 }, { cat: 'HD', order: 2 },
{ cat: 'HR', order: 1 }, { cat: 'HR', order: 2 },
{ cat: 'DT', order: 1 }, { cat: 'DT', order: 2 },
{ cat: 'NM', order: 1 },
{ cat: 'NM', order: 2 },
{ cat: 'NM', order: 3 },
{ cat: 'HD', order: 1 },
{ cat: 'HD', order: 2 },
{ cat: 'HR', order: 1 },
{ cat: 'HR', order: 2 },
{ cat: 'DT', order: 1 },
{ cat: 'DT', order: 2 },
{ cat: 'FM', order: 1 },
{ cat: 'TB', order: 1 }
];
@@ -833,7 +1090,9 @@ async function seedMatches(
}
// Strip temp fields for insert
const participantRows = allParticipants.map(({ _tempIdx, _matchDataIdx, _side, ...rest }) => rest);
const participantRows = allParticipants.map(
({ _tempIdx, _matchDataIdx, _side, ...rest }) => rest
);
const insertedParticipants = await batchInsert(matchParticipant, participantRows);
// Build players
@@ -851,7 +1110,9 @@ async function seedMatches(
});
}
const playerRows = allPlayers.map(({ _participantInsertIdx, _matchDataIdx, _side, ...rest }) => rest);
const playerRows = allPlayers.map(
({ _participantInsertIdx, _matchDataIdx, _side, ...rest }) => rest
);
const insertedPlayers = await batchInsert(matchParticipantPlayer, playerRows);
// Build a lookup: matchDataIdx + side -> participant ID and player ID
@@ -948,7 +1209,8 @@ async function seedMatches(
winnerParticipantId: gameWinner?.participantId || null,
state: gameState,
startedAt: gameStarted,
finishedAt: gameState === 'FINISHED' ? new Date(gameStarted.getTime() + randInt(2, 8) * 60000) : null,
finishedAt:
gameState === 'FINISHED' ? new Date(gameStarted.getTime() + randInt(2, 8) * 60000) : null,
_scores: [
{ playerId: p1.playerId, ...score1 },
{ playerId: p2.playerId, ...score2 }
@@ -988,8 +1250,7 @@ async function seedMatches(
// Update match winners and participant scores
for (const [idx, winner] of matchWinners) {
const mi = insertedMatches[idx];
await db
.execute(sql`UPDATE match SET winner_id = ${winner.winnerId} WHERE id = ${mi.id}`);
await db.execute(sql`UPDATE match SET winner_id = ${winner.winnerId} WHERE id = ${mi.id}`);
const p1 = participantLookup.get(`${idx}-1`)!;
const p2 = participantLookup.get(`${idx}-2`)!;
@@ -1023,7 +1284,13 @@ async function seedMatches(
async function seedNotifications(users: UserData[]): Promise<void> {
const t = timer();
const types = ['match_created', 'match_finished', 'match_cancelled', 'team_invite', 'match_invite'];
const types = [
'match_created',
'match_finished',
'match_cancelled',
'team_invite',
'match_invite'
];
const rows: any[] = [];
for (const u of users) {