L'utente ha un hosting Virtualmin esistente (musictools.djluza.com)
con Apache + Node 20 + MariaDB + PM2. Tutta l'infrastruttura
Cloudflare (Workers/D1/R2/Pages) e' rimpiazzata con un backend
Express che gira sul server gia' presente, a costo zero.
Cambia:
- server/src/*.ts (Workers) -> server/src/*.js (Node ESM puro)
- D1 (sqlite) -> MariaDB 10.11 via mysql2/promise
- R2 (storage) -> filesystem locale ~/builds/ + signed URL HMAC
- wrangler.toml -> ecosystem.config.cjs (PM2)
- Aggiunto src/migrate.js: runner SQL idempotente
Endpoints invariati - l'app desktop non vede differenze:
- POST /api/license/{activate,validate,deactivate}
- GET /api/latest, /api/download
- POST /api/webhook/lemonsqueezy
- GET /api/health
README riscritto con istruzioni complete: creazione DB,
deploy via rsync/git, config Apache reverse proxy via
Virtualmin, gestione PM2, backup, workflow release.
Email rimane su Resend (deliverability) - free tier sufficiente.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
66 lines
1.8 KiB
JavaScript
66 lines
1.8 KiB
JavaScript
/**
|
|
* Applica i file SQL in migrations/ in ordine alfabetico.
|
|
* Tiene traccia dei file gia eseguiti in una tabella `schema_migrations`.
|
|
*
|
|
* Run: npm run migrate
|
|
*/
|
|
|
|
import "dotenv/config";
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import pool, { query, exec } from "./db.js";
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
const MIG_DIR = path.resolve(__dirname, "../migrations");
|
|
|
|
async function ensureMigrationsTable() {
|
|
await exec(`
|
|
CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
filename VARCHAR(255) NOT NULL PRIMARY KEY,
|
|
applied_at BIGINT UNSIGNED NOT NULL
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
|
`);
|
|
}
|
|
|
|
async function run() {
|
|
await ensureMigrationsTable();
|
|
const applied = new Set(
|
|
(await query("SELECT filename FROM schema_migrations")).map((r) => r.filename),
|
|
);
|
|
|
|
const files = fs.readdirSync(MIG_DIR)
|
|
.filter((f) => f.endsWith(".sql"))
|
|
.sort();
|
|
|
|
for (const f of files) {
|
|
if (applied.has(f)) {
|
|
console.log(`[migrate] skip ${f} (gia applicato)`);
|
|
continue;
|
|
}
|
|
const sql = fs.readFileSync(path.join(MIG_DIR, f), "utf-8");
|
|
console.log(`[migrate] applico ${f}`);
|
|
// mysql2 supporta multipleStatements ma e' rischioso; splittiamo manualmente.
|
|
const statements = sql
|
|
.split(/;\s*\n/)
|
|
.map((s) => s.trim())
|
|
.filter((s) => s.length > 0 && !s.startsWith("--"));
|
|
for (const stmt of statements) {
|
|
await exec(stmt);
|
|
}
|
|
await exec(
|
|
"INSERT INTO schema_migrations (filename, applied_at) VALUES (?, ?)",
|
|
[f, Math.floor(Date.now() / 1000)],
|
|
);
|
|
console.log(`[migrate] ok ${f}`);
|
|
}
|
|
|
|
await pool.end();
|
|
console.log("[migrate] done");
|
|
}
|
|
|
|
run().catch((e) => {
|
|
console.error("[migrate] errore:", e);
|
|
process.exit(1);
|
|
});
|