dev1/backend/server3.js

335 lines
9.8 KiB
JavaScript

// 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) => {
const { career_name } = req.body;
if (!career_name) {
return res.status(400).json({ error: 'Career name is required.' });
}
try {
// 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 the existing path — do NOT define or reuse newCareerPathId
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'
});
}
// Only define newCareerPathId *when* creating a new path
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' // Action flag for newly created path
});
} 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}`);
});