diff --git a/README.md b/README.md index 1a08863..a5b4e22 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ An AI-powered chatbot named **Mya**, designed by Baba Virtuehearts. Mya provides - **Framework:** [Next.js 15 (App Router)](https://nextjs.org/) - **Styling:** [Tailwind CSS](https://tailwindcss.com/) -- **Database:** [Prisma](https://www.prisma.io/) with **SQLite** +- **Database:** [Drizzle ORM](https://orm.drizzle.team/) with [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) - **Authentication:** [NextAuth.js](https://next-auth.js.org/) - **AI Integration:** [OpenRouter API](https://openrouter.ai/) (meta-llama/llama-3.1-8b-instruct:free) @@ -82,7 +82,7 @@ OPENROUTER_API_KEY="your-openrouter-api-key" 2. **Database Preparation:** Ensure you have run the initial migration/push to your local SQLite database: ```bash - npx prisma db push + npm run db:push ``` ### Running the Application diff --git a/package-lock.json b/package-lock.json index 65670fb..bfdcbc8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,13 +7,13 @@ "": { "name": "virtuehearts-reiki-training", "version": "0.1.0", - "hasInstallScript": true, "dependencies": { "@auth/drizzle-adapter": "^1.11.1", "axios": "^1.7.9", "bcryptjs": "^2.4.3", "better-sqlite3": "^12.6.2", "clsx": "^2.1.1", + "dotenv": "^17.2.3", "drizzle-orm": "^0.45.1", "framer-motion": "^12.0.6", "lucide-react": "^0.474.0", @@ -3520,6 +3520,18 @@ "node": ">=0.10.0" } }, + "node_modules/dotenv": { + "version": "17.2.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", + "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/drizzle-kit": { "version": "0.31.8", "resolved": "https://registry.npmjs.org/drizzle-kit/-/drizzle-kit-0.31.8.tgz", diff --git a/package.json b/package.json index 1b84e49..e42be34 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "build": "next build", "start": "node scripts/init.js && next start", "lint": "next lint", + "test": "node scripts/verify-db.js", "db:push": "drizzle-kit push" }, "dependencies": { @@ -15,6 +16,7 @@ "bcryptjs": "^2.4.3", "better-sqlite3": "^12.6.2", "clsx": "^2.1.1", + "dotenv": "^17.2.3", "drizzle-orm": "^0.45.1", "framer-motion": "^12.0.6", "lucide-react": "^0.474.0", diff --git a/scripts/init.js b/scripts/init.js index f032424..aec7b29 100644 --- a/scripts/init.js +++ b/scripts/init.js @@ -1,6 +1,7 @@ const fs = require('fs'); const path = require('path'); const crypto = require('crypto'); +const { execSync } = require('child_process'); const envPath = path.join(process.cwd(), '.env'); @@ -34,6 +35,38 @@ function main() { console.log('Environment file is already complete.'); } + // Set environment variables for the current process + require('dotenv').config(); + + console.log('--- Syncing Database Schema ---'); + try { + execSync('npm run db:push', { stdio: 'inherit' }); + console.log('Database schema synced successfully.'); + } catch (error) { + console.error('Failed to sync database schema:', error.message); + process.exit(1); + } + + console.log('--- Verifying Database ---'); + try { + const Database = require('better-sqlite3'); + const dbPath = process.env.DATABASE_URL.replace('file:', ''); + const db = new Database(dbPath); + + // Simple check to ensure database is working + const result = db.prepare('SELECT 1 as check_val').get(); + if (result && result.check_val === 1) { + console.log('Database connection verified.'); + } else { + throw new Error('Database verification failed: unexpected result'); + } + + db.close(); + } catch (error) { + console.error('Database verification error:', error.message); + process.exit(1); + } + console.log('--- Initialization Complete ---'); } diff --git a/scripts/verify-db.js b/scripts/verify-db.js new file mode 100644 index 0000000..8c2ef17 --- /dev/null +++ b/scripts/verify-db.js @@ -0,0 +1,90 @@ +const Database = require('better-sqlite3'); +const fs = require('fs'); +const path = require('path'); +const crypto = require('crypto'); + +// Load .env +require('dotenv').config(); + +const dbPath = (process.env.DATABASE_URL || 'file:./dev.db').replace('file:', ''); +console.log(`Connecting to database at: ${dbPath}`); + +let db; +try { + db = new Database(dbPath); +} catch (error) { + console.error('Failed to open database:', error.message); + process.exit(1); +} + +console.log('Verifying tables...'); +const tables = db.prepare("SELECT name FROM sqlite_master WHERE type='table'").all().map(t => t.name); +console.log('Found tables:', tables.join(', ')); + +const expectedTables = ['user', 'aiSettings', 'chatMessage', 'message', 'intake', 'progress', 'reflection']; +let missingTables = []; +expectedTables.forEach(table => { + if (tables.includes(table)) { + console.log(` [OK] Table "${table}" exists.`); + } else { + console.error(` [ERROR] Table "${table}" is missing!`); + missingTables.push(table); + } +}); + +if (missingTables.length > 0) { + console.error('Database verification failed: missing tables.'); + process.exit(1); +} + +// Try a simple insert/select on user table +console.log('Testing CRUD on "user" table...'); +const testEmail = `test-${Date.now()}@example.com`; +const testId = crypto.randomUUID(); + +try { + db.prepare("INSERT INTO user (id, email, role, status) VALUES (?, ?, ?, ?)").run( + testId, + testEmail, + 'USER', + 'PENDING' + ); + + const user = db.prepare("SELECT * FROM user WHERE email = ?").get(testEmail); + if (user && user.email === testEmail) { + console.log(' [OK] User inserted and retrieved successfully.'); + } else { + throw new Error('Failed to retrieve inserted user.'); + } + + // Clean up + db.prepare("DELETE FROM user WHERE email = ?").run(testEmail); + console.log(' [OK] Test user deleted.'); +} catch (error) { + console.error(' [ERROR] CRUD test failed:', error.message); + process.exit(1); +} + +// Test aiSettings as it's critical for the AI assistant +console.log('Testing "aiSettings" table...'); +try { + const settingsCount = db.prepare("SELECT COUNT(*) as count FROM aiSettings").get().count; + console.log(` [INFO] Found ${settingsCount} entries in aiSettings.`); + + // If empty, ensure we can insert (though the app should handle this at runtime) + if (settingsCount === 0) { + console.log(' [INFO] Inserting default aiSettings...'); + db.prepare("INSERT INTO aiSettings (id, systemPrompt) VALUES (?, ?)").run('default', 'Test prompt'); + const newCount = db.prepare("SELECT COUNT(*) as count FROM aiSettings").get().count; + if (newCount === 1) { + console.log(' [OK] Default aiSettings inserted.'); + db.prepare("DELETE FROM aiSettings WHERE id = 'default'").run(); + } + } +} catch (error) { + console.error(' [ERROR] aiSettings test failed:', error.message); + process.exit(1); +} + +db.close(); +console.log('Database architecture verification successful!');