Files
football-next/scripts/reset-and-seed.ts
2026-05-03 17:01:46 +03:30

227 lines
16 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { Client } from "pg";
import bcrypt from "bcryptjs";
import { config } from "dotenv";
config(); // load .env
const DATABASE_URL = process.env.DATABASE_URL!;
const DEFAULT_RULES = {
GK: { GOAL: 10, ASSIST: 3, YELLOW_CARD: -1, RED_CARD: -3, SECOND_YELLOW: -3, CLEAN_SHEET: 6, PENALTY_SAVED: 5, PENALTY_MISSED: -2, OWN_GOAL: -2, MOTM: 3, EXTRA_TIME_BONUS: 1, INJURY_NO_SUB: -1 },
DEF: { GOAL: 8, ASSIST: 3, YELLOW_CARD: -1, RED_CARD: -3, SECOND_YELLOW: -3, CLEAN_SHEET: 4, PENALTY_SAVED: 0, PENALTY_MISSED: -2, OWN_GOAL: -2, MOTM: 3, EXTRA_TIME_BONUS: 1, INJURY_NO_SUB: -1 },
MID: { GOAL: 5, ASSIST: 3, YELLOW_CARD: -2, RED_CARD: -3, SECOND_YELLOW: -3, CLEAN_SHEET: 1, PENALTY_SAVED: 0, PENALTY_MISSED: -2, OWN_GOAL: -2, MOTM: 3, EXTRA_TIME_BONUS: 1, INJURY_NO_SUB: -1 },
FWD: { GOAL: 4, ASSIST: 3, YELLOW_CARD: -1, RED_CARD: -3, SECOND_YELLOW: -3, CLEAN_SHEET: 0, PENALTY_SAVED: 0, PENALTY_MISSED: -2, OWN_GOAL: -2, MOTM: 3, EXTRA_TIME_BONUS: 1, INJURY_NO_SUB: -1 },
};
const COUNTRY_FORMATIONS: Record<string, string> = {
BRA:"4-3-3",FRA:"4-3-3",ARG:"4-3-3",ENG:"4-3-3",ESP:"4-3-3",GER:"4-2-3-1",POR:"4-3-3",NED:"4-3-3",
BEL:"4-3-3",CRO:"4-3-3",MAR:"4-3-3",IRN:"4-5-1",URU:"4-3-3",SEN:"4-3-3",KOR:"4-3-3",JPN:"4-3-3",
MEX:"4-3-3",USA:"4-3-3",CAN:"4-3-3",AUS:"4-3-3",POL:"4-3-3",DEN:"4-3-3",SUI:"4-2-3-1",SRB:"3-4-3",
WAL:"5-3-2",TUN:"4-3-3",CMR:"4-3-3",GHA:"4-2-3-1",ECU:"4-3-3",QAT:"5-3-2",KSA:"4-3-3",CRC:"5-4-1",
};
const COUNTRIES = [
{name:"برزیل",code:"BRA",flag:"🇧🇷",group:"G"},{name:"سربیا",code:"SRB",flag:"🇷🇸",group:"G"},
{name:"سوئیس",code:"SUI",flag:"🇨🇭",group:"G"},{name:"کامرون",code:"CMR",flag:"🇨🇲",group:"G"},
{name:"فرانسه",code:"FRA",flag:"🇫🇷",group:"D"},{name:"استرالیا",code:"AUS",flag:"🇦🇺",group:"D"},
{name:"دانمارک",code:"DEN",flag:"🇩🇰",group:"D"},{name:"تونس",code:"TUN",flag:"🇹🇳",group:"D"},
{name:"آرژانتین",code:"ARG",flag:"🇦🇷",group:"C"},{name:"عربستان",code:"KSA",flag:"🇸🇦",group:"C"},
{name:"مکزیک",code:"MEX",flag:"🇲🇽",group:"C"},{name:"لهستان",code:"POL",flag:"🇵🇱",group:"C"},
{name:"ایران",code:"IRN",flag:"🇮🇷",group:"B"},{name:"انگلیس",code:"ENG",flag:"🏴󠁧󠁢󠁥󠁮󠁧󠁿",group:"B"},
{name:"آمریکا",code:"USA",flag:"🇺🇸",group:"B"},{name:"ولز",code:"WAL",flag:"🏴󠁧󠁢󠁷󠁬󠁳󠁿",group:"B"},
{name:"آلمان",code:"GER",flag:"🇩🇪",group:"E"},{name:"ژاپن",code:"JPN",flag:"🇯🇵",group:"E"},
{name:"اسپانیا",code:"ESP",flag:"🇪🇸",group:"E"},{name:"کاستاریکا",code:"CRC",flag:"🇨🇷",group:"E"},
{name:"بلژیک",code:"BEL",flag:"🇧🇪",group:"F"},{name:"کانادا",code:"CAN",flag:"🇨🇦",group:"F"},
{name:"مراکش",code:"MAR",flag:"🇲🇦",group:"F"},{name:"کرواسی",code:"CRO",flag:"🇭🇷",group:"F"},
{name:"پرتغال",code:"POR",flag:"🇵🇹",group:"H"},{name:"غنا",code:"GHA",flag:"🇬🇭",group:"H"},
{name:"اروگوئه",code:"URU",flag:"🇺🇾",group:"H"},{name:"کره جنوبی",code:"KOR",flag:"🇰🇷",group:"H"},
{name:"هلند",code:"NED",flag:"🇳🇱",group:"A"},{name:"سنگال",code:"SEN",flag:"🇸🇳",group:"A"},
{name:"اکوادور",code:"ECU",flag:"🇪🇨",group:"A"},{name:"قطر",code:"QAT",flag:"🇶🇦",group:"A"},
];
const PLAYERS_DATA = [
{name:"آلیسون بکر",pos:"GK",code:"BRA",price:6.0,pts:42},{name:"تیاگو سیلوا",pos:"DEF",code:"BRA",price:6.5,pts:38},
{name:"مارکینیوس",pos:"DEF",code:"BRA",price:6.0,pts:35},{name:"کاسمیرو",pos:"MID",code:"BRA",price:8.0,pts:52},
{name:"نیمار",pos:"FWD",code:"BRA",price:11.5,pts:68},{name:"وینیسیوس جونیور",pos:"FWD",code:"BRA",price:10.5,pts:72},
{name:"ریچارلیسون",pos:"FWD",code:"BRA",price:8.5,pts:55},{name:"هوگو لوریس",pos:"GK",code:"FRA",price:6.0,pts:40},
{name:"رافائل واران",pos:"DEF",code:"FRA",price:6.5,pts:36},{name:"کیلیان امباپه",pos:"FWD",code:"FRA",price:12.5,pts:88},
{name:"آنتوان گریزمان",pos:"MID",code:"FRA",price:9.5,pts:62},{name:"اولیویه ژیرو",pos:"FWD",code:"FRA",price:7.5,pts:48},
{name:"اوسمان دمبله",pos:"FWD",code:"FRA",price:8.0,pts:44},{name:"امیلیانو مارتینز",pos:"GK",code:"ARG",price:6.5,pts:55},
{name:"لیونل مسی",pos:"FWD",code:"ARG",price:12.5,pts:92},{name:"خولیان آلوارز",pos:"FWD",code:"ARG",price:8.5,pts:60},
{name:"رودریگو دپاول",pos:"MID",code:"ARG",price:7.5,pts:45},{name:"نیکولاس اوتامندی",pos:"DEF",code:"ARG",price:5.5,pts:32},
{name:"جوردن پیکفورد",pos:"GK",code:"ENG",price:5.5,pts:38},{name:"جود بلینگهام",pos:"MID",code:"ENG",price:10.5,pts:75},
{name:"هری کین",pos:"FWD",code:"ENG",price:11.0,pts:70},{name:"بوکایو ساکا",pos:"MID",code:"ENG",price:8.5,pts:58},
{name:"فیل فودن",pos:"MID",code:"ENG",price:9.0,pts:62},{name:"جان استونز",pos:"DEF",code:"ENG",price:6.0,pts:34},
{name:"اونای سیمون",pos:"GK",code:"ESP",price:5.5,pts:36},{name:"پدری",pos:"MID",code:"ESP",price:9.0,pts:60},
{name:"گاوی",pos:"MID",code:"ESP",price:8.5,pts:55},{name:"آلوارو موراتا",pos:"FWD",code:"ESP",price:7.5,pts:46},
{name:"دنی اولمو",pos:"MID",code:"ESP",price:7.0,pts:42},{name:"مانوئل نویر",pos:"GK",code:"GER",price:6.0,pts:38},
{name:"توماس مولر",pos:"MID",code:"GER",price:8.0,pts:50},{name:"کای هاورتز",pos:"MID",code:"GER",price:8.5,pts:52},
{name:"یامله موسیالا",pos:"MID",code:"GER",price:9.0,pts:65},{name:"آنتونیو رودیگر",pos:"DEF",code:"GER",price:5.5,pts:30},
{name:"دیوگو کوستا",pos:"GK",code:"POR",price:5.5,pts:35},{name:"کریستیانو رونالدو",pos:"FWD",code:"POR",price:11.0,pts:65},
{name:"برونو فرناندز",pos:"MID",code:"POR",price:10.0,pts:70},{name:"رافائل لئائو",pos:"FWD",code:"POR",price:8.5,pts:55},
{name:"روبن دیاز",pos:"DEF",code:"POR",price:6.0,pts:36},{name:"علیرضا بیرانوند",pos:"GK",code:"IRN",price:5.0,pts:28},
{name:"مهدی طارمی",pos:"FWD",code:"IRN",price:8.0,pts:50},{name:"سردار آزمون",pos:"FWD",code:"IRN",price:7.5,pts:44},
{name:"علی کریمی",pos:"MID",code:"IRN",price:6.0,pts:32},{name:"رامین رضاییان",pos:"DEF",code:"IRN",price:5.0,pts:22},
{name:"یاسین بونو",pos:"GK",code:"MAR",price:6.5,pts:58},{name:"اشرف حکیمی",pos:"DEF",code:"MAR",price:8.0,pts:62},
{name:"حکیم زیاش",pos:"MID",code:"MAR",price:7.5,pts:48},{name:"یوسف النصیری",pos:"FWD",code:"MAR",price:7.0,pts:45},
{name:"دومینیک لیواکوویچ",pos:"GK",code:"CRO",price:6.0,pts:50},{name:"لوکا مودریچ",pos:"MID",code:"CRO",price:9.5,pts:68},
{name:"ایوان پریشیچ",pos:"MID",code:"CRO",price:7.5,pts:48},{name:"آندره کراماریچ",pos:"FWD",code:"CRO",price:7.0,pts:44},
{name:"آندریس نوپرت",pos:"GK",code:"NED",price:5.5,pts:36},{name:"ویرخیل فان دایک",pos:"DEF",code:"NED",price:7.0,pts:48},
{name:"دنزل دامفریس",pos:"DEF",code:"NED",price:7.5,pts:52},{name:"کودی گاکپو",pos:"FWD",code:"NED",price:8.0,pts:58},
{name:"تیبو کورتوا",pos:"GK",code:"BEL",price:6.5,pts:44},{name:"کوین دبروینه",pos:"MID",code:"BEL",price:11.0,pts:72},
{name:"رومله لوکاکو",pos:"FWD",code:"BEL",price:9.5,pts:55},
];
function cuid() {
return 'c' + Math.random().toString(36).slice(2, 11) + Date.now().toString(36);
}
async function main() {
const client = new Client({ connectionString: DATABASE_URL });
await client.connect();
console.log("✅ Connected to database");
// ─── پاک کردن همه داده‌ها ────────────────────────────
console.log("🗑️ Clearing all data...");
await client.query(`
TRUNCATE TABLE "Payment", "TeamPlayer", "Team", "Session",
"MatchLineup", "MatchEvent", "PlayerMatchStat", "ScoringRule",
"Match", "Player", "Country", "Group", "Round", "Gameweek",
"Package", "User"
RESTART IDENTITY CASCADE
`);
console.log("✅ Cleared");
// ─── ادمین ────────────────────────────────────────────
console.log("👤 Creating users...");
const adminPwd = await bcrypt.hash("admin123", 10);
const userPwd = await bcrypt.hash("user123", 10);
await client.query(`
INSERT INTO "User" (id, email, name, password, role, "createdAt")
VALUES
($1, 'admin@worldcup.com', 'ادمین', $2, 'ADMIN', NOW()),
($3, 'ali@test.com', 'علی احمدی', $4, 'USER', NOW()),
($5, 'sara@test.com', 'سارا رضایی', $4, 'USER', NOW()),
($6, 'reza@test.com', 'رضا محمدی', $4, 'USER', NOW())
`, [cuid(), adminPwd, cuid(), userPwd, cuid(), cuid()]);
// ─── گروه‌ها ──────────────────────────────────────────
console.log("🏟️ Creating groups...");
const groupMap: Record<string, string> = {};
for (const name of ["A","B","C","D","E","F","G","H"]) {
const id = cuid();
await client.query(`INSERT INTO "Group" (id, name) VALUES ($1, $2)`, [id, name]);
groupMap[name] = id;
}
// ─── تیم‌های ملی ─────────────────────────────────────
console.log("🌍 Creating countries...");
const countryMap: Record<string, string> = {};
for (const c of COUNTRIES) {
const id = cuid();
const formation = COUNTRY_FORMATIONS[c.code] ?? "4-3-3";
await client.query(
`INSERT INTO "Country" (id, name, code, "flagUrl", "groupId", "defaultFormation") VALUES ($1,$2,$3,$4,$5,$6)`,
[id, c.name, c.code, c.flag, groupMap[c.group], formation]
);
countryMap[c.code] = id;
}
// ─── بازیکنان ─────────────────────────────────────────
console.log("⚽ Creating players...");
for (const p of PLAYERS_DATA) {
const countryId = countryMap[p.code];
if (!countryId) continue;
await client.query(
`INSERT INTO "Player" (id, name, position, "countryId", price, "totalPoints", "isActive", "createdAt", "updatedAt")
VALUES ($1,$2,$3,$4,$5,$6,true,NOW(),NOW())`,
[cuid(), p.name, p.pos, countryId, p.price, p.pts]
);
}
// ─── قوانین امتیازدهی ────────────────────────────────
console.log("📊 Creating scoring rules...");
for (const [pos, rules] of Object.entries(DEFAULT_RULES)) {
for (const [eventType, points] of Object.entries(rules)) {
await client.query(
`INSERT INTO "ScoringRule" (id, position, "eventType", points, "updatedAt")
VALUES ($1,$2,$3,$4,NOW())`,
[cuid(), pos, eventType, points]
);
}
}
// ─── دورها ────────────────────────────────────────────
console.log("🔄 Creating rounds...");
const rounds: Record<number, string> = {};
const roundsData = [
{num:1, name:"دور اول - مرحله گروهی", active:true, deadline:"2026-06-15T10:00:00Z"},
{num:2, name:"دور دوم - مرحله گروهی", active:false, deadline:"2026-06-22T10:00:00Z"},
{num:3, name:"دور سوم - مرحله گروهی", active:false, deadline:"2026-06-29T10:00:00Z"},
{num:4, name:"دور چهارم - یک‌هشتم نهایی",active:false, deadline:"2026-07-05T10:00:00Z"},
];
for (const r of roundsData) {
const id = cuid();
await client.query(
`INSERT INTO "Round" (id, number, name, "isActive", deadline, "createdAt") VALUES ($1,$2,$3,$4,$5,NOW())`,
[id, r.num, r.name, r.active, r.deadline]
);
rounds[r.num] = id;
}
// ─── بازی‌ها ──────────────────────────────────────────
console.log("🏆 Creating matches...");
const matchesData = [
{home:"QAT",away:"ECU",hS:0, aS:2, st:"FINISHED", date:"2026-06-20T16:00:00Z",r:1},
{home:"ENG",away:"IRN",hS:6, aS:2, st:"FINISHED", date:"2026-06-21T13:00:00Z",r:1},
{home:"ARG",away:"KSA",hS:1, aS:2, st:"FINISHED", date:"2026-06-22T10:00:00Z",r:1},
{home:"FRA",away:"AUS",hS:4, aS:1, st:"FINISHED", date:"2026-06-22T19:00:00Z",r:1},
{home:"GER",away:"JPN",hS:1, aS:2, st:"FINISHED", date:"2026-06-23T13:00:00Z",r:1},
{home:"ESP",away:"CRC",hS:7, aS:0, st:"FINISHED", date:"2026-06-23T19:00:00Z",r:1},
{home:"BEL",away:"CAN",hS:1, aS:0, st:"FINISHED", date:"2026-06-23T16:00:00Z",r:1},
{home:"BRA",away:"SRB",hS:2, aS:0, st:"FINISHED", date:"2026-06-24T19:00:00Z",r:1},
{home:"POR",away:"GHA",hS:3, aS:2, st:"FINISHED", date:"2026-06-24T16:00:00Z",r:1},
{home:"MAR",away:"CRO",hS:0, aS:0, st:"FINISHED", date:"2026-06-23T10:00:00Z",r:1},
{home:"NED",away:"SEN",hS:2, aS:0, st:"FINISHED", date:"2026-06-21T16:00:00Z",r:1},
{home:"IRN",away:"WAL",hS:2, aS:0, st:"FINISHED", date:"2026-06-25T13:00:00Z",r:2},
{home:"FRA",away:"DEN",hS:2, aS:1, st:"FINISHED", date:"2026-06-26T19:00:00Z",r:2},
{home:"ARG",away:"MEX",hS:2, aS:0, st:"FINISHED", date:"2026-06-26T22:00:00Z",r:2},
{home:"BRA",away:"SUI",hS:1, aS:0, st:"FINISHED", date:"2026-06-28T19:00:00Z",r:2},
{home:"ENG",away:"USA",hS:0, aS:0, st:"FINISHED", date:"2026-06-25T19:00:00Z",r:2},
{home:"BRA",away:"CMR",hS:1, aS:0, st:"SCHEDULED", date:"2026-07-02T19:00:00Z",r:3},
{home:"FRA",away:"TUN",hS:null,aS:null,st:"SCHEDULED",date:"2026-07-01T16:00:00Z",r:3},
{home:"NED",away:"ARG",hS:2, aS:2, st:"FINISHED", date:"2026-12-09T19:00:00Z",r:4,stage:"ROUND_OF_16"},
{home:"FRA",away:"ENG",hS:2, aS:1, st:"FINISHED", date:"2026-12-10T19:00:00Z",r:4,stage:"ROUND_OF_16"},
{home:"ARG",away:"FRA",hS:3, aS:3, st:"FINISHED", date:"2026-12-18T15:00:00Z",r:4,stage:"FINAL"},
];
for (const m of matchesData) {
const homeId = countryMap[m.home], awayId = countryMap[m.away];
if (!homeId || !awayId) continue;
await client.query(
`INSERT INTO "Match" (id,"homeTeamId","awayTeamId","homeScore","awayScore",status,stage,"matchDate","roundId","createdAt")
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,NOW())`,
[cuid(), homeId, awayId, m.hS ?? null, m.aS ?? null, m.st, (m as any).stage ?? "GROUP", m.date, rounds[m.r]]
);
}
// ─── پکیج‌ها ──────────────────────────────────────────
console.log("📦 Creating packages...");
for (const pkg of [
{name:"پکیج نقره‌ای", budgetBonus:10, price:50000, desc:"۱۰ میلیون به بودجه اضافه کن"},
{name:"پکیج طلایی", budgetBonus:20, price:90000, desc:"۲۰ میلیون به بودجه اضافه کن"},
{name:"پکیج الماس", budgetBonus:30, price:120000,desc:"۳۰ میلیون به بودجه اضافه کن"},
]) {
await client.query(
`INSERT INTO "Package" (id, name, "budgetBonus", price, description, "isActive") VALUES ($1,$2,$3,$4,$5,true)`,
[cuid(), pkg.name, pkg.budgetBonus, pkg.price, pkg.desc]
);
}
await client.end();
console.log("\n✅ Seed done!");
console.log(" admin@worldcup.com / admin123");
console.log(" ali@test.com / user123");
}
main().catch(e => { console.error("❌", e); process.exit(1); });