/** * Database Schema Setup Script * Run this script once to create all required tables * Usage: npm run db:setup */ require('dotenv').config(); const { pool } = require('./connection'); // SQL statements to create tables (matching Developer Guide Part 2) const createTablesSQL = ` -- ============================================ -- TABLE: users -- ============================================ CREATE TABLE IF NOT EXISTS users ( id SERIAL PRIMARY KEY, email VARCHAR(255) UNIQUE NOT NULL, password_hash VARCHAR(255) NOT NULL, company_name VARCHAR(255), phone VARCHAR(20), email_verified BOOLEAN DEFAULT FALSE, verification_token VARCHAR(255), plan VARCHAR(50) DEFAULT 'free', plan_started_at TIMESTAMP, plan_expires_at TIMESTAMP, monthly_quota INTEGER DEFAULT 100, calls_this_month INTEGER DEFAULT 0, quota_reset_date DATE, razorpay_customer_id VARCHAR(100), razorpay_subscription_id VARCHAR(100), created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW(), last_login_at TIMESTAMP, is_active BOOLEAN DEFAULT TRUE ); CREATE INDEX IF NOT EXISTS idx_users_email ON users(email); -- ============================================ -- TABLE: api_keys -- ============================================ CREATE TABLE IF NOT EXISTS api_keys ( id SERIAL PRIMARY KEY, user_id INTEGER REFERENCES users(id) ON DELETE CASCADE, key_prefix VARCHAR(20) NOT NULL, key_hash VARCHAR(255) NOT NULL, key_hint VARCHAR(10), name VARCHAR(100) DEFAULT 'Default', is_test_key BOOLEAN DEFAULT FALSE, is_active BOOLEAN DEFAULT TRUE, last_used_at TIMESTAMP, total_calls INTEGER DEFAULT 0, created_at TIMESTAMP DEFAULT NOW(), expires_at TIMESTAMP ); CREATE INDEX IF NOT EXISTS idx_api_keys_user ON api_keys(user_id); CREATE INDEX IF NOT EXISTS idx_api_keys_hash ON api_keys(key_hash); -- ============================================ -- TABLE: api_calls -- ============================================ CREATE TABLE IF NOT EXISTS api_calls ( id SERIAL PRIMARY KEY, user_id INTEGER REFERENCES users(id), api_key_id INTEGER REFERENCES api_keys(id), endpoint VARCHAR(100) NOT NULL, method VARCHAR(10) NOT NULL, request_params JSONB, response_status INTEGER, response_time_ms INTEGER, success BOOLEAN, error_message VARCHAR(500), credits_used INTEGER DEFAULT 1, is_billable BOOLEAN DEFAULT TRUE, ip_address VARCHAR(45), user_agent VARCHAR(500), called_at TIMESTAMP DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_api_calls_user ON api_calls(user_id); CREATE INDEX IF NOT EXISTS idx_api_calls_date ON api_calls(called_at); CREATE INDEX IF NOT EXISTS idx_api_calls_endpoint ON api_calls(endpoint); -- ============================================ -- TABLE: ifsc_codes -- ============================================ CREATE TABLE IF NOT EXISTS ifsc_codes ( id SERIAL PRIMARY KEY, ifsc VARCHAR(11) UNIQUE NOT NULL, bank_name VARCHAR(255) NOT NULL, branch VARCHAR(255), address TEXT, city VARCHAR(100), district VARCHAR(100), state VARCHAR(100), contact VARCHAR(100), upi_enabled BOOLEAN DEFAULT FALSE, rtgs_enabled BOOLEAN DEFAULT TRUE, neft_enabled BOOLEAN DEFAULT TRUE, imps_enabled BOOLEAN DEFAULT TRUE, micr_code VARCHAR(20), swift_code VARCHAR(20), updated_at TIMESTAMP DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_ifsc ON ifsc_codes(ifsc); -- ============================================ -- TABLE: pincodes -- ============================================ CREATE TABLE IF NOT EXISTS pincodes ( id SERIAL PRIMARY KEY, pincode VARCHAR(6) NOT NULL, office_name VARCHAR(255), office_type VARCHAR(50), district VARCHAR(100), division VARCHAR(100), region VARCHAR(100), state VARCHAR(100), latitude DECIMAL(10, 8), longitude DECIMAL(11, 8), updated_at TIMESTAMP DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_pincode ON pincodes(pincode); -- ============================================ -- TABLE: pan_data -- ============================================ CREATE TABLE IF NOT EXISTS pan_data ( pan_number VARCHAR(10) PRIMARY KEY, full_name VARCHAR(255), father_name VARCHAR(255), date_of_birth DATE, gender VARCHAR(10), category VARCHAR(50), status VARCHAR(50), updated_at TIMESTAMP DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_pan_number ON pan_data(pan_number); -- ============================================ -- TABLE: subscriptions -- ============================================ CREATE TABLE IF NOT EXISTS subscriptions ( id SERIAL PRIMARY KEY, user_id INTEGER REFERENCES users(id), razorpay_subscription_id VARCHAR(100), razorpay_payment_id VARCHAR(100), razorpay_plan_id VARCHAR(100), plan_name VARCHAR(50), amount DECIMAL(10, 2), currency VARCHAR(3) DEFAULT 'INR', status VARCHAR(50), current_period_start TIMESTAMP, current_period_end TIMESTAMP, created_at TIMESTAMP DEFAULT NOW(), cancelled_at TIMESTAMP ); CREATE INDEX IF NOT EXISTS idx_subscriptions_user ON subscriptions(user_id); -- ============================================ -- TABLE: invoices -- ============================================ CREATE TABLE IF NOT EXISTS invoices ( id SERIAL PRIMARY KEY, user_id INTEGER REFERENCES users(id), subscription_id INTEGER REFERENCES subscriptions(id), invoice_number VARCHAR(50) UNIQUE, amount DECIMAL(10, 2), tax_amount DECIMAL(10, 2), total_amount DECIMAL(10, 2), currency VARCHAR(3) DEFAULT 'INR', status VARCHAR(50), razorpay_invoice_id VARCHAR(100), razorpay_payment_id VARCHAR(100), invoice_date DATE, due_date DATE, paid_at TIMESTAMP, pdf_url VARCHAR(500), created_at TIMESTAMP DEFAULT NOW() ); `; /** * Run database setup */ const setupDatabase = async () => { console.log('šŸš€ Starting database setup...\n'); const client = await pool.connect(); try { // Start transaction await client.query('BEGIN'); // Create tables console.log('šŸ“¦ Creating tables...'); await client.query(createTablesSQL); console.log('āœ… Tables created successfully\n'); // Commit transaction await client.query('COMMIT'); // Display created tables const tablesResult = await client.query(` SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' AND table_type = 'BASE TABLE' ORDER BY table_name; `); console.log('šŸ“‹ Created tables:'); tablesResult.rows.forEach((row, index) => { console.log(` ${index + 1}. ${row.table_name}`); }); console.log('\nāœ… Database setup completed successfully!'); } catch (error) { // Rollback on error await client.query('ROLLBACK'); console.error('\nāŒ Database setup failed:', error.message); console.error('šŸ”„ Transaction rolled back'); throw error; } finally { client.release(); } }; // Run setup setupDatabase() .then(() => { console.log('\nšŸ‘‹ Exiting...'); process.exit(0); }) .catch((error) => { console.error('\nšŸ’„ Fatal error:', error); process.exit(1); });