fix(files): принадлежность файла задаче — дозаполнение task_id/field_id + MCP-проверка с сериализацией значений

Живой тест показал: 77 файлов отслеживаются, но get_task_file отвечал «не
относится к задаче» — старые backfill-строки без task_id/field_id (ON CONFLICT
DO NOTHING их не обновлял), а проверка по file-полям искала ключ только в
строковых значениях (file-поля хранятся объектом/массивом).
- MCP get_task_file: поиск ключа в JSON.stringify(value) для нестроковых значений;
- trackFileOnDemand: у существующих строк без task_id дозаполняет taskId/fieldId;
- startup-backfill: UPDATE task_id/field_id из task_field_values и task_messages
  для строк с task_id IS NULL.
This commit is contained in:
2026-09-29 10:50:34 +03:00
parent 102f5410b1
commit 0b4324c23d
3 changed files with 68 additions and 3 deletions

View File

@@ -997,6 +997,52 @@ async function runStartupDataPatches() {
ON CONFLICT (file_key) DO NOTHING
`).catch(() => {});
// Дозаполнение task_id/field_id у старых строк, вставленных без привязки
// (ранние версии трекинга). ON CONFLICT DO NOTHING их не обновляет.
await db.execute(sql`
UPDATE file_uploads fu
SET task_id = src.task_id, field_id = src.field_id
FROM (
SELECT DISTINCT
CASE
WHEN elem->>'url' LIKE '%/api/files/%' THEN regexp_replace(elem->>'url', '^.*/api/files/', '')
WHEN elem->>'url' LIKE '%/uploads/%' THEN regexp_replace(elem->>'url', '^.*/uploads/', '')
END AS fkey,
tfv.task_id,
tfv.field_id
FROM task_field_values tfv
JOIN form_fields ff ON tfv.field_id = ff.id
CROSS JOIN LATERAL jsonb_array_elements(
CASE WHEN jsonb_typeof(tfv.value) = 'array' THEN tfv.value ELSE jsonb_build_array(tfv.value) END
) elem
WHERE ff.type = 'file'
AND (elem->>'url' LIKE '%/api/files/%' OR elem->>'url' LIKE '%/uploads/%')
) src
WHERE fu.file_key = src.fkey
AND src.fkey IS NOT NULL
AND fu.task_id IS NULL
`).catch(() => {});
await db.execute(sql`
UPDATE file_uploads fu
SET task_id = src.task_id
FROM (
SELECT DISTINCT
CASE
WHEN att->>'url' LIKE '%/api/files/%' THEN regexp_replace(att->>'url', '^.*/api/files/', '')
WHEN att->>'url' LIKE '%/uploads/%' THEN regexp_replace(att->>'url', '^.*/uploads/', '')
END AS fkey,
tm.task_id
FROM task_messages tm
CROSS JOIN LATERAL jsonb_array_elements(tm.attachments) att
WHERE tm.attachments IS NOT NULL
AND jsonb_typeof(tm.attachments) = 'array'
AND (att->>'url' LIKE '%/api/files/%' OR att->>'url' LIKE '%/uploads/%')
) src
WHERE fu.file_key = src.fkey
AND src.fkey IS NOT NULL
AND fu.task_id IS NULL
`).catch(() => {});
// RLS policies are created by migration 0011_rls_tasks_notnull.sql.
// Startup only activates ENABLE + FORCE ROW LEVEL SECURITY when ENABLE_RLS=true,
// then verifies all tables are correctly protected before allowing traffic.