|
| 1 | +-- ============================================================================= |
| 2 | +-- 003_config_tables.sql |
| 3 | +-- Six singleton config tables for the CodingCat.dev Automated Content Engine. |
| 4 | +-- Each table enforces a single row via CHECK (id = 1). |
| 5 | +-- RLS: service_role can SELECT/UPDATE; anon and authenticated are blocked. |
| 6 | +-- ============================================================================= |
| 7 | + |
| 8 | +-- --------------------------------------------------------------------------- |
| 9 | +-- Shared trigger function: auto-update updated_at on any row change |
| 10 | +-- --------------------------------------------------------------------------- |
| 11 | +CREATE OR REPLACE FUNCTION update_updated_at() |
| 12 | +RETURNS trigger AS $$ |
| 13 | +BEGIN |
| 14 | + NEW.updated_at = now(); |
| 15 | + RETURN NEW; |
| 16 | +END; |
| 17 | +$$ LANGUAGE plpgsql; |
| 18 | + |
| 19 | +-- ========================================================================= |
| 20 | +-- 1. pipeline_config |
| 21 | +-- ========================================================================= |
| 22 | +CREATE TABLE IF NOT EXISTS pipeline_config ( |
| 23 | + id integer PRIMARY KEY DEFAULT 1 CHECK (id = 1), |
| 24 | + gemini_model text NOT NULL DEFAULT 'gemini-2.0-flash', |
| 25 | + elevenlabs_voice_id text NOT NULL DEFAULT 'pNInz6obpgDQGcFmaJgB', |
| 26 | + youtube_upload_visibility text NOT NULL DEFAULT 'private', |
| 27 | + youtube_channel_id text NOT NULL DEFAULT '', |
| 28 | + enable_notebooklm_research boolean NOT NULL DEFAULT false, |
| 29 | + quality_threshold integer NOT NULL DEFAULT 50, |
| 30 | + stuck_timeout_minutes integer NOT NULL DEFAULT 30, |
| 31 | + max_ideas_per_run integer NOT NULL DEFAULT 1, |
| 32 | + updated_at timestamptz NOT NULL DEFAULT now() |
| 33 | +); |
| 34 | + |
| 35 | +ALTER TABLE pipeline_config ENABLE ROW LEVEL SECURITY; |
| 36 | + |
| 37 | +CREATE POLICY "service_role can read pipeline_config" |
| 38 | + ON pipeline_config FOR SELECT |
| 39 | + TO service_role |
| 40 | + USING (true); |
| 41 | + |
| 42 | +CREATE POLICY "service_role can update pipeline_config" |
| 43 | + ON pipeline_config FOR UPDATE |
| 44 | + TO service_role |
| 45 | + USING (true) |
| 46 | + WITH CHECK (true); |
| 47 | + |
| 48 | +INSERT INTO pipeline_config (id) VALUES (1) ON CONFLICT (id) DO NOTHING; |
| 49 | + |
| 50 | +CREATE TRIGGER trg_pipeline_config_updated_at |
| 51 | + BEFORE UPDATE ON pipeline_config |
| 52 | + FOR EACH ROW EXECUTE FUNCTION update_updated_at(); |
| 53 | + |
| 54 | +COMMENT ON TABLE pipeline_config IS 'Core video pipeline settings: Gemini model, voice, YouTube visibility, quality thresholds'; |
| 55 | + |
| 56 | +-- ========================================================================= |
| 57 | +-- 2. remotion_config |
| 58 | +-- ========================================================================= |
| 59 | +CREATE TABLE IF NOT EXISTS remotion_config ( |
| 60 | + id integer PRIMARY KEY DEFAULT 1 CHECK (id = 1), |
| 61 | + aws_region text NOT NULL DEFAULT 'us-east-1', |
| 62 | + function_name text NOT NULL DEFAULT '', |
| 63 | + serve_url text NOT NULL DEFAULT '', |
| 64 | + max_render_timeout_sec integer NOT NULL DEFAULT 240, |
| 65 | + memory_mb integer NOT NULL DEFAULT 2048, |
| 66 | + disk_mb integer NOT NULL DEFAULT 2048, |
| 67 | + updated_at timestamptz NOT NULL DEFAULT now() |
| 68 | +); |
| 69 | + |
| 70 | +ALTER TABLE remotion_config ENABLE ROW LEVEL SECURITY; |
| 71 | + |
| 72 | +CREATE POLICY "service_role can read remotion_config" |
| 73 | + ON remotion_config FOR SELECT |
| 74 | + TO service_role |
| 75 | + USING (true); |
| 76 | + |
| 77 | +CREATE POLICY "service_role can update remotion_config" |
| 78 | + ON remotion_config FOR UPDATE |
| 79 | + TO service_role |
| 80 | + USING (true) |
| 81 | + WITH CHECK (true); |
| 82 | + |
| 83 | +INSERT INTO remotion_config (id) VALUES (1) ON CONFLICT (id) DO NOTHING; |
| 84 | + |
| 85 | +CREATE TRIGGER trg_remotion_config_updated_at |
| 86 | + BEFORE UPDATE ON remotion_config |
| 87 | + FOR EACH ROW EXECUTE FUNCTION update_updated_at(); |
| 88 | + |
| 89 | +COMMENT ON TABLE remotion_config IS 'Remotion Lambda rendering: AWS region, function name, serve URL, resource limits'; |
| 90 | + |
| 91 | +-- ========================================================================= |
| 92 | +-- 3. content_config |
| 93 | +-- ========================================================================= |
| 94 | +CREATE TABLE IF NOT EXISTS content_config ( |
| 95 | + id integer PRIMARY KEY DEFAULT 1 CHECK (id = 1), |
| 96 | + rss_feeds jsonb NOT NULL DEFAULT '[{"name":"HN Top","url":"https://hnrss.org/newest?points=100&count=20"},{"name":"Dev.to JavaScript","url":"https://dev.to/feed/tag/javascript"},{"name":"Dev.to WebDev","url":"https://dev.to/feed/tag/webdev"},{"name":"CSS-Tricks","url":"https://css-tricks.com/feed/"},{"name":"Chromium Blog","url":"https://blog.chromium.org/feeds/posts/default"},{"name":"web.dev","url":"https://web.dev/feed.xml"},{"name":"Smashing Magazine","url":"https://www.smashingmagazine.com/feed/"},{"name":"JavaScript Weekly","url":"https://javascriptweekly.com/rss/"}]'::jsonb, |
| 97 | + trend_sources_enabled jsonb NOT NULL DEFAULT '{"hn":true,"devto":true,"blogs":true,"youtube":true,"github":true}'::jsonb, |
| 98 | + system_instruction text NOT NULL DEFAULT 'You are a content strategist and scriptwriter for CodingCat.dev, a web development education channel run by Alex Patterson. |
| 99 | +
|
| 100 | +Your style is inspired by Cleo Abram''s "Huge If True" — you make complex technical topics feel exciting, accessible, and important. Key principles: |
| 101 | +- Start with a BOLD claim or surprising fact that makes people stop scrolling |
| 102 | +- Use analogies and real-world comparisons to explain technical concepts |
| 103 | +- Build tension: "Here''s the problem... here''s why it matters... here''s the breakthrough" |
| 104 | +- Keep energy HIGH — short sentences, active voice, conversational tone |
| 105 | +- End with a clear takeaway that makes the viewer feel smarter |
| 106 | +- Target audience: developers who want to stay current but don''t have time to read everything |
| 107 | +
|
| 108 | +Script format: 60-90 second explainer videos. Think TikTok/YouTube Shorts energy with real educational depth. |
| 109 | +
|
| 110 | +CodingCat.dev covers: React, Next.js, TypeScript, Svelte, web APIs, CSS, Node.js, cloud services, AI/ML for developers, and web platform updates.', |
| 111 | + target_video_duration_sec integer NOT NULL DEFAULT 90, |
| 112 | + scene_count_min integer NOT NULL DEFAULT 3, |
| 113 | + scene_count_max integer NOT NULL DEFAULT 5, |
| 114 | + updated_at timestamptz NOT NULL DEFAULT now() |
| 115 | +); |
| 116 | + |
| 117 | +ALTER TABLE content_config ENABLE ROW LEVEL SECURITY; |
| 118 | + |
| 119 | +CREATE POLICY "service_role can read content_config" |
| 120 | + ON content_config FOR SELECT |
| 121 | + TO service_role |
| 122 | + USING (true); |
| 123 | + |
| 124 | +CREATE POLICY "service_role can update content_config" |
| 125 | + ON content_config FOR UPDATE |
| 126 | + TO service_role |
| 127 | + USING (true) |
| 128 | + WITH CHECK (true); |
| 129 | + |
| 130 | +INSERT INTO content_config (id) VALUES (1) ON CONFLICT (id) DO NOTHING; |
| 131 | + |
| 132 | +CREATE TRIGGER trg_content_config_updated_at |
| 133 | + BEFORE UPDATE ON content_config |
| 134 | + FOR EACH ROW EXECUTE FUNCTION update_updated_at(); |
| 135 | + |
| 136 | +COMMENT ON TABLE content_config IS 'Content discovery and generation: RSS feeds, trend sources, system prompt, video duration'; |
| 137 | + |
| 138 | +-- ========================================================================= |
| 139 | +-- 4. sponsor_config |
| 140 | +-- ========================================================================= |
| 141 | +CREATE TABLE IF NOT EXISTS sponsor_config ( |
| 142 | + id integer PRIMARY KEY DEFAULT 1 CHECK (id = 1), |
| 143 | + cooldown_days integer NOT NULL DEFAULT 14, |
| 144 | + rate_card_tiers jsonb NOT NULL DEFAULT '[{"name":"starter","description":"5k-10k impressions","price":500},{"name":"growth","description":"10k-50k impressions","price":1500},{"name":"premium","description":"50k+ impressions","price":3000}]'::jsonb, |
| 145 | + outreach_email_template text NOT NULL DEFAULT 'Hi {{companyName}}, |
| 146 | +
|
| 147 | +I run CodingCat.dev, a web development education channel. We''d love to explore a sponsorship opportunity with you. |
| 148 | +
|
| 149 | +Best, |
| 150 | +Alex Patterson', |
| 151 | + max_outreach_per_run integer NOT NULL DEFAULT 10, |
| 152 | + updated_at timestamptz NOT NULL DEFAULT now() |
| 153 | +); |
| 154 | + |
| 155 | +ALTER TABLE sponsor_config ENABLE ROW LEVEL SECURITY; |
| 156 | + |
| 157 | +CREATE POLICY "service_role can read sponsor_config" |
| 158 | + ON sponsor_config FOR SELECT |
| 159 | + TO service_role |
| 160 | + USING (true); |
| 161 | + |
| 162 | +CREATE POLICY "service_role can update sponsor_config" |
| 163 | + ON sponsor_config FOR UPDATE |
| 164 | + TO service_role |
| 165 | + USING (true) |
| 166 | + WITH CHECK (true); |
| 167 | + |
| 168 | +INSERT INTO sponsor_config (id) VALUES (1) ON CONFLICT (id) DO NOTHING; |
| 169 | + |
| 170 | +CREATE TRIGGER trg_sponsor_config_updated_at |
| 171 | + BEFORE UPDATE ON sponsor_config |
| 172 | + FOR EACH ROW EXECUTE FUNCTION update_updated_at(); |
| 173 | + |
| 174 | +COMMENT ON TABLE sponsor_config IS 'Sponsor pipeline: cooldown periods, rate card tiers, outreach templates'; |
| 175 | + |
| 176 | +-- ========================================================================= |
| 177 | +-- 5. distribution_config |
| 178 | +-- ========================================================================= |
| 179 | +CREATE TABLE IF NOT EXISTS distribution_config ( |
| 180 | + id integer PRIMARY KEY DEFAULT 1 CHECK (id = 1), |
| 181 | + notification_emails jsonb NOT NULL DEFAULT '["alex@codingcat.dev"]'::jsonb, |
| 182 | + youtube_description_template text NOT NULL DEFAULT '{{title}} |
| 183 | +
|
| 184 | +{{summary}} |
| 185 | +
|
| 186 | +🔗 Learn more at https://codingcat.dev |
| 187 | +
|
| 188 | +#webdev #coding #programming', |
| 189 | + youtube_default_tags jsonb NOT NULL DEFAULT '["web development","coding","programming","tutorial","codingcat"]'::jsonb, |
| 190 | + resend_from_email text NOT NULL DEFAULT 'content@codingcat.dev', |
| 191 | + updated_at timestamptz NOT NULL DEFAULT now() |
| 192 | +); |
| 193 | + |
| 194 | +ALTER TABLE distribution_config ENABLE ROW LEVEL SECURITY; |
| 195 | + |
| 196 | +CREATE POLICY "service_role can read distribution_config" |
| 197 | + ON distribution_config FOR SELECT |
| 198 | + TO service_role |
| 199 | + USING (true); |
| 200 | + |
| 201 | +CREATE POLICY "service_role can update distribution_config" |
| 202 | + ON distribution_config FOR UPDATE |
| 203 | + TO service_role |
| 204 | + USING (true) |
| 205 | + WITH CHECK (true); |
| 206 | + |
| 207 | +INSERT INTO distribution_config (id) VALUES (1) ON CONFLICT (id) DO NOTHING; |
| 208 | + |
| 209 | +CREATE TRIGGER trg_distribution_config_updated_at |
| 210 | + BEFORE UPDATE ON distribution_config |
| 211 | + FOR EACH ROW EXECUTE FUNCTION update_updated_at(); |
| 212 | + |
| 213 | +COMMENT ON TABLE distribution_config IS 'Distribution: notification emails, YouTube templates, default tags'; |
| 214 | + |
| 215 | +-- ========================================================================= |
| 216 | +-- 6. gcs_config |
| 217 | +-- ========================================================================= |
| 218 | +CREATE TABLE IF NOT EXISTS gcs_config ( |
| 219 | + id integer PRIMARY KEY DEFAULT 1 CHECK (id = 1), |
| 220 | + bucket_name text NOT NULL DEFAULT 'codingcatdev-content-engine', |
| 221 | + project_id text NOT NULL DEFAULT 'codingcatdev', |
| 222 | + updated_at timestamptz NOT NULL DEFAULT now() |
| 223 | +); |
| 224 | + |
| 225 | +ALTER TABLE gcs_config ENABLE ROW LEVEL SECURITY; |
| 226 | + |
| 227 | +CREATE POLICY "service_role can read gcs_config" |
| 228 | + ON gcs_config FOR SELECT |
| 229 | + TO service_role |
| 230 | + USING (true); |
| 231 | + |
| 232 | +CREATE POLICY "service_role can update gcs_config" |
| 233 | + ON gcs_config FOR UPDATE |
| 234 | + TO service_role |
| 235 | + USING (true) |
| 236 | + WITH CHECK (true); |
| 237 | + |
| 238 | +INSERT INTO gcs_config (id) VALUES (1) ON CONFLICT (id) DO NOTHING; |
| 239 | + |
| 240 | +CREATE TRIGGER trg_gcs_config_updated_at |
| 241 | + BEFORE UPDATE ON gcs_config |
| 242 | + FOR EACH ROW EXECUTE FUNCTION update_updated_at(); |
| 243 | + |
| 244 | +COMMENT ON TABLE gcs_config IS 'Google Cloud Storage: bucket name and project ID'; |
0 commit comments