use prettier formatting
This commit is contained in:
@@ -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++;
|
||||
|
||||
@@ -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-%'`
|
||||
);
|
||||
|
||||
@@ -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');
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user