fix(files): backfill — regexp_replace вместо substring FROM (возвращал префикс, а не ключ) + удаление мусорных ключей со слэшами

substring(url FROM '^.*\/api/files/') в PG возвращает СОВПАВШУЮ часть (префикс
URL), а не остаток — backfill писал бы мусорный file_key. regexp_replace
проверен на проде: ключ извлекается корректно из абсолютных и относительных URL.
Добавлен DELETE file_uploads с file_key LIKE '%/%' (ключи слэшей не содержат).
This commit is contained in:
2026-09-29 10:41:53 +03:00
parent 4bac21ff71
commit 102f5410b1

View File

@@ -923,6 +923,10 @@ async function runStartupDataPatches() {
// Паттерны '%/api/files/%' и '%/uploads/%' покрывают и относительные, и старые // Паттерны '%/api/files/%' и '%/uploads/%' покрывают и относительные, и старые
// АБСОЛЮТНЫЕ URL (https://iistwin.ru/api/files/...) — раньше они пропускались, // АБСОЛЮТНЫЕ URL (https://iistwin.ru/api/files/...) — раньше они пропускались,
// и такие файлы были недоступны ботам («не отслеживается»). // и такие файлы были недоступны ботам («не отслеживается»).
// Защита от мусорных ключей (file_key никогда не содержит '/'): удаляем строки,
// вставленные ошибочными/старыми версиями backfill (например, префикс URL целиком).
await db.execute(sql`DELETE FROM file_uploads WHERE file_key LIKE '%/%'`).catch(() => {});
// Single-file field values: { url, name, size } // Single-file field values: { url, name, size }
await db.execute(sql` await db.execute(sql`
INSERT INTO file_uploads (organization_id, uploaded_by, file_key, original_name, size_bytes, task_id, field_id) INSERT INTO file_uploads (organization_id, uploaded_by, file_key, original_name, size_bytes, task_id, field_id)
@@ -930,8 +934,8 @@ async function runStartupDataPatches() {
t.organization_id, t.organization_id,
t.created_by, t.created_by,
CASE CASE
WHEN tfv.value->>'url' LIKE '%/api/files/%' THEN substring(tfv.value->>'url' FROM '^.*/api/files/') WHEN tfv.value->>'url' LIKE '%/api/files/%' THEN regexp_replace(tfv.value->>'url', '^.*/api/files/', '')
WHEN tfv.value->>'url' LIKE '%/uploads/%' THEN substring(tfv.value->>'url' FROM '^.*/uploads/') WHEN tfv.value->>'url' LIKE '%/uploads/%' THEN regexp_replace(tfv.value->>'url', '^.*/uploads/', '')
END AS file_key, END AS file_key,
tfv.value->>'name', tfv.value->>'name',
(tfv.value->>'size')::integer, (tfv.value->>'size')::integer,
@@ -953,8 +957,8 @@ async function runStartupDataPatches() {
t.organization_id, t.organization_id,
t.created_by, t.created_by,
CASE CASE
WHEN elem->>'url' LIKE '%/api/files/%' THEN substring(elem->>'url' FROM '^.*/api/files/') WHEN elem->>'url' LIKE '%/api/files/%' THEN regexp_replace(elem->>'url', '^.*/api/files/', '')
WHEN elem->>'url' LIKE '%/uploads/%' THEN substring(elem->>'url' FROM '^.*/uploads/') WHEN elem->>'url' LIKE '%/uploads/%' THEN regexp_replace(elem->>'url', '^.*/uploads/', '')
END AS file_key, END AS file_key,
elem->>'name', elem->>'name',
(elem->>'size')::integer, (elem->>'size')::integer,
@@ -977,8 +981,8 @@ async function runStartupDataPatches() {
f.organization_id, f.organization_id,
COALESCE(tm.author_id, t.created_by), COALESCE(tm.author_id, t.created_by),
CASE CASE
WHEN att->>'url' LIKE '%/api/files/%' THEN substring(att->>'url' FROM '^.*/api/files/') WHEN att->>'url' LIKE '%/api/files/%' THEN regexp_replace(att->>'url', '^.*/api/files/', '')
WHEN att->>'url' LIKE '%/uploads/%' THEN substring(att->>'url' FROM '^.*/uploads/') WHEN att->>'url' LIKE '%/uploads/%' THEN regexp_replace(att->>'url', '^.*/uploads/', '')
END AS file_key, END AS file_key,
att->>'name', att->>'name',
(att->>'size')::integer, (att->>'size')::integer,