feat(academic): защита активов материалов

This commit is contained in:
2026-10-27 19:41:37 +03:00
parent 3fd0d41cf2
commit 9aef82e217
13 changed files with 719 additions and 23 deletions

View File

@@ -0,0 +1,44 @@
-- Backfill связей статья → актив из markdown-ссылок.
-- Исторические статьи уже содержат публичные URL /api/v1/content/assets/{name},
-- но строки article_assets не создавались при импорте. Восстанавливаем связи,
-- чтобы видимость актива наследовалась от статьи/раздела/главы.
WITH refs AS (
SELECT DISTINCT
a.id AS article_id,
(m.match)[1] AS asset_name
FROM articles a
CROSS JOIN LATERAL regexp_matches(
a.body_md,
'/api/v1/content/assets/([A-Za-z0-9._-]+)',
'g'
) AS m(match)
)
INSERT INTO article_assets (article_id, filename, stored_path, mime, size)
SELECT r.article_id,
r.asset_name,
r.asset_name,
CASE lower(substring(r.asset_name from '\.([^.]+)$'))
WHEN 'png' THEN 'image/png'
WHEN 'jpg' THEN 'image/jpeg'
WHEN 'jpeg' THEN 'image/jpeg'
WHEN 'gif' THEN 'image/gif'
WHEN 'svg' THEN 'image/svg+xml'
WHEN 'webp' THEN 'image/webp'
WHEN 'mp4' THEN 'video/mp4'
WHEN 'webm' THEN 'video/webm'
WHEN 'ogg' THEN 'video/ogg'
WHEN 'ogv' THEN 'video/ogg'
ELSE 'application/octet-stream'
END,
0
FROM refs r
WHERE NOT EXISTS (
SELECT 1
FROM article_assets aa
WHERE aa.article_id = r.article_id
AND (
aa.filename = r.asset_name
OR aa.stored_path = r.asset_name
OR right(aa.stored_path, length(r.asset_name)) = r.asset_name
)
);