- Implement complete authentication system with JWT token validation - Add auth provider with persistent login state across page refreshes - Create multilingual login/register forms with Material-UI components - Fix token validation using raw SQL queries to bypass Prisma sync issues - Add comprehensive error handling for expired/invalid tokens - Create profile and settings pages with full i18n support - Add proper user role management (admin/user) with database sync - Implement secure middleware with CSRF protection and auth checks - Add debug endpoints for troubleshooting authentication issues - Fix Zustand store persistence for authentication state 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
75 lines
2.8 KiB
TypeScript
75 lines
2.8 KiB
TypeScript
import 'dotenv/config'
|
|
import { Pool } from 'pg'
|
|
|
|
async function main() {
|
|
const pool = new Pool({ connectionString: process.env.DATABASE_URL })
|
|
const schema = (process.env.VECTOR_SCHEMA || 'ai_bible').replace(/[^a-zA-Z0-9_]/g, '')
|
|
const source = `${schema}.bv_ro_fidela`
|
|
const target = `${schema}.bv_ro_cornilescu`
|
|
|
|
const client = await pool.connect()
|
|
try {
|
|
console.log('Cloning vector table from', source, 'to', target)
|
|
await client.query('BEGIN')
|
|
await client.query(`CREATE EXTENSION IF NOT EXISTS vector;`)
|
|
await client.query(`CREATE SCHEMA IF NOT EXISTS "${schema}";`)
|
|
// Create target table if not exists with same structure
|
|
await client.query(`
|
|
DO $$
|
|
BEGIN
|
|
IF NOT EXISTS (
|
|
SELECT 1 FROM information_schema.tables
|
|
WHERE table_schema = '${schema}' AND table_name = 'bv_ro_cornilescu') THEN
|
|
EXECUTE format('CREATE TABLE %I.%I (LIKE %I.%I INCLUDING ALL)', '${schema}', 'bv_ro_cornilescu', '${schema}', 'bv_ro_fidela');
|
|
END IF;
|
|
END$$;`)
|
|
|
|
// Insert rows if target empty
|
|
const cnt = await client.query(`SELECT count(*)::int AS c FROM ${target}`)
|
|
if ((cnt.rows?.[0]?.c ?? 0) === 0) {
|
|
console.log('Copying rows...')
|
|
await client.query(`
|
|
INSERT INTO ${target} (testament, book, chapter, verse, text_raw, text_norm, tsv, embedding, created_at, updated_at)
|
|
SELECT testament, book, chapter, verse, text_raw, text_norm, tsv, embedding, created_at, updated_at
|
|
FROM ${source}
|
|
ON CONFLICT DO NOTHING
|
|
`)
|
|
} else {
|
|
console.log('Target already has rows, skipping copy')
|
|
}
|
|
|
|
// Create indexes if not exist
|
|
await client.query(`
|
|
CREATE UNIQUE INDEX IF NOT EXISTS ux_ref_bv_ro_cornilescu ON ${target} (book, chapter, verse);
|
|
CREATE INDEX IF NOT EXISTS idx_tsv_bv_ro_cornilescu ON ${target} USING GIN (tsv);
|
|
CREATE INDEX IF NOT EXISTS idx_book_ch_bv_ro_cornilescu ON ${target} (book, chapter);
|
|
CREATE INDEX IF NOT EXISTS idx_testament_bv_ro_cornilescu ON ${target} (testament);
|
|
`)
|
|
|
|
await client.query('COMMIT')
|
|
console.log('Rows copied and indexes created. Running post-copy maintenance...')
|
|
|
|
// Run maintenance commands outside of transaction
|
|
await client.query(`VACUUM ANALYZE ${target};`)
|
|
try {
|
|
await client.query(`
|
|
CREATE INDEX IF NOT EXISTS idx_vec_ivfflat_bv_ro_cornilescu
|
|
ON ${target} USING ivfflat (embedding vector_cosine_ops)
|
|
WITH (lists = 100);
|
|
`)
|
|
} catch (e) {
|
|
console.warn('IVFFLAT index creation hit memory limits; skipping for now. You can create it later with higher maintenance_work_mem.')
|
|
}
|
|
console.log('Clone completed.')
|
|
} catch (e) {
|
|
await client.query('ROLLBACK')
|
|
console.error('Clone error:', e)
|
|
process.exit(1)
|
|
} finally {
|
|
client.release()
|
|
await pool.end()
|
|
}
|
|
}
|
|
|
|
main()
|