main
Deploy Iddaai Backend / build-and-deploy (push) Successful in 2m42s

This commit is contained in:
2026-04-26 03:07:18 +03:00
parent 1623432039
commit a338d02244
20 changed files with 818 additions and 160 deletions
+123
View File
@@ -0,0 +1,123 @@
/**
* Repair Feeder - Fix incomplete matches
* Usage: npm run feeder:repair
*
* Finds matches in DB that are missing stats or lineups
* and re-fetches them from the API.
*/
import { NestFactory } from "@nestjs/core";
import { Logger } from "@nestjs/common";
import { PrismaService } from "../database/prisma.service";
import { FeederService } from "../modules/feeder/feeder.service";
async function bootstrap() {
process.env.FEEDER_MODE = "historical";
const logger = new Logger("FeederRepair");
logger.log("🔧 Starting feeder repair scan...");
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { AppModule } = require("../app.module");
const app = await NestFactory.createApplicationContext(AppModule, {
logger: ["log", "error", "warn"],
});
const prisma = app.get(PrismaService);
const feederService = app.get(FeederService);
try {
// Find football matches missing stats (no football_team_stats rows)
const matchesMissingStats = await prisma.$queryRaw<
Array<{ id: string; match_name: string }>
>`
SELECT m.id, m.match_name
FROM matches m
LEFT JOIN football_team_stats fts ON fts.match_id = m.id
WHERE m.sport = 'football'
AND m.state = 'Ended'
AND fts.id IS NULL
ORDER BY m.mst_utc DESC
`;
// Find football matches missing lineups (< 18 participation rows)
const matchesMissingLineups = await prisma.$queryRaw<
Array<{ id: string; match_name: string; cnt: bigint }>
>`
SELECT m.id, m.match_name, COUNT(mpp.id) as cnt
FROM matches m
LEFT JOIN match_player_participation mpp ON mpp.match_id = m.id
WHERE m.sport = 'football'
AND m.state = 'Ended'
GROUP BY m.id, m.match_name
HAVING COUNT(mpp.id) < 18
ORDER BY m.mst_utc DESC
`;
// Combine unique match IDs
const repairSet = new Set<string>();
for (const m of matchesMissingStats) repairSet.add(m.id);
for (const m of matchesMissingLineups) repairSet.add(m.id);
logger.log(
`📊 Found ${repairSet.size} incomplete matches (${matchesMissingStats.length} missing stats, ${matchesMissingLineups.length} missing lineups)`,
);
if (repairSet.size === 0) {
logger.log("✅ No incomplete matches found. Everything is clean!");
await app.close();
process.exit(0);
}
let repaired = 0;
let failed = 0;
const matchIds = Array.from(repairSet);
for (let i = 0; i < matchIds.length; i++) {
const matchId = matchIds[i];
// Rate limiting
if (i > 0 && i % 10 === 0) {
logger.log(
`⏸️ Cooldown after 10 repairs... (${repaired} repaired, ${failed} failed, ${matchIds.length - i} remaining)`,
);
await new Promise((r) => setTimeout(r, 5000));
}
await new Promise((r) => setTimeout(r, 500));
try {
const result = await feederService.refreshMatch(matchId, "all");
if (result.success) {
repaired++;
if (repaired % 25 === 0) {
logger.log(`🔧 Progress: ${repaired}/${matchIds.length} repaired`);
}
} else {
failed++;
logger.warn(
`❌ [${matchId}] Repair failed: ${result.error || "unknown"}`,
);
}
} catch (e: any) {
failed++;
logger.error(`❌ [${matchId}] Repair exception: ${e.message}`);
}
}
logger.log(
`🎉 REPAIR COMPLETE: ${repaired} repaired, ${failed} failed out of ${matchIds.length} total`,
);
} catch (error: any) {
logger.error(`❌ Repair failed: ${error.message}`);
logger.error(error.stack);
process.exit(1);
} finally {
await app.close();
}
process.exit(0);
}
void bootstrap();