dev1/backend/server3.js

520 lines
15 KiB
JavaScript

// server3.js
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';
// If you still need the projection logic somewhere else
import { simulateFinancialProjection } from '../src/utils/FinancialProjectionService.js';
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' });
}
};
/* ------------------------------------------------------------------
CAREER PROFILE ENDPOINTS
(Renamed from planned-path to career-profile)
------------------------------------------------------------------ */
// GET the latest selected career profile
app.get('/api/premium/career-profile/latest', authenticatePremiumUser, async (req, res) => {
try {
const row = await db.get(`
SELECT *
FROM career_paths
WHERE user_id = ?
ORDER BY start_date DESC
LIMIT 1
`, [req.userId]);
res.json(row || {});
} catch (error) {
console.error('Error fetching latest career profile:', error);
res.status(500).json({ error: 'Failed to fetch latest career profile' });
}
});
// GET all career profiles for the user
app.get('/api/premium/career-profile/all', authenticatePremiumUser, async (req, res) => {
try {
const rows = await db.all(`
SELECT *
FROM career_paths
WHERE user_id = ?
ORDER BY start_date ASC
`, [req.userId]);
res.json({ careerPaths: rows });
} catch (error) {
console.error('Error fetching career profiles:', error);
res.status(500).json({ error: 'Failed to fetch career profiles' });
}
});
// POST a new career profile
app.post('/api/premium/career-profile', authenticatePremiumUser, async (req, res) => {
const {
career_name,
status,
start_date,
projected_end_date,
college_enrollment_status,
currently_working
} = req.body;
// If you need to ensure the user gave us a career_name:
if (!career_name) {
return res.status(400).json({ error: 'career_name is required.' });
}
try {
const newCareerPathId = uuidv4();
const now = new Date().toISOString();
await db.run(`
INSERT INTO career_paths (
id,
user_id,
career_name,
status,
start_date,
projected_end_date,
college_enrollment_status,
currently_working,
created_at,
updated_at
)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(user_id, career_name)
DO UPDATE SET
status = excluded.status,
start_date = excluded.start_date,
projected_end_date = excluded.projected_end_date,
college_enrollment_status = excluded.college_enrollment_status,
currently_working = excluded.currently_working,
updated_at = ?
`, [
newCareerPathId, // id
req.userId, // user_id
career_name, // career_name
status || 'planned', // status (if null, default to 'planned')
start_date || now,
projected_end_date || null,
college_enrollment_status || null,
currently_working || null,
now, // created_at
now, // updated_at on initial insert
now // updated_at on conflict
]);
// Optionally fetch the row's ID after upsert
const result = await db.get(`
SELECT id
FROM career_paths
WHERE user_id = ?
AND career_name = ?
`, [req.userId, career_name]);
res.status(200).json({
message: 'Career profile upserted.',
career_path_id: result?.id
});
} catch (error) {
console.error('Error upserting career profile:', error);
res.status(500).json({ error: 'Failed to upsert career profile.' });
}
});
/* ------------------------------------------------------------------
MILESTONES (same as before)
------------------------------------------------------------------ */
app.post('/api/premium/milestone', authenticatePremiumUser, async (req, res) => {
// ... no changes, same logic ...
});
// GET, PUT, DELETE milestones
// ... no changes ...
/* ------------------------------------------------------------------
FINANCIAL PROFILES (Renamed emergency_contribution)
------------------------------------------------------------------ */
app.get('/api/premium/financial-profile', authenticatePremiumUser, async (req, res) => {
try {
const row = await db.get(`
SELECT *
FROM financial_profiles
WHERE user_id = ?
`, [req.userId]);
res.json(row || {});
} catch (error) {
console.error('Error fetching financial profile:', error);
res.status(500).json({ error: 'Failed to fetch financial profile' });
}
});
app.post('/api/premium/financial-profile', authenticatePremiumUser, async (req, res) => {
const {
current_salary,
additional_income,
monthly_expenses,
monthly_debt_payments,
retirement_savings,
retirement_contribution,
emergency_fund,
emergency_contribution,
extra_cash_emergency_pct,
extra_cash_retirement_pct
} = req.body;
try {
// Check if row exists
const existing = await db.get(`
SELECT user_id
FROM financial_profiles
WHERE user_id = ?
`, [req.userId]);
if (!existing) {
// Insert new row
await db.run(`
INSERT INTO financial_profiles (
user_id,
current_salary,
additional_income,
monthly_expenses,
monthly_debt_payments,
retirement_savings,
emergency_fund,
retirement_contribution,
emergency_contribution,
extra_cash_emergency_pct,
extra_cash_retirement_pct,
created_at,
updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
`, [
req.userId,
current_salary || 0,
additional_income || 0,
monthly_expenses || 0,
monthly_debt_payments || 0,
retirement_savings || 0,
emergency_fund || 0,
retirement_contribution || 0,
emergency_contribution || 0, // store new field
extra_cash_emergency_pct || 0,
extra_cash_retirement_pct || 0
]);
} else {
// Update existing
await db.run(`
UPDATE financial_profiles
SET
current_salary = ?,
additional_income = ?,
monthly_expenses = ?,
monthly_debt_payments = ?,
retirement_savings = ?,
emergency_fund = ?,
retirement_contribution = ?,
emergency_contribution = ?,
extra_cash_emergency_pct = ?,
extra_cash_retirement_pct = ?,
updated_at = CURRENT_TIMESTAMP
WHERE user_id = ?
`, [
current_salary || 0,
additional_income || 0,
monthly_expenses || 0,
monthly_debt_payments || 0,
retirement_savings || 0,
emergency_fund || 0,
retirement_contribution || 0,
emergency_contribution || 0, // updated field
extra_cash_emergency_pct || 0,
extra_cash_retirement_pct || 0,
req.userId
]);
}
res.json({ message: 'Financial profile saved/updated.' });
} catch (error) {
console.error('Error saving financial profile:', error);
res.status(500).json({ error: 'Failed to save financial profile.' });
}
});
/* ------------------------------------------------------------------
COLLEGE PROFILES
------------------------------------------------------------------ */
app.post('/api/premium/college-profile', authenticatePremiumUser, async (req, res) => {
const {
career_path_id,
selected_school,
selected_program,
program_type,
is_in_state,
is_in_district,
college_enrollment_status,
is_online,
credit_hours_per_year,
credit_hours_required,
hours_completed,
program_length,
expected_graduation,
existing_college_debt,
interest_rate,
loan_term,
loan_deferral_until_graduation,
extra_payment,
expected_salary,
academic_calendar,
annual_financial_aid,
tuition,
tuition_paid
} = req.body;
try {
const id = uuidv4();
const user_id = req.userId;
await db.run(`
INSERT INTO college_profiles (
id,
user_id,
career_path_id,
selected_school,
selected_program,
program_type,
is_in_state,
is_in_district,
college_enrollment_status,
annual_financial_aid,
is_online,
credit_hours_per_year,
hours_completed,
program_length,
credit_hours_required,
expected_graduation,
existing_college_debt,
interest_rate,
loan_term,
loan_deferral_until_graduation,
extra_payment,
expected_salary,
academic_calendar,
tuition,
tuition_paid,
created_at,
updated_at
) VALUES (
?, -- id
?, -- user_id
?, -- career_path_id
?, -- selected_school
?, -- selected_program
?, -- program_type
?, -- is_in_state
?, -- is_in_district
?, -- college_enrollment_status
?, -- annual_financial_aid
?, -- is_online
?, -- credit_hours_per_year
?, -- hours_completed
?, -- program_length
?, -- credit_hours_required
?, -- expected_graduation
?, -- existing_college_debt
?, -- interest_rate
?, -- loan_term
?, -- loan_deferral_until_graduation
?, -- extra_payment
?, -- expected_salary
?, -- academic_calendar
?, -- tuition
?, -- tuition_paid
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP
)
`, [
id,
user_id,
career_path_id,
selected_school,
selected_program,
program_type || null,
is_in_state ? 1 : 0,
is_in_district ? 1 : 0,
college_enrollment_status || null,
annual_financial_aid || 0,
is_online ? 1 : 0,
credit_hours_per_year || 0,
hours_completed || 0,
program_length || 0,
credit_hours_required || 0,
expected_graduation || null,
existing_college_debt || 0,
interest_rate || 0,
loan_term || 10,
loan_deferral_until_graduation ? 1 : 0,
extra_payment || 0,
expected_salary || 0,
academic_calendar || 'semester',
tuition || 0,
tuition_paid || 0
]);
res.status(201).json({
message: 'College profile saved.',
collegeProfileId: id
});
} catch (error) {
console.error('Error saving college profile:', error);
res.status(500).json({ error: 'Failed to save college profile.' });
}
});
/* ------------------------------------------------------------------
FINANCIAL PROJECTIONS
------------------------------------------------------------------ */
app.post('/api/premium/financial-projection/:careerPathId', authenticatePremiumUser, async (req, res) => {
const { careerPathId } = req.params;
const { projectionData, loanPaidOffMonth, finalEmergencySavings, finalRetirementSavings, finalLoanBalance } = req.body;
try {
const projectionId = uuidv4();
await db.run(`
INSERT INTO financial_projections (
id, user_id, career_path_id, projection_data,
loan_paid_off_month, final_emergency_savings,
final_retirement_savings, final_loan_balance,
created_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
`, [
projectionId,
req.userId,
careerPathId,
JSON.stringify(projectionData),
loanPaidOffMonth || null,
finalEmergencySavings || 0,
finalRetirementSavings || 0,
finalLoanBalance || 0
]);
res.status(201).json({ message: 'Financial projection saved.', projectionId });
} catch (error) {
console.error('Error saving financial projection:', error);
res.status(500).json({ error: 'Failed to save financial projection.' });
}
});
app.get('/api/premium/financial-projection/:careerPathId', authenticatePremiumUser, async (req, res) => {
const { careerPathId } = req.params;
try {
const row = await db.get(`
SELECT projection_data, loan_paid_off_month,
final_emergency_savings, final_retirement_savings, final_loan_balance
FROM financial_projections
WHERE user_id = ?
AND career_path_id = ?
ORDER BY created_at DESC
LIMIT 1
`, [req.userId, careerPathId]);
if (!row) {
return res.status(404).json({ error: 'Projection not found.' });
}
const parsedProjectionData = JSON.parse(row.projection_data);
res.status(200).json({
projectionData: parsedProjectionData,
loanPaidOffMonth: row.loan_paid_off_month,
finalEmergencySavings: row.final_emergency_savings,
finalRetirementSavings: row.final_retirement_savings,
finalLoanBalance: row.final_loan_balance
});
} catch (error) {
console.error('Error fetching financial projection:', error);
res.status(500).json({ error: 'Failed to fetch financial projection.' });
}
});
/* ------------------------------------------------------------------
ROI ANALYSIS (placeholder)
------------------------------------------------------------------ */
app.get('/api/premium/roi-analysis', authenticatePremiumUser, async (req, res) => {
try {
const userCareer = await db.get(`
SELECT * FROM career_paths
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.career_name,
salary: 80000,
tuition: 50000,
netGain: 80000 - 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}`);
});