Files
flatrender/backend/db/migrations/06_content_projects.sql
soroush.asadi 90ac0b81d1 feat: V2 microservices stack — backend services, gateway, JWT auth
Add full V2 architecture: identity, content, studio (.NET 10) and file,
render, notification, gateway (Go) services with vendored deps, plus DB
migrations, event/API contracts, and an init-db script.

Wire the Next.js frontend to the gateway: server-side JWT auth routes
(login/register/refresh/logout/me), gateway fetch helper, and session/
cookie/jwt helpers under src/lib.

Containerize the stack via docker-compose.v2.yml and per-service
Dockerfiles. Base images resolve through a Nexus mirror (Docker Hub) and
MCR directly; npm/NuGet pull from Nexus groups. Self-host fonts via
next/font/local to avoid Google Fonts (geo-blocked).

Add CI workflow and ignore .env.v2, *.stackdump, and .NET bin/obj.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 23:29:31 +03:30

144 lines
6.1 KiB
SQL

-- =====================================================================
-- CONTENT SCHEMA — Part 2: Project Containers & Projects (Templates)
-- =====================================================================
SET search_path TO content, public;
-- Tenants can mark projects as private (only their users see them)
-- or use the global FlatRender catalog.
CREATE TYPE resolution_kind AS ENUM ('HD','FullHD','TwoK','FourK');
-- ---------------------------------------------------------------------
-- project_containers — the "product" (template pack)
-- ---------------------------------------------------------------------
CREATE TABLE project_containers (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID, -- NULL = global
-- FK to identity.tenants is enforced via service code (cross-schema FK kept loose)
slug CITEXT NOT NULL UNIQUE,
name TEXT NOT NULL,
description TEXT,
keywords TEXT,
news_text TEXT,
-- Media
image TEXT,
demo TEXT,
full_demo TEXT,
mini_demo TEXT,
demo_script_tag TEXT,
-- Modes & classifications
is_published BOOLEAN NOT NULL DEFAULT FALSE,
is_premium BOOLEAN NOT NULL DEFAULT FALSE,
is_mockup BOOLEAN NOT NULL DEFAULT FALSE,
primary_mode choose_mode NOT NULL DEFAULT 'FLEXIBLE',
-- Stats (denormalized for speed)
rate_avg NUMERIC(3,2),
rate_count INT NOT NULL DEFAULT 0,
view_count BIGINT NOT NULL DEFAULT 0,
use_count BIGINT NOT NULL DEFAULT 0,
sort INT NOT NULL DEFAULT 0,
sort_date TIMESTAMPTZ NOT NULL DEFAULT NOW(),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
deleted_at TIMESTAMPTZ
);
CREATE INDEX idx_containers_published ON project_containers(is_published, sort_date DESC) WHERE deleted_at IS NULL;
CREATE INDEX idx_containers_tenant ON project_containers(tenant_id) WHERE deleted_at IS NULL;
CREATE INDEX idx_containers_name_trgm ON project_containers USING gin (name gin_trgm_ops);
CREATE TRIGGER tg_containers_updated_at
BEFORE UPDATE ON project_containers FOR EACH ROW EXECUTE FUNCTION public.tg_set_updated_at();
-- ---------------------------------------------------------------------
-- M2M: container ↔ categories
-- ---------------------------------------------------------------------
CREATE TABLE container_categories (
container_id UUID NOT NULL REFERENCES project_containers(id) ON DELETE CASCADE,
category_id UUID NOT NULL REFERENCES categories(id) ON DELETE CASCADE,
sort INT NOT NULL DEFAULT 0,
PRIMARY KEY (container_id, category_id)
);
CREATE INDEX idx_cc_category ON container_categories(category_id);
-- ---------------------------------------------------------------------
-- M2M: container ↔ tags
-- ---------------------------------------------------------------------
CREATE TABLE container_tags (
container_id UUID NOT NULL REFERENCES project_containers(id) ON DELETE CASCADE,
tag_id UUID NOT NULL REFERENCES tags(id) ON DELETE CASCADE,
PRIMARY KEY (container_id, tag_id)
);
CREATE INDEX idx_ct_tag ON container_tags(tag_id);
-- ---------------------------------------------------------------------
-- projects — one aspect-ratio variant of a container
-- ---------------------------------------------------------------------
CREATE TABLE projects (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
container_id UUID NOT NULL REFERENCES project_containers(id) ON DELETE CASCADE,
project_server_id UUID REFERENCES project_servers(id),
name TEXT NOT NULL,
description TEXT,
image TEXT,
full_demo TEXT,
demo_script_tag TEXT,
download_link TEXT,
-- AEP file storage
aep_minio_bucket TEXT,
aep_minio_key TEXT,
aep_file_url TEXT,
aep_file_md5 TEXT, -- for node cache check
aep_file_size_bytes BIGINT,
aep_uploaded_at TIMESTAMPTZ,
folder TEXT, -- legacy path on server
-- Geometry
original_width INT NOT NULL,
original_height INT NOT NULL,
aspect TEXT, -- '16:9','9:16','1:1','4:5',...
-- Timing
project_duration_sec NUMERIC(8,2) NOT NULL,
min_duration_sec NUMERIC(8,2),
max_duration_sec NUMERIC(8,2),
free_fps INT NOT NULL DEFAULT 30,
-- Mode
choose_mode choose_mode NOT NULL,
resolution resolution_kind NOT NULL DEFAULT 'FullHD',
vip_factor NUMERIC(4,2) NOT NULL DEFAULT 1.0,
render_aep_comp TEXT NOT NULL DEFAULT 'flatrender', -- main comp name
-- Misc (legacy artifacts to preserve)
shared_layer_image TEXT,
shared_colors_svg TEXT,
shared_color_presets_svg TEXT,
-- Status
is_published BOOLEAN NOT NULL DEFAULT FALSE,
sort INT NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
deleted_at TIMESTAMPTZ
);
CREATE INDEX idx_projects_container ON projects(container_id) WHERE deleted_at IS NULL;
CREATE INDEX idx_projects_published ON projects(is_published) WHERE deleted_at IS NULL;
CREATE INDEX idx_projects_aep_md5 ON projects(aep_file_md5) WHERE aep_file_md5 IS NOT NULL;
CREATE TRIGGER tg_projects_updated_at
BEFORE UPDATE ON projects FOR EACH ROW EXECUTE FUNCTION public.tg_set_updated_at();