fix: restore word-level proxy for Habbo fonts, pre-cache all 175 fonts for 'Atom'
This commit is contained in:
1 parent
ac75f9c80a
commit
8a58bcb252
6481 files changed
+145
-731
No files matched your search
@@ -1,150 +0,0 @@
|
||||
/**
|
||||
* DB migration runner for the manual SQL files in prisma/migrations/.
|
||||
*
|
||||
* Why a custom runner: the database is shared with the Arcturus emulator, so we
|
||||
* apply CMS-only schema changes via hand-written idempotent SQL (every file is
|
||||
* `IF NOT EXISTS`-guarded) instead of `prisma migrate dev`, whose auto-diff
|
||||
* would try to DROP emulator-owned tables. An `_applied_migrations` audit table
|
||||
* tracks what ran so the workflow doesn't rely on memory.
|
||||
*
|
||||
* pnpm db:migrate run pending migrations
|
||||
* pnpm db:migrate:status print pending vs applied
|
||||
* pnpm db:migrate --mark <f> mark an already-applied file without running it
|
||||
*/
|
||||
|
||||
import "dotenv/config";
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { createHash } from "node:crypto";
|
||||
import { readdir, readFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
|
||||
const MIGRATIONS_DIR = join(process.cwd(), "prisma", "migrations");
|
||||
|
||||
async function ensureAuditTable(): Promise<void> {
|
||||
await prisma.$executeRawUnsafe(`
|
||||
CREATE TABLE IF NOT EXISTS _applied_migrations (
|
||||
filename VARCHAR(255) NOT NULL,
|
||||
applied_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
sha256 CHAR(64) NULL,
|
||||
PRIMARY KEY (filename)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
||||
`);
|
||||
}
|
||||
|
||||
async function appliedSet(): Promise<Set<string>> {
|
||||
const rows = await prisma.$queryRawUnsafe<{ filename: string }[]>(
|
||||
"SELECT filename FROM _applied_migrations",
|
||||
);
|
||||
return new Set(rows.map((r) => r.filename));
|
||||
}
|
||||
|
||||
async function listMigrations(): Promise<string[]> {
|
||||
const files = await readdir(MIGRATIONS_DIR);
|
||||
return files.filter((f) => f.endsWith(".sql")).sort();
|
||||
}
|
||||
|
||||
async function sha256(filename: string): Promise<string> {
|
||||
const content = await readFile(join(MIGRATIONS_DIR, filename));
|
||||
return createHash("sha256").update(content).digest("hex");
|
||||
}
|
||||
|
||||
function runPrismaDbExecute(filename: string): boolean {
|
||||
const file = join(MIGRATIONS_DIR, filename);
|
||||
// shell:true so `npx` resolves to npx.cmd on Windows (direct .cmd spawn is
|
||||
// blocked there since CVE-2024-27980). Quote the path for spaces.
|
||||
const result = spawnSync("npx", ["prisma", "db", "execute", "--file", `"${file}"`], {
|
||||
stdio: "inherit",
|
||||
env: process.env,
|
||||
shell: true,
|
||||
});
|
||||
return result.status === 0;
|
||||
}
|
||||
|
||||
async function recordApplied(filename: string): Promise<void> {
|
||||
const hash = await sha256(filename);
|
||||
await prisma.$executeRawUnsafe(
|
||||
"INSERT INTO _applied_migrations (filename, sha256) VALUES (?, ?) " +
|
||||
"ON DUPLICATE KEY UPDATE applied_at = CURRENT_TIMESTAMP(3), sha256 = VALUES(sha256)",
|
||||
filename,
|
||||
hash,
|
||||
);
|
||||
}
|
||||
|
||||
async function statusOnly(): Promise<void> {
|
||||
await ensureAuditTable();
|
||||
const applied = await appliedSet();
|
||||
const files = await listMigrations();
|
||||
if (files.length === 0) {
|
||||
console.log("No migrations found in prisma/migrations/.");
|
||||
return;
|
||||
}
|
||||
console.log("Status:");
|
||||
for (const f of files) {
|
||||
const isApplied = applied.has(f);
|
||||
console.log(` ${isApplied ? "OK" : " ."} ${f}${isApplied ? "" : " (PENDING)"}`);
|
||||
}
|
||||
const pending = files.filter((f) => !applied.has(f));
|
||||
console.log(`\n${pending.length} pending / ${files.length} total`);
|
||||
}
|
||||
|
||||
async function markApplied(filename: string): Promise<void> {
|
||||
await ensureAuditTable();
|
||||
const files = await listMigrations();
|
||||
if (!files.includes(filename)) {
|
||||
console.error(`File not found in prisma/migrations/: ${filename}`);
|
||||
process.exit(1);
|
||||
}
|
||||
await recordApplied(filename);
|
||||
console.log(`Marked ${filename} as applied (no SQL was run).`);
|
||||
}
|
||||
|
||||
async function applyPending(): Promise<void> {
|
||||
await ensureAuditTable();
|
||||
const applied = await appliedSet();
|
||||
const files = await listMigrations();
|
||||
if (files.length === 0) {
|
||||
console.log("No migrations found in prisma/migrations/.");
|
||||
return;
|
||||
}
|
||||
let ran = 0;
|
||||
for (const f of files) {
|
||||
if (applied.has(f)) {
|
||||
console.log(`OK ${f} (already applied)`);
|
||||
continue;
|
||||
}
|
||||
console.log(`-> applying ${f} ...`);
|
||||
if (!runPrismaDbExecute(f)) {
|
||||
console.error(`FAILED ${f}; aborting`);
|
||||
process.exit(1);
|
||||
}
|
||||
await recordApplied(f);
|
||||
ran++;
|
||||
}
|
||||
console.log(ran === 0 ? "\nNothing to do — DB already in sync." : `\nApplied ${ran} migration(s).`);
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const args = process.argv.slice(2);
|
||||
try {
|
||||
if (args[0] === "--status") {
|
||||
await statusOnly();
|
||||
} else if (args[0] === "--mark") {
|
||||
const filename = args[1];
|
||||
if (!filename) {
|
||||
console.error("Usage: pnpm db:migrate --mark <filename>");
|
||||
process.exit(1);
|
||||
}
|
||||
await markApplied(filename);
|
||||
} else {
|
||||
await applyPending();
|
||||
}
|
||||
} finally {
|
||||
await prisma.$disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((e) => {
|
||||
console.error("FAILED:", e instanceof Error ? e.message : e);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in new issue
Block a user