Release: weekly deploy

This commit is contained in:
Danila Khodjaef
2026-07-27 20:14:51 +03:00
parent 485c7d3e6f
commit 23e1210e86
2 changed files with 30 additions and 1 deletions
+5
View File
@@ -32,6 +32,11 @@ function sqlLiteral(value) {
if (typeof value === 'boolean') return value ? 'TRUE' : 'FALSE';
if (value instanceof Date) return `'${value.toISOString()}'`;
if (typeof value === 'number' || typeof value === 'bigint') return String(value);
// node-pg returns JS arrays for Postgres array columns (e.g. users.permissions TEXT[])
if (Array.isArray(value)) {
if (value.length === 0) return `ARRAY[]::text[]`;
return `ARRAY[${value.map((item) => sqlLiteral(item)).join(', ')}]`;
}
if (typeof value === 'object') {
return `'${JSON.stringify(value).replace(/'/g, "''")}'`;
}
+25 -1
View File
@@ -27,6 +27,28 @@ function getInsertTableName(statement) {
return match ? match[1] : null;
}
/**
* Older backups serialized TEXT[] (e.g. users.permissions) via JSON.stringify,
* producing '["a","b"]' which Postgres rejects as an array literal. Rewrite
* JSON string-array literals to ARRAY['a','b']::text[].
*/
function coerceJsonStringArrayLiterals(statement) {
return statement.replace(/'(\[(?:"(?:\\.|[^"\\])*"(?:\s*,\s*"(?:\\.|[^"\\])*")*)?\])'/g, (full, jsonBody) => {
let arr;
try {
arr = JSON.parse(jsonBody);
} catch {
return full;
}
if (!Array.isArray(arr) || !arr.every((item) => typeof item === 'string')) {
return full;
}
if (arr.length === 0) return `ARRAY[]::text[]`;
const items = arr.map((s) => `'${String(s).replace(/'/g, "''")}'`).join(', ');
return `ARRAY[${items}]::text[]`;
});
}
function filterInsertsForRestore(inserts, prod) {
if (!prod) return inserts;
return inserts.filter((statement) => {
@@ -113,7 +135,9 @@ async function main() {
const config = prod ? loadProdPgConfig() : loadDevPgConfig();
const dbName = config.database;
const allInserts = extractInsertStatements(fs.readFileSync(filePath, 'utf8'));
const allInserts = extractInsertStatements(fs.readFileSync(filePath, 'utf8')).map(
coerceJsonStringArrayLiterals
);
const inserts = filterInsertsForRestore(allInserts, prod);
const skippedInserts = prod ? allInserts.length - inserts.length : 0;