source: Klonkt/src/db/migrations/001-init.sql@ d2b7247

main
Last change on this file since d2b7247 was d2b7247, checked in by Robin Genis <roboburr@…>, 2 months ago

chore(branding): stale product names in comments + deploy templates -> Klonkt

Cosmetic only — comments (CSS/EJS/SQL/ecosystem.config.cjs headers) and the deploy/ doc
templates: old names (PrutCMS/PrutFolio) -> Klonkt, stale Prutter (removed DM feature) mentions
dropped, deploy/ Dutch comments translated to English. No code, identifiers, rendered strings,
package name, PWA id, real infra paths, or roboburr refs touched.

  • Property mode set to 100644
File size: 7.9 KB
Line 
1-- Klonkt — initial schema
2-- Forked from a v9 PHP, file-based CMS → SQLite/Node
3-- Complete schema with the v9 features (Forum, Audio, Themes) + v10 CRDT
4
5-- ==================== USERS ====================
6CREATE TABLE IF NOT EXISTS users (
7 id TEXT PRIMARY KEY,
8 username TEXT UNIQUE NOT NULL,
9 email TEXT UNIQUE NOT NULL,
10 password_hash TEXT NOT NULL,
11 role TEXT DEFAULT 'member', -- member, admin, god
12 avatar_url TEXT,
13 bio TEXT,
14 theme TEXT DEFAULT 'dark', -- dark, light
15 palette TEXT DEFAULT 'sage', -- sage, paper, ocean, forest, stone, midnight, sunset, cream
16 created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
17 updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
18);
19
20-- ==================== SITES ====================
21CREATE TABLE IF NOT EXISTS sites (
22 id TEXT PRIMARY KEY,
23 slug TEXT UNIQUE NOT NULL,
24 title TEXT NOT NULL,
25 description TEXT,
26 tagline TEXT,
27 owner_id TEXT NOT NULL,
28 parent_site_id TEXT,
29
30 -- v9 config fields
31 language TEXT DEFAULT 'nl',
32 author TEXT,
33 palette TEXT DEFAULT 'sage',
34 accent TEXT DEFAULT '#c2410c',
35 theme_override TEXT,
36
37 -- Social/SEO
38 social_title TEXT,
39 social_description TEXT,
40 social_image TEXT,
41 og_image_default TEXT,
42 default_cover TEXT,
43 default_description TEXT,
44 canonical TEXT,
45
46 -- Verification metas
47 google_verification TEXT,
48 bing_verification TEXT,
49 pinterest_verification TEXT,
50 yandex_verification TEXT,
51 og_locale TEXT,
52 facebook_app_id TEXT,
53
54 -- Customization
55 custom_css TEXT,
56 custom_head_html TEXT,
57 custom_foot_html TEXT,
58 title_template TEXT DEFAULT '{title} — {site}',
59
60 -- Settings
61 is_public INTEGER DEFAULT 1,
62 closed_circle_mode INTEGER DEFAULT 0,
63 robots_index INTEGER DEFAULT 1,
64 require_login_to_comment INTEGER DEFAULT 1,
65 enable_audio_player INTEGER DEFAULT 1,
66 profile_photo TEXT,
67
68 origin_server TEXT DEFAULT 'local',
69 created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
70 updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
71 FOREIGN KEY (owner_id) REFERENCES users(id),
72 FOREIGN KEY (parent_site_id) REFERENCES sites(id)
73);
74
75-- ==================== SITE MEMBERS ====================
76CREATE TABLE IF NOT EXISTS site_members (
77 site_id TEXT NOT NULL,
78 user_id TEXT NOT NULL,
79 role TEXT DEFAULT 'member',
80 PRIMARY KEY (site_id, user_id),
81 FOREIGN KEY (site_id) REFERENCES sites(id),
82 FOREIGN KEY (user_id) REFERENCES users(id)
83);
84
85-- ==================== POSTS ====================
86CREATE TABLE IF NOT EXISTS posts (
87 id TEXT PRIMARY KEY,
88 site_id TEXT NOT NULL,
89 slug TEXT NOT NULL,
90 author_id TEXT NOT NULL,
91 title TEXT,
92 content TEXT,
93 excerpt TEXT,
94 status TEXT DEFAULT 'draft',
95 cover_image_url TEXT,
96 pinned INTEGER DEFAULT 0,
97 type TEXT DEFAULT 'post',
98 tags TEXT,
99 published_at DATETIME,
100 created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
101 updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
102 yjs_binary BLOB,
103 origin_server TEXT DEFAULT 'local',
104 FOREIGN KEY (site_id) REFERENCES sites(id),
105 FOREIGN KEY (author_id) REFERENCES users(id),
106 UNIQUE (site_id, slug)
107);
108
109-- ==================== COMMENTS (FORUM) ====================
110CREATE TABLE IF NOT EXISTS comments (
111 id TEXT PRIMARY KEY,
112 post_id TEXT NOT NULL,
113 author_id TEXT NOT NULL,
114 parent_comment_id TEXT,
115 content TEXT NOT NULL,
116 status TEXT DEFAULT 'pending',
117 yjs_binary BLOB,
118 created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
119 updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
120 FOREIGN KEY (post_id) REFERENCES posts(id),
121 FOREIGN KEY (author_id) REFERENCES users(id),
122 FOREIGN KEY (parent_comment_id) REFERENCES comments(id)
123);
124
125-- ==================== PRUTTER (DM SYSTEM) ====================
126CREATE TABLE IF NOT EXISTS conversations (
127 id TEXT PRIMARY KEY,
128 user_a_id TEXT NOT NULL,
129 user_b_id TEXT NOT NULL,
130 site_id TEXT,
131 last_message_at DATETIME,
132 created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
133 FOREIGN KEY (user_a_id) REFERENCES users(id),
134 FOREIGN KEY (user_b_id) REFERENCES users(id),
135 FOREIGN KEY (site_id) REFERENCES sites(id),
136 UNIQUE (user_a_id, user_b_id, site_id)
137);
138
139CREATE TABLE IF NOT EXISTS messages (
140 id TEXT PRIMARY KEY,
141 conversation_id TEXT NOT NULL,
142 author_id TEXT NOT NULL,
143 content TEXT NOT NULL,
144 read_at DATETIME,
145 created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
146 FOREIGN KEY (conversation_id) REFERENCES conversations(id),
147 FOREIGN KEY (author_id) REFERENCES users(id)
148);
149
150-- ==================== MEDIA ====================
151CREATE TABLE IF NOT EXISTS media (
152 id TEXT PRIMARY KEY,
153 site_id TEXT NOT NULL,
154 filename TEXT NOT NULL,
155 mime_type TEXT NOT NULL,
156 size INTEGER NOT NULL,
157 storage_path TEXT NOT NULL,
158 created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
159 FOREIGN KEY (site_id) REFERENCES sites(id)
160);
161
162-- ==================== AUDIO TRACKS ====================
163CREATE TABLE IF NOT EXISTS audio_tracks (
164 id TEXT PRIMARY KEY,
165 site_id TEXT NOT NULL,
166 title TEXT NOT NULL,
167 artist TEXT,
168 duration INTEGER,
169 media_id TEXT,
170 position INTEGER DEFAULT 0,
171 created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
172 FOREIGN KEY (site_id) REFERENCES sites(id),
173 FOREIGN KEY (media_id) REFERENCES media(id)
174);
175
176-- ==================== PLAYLISTS ====================
177-- First-class playlist entity (from v9). Posts reference playlists by id;
178-- editing a playlist propagates to every post that embeds it via [[playlist:id]].
179-- `kind` distinguishes display style: 'album' (numbered list) vs 'playlist' (per-track thumbnails).
180CREATE TABLE IF NOT EXISTS playlists (
181 id TEXT PRIMARY KEY, -- slug-style id, e.g. "ai-covers"
182 site_id TEXT NOT NULL,
183 title TEXT NOT NULL,
184 artist TEXT,
185 year INTEGER,
186 cover_url TEXT,
187 kind TEXT DEFAULT 'album', -- 'album' | 'playlist'
188 created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
189 updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
190 FOREIGN KEY (site_id) REFERENCES sites(id)
191);
192
193-- Junction table — preserves track ordering via `position`.
194-- Composite PK prevents the same track being listed twice in the same playlist
195-- but allows the same track to appear in many playlists.
196CREATE TABLE IF NOT EXISTS playlist_tracks (
197 playlist_id TEXT NOT NULL,
198 track_id TEXT NOT NULL,
199 position INTEGER NOT NULL DEFAULT 0,
200 PRIMARY KEY (playlist_id, track_id),
201 FOREIGN KEY (playlist_id) REFERENCES playlists(id) ON DELETE CASCADE,
202 FOREIGN KEY (track_id) REFERENCES audio_tracks(id) ON DELETE CASCADE
203);
204CREATE INDEX IF NOT EXISTS idx_playlist_tracks_pos
205 ON playlist_tracks(playlist_id, position);
206
207-- ==================== NOTIFICATIONS ====================
208CREATE TABLE IF NOT EXISTS notifications (
209 id TEXT PRIMARY KEY,
210 user_id TEXT NOT NULL,
211 type TEXT,
212 title TEXT,
213 message TEXT,
214 link TEXT,
215 read_at DATETIME,
216 created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
217 FOREIGN KEY (user_id) REFERENCES users(id)
218);
219
220-- ==================== SEARCH (FTS5) ====================
221CREATE VIRTUAL TABLE IF NOT EXISTS posts_fts USING fts5(
222 content,
223 title,
224 author,
225 post_id UNINDEXED
226);
227
228-- ==================== FEDERATION ====================
229CREATE TABLE IF NOT EXISTS federation_log (
230 id TEXT PRIMARY KEY,
231 entity_type TEXT,
232 entity_id TEXT,
233 origin_server TEXT,
234 action TEXT,
235 yjs_update BLOB,
236 created_at DATETIME DEFAULT CURRENT_TIMESTAMP
237);
238
239-- ==================== SESSIONS ====================
240CREATE TABLE IF NOT EXISTS sessions (
241 sid TEXT PRIMARY KEY,
242 data TEXT,
243 expiresAt DATETIME
244);
245
246-- ==================== INDEXES ====================
247CREATE INDEX IF NOT EXISTS idx_posts_site ON posts(site_id);
248CREATE INDEX IF NOT EXISTS idx_posts_status ON posts(status);
249CREATE INDEX IF NOT EXISTS idx_comments_post ON comments(post_id);
250CREATE INDEX IF NOT EXISTS idx_messages_conv ON messages(conversation_id);
251CREATE INDEX IF NOT EXISTS idx_notifications_user ON notifications(user_id);
Note: See TracBrowser for help on using the repository browser.