- number-поля теперь рендерятся как text + inputMode=numeric, чтобы браузер не округлял значения через input type=number - пробелы при вставке в number-поля удаляются - бэкенд нормализует значения number-полей в строку перед сохранением - добавлен хелпер normalizeFieldValueForStorage Closes: искажение расчётного счёта и других длинных числовых полей
78 lines
2.0 KiB
JavaScript
78 lines
2.0 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Docker-friendly migration runner.
|
|
* Applies SQL files from ./migrations/ in order using pg directly,
|
|
* without drizzle-kit (which can hang in non-TTY Docker environments).
|
|
*/
|
|
|
|
import pg from "pg";
|
|
import fs from "fs";
|
|
import path from "path";
|
|
import { fileURLToPath } from "url";
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
const MIGRATIONS_DIR = path.resolve(__dirname, "../migrations");
|
|
|
|
const url = process.env.DATABASE_URL;
|
|
if (!url) {
|
|
console.error("[Migrate] ERROR: DATABASE_URL is not set.");
|
|
process.exit(1);
|
|
}
|
|
|
|
const client = new pg.Client({ connectionString: url });
|
|
|
|
try {
|
|
await client.connect();
|
|
|
|
await client.query(`
|
|
CREATE TABLE IF NOT EXISTS __drizzle_migrations (
|
|
id SERIAL PRIMARY KEY,
|
|
name TEXT NOT NULL UNIQUE,
|
|
applied_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
)
|
|
`);
|
|
|
|
const { rows: applied } = await client.query(
|
|
"SELECT name FROM __drizzle_migrations ORDER BY id"
|
|
);
|
|
const appliedSet = new Set(applied.map((r) => r.name));
|
|
|
|
const files = fs
|
|
.readdirSync(MIGRATIONS_DIR)
|
|
.filter((f) => f.endsWith(".sql"))
|
|
.sort();
|
|
|
|
let count = 0;
|
|
for (const file of files) {
|
|
if (appliedSet.has(file)) continue;
|
|
|
|
const sql = fs.readFileSync(path.join(MIGRATIONS_DIR, file), "utf8");
|
|
console.log(`[Migrate] Applying ${file}…`);
|
|
await client.query("BEGIN");
|
|
try {
|
|
await client.query(sql);
|
|
await client.query(
|
|
"INSERT INTO __drizzle_migrations (name) VALUES ($1)",
|
|
[file]
|
|
);
|
|
await client.query("COMMIT");
|
|
count++;
|
|
} catch (err) {
|
|
await client.query("ROLLBACK");
|
|
console.error(`[Migrate] ERROR in ${file}:`, err.message);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
if (count === 0) {
|
|
console.log("[Migrate] All migrations already applied, nothing to do.");
|
|
} else {
|
|
console.log(`[Migrate] Done — applied ${count} migration(s).`);
|
|
}
|
|
|
|
await client.end();
|
|
} catch (err) {
|
|
console.error("[Migrate] Fatal error:", err.message);
|
|
process.exit(1);
|
|
}
|