// server3.js - Premium Services API import express from 'express'; import cors from 'cors'; import helmet from 'helmet'; import dotenv from 'dotenv'; import { open } from 'sqlite'; import sqlite3 from 'sqlite3'; import jwt from 'jsonwebtoken'; import { v4 as uuidv4 } from 'uuid'; import path from 'path'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); dotenv.config({ path: path.resolve(__dirname, '..', '.env') }); const app = express(); const PORT = process.env.PREMIUM_PORT || 5002; let db; const initDB = async () => { try { db = await open({ filename: '/home/jcoakley/aptiva-dev1-app/user_profile.db', driver: sqlite3.Database }); console.log('Connected to user_profile.db for Premium Services.'); } catch (error) { console.error('Error connecting to premium database:', error); } }; initDB(); app.use(helmet()); app.use(express.json()); const allowedOrigins = ['https://dev1.aptivaai.com']; app.use(cors({ origin: allowedOrigins, credentials: true })); const authenticatePremiumUser = (req, res, next) => { const token = req.headers.authorization?.split(' ')[1]; if (!token) return res.status(401).json({ error: 'Premium authorization required' }); try { const SECRET_KEY = process.env.SECRET_KEY || 'supersecurekey'; const { userId } = jwt.verify(token, SECRET_KEY); req.userId = userId; next(); } catch (error) { return res.status(403).json({ error: 'Invalid or expired token' }); } }; // Get latest selected planned path app.get('/api/premium/planned-path/latest', authenticatePremiumUser, async (req, res) => { try { const row = await db.get( `SELECT * FROM career_path WHERE user_id = ? ORDER BY start_date DESC LIMIT 1`, [req.userId] ); res.json(row || {}); } catch (error) { console.error('Error fetching latest career path:', error); res.status(500).json({ error: 'Failed to fetch latest planned path' }); } }); // Get all planned paths for the user app.get('/api/premium/planned-path/all', authenticatePremiumUser, async (req, res) => { try { const rows = await db.all( `SELECT * FROM career_path WHERE user_id = ? ORDER BY start_date ASC`, [req.userId] ); res.json({ careerPath: rows }); } catch (error) { console.error('Error fetching career paths:', error); res.status(500).json({ error: 'Failed to fetch planned paths' }); } }); // Save a new planned path app.post('/api/premium/planned-path', authenticatePremiumUser, async (req, res) => { let { career_name } = req.body; if (!career_name) { return res.status(400).json({ error: 'Career name is required.' }); } try { // Ensure that career_name is always a string if (typeof career_name !== 'string') { console.warn('career_name was not a string. Converting to string.'); career_name = String(career_name); // Convert to string } // Check if the career path already exists for the user const existingCareerPath = await db.get( `SELECT id FROM career_path WHERE user_id = ? AND career_name = ?`, [req.userId, career_name] ); if (existingCareerPath) { return res.status(200).json({ message: 'Career path already exists. Would you like to reload it or create a new one?', career_path_id: existingCareerPath.id, action_required: 'reload_or_create' }); } // Define a new career path id and insert into the database const newCareerPathId = uuidv4(); await db.run( `INSERT INTO career_path (id, user_id, career_name) VALUES (?, ?, ?)`, [newCareerPathId, req.userId, career_name] ); res.status(201).json({ message: 'Career path saved.', career_path_id: newCareerPathId, action_required: 'new_created' }); } catch (error) { console.error('Error saving career path:', error); res.status(500).json({ error: 'Failed to save career path.' }); } }); // Save a new milestone app.post('/api/premium/milestones', authenticatePremiumUser, async (req, res) => { const { milestone_type, title, description, date, career_path_id, salary_increase, status = 'planned', date_completed = null, context_snapshot = null } = req.body; if (!milestone_type || !title || !description || !date) { return res.status(400).json({ error: 'Missing required fields' }); } try { await db.run( `INSERT INTO milestones ( user_id, milestone_type, title, description, date, career_path_id, salary_increase, status, date_completed, context_snapshot, progress, updated_at ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)`, [ req.userId, milestone_type, title, description, date, career_path_id, salary_increase || null, status, date_completed, context_snapshot ] ); res.status(201).json({ message: 'Milestone saved successfully' }); } catch (error) { console.error('Error saving milestone:', error); res.status(500).json({ error: 'Failed to save milestone' }); } }); // Get all milestones app.get('/api/premium/milestones', authenticatePremiumUser, async (req, res) => { try { const milestones = await db.all( `SELECT * FROM milestones WHERE user_id = ? ORDER BY date ASC`, [req.userId] ); const mapped = milestones.map(m => ({ title: m.title, description: m.description, date: m.date, type: m.milestone_type, progress: m.progress || 0, career_path_id: m.career_path_id })); res.json({ milestones }); } catch (error) { console.error('Error fetching milestones:', error); res.status(500).json({ error: 'Failed to fetch milestones' }); } }); /// Update an existing milestone app.put('/api/premium/milestones/:id', authenticatePremiumUser, async (req, res) => { try { const { id } = req.params; const numericId = parseInt(id, 10); // 👈 Block-defined for SQLite safety if (isNaN(numericId)) { return res.status(400).json({ error: 'Invalid milestone ID' }); } const { milestone_type, title, description, date, progress, status, date_completed, salary_increase, context_snapshot, } = req.body; console.log('Updating milestone with:', { milestone_type, title, description, date, progress, status, date_completed, salary_increase, context_snapshot, id: numericId, userId: req.userId }); await db.run( `UPDATE milestones SET milestone_type = ?, title = ?, description = ?, date = ?, progress = ?, status = ?, date_completed = ?, salary_increase = ?, context_snapshot = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ? AND user_id = ?`, [ milestone_type, title, description, date, progress || 0, status || 'planned', date_completed, salary_increase || null, context_snapshot || null, numericId, // 👈 used here in the query req.userId ] ); res.status(200).json({ message: 'Milestone updated successfully' }); } catch (error) { console.error('Error updating milestone:', error.message, error.stack); res.status(500).json({ error: 'Failed to update milestone' }); } }); app.delete('/api/premium/milestones/:id', authenticatePremiumUser, async (req, res) => { const { id } = req.params; try { await db.run(`DELETE FROM milestones WHERE id = ? AND user_id = ?`, [id, req.userId]); res.status(200).json({ message: 'Milestone deleted successfully' }); } catch (error) { console.error('Error deleting milestone:', error); res.status(500).json({ error: 'Failed to delete milestone' }); } }); // Archive current career to history app.post('/api/premium/career-history', authenticatePremiumUser, async (req, res) => { const { career_path_id, company } = req.body; if (!career_path_id || !company) { return res.status(400).json({ error: 'Career path ID and company are required' }); } try { const career = await db.get(`SELECT * FROM career_path WHERE id = ? AND user_id = ?`, [career_path_id, req.userId]); if (!career) { return res.status(404).json({ error: 'Career path not found' }); } await db.run( `INSERT INTO career_history (user_id, job_title, company, start_date) VALUES (?, ?, ?, DATE('now'))`, [req.userId, career.job_title, company] ); await db.run(`DELETE FROM career_path WHERE id = ?`, [career_path_id]); res.status(201).json({ message: 'Career moved to history successfully' }); } catch (error) { console.error('Error moving career to history:', error); res.status(500).json({ error: 'Failed to update career history' }); } }); // Retrieve career history app.get('/api/premium/career-history', authenticatePremiumUser, async (req, res) => { try { const history = await db.all( `SELECT * FROM career_history WHERE user_id = ? ORDER BY start_date DESC;`, [req.userId] ); res.json({ careerHistory: history }); } catch (error) { console.error('Error fetching career history:', error); res.status(500).json({ error: 'Failed to fetch career history' }); } }); // ROI Analysis (placeholder logic) app.get('/api/premium/roi-analysis', authenticatePremiumUser, async (req, res) => { try { const userCareer = await db.get( `SELECT * FROM career_path WHERE user_id = ? ORDER BY start_date DESC LIMIT 1`, [req.userId] ); if (!userCareer) return res.status(404).json({ error: 'No planned path found for user' }); const roi = { jobTitle: userCareer.job_title, salary: userCareer.salary, tuition: 50000, netGain: userCareer.salary - 50000 }; res.json(roi); } catch (error) { console.error('Error calculating ROI:', error); res.status(500).json({ error: 'Failed to calculate ROI' }); } }); app.listen(PORT, () => { console.log(`Premium server running on http://localhost:${PORT}`); });