Initial backend import

This commit is contained in:
TerryM
2026-05-16 00:18:22 +08:00
commit 141d92dc15
22 changed files with 2028 additions and 0 deletions

115
migrations/001_init.sql Normal file
View File

@@ -0,0 +1,115 @@
-- ARK 資料庫 initial schema
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
CREATE TABLE admins (
id SERIAL PRIMARY KEY,
email TEXT UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE categories (
id SERIAL PRIMARY KEY,
slug TEXT UNIQUE NOT NULL,
name_zh_tw TEXT NOT NULL,
name_zh_cn TEXT,
name_en TEXT,
description_zh_tw TEXT,
icon_key TEXT DEFAULT 'folder',
sort_order INT NOT NULL DEFAULT 0,
is_visible BOOLEAN NOT NULL DEFAULT TRUE,
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE tags (
id SERIAL PRIMARY KEY,
name TEXT UNIQUE NOT NULL,
slug TEXT UNIQUE NOT NULL
);
CREATE TABLE resources (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
category_id INT NOT NULL REFERENCES categories(id),
title TEXT NOT NULL,
description TEXT,
type TEXT NOT NULL,
language TEXT NOT NULL DEFAULT 'zh-TW',
cover_image TEXT,
file_url TEXT,
preview_url TEXT,
external_url TEXT,
body_text TEXT,
badge_label TEXT,
is_public BOOLEAN NOT NULL DEFAULT TRUE,
is_downloadable BOOLEAN NOT NULL DEFAULT TRUE,
is_recommended BOOLEAN NOT NULL DEFAULT FALSE,
sort_order INT NOT NULL DEFAULT 0,
status TEXT NOT NULL DEFAULT 'draft',
published_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
view_count INT NOT NULL DEFAULT 0,
download_count INT NOT NULL DEFAULT 0,
favorite_count INT NOT NULL DEFAULT 0,
share_count INT NOT NULL DEFAULT 0
);
CREATE INDEX idx_resources_category ON resources(category_id);
CREATE INDEX idx_resources_status_public ON resources(status, is_public);
CREATE INDEX idx_resources_published ON resources(published_at DESC NULLS LAST);
CREATE INDEX idx_resources_recommended ON resources(is_recommended) WHERE is_recommended = TRUE;
CREATE TABLE resource_tags (
resource_id UUID NOT NULL REFERENCES resources(id) ON DELETE CASCADE,
tag_id INT NOT NULL REFERENCES tags(id) ON DELETE CASCADE,
PRIMARY KEY (resource_id, tag_id)
);
CREATE TABLE search_logs (
id BIGSERIAL PRIMARY KEY,
query TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Seed categories (from 文案)
INSERT INTO categories (slug, name_zh_tw, name_zh_cn, name_en, description_zh_tw, icon_key, sort_order) VALUES
('project-ppt', '項目資料PPT', '项目资料PPT', 'Project (PPT)', 'ARK 項目介紹、簡報與對外展示資料', 'folder', 1),
('daily-class', '每日課堂(空課)', '每日课堂(空课)', 'Daily class', '每日課堂、夜聊、培訓與回放', 'calendar', 2),
('official-announcement', '官方公告(推特)', '官方公告(推特)', 'Official news', '官方公告、推文與重要通知', 'megaphone', 3),
('academy-materials', '學堂教育(教材)', '学堂教育(教材)', 'Academy materials', '學堂教材、版書與培訓文檔', 'graduation', 4),
('global-evangelism', '全球布道(影片)', '全球布道(影片)', 'Global outreach', '布道與活動影片', 'globe', 5),
('daily-poster', '每日海報', '每日海报', 'Daily posters', '每日宣傳與活動海報', 'image', 6),
('community-tweets', '社群動向(推文)', '社群动向(推文)', 'Community', '社群推文與推廣文案', 'chat', 7),
('video-hub', '視頻匯總(影片)', '视频汇总(影片)', 'Video hub', '影片資料匯總', 'film', 8),
('subsidy-policy', '補貼政策(活動)', '补贴政策(活动)', 'Campaigns', '補貼、活動規則與激勵方案', 'gift', 9),
('how-to', '操作指南(教程)', '操作指南(教程)', 'Tutorials', '操作教學與使用流程', 'book', 10),
('official-assets', '官方物料(物料)', '官方物料(物料)', 'Brand assets', 'Logo、模板與品牌素材', 'palette', 11),
('media-coverage', '媒體收錄(新聞)', '媒体收录(新闻)', 'Press', '外部媒體與新聞報導', 'newspaper', 12),
('academy-video', '學堂教育(影片)', '学堂教育(影片)', 'Academy video', '學堂課程與培訓影片', 'play', 13),
('general', 'General', 'General', 'General', '綜合或未分類資料', 'hash', 14);
INSERT INTO tags (name, slug) VALUES
('官方推薦', 'official'),
('新人必看', 'newcomer'),
('可下載', 'downloadable'),
('公告', 'announcement'),
('教程', 'tutorial'),
('海報', 'poster');
-- Demo resources (published)
INSERT INTO resources (category_id, title, description, type, language, cover_image, file_url, is_public, is_downloadable, is_recommended, status, published_at, badge_label, sort_order)
SELECT c.id, 'ARK 項目介紹簡報(示例)', '適合線下宣講與新人培訓。', 'ppt', 'zh-TW', '/uploads/placeholder-cover.svg', '/uploads/placeholder-cover.svg', TRUE, TRUE, TRUE, 'published', NOW() - INTERVAL '2 days', '新人必看', 10
FROM categories c WHERE c.slug = 'project-ppt' LIMIT 1;
INSERT INTO resources (category_id, title, description, type, language, cover_image, preview_url, is_public, is_downloadable, is_recommended, status, published_at, badge_label, sort_order)
SELECT c.id, '每日海報 | 示例', '適合每日社群推廣轉發。', 'image', 'zh-TW', '/uploads/placeholder-cover.svg', '/uploads/placeholder-cover.svg', TRUE, TRUE, TRUE, 'published', NOW() - INTERVAL '1 day', '最新公告', 20
FROM categories c WHERE c.slug = 'daily-poster' LIMIT 1;
INSERT INTO resources (category_id, title, description, type, language, external_url, is_public, is_downloadable, is_recommended, status, published_at, sort_order)
SELECT c.id, '外部報導連結(示例)', '第三方媒體文章(示例)。', 'link', 'zh-TW', 'https://example.com', TRUE, FALSE, FALSE, 'published', NOW() - INTERVAL '3 hours', 5
FROM categories c WHERE c.slug = 'media-coverage' LIMIT 1;
INSERT INTO resources (category_id, title, description, type, language, cover_image, file_url, is_public, is_downloadable, status, published_at, sort_order)
SELECT c.id, '全球布道影片(示例)', '活動精華剪輯。', 'video', 'zh-TW', '/uploads/placeholder-cover.svg', 'https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4', TRUE, TRUE, 'published', NOW() - INTERVAL '5 days', 8
FROM categories c WHERE c.slug = 'global-evangelism' LIMIT 1;