/** * Posts Module - Database * Creates zen_posts and zen_posts_category tables. */ import { query } from '@hykocx/zen/database'; import { getPostsConfig } from './config.js'; async function tableExists(tableName) { const result = await query( `SELECT EXISTS ( SELECT FROM information_schema.tables WHERE table_schema = 'public' AND table_name = $1 )`, [tableName] ); return result.rows[0].exists; } async function createPostsCategoryTable() { const tableName = 'zen_posts_category'; const exists = await tableExists(tableName); if (exists) { console.log(`- Table already exists: ${tableName}`); return { created: false, tableName }; } await query(` CREATE TABLE zen_posts_category ( id SERIAL PRIMARY KEY, post_type VARCHAR(100) NOT NULL, title VARCHAR(255) NOT NULL, description TEXT, is_active BOOLEAN DEFAULT true, created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP ) `); await query(`CREATE INDEX idx_zen_posts_category_post_type ON zen_posts_category(post_type)`); await query(`CREATE INDEX idx_zen_posts_category_is_active ON zen_posts_category(is_active)`); console.log(`✓ Created table: ${tableName}`); return { created: true, tableName }; } async function createPostsTable() { const tableName = 'zen_posts'; const exists = await tableExists(tableName); if (exists) { console.log(`- Table already exists: ${tableName}`); return { created: false, tableName }; } await query(` CREATE TABLE zen_posts ( id SERIAL PRIMARY KEY, post_type VARCHAR(100) NOT NULL, slug VARCHAR(500) NOT NULL, data JSONB NOT NULL DEFAULT '{}', category_id INTEGER REFERENCES zen_posts_category(id) ON DELETE SET NULL, created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, UNIQUE(post_type, slug) ) `); await query(`CREATE INDEX idx_zen_posts_post_type ON zen_posts(post_type)`); await query(`CREATE INDEX idx_zen_posts_post_type_slug ON zen_posts(post_type, slug)`); await query(`CREATE INDEX idx_zen_posts_category_id ON zen_posts(category_id)`); await query(`CREATE INDEX idx_zen_posts_data_gin ON zen_posts USING GIN (data)`); console.log(`✓ Created table: ${tableName}`); return { created: true, tableName }; } async function createPostsRelationsTable() { const tableName = 'zen_posts_relations'; const exists = await tableExists(tableName); if (exists) { console.log(`- Table already exists: ${tableName}`); return { created: false, tableName }; } await query(` CREATE TABLE zen_posts_relations ( id SERIAL PRIMARY KEY, post_id INTEGER NOT NULL REFERENCES zen_posts(id) ON DELETE CASCADE, field_name VARCHAR(100) NOT NULL, related_post_id INTEGER NOT NULL REFERENCES zen_posts(id) ON DELETE CASCADE, sort_order INTEGER DEFAULT 0, UNIQUE(post_id, field_name, related_post_id) ) `); await query(`CREATE INDEX idx_zen_posts_relations_post_id ON zen_posts_relations(post_id)`); await query(`CREATE INDEX idx_zen_posts_relations_related ON zen_posts_relations(related_post_id)`); console.log(`✓ Created table: ${tableName}`); return { created: true, tableName }; } /** * Create all posts-related tables. * zen_posts_category is only created if at least one type uses the 'category' field. * zen_posts_relations is only created if at least one type uses the 'relation' field. * @returns {Promise} */ export async function createTables() { const created = []; const skipped = []; const config = getPostsConfig(); const needsRelations = Object.values(config.types).some(t => t.hasRelations); // zen_posts_category must always be created before zen_posts // because zen_posts has a FK reference to it console.log('\n--- Posts Categories ---'); const catResult = await createPostsCategoryTable(); if (catResult.created) created.push(catResult.tableName); else skipped.push(catResult.tableName); console.log('\n--- Posts ---'); const postResult = await createPostsTable(); if (postResult.created) created.push(postResult.tableName); else skipped.push(postResult.tableName); if (needsRelations) { console.log('\n--- Posts Relations ---'); const relResult = await createPostsRelationsTable(); if (relResult.created) created.push(relResult.tableName); else skipped.push(relResult.tableName); } return { created, skipped }; }