Skip to content

Commit 43d775d

Browse files
Merge pull request #274 from guillermoscript/fix/migration-fix
feat: Introduce migration management scripts and refine database sche…
2 parents 0a18396 + 140adca commit 43d775d

4 files changed

Lines changed: 118 additions & 8 deletions

File tree

scripts/check-migrations.mjs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import pg from 'pg';
2+
3+
const client = new pg.Client({
4+
connectionString: 'postgresql://postgres.tcqqnjfwmbfwcyhafbbt:R808Z4QSPxGet1mC@aws-0-us-east-1.pooler.supabase.com:6543/postgres',
5+
ssl: { rejectUnauthorized: false }
6+
});
7+
8+
await client.connect();
9+
const { rows } = await client.query('SELECT version, name FROM supabase_migrations.schema_migrations ORDER BY version');
10+
console.log(`Total applied: ${rows.length}`);
11+
console.log('Last 10:');
12+
rows.slice(-10).forEach(r => console.log(' ', r.version, r.name || ''));
13+
await client.end();

scripts/manual-migrate.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import pg from 'pg';
2+
import fs from 'fs';
3+
import path from 'path';
4+
5+
const connectionString = "postgresql://postgres.tcqqnjfwmbfwcyhafbbt:R808Z4QSPxGet1mC@aws-0-us-east-1.pooler.supabase.com:6543/postgres";
6+
7+
async function migrate() {
8+
const client = new pg.Client({
9+
connectionString,
10+
ssl: { rejectUnauthorized: false }
11+
});
12+
13+
try {
14+
await client.connect();
15+
console.log('Connected to database');
16+
17+
// Get applied migrations
18+
const { rows } = await client.query('SELECT version FROM supabase_migrations.schema_migrations');
19+
const applied = new Set(rows.map(r => r.version));
20+
21+
const migrationsDir = './supabase/migrations';
22+
const files = fs.readdirSync(migrationsDir)
23+
.filter(f => f.endsWith('.sql'))
24+
.sort();
25+
26+
for (const file of files) {
27+
const version = file.split('_')[0];
28+
if (applied.has(version)) {
29+
console.log(`Skipping applied migration: ${file}`);
30+
continue;
31+
}
32+
33+
console.log(`Applying migration: ${file}`);
34+
const sql = fs.readFileSync(path.join(migrationsDir, file), 'utf8');
35+
36+
await client.query('BEGIN');
37+
try {
38+
// We use the simple query protocol by passing just a string to client.query
39+
// which helps avoid prepared statement issues in transaction mode
40+
await client.query(sql);
41+
await client.query('INSERT INTO supabase_migrations.schema_migrations (version) VALUES ($1)', [version]);
42+
await client.query('COMMIT');
43+
console.log(`Successfully applied ${file}`);
44+
} catch (err) {
45+
await client.query('ROLLBACK');
46+
console.log(`Failed to apply ${file}`);
47+
throw err;
48+
}
49+
}
50+
51+
console.log('All migrations completed successfully');
52+
} catch (err) {
53+
console.error('Migration failed:', err);
54+
process.exit(1);
55+
} finally {
56+
await client.end();
57+
}
58+
}
59+
60+
migrate();

supabase/migrations/20260308210000_replace_landing_pages_with_puck.sql

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,51 @@
22
-- No existing users with custom landing pages, so safe to drop columns.
33

44
-- Replace sections/settings columns with puck_data on landing_pages
5-
ALTER TABLE landing_pages DROP COLUMN IF EXISTS sections;
6-
ALTER TABLE landing_pages DROP COLUMN IF EXISTS settings;
7-
ALTER TABLE landing_pages ADD COLUMN puck_data jsonb NOT NULL DEFAULT '{}';
5+
DO $$
6+
BEGIN
7+
IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'landing_pages') THEN
8+
ALTER TABLE landing_pages DROP COLUMN IF EXISTS sections;
9+
ALTER TABLE landing_pages DROP COLUMN IF EXISTS settings;
10+
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'landing_pages' AND column_name = 'puck_data') THEN
11+
ALTER TABLE landing_pages ADD COLUMN puck_data jsonb NOT NULL DEFAULT '{}';
12+
END IF;
13+
ELSE
14+
CREATE TABLE landing_pages (
15+
page_id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
16+
tenant_id uuid NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
17+
title text NOT NULL,
18+
slug text NOT NULL,
19+
is_published boolean NOT NULL DEFAULT false,
20+
puck_data jsonb NOT NULL DEFAULT '{}',
21+
created_at timestamptz NOT NULL DEFAULT now(),
22+
updated_at timestamptz NOT NULL DEFAULT now(),
23+
UNIQUE (tenant_id, slug)
24+
);
25+
ALTER TABLE landing_pages ENABLE ROW LEVEL SECURITY;
26+
CREATE POLICY "Tenant members can view landing pages" ON landing_pages FOR SELECT USING (tenant_id = (SELECT current_setting('request.jwt.claims', true)::jsonb->>'tenant_id')::uuid);
27+
CREATE POLICY "Tenant admins can manage landing pages" ON landing_pages FOR ALL USING (tenant_id = (SELECT current_setting('request.jwt.claims', true)::jsonb->>'tenant_id')::uuid);
28+
END IF;
29+
END $$;
830

931
-- Replace sections/settings columns with puck_data on landing_page_templates
10-
ALTER TABLE landing_page_templates DROP COLUMN IF EXISTS sections;
11-
ALTER TABLE landing_page_templates DROP COLUMN IF EXISTS settings;
12-
ALTER TABLE landing_page_templates ADD COLUMN puck_data jsonb NOT NULL DEFAULT '{}';
32+
DO $$
33+
BEGIN
34+
IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'landing_page_templates') THEN
35+
ALTER TABLE landing_page_templates DROP COLUMN IF EXISTS sections;
36+
ALTER TABLE landing_page_templates DROP COLUMN IF EXISTS settings;
37+
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'landing_page_templates' AND column_name = 'puck_data') THEN
38+
ALTER TABLE landing_page_templates ADD COLUMN puck_data jsonb NOT NULL DEFAULT '{}';
39+
END IF;
40+
ELSE
41+
CREATE TABLE landing_page_templates (
42+
template_id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
43+
name text NOT NULL,
44+
description text,
45+
puck_data jsonb NOT NULL DEFAULT '{}',
46+
created_at timestamptz NOT NULL DEFAULT now()
47+
);
48+
END IF;
49+
END $$;
1350

1451
-- Create storage bucket for landing page assets (images, etc.)
1552
INSERT INTO storage.buckets (id, name, public, file_size_limit, allowed_mime_types)

supabase/migrations/20260310000000_lesson_resources_sequential_scheduling.sql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,13 +138,13 @@ END;
138138
$$;
139139

140140
-- Schedule via pg_cron every 5 minutes (requires pg_cron extension)
141-
DO $$
141+
DO $do$
142142
BEGIN
143143
IF EXISTS (SELECT 1 FROM pg_extension WHERE extname = 'pg_cron') THEN
144144
PERFORM cron.schedule('publish-scheduled-lessons', '*/5 * * * *', $$SELECT publish_scheduled_lessons()$$);
145145
END IF;
146146
END
147-
$$;
147+
$do$;
148148

149149
-- 1e. Storage bucket for lesson resources
150150
INSERT INTO storage.buckets (id, name, public, file_size_limit, allowed_mime_types)

0 commit comments

Comments
 (0)