v1.10.88 품질·보안 개선과 미디어 카탈로그 database 전환을 반영한다.
세션·로그인 제한, ZIP/업로드 스트리밍, CSP 최소 강제와 Report-Only, Playwright 격리 검증을 추가하고 미디어 목록을 reconcile 후 database 모드로 고정한다. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
9
db/migrations/067_users_session_version.sql
Normal file
9
db/migrations/067_users_session_version.sql
Normal file
@@ -0,0 +1,9 @@
|
||||
ALTER TABLE users
|
||||
ADD COLUMN IF NOT EXISTS session_version INTEGER NOT NULL DEFAULT 1;
|
||||
|
||||
ALTER TABLE users
|
||||
DROP CONSTRAINT IF EXISTS users_session_version_check;
|
||||
|
||||
ALTER TABLE users
|
||||
ADD CONSTRAINT users_session_version_check
|
||||
CHECK (session_version > 0);
|
||||
22
db/migrations/068_csp_violation_aggregates.sql
Normal file
22
db/migrations/068_csp_violation_aggregates.sql
Normal file
@@ -0,0 +1,22 @@
|
||||
CREATE TABLE IF NOT EXISTS csp_violation_aggregates (
|
||||
policy_version TEXT NOT NULL,
|
||||
document_path TEXT NOT NULL,
|
||||
effective_directive TEXT NOT NULL,
|
||||
blocked_origin TEXT NOT NULL,
|
||||
user_agent_family TEXT NOT NULL,
|
||||
violation_count BIGINT NOT NULL DEFAULT 1,
|
||||
first_seen_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
last_seen_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
PRIMARY KEY (
|
||||
policy_version,
|
||||
document_path,
|
||||
effective_directive,
|
||||
blocked_origin,
|
||||
user_agent_family
|
||||
),
|
||||
CONSTRAINT csp_violation_aggregates_count_check
|
||||
CHECK (violation_count > 0)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS csp_violation_aggregates_last_seen_idx
|
||||
ON csp_violation_aggregates (last_seen_at DESC);
|
||||
148
db/migrations/069_media_catalog.sql
Normal file
148
db/migrations/069_media_catalog.sql
Normal file
@@ -0,0 +1,148 @@
|
||||
ALTER TABLE media_metadata
|
||||
ADD COLUMN IF NOT EXISTS id BIGINT GENERATED BY DEFAULT AS IDENTITY,
|
||||
ADD COLUMN IF NOT EXISTS file_name TEXT,
|
||||
ADD COLUMN IF NOT EXISTS storage_key TEXT,
|
||||
ADD COLUMN IF NOT EXISTS extension TEXT,
|
||||
ADD COLUMN IF NOT EXISTS kind TEXT,
|
||||
ADD COLUMN IF NOT EXISTS role TEXT,
|
||||
ADD COLUMN IF NOT EXISTS size_bytes BIGINT,
|
||||
ADD COLUMN IF NOT EXISTS modified_at TIMESTAMPTZ,
|
||||
ADD COLUMN IF NOT EXISTS parent_id BIGINT,
|
||||
ADD COLUMN IF NOT EXISTS origin TEXT,
|
||||
ADD COLUMN IF NOT EXISTS status TEXT,
|
||||
ADD COLUMN IF NOT EXISTS last_seen_at TIMESTAMPTZ,
|
||||
ADD COLUMN IF NOT EXISTS missing_since TIMESTAMPTZ,
|
||||
ADD COLUMN IF NOT EXISTS error_detail TEXT;
|
||||
|
||||
UPDATE media_metadata
|
||||
SET
|
||||
file_name = COALESCE(file_name, regexp_replace(url, '^.*/', '')),
|
||||
storage_key = COALESCE(storage_key, regexp_replace(url, '^/uploads/', '')),
|
||||
extension = COALESCE(extension, lower(substring(url from '\.[^.?#/]+$'))),
|
||||
kind = COALESCE(kind, 'file'),
|
||||
role = COALESCE(role, 'original'),
|
||||
origin = COALESCE(origin, 'external'),
|
||||
status = COALESCE(status, 'staging')
|
||||
WHERE file_name IS NULL
|
||||
OR storage_key IS NULL
|
||||
OR kind IS NULL
|
||||
OR role IS NULL
|
||||
OR origin IS NULL
|
||||
OR status IS NULL;
|
||||
|
||||
ALTER TABLE media_metadata
|
||||
ALTER COLUMN file_name SET NOT NULL,
|
||||
ALTER COLUMN storage_key SET NOT NULL,
|
||||
ALTER COLUMN kind SET NOT NULL,
|
||||
ALTER COLUMN role SET NOT NULL,
|
||||
ALTER COLUMN origin SET NOT NULL,
|
||||
ALTER COLUMN status SET NOT NULL;
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS media_metadata_id_unique_idx
|
||||
ON media_metadata (id);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS media_metadata_storage_key_unique_idx
|
||||
ON media_metadata (storage_key);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS media_metadata_status_updated_idx
|
||||
ON media_metadata (status, modified_at DESC);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS media_metadata_kind_role_idx
|
||||
ON media_metadata (kind, role);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS media_metadata_parent_idx
|
||||
ON media_metadata (parent_id)
|
||||
WHERE parent_id IS NOT NULL;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS media_metadata_last_seen_idx
|
||||
ON media_metadata (last_seen_at DESC);
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM pg_constraint
|
||||
WHERE conname = 'media_metadata_kind_check'
|
||||
) THEN
|
||||
ALTER TABLE media_metadata
|
||||
ADD CONSTRAINT media_metadata_kind_check
|
||||
CHECK (kind IN ('image', 'video', 'audio', 'file'));
|
||||
END IF;
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM pg_constraint
|
||||
WHERE conname = 'media_metadata_status_check'
|
||||
) THEN
|
||||
ALTER TABLE media_metadata
|
||||
ADD CONSTRAINT media_metadata_status_check
|
||||
CHECK (status IN ('staging', 'active', 'missing', 'deleting', 'error'));
|
||||
END IF;
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM pg_constraint
|
||||
WHERE conname = 'media_metadata_origin_check'
|
||||
) THEN
|
||||
ALTER TABLE media_metadata
|
||||
ADD CONSTRAINT media_metadata_origin_check
|
||||
CHECK (origin IN ('upload', 'import', 'external', 'derived', 'system'));
|
||||
END IF;
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM pg_constraint
|
||||
WHERE conname = 'media_metadata_parent_fk'
|
||||
) THEN
|
||||
ALTER TABLE media_metadata
|
||||
ADD CONSTRAINT media_metadata_parent_fk
|
||||
FOREIGN KEY (parent_id)
|
||||
REFERENCES media_metadata (id)
|
||||
ON DELETE SET NULL;
|
||||
END IF;
|
||||
END
|
||||
$$;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS media_references (
|
||||
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
media_id BIGINT REFERENCES media_metadata (id) ON DELETE CASCADE,
|
||||
media_url TEXT NOT NULL,
|
||||
reference_type TEXT NOT NULL,
|
||||
reference_id TEXT NOT NULL,
|
||||
reference_field TEXT NOT NULL,
|
||||
reference_title TEXT NOT NULL DEFAULT '',
|
||||
reference_slug TEXT NOT NULL DEFAULT '',
|
||||
reference_status TEXT NOT NULL DEFAULT '',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
CONSTRAINT media_references_target_unique
|
||||
UNIQUE (media_url, reference_type, reference_id, reference_field)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS media_references_media_id_idx
|
||||
ON media_references (media_id)
|
||||
WHERE media_id IS NOT NULL;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS media_references_url_idx
|
||||
ON media_references (media_url);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS media_references_target_idx
|
||||
ON media_references (reference_type, reference_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS media_catalog_state (
|
||||
singleton BOOLEAN PRIMARY KEY DEFAULT true,
|
||||
ready BOOLEAN NOT NULL DEFAULT false,
|
||||
reconciled_at TIMESTAMPTZ,
|
||||
filesystem_count BIGINT NOT NULL DEFAULT 0,
|
||||
filesystem_bytes BIGINT NOT NULL DEFAULT 0,
|
||||
catalog_count BIGINT NOT NULL DEFAULT 0,
|
||||
catalog_bytes BIGINT NOT NULL DEFAULT 0,
|
||||
error_detail TEXT,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
CONSTRAINT media_catalog_state_singleton_check
|
||||
CHECK (singleton = true)
|
||||
);
|
||||
|
||||
INSERT INTO media_catalog_state (singleton, ready)
|
||||
VALUES (true, false)
|
||||
ON CONFLICT (singleton) DO NOTHING;
|
||||
Reference in New Issue
Block a user