From 4f8de209f309eaa237e2e4fccbbe132f08b562d4 Mon Sep 17 00:00:00 2001 From: TheFlow Date: Mon, 6 Oct 2025 23:28:42 +1300 Subject: [PATCH] feat: add MongoDB systemd service and database initialization - Create mongodb-tractatus.service for systemd management - Add installation script for service setup - Create init-db.js with complete collection schemas and indexes - Configure 10 MongoDB collections: documents, blog_posts, media_inquiries, case_submissions, resources, moderation_queue, users, citations, translations, koha_donations - Add indexes for performance optimization - Include verification and statistics output MongoDB Port: 27017 Database: tractatus_dev Status: Ready for service installation --- scripts/init-db.js | 202 +++++++++++++++++++++++++++++ scripts/install-mongodb-service.sh | 53 ++++++++ scripts/mongodb-tractatus.service | 37 ++++++ 3 files changed, 292 insertions(+) create mode 100644 scripts/init-db.js create mode 100755 scripts/install-mongodb-service.sh create mode 100644 scripts/mongodb-tractatus.service diff --git a/scripts/init-db.js b/scripts/init-db.js new file mode 100644 index 00000000..caf309b0 --- /dev/null +++ b/scripts/init-db.js @@ -0,0 +1,202 @@ +#!/usr/bin/env node +/** + * Database Initialization Script for Tractatus + * + * Creates all required collections and indexes for the Tractatus platform + * Run with: node scripts/init-db.js + */ + +const { MongoClient } = require('mongodb'); +const path = require('path'); + +// Load environment variables +require('dotenv').config({ path: path.join(__dirname, '../.env') }); + +const MONGODB_URI = process.env.MONGODB_URI || 'mongodb://localhost:27017/tractatus_dev'; +const DB_NAME = process.env.MONGODB_DB || 'tractatus_dev'; + +/** + * Collection schemas and indexes + */ +const COLLECTIONS = { + documents: { + indexes: [ + { keys: { slug: 1 }, options: { unique: true } }, + { keys: { quadrant: 1 } }, + { keys: { 'metadata.document_code': 1 } }, + { keys: { search_index: 'text' } }, + { keys: { 'metadata.date_created': -1 } } + ] + }, + + blog_posts: { + indexes: [ + { keys: { slug: 1 }, options: { unique: true } }, + { keys: { status: 1 } }, + { keys: { published_at: -1 } }, + { keys: { 'author.type': 1 } }, + { keys: { tags: 1 } } + ] + }, + + media_inquiries: { + indexes: [ + { keys: { 'contact.email': 1 } }, + { keys: { status: 1 } }, + { keys: { created_at: -1 } }, + { keys: { 'ai_triage.urgency': 1 } } + ] + }, + + case_submissions: { + indexes: [ + { keys: { 'submitter.email': 1 } }, + { keys: { 'moderation.status': 1 } }, + { keys: { submitted_at: -1 } }, + { keys: { 'ai_review.relevance_score': -1 } } + ] + }, + + resources: { + indexes: [ + { keys: { url: 1 }, options: { unique: true } }, + { keys: { category: 1 } }, + { keys: { status: 1 } }, + { keys: { alignment_score: -1 } } + ] + }, + + moderation_queue: { + indexes: [ + { keys: { item_type: 1 } }, + { keys: { status: 1 } }, + { keys: { priority: 1 } }, + { keys: { created_at: -1 } }, + { keys: { quadrant: 1 } } + ] + }, + + users: { + indexes: [ + { keys: { email: 1 }, options: { unique: true } }, + { keys: { role: 1 } }, + { keys: { created_at: -1 } } + ] + }, + + citations: { + indexes: [ + { keys: { document_id: 1 } }, + { keys: { citation_type: 1 } }, + { keys: { cited_at: -1 } } + ] + }, + + translations: { + indexes: [ + { keys: { original_id: 1, language: 1 }, options: { unique: true } }, + { keys: { language: 1 } }, + { keys: { status: 1 } } + ] + }, + + koha_donations: { + indexes: [ + { keys: { stripe_payment_id: 1 }, options: { unique: true } }, + { keys: { 'donor.email': 1 } }, + { keys: { frequency: 1 } }, + { keys: { timestamp: -1 } }, + { keys: { status: 1 } } + ] + } +}; + +/** + * Initialize database + */ +async function initializeDatabase() { + console.log('šŸš€ Starting Tractatus database initialization...\n'); + console.log(`MongoDB URI: ${MONGODB_URI}`); + console.log(`Database: ${DB_NAME}\n`); + + const client = new MongoClient(MONGODB_URI); + + try { + await client.connect(); + console.log('āœ… Connected to MongoDB\n'); + + const db = client.db(DB_NAME); + + // Get existing collections + const existingCollections = await db.listCollections().toArray(); + const existingNames = existingCollections.map(c => c.name); + + // Create collections and indexes + for (const [collectionName, config] of Object.entries(COLLECTIONS)) { + console.log(`šŸ“¦ Processing collection: ${collectionName}`); + + // Create collection if it doesn't exist + if (!existingNames.includes(collectionName)) { + await db.createCollection(collectionName); + console.log(` āœ“ Created collection`); + } else { + console.log(` āŠ™ Collection already exists`); + } + + // Create indexes + const collection = db.collection(collectionName); + for (const indexSpec of config.indexes) { + try { + const indexName = await collection.createIndex( + indexSpec.keys, + indexSpec.options || {} + ); + console.log(` āœ“ Created index: ${indexName}`); + } catch (error) { + if (error.code === 85 || error.code === 86) { + // Index already exists + console.log(` āŠ™ Index already exists`); + } else { + throw error; + } + } + } + console.log(''); + } + + // Verify collections + console.log('šŸ” Verifying database setup...'); + const finalCollections = await db.listCollections().toArray(); + console.log(` āœ“ Total collections: ${finalCollections.length}`); + + // Display collection statistics + console.log('\nšŸ“Š Collection Statistics:'); + for (const collectionName of Object.keys(COLLECTIONS)) { + const collection = db.collection(collectionName); + const count = await collection.countDocuments(); + const indexes = await collection.indexes(); + console.log(` ${collectionName}:`); + console.log(` Documents: ${count}`); + console.log(` Indexes: ${indexes.length}`); + } + + console.log('\n✨ Database initialization complete!\n'); + console.log('Next steps:'); + console.log(' 1. Run document migration: npm run migrate:docs'); + console.log(' 2. Create admin user: npm run seed:admin'); + console.log(' 3. Start development server: npm run dev\n'); + + } catch (error) { + console.error('āŒ Error initializing database:', error); + process.exit(1); + } finally { + await client.close(); + } +} + +// Run initialization +if (require.main === module) { + initializeDatabase().catch(console.error); +} + +module.exports = { initializeDatabase, COLLECTIONS }; diff --git a/scripts/install-mongodb-service.sh b/scripts/install-mongodb-service.sh new file mode 100755 index 00000000..528a008c --- /dev/null +++ b/scripts/install-mongodb-service.sh @@ -0,0 +1,53 @@ +#!/bin/bash +# +# Install mongodb-tractatus systemd service +# Run with: sudo ./install-mongodb-service.sh +# + +set -e + +SERVICE_NAME="mongodb-tractatus.service" +SERVICE_FILE="./mongodb-tractatus.service" +SYSTEMD_DIR="/etc/systemd/system" + +echo "Installing MongoDB Tractatus systemd service..." + +# Check if running as root +if [ "$EUID" -ne 0 ]; then + echo "Error: This script must be run with sudo" + exit 1 +fi + +# Check if service file exists +if [ ! -f "$SERVICE_FILE" ]; then + echo "Error: $SERVICE_FILE not found in current directory" + exit 1 +fi + +# Copy service file to systemd directory +echo "Copying service file to $SYSTEMD_DIR..." +cp "$SERVICE_FILE" "$SYSTEMD_DIR/$SERVICE_NAME" + +# Set correct permissions +chmod 644 "$SYSTEMD_DIR/$SERVICE_NAME" + +# Reload systemd daemon +echo "Reloading systemd daemon..." +systemctl daemon-reload + +# Enable service to start on boot +echo "Enabling service to start on boot..." +systemctl enable "$SERVICE_NAME" + +echo "" +echo "MongoDB Tractatus service installed successfully!" +echo "" +echo "Commands:" +echo " Start: sudo systemctl start $SERVICE_NAME" +echo " Stop: sudo systemctl stop $SERVICE_NAME" +echo " Restart: sudo systemctl restart $SERVICE_NAME" +echo " Status: sudo systemctl status $SERVICE_NAME" +echo " Logs: sudo journalctl -u $SERVICE_NAME -f" +echo "" +echo "To start the service now, run:" +echo " sudo systemctl start $SERVICE_NAME" diff --git a/scripts/mongodb-tractatus.service b/scripts/mongodb-tractatus.service new file mode 100644 index 00000000..b4b2265a --- /dev/null +++ b/scripts/mongodb-tractatus.service @@ -0,0 +1,37 @@ +[Unit] +Description=MongoDB Database Server for Tractatus +Documentation=https://docs.mongodb.org/manual +After=network.target + +[Service] +Type=forking +User=theflow +Group=theflow + +# MongoDB executable and configuration +ExecStart=/home/theflow/projects/mongodb/mongodb-server/bin/mongod \ + --port 27017 \ + --dbpath /home/theflow/projects/tractatus/data/mongodb \ + --logpath /home/theflow/projects/tractatus/logs/mongodb.log \ + --fork \ + --quiet + +ExecStop=/home/theflow/projects/mongodb/mongodb-server/bin/mongod \ + --port 27017 \ + --dbpath /home/theflow/projects/tractatus/data/mongodb \ + --shutdown + +# Restart policy +Restart=on-failure +RestartSec=5 + +# Security settings +PrivateTmp=true +NoNewPrivileges=true +LimitNOFILE=64000 + +# Working directory +WorkingDirectory=/home/theflow/projects/tractatus + +[Install] +WantedBy=multi-user.target