import express from 'express'; import axios from 'axios'; import cors from 'cors'; import dotenv from 'dotenv'; import { fileURLToPath } from 'url'; import path from 'path'; import sqlite3 from 'sqlite3'; // Import SQLite import bodyParser from 'body-parser'; import bcrypt from 'bcrypt'; import jwt from 'jsonwebtoken'; // For token-based authentication // Derive __dirname for ES modules const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const dbPath = path.resolve('/home/jcoakley/aptiva-dev1-app/user_profile.db'); const db = new sqlite3.Database(dbPath, (err) => { if (err) { console.error('Error connecting to database:', err.message); } else { console.log('Connected to user_profile.db'); } }); dotenv.config({ path: path.resolve('/home/jcoakley/aptiva-dev1-app/.env') }); // Adjust the path based on your folder structure const SECRET_KEY = process.env.SECRET_KEY || 'supersecurekey'; // Use a secure key in production console.log('ONET_USERNAME:', process.env.ONET_USERNAME); console.log('ONET_PASSWORD:', process.env.ONET_PASSWORD); console.log('Current Working Directory:', process.cwd()); const app = express(); const PORT = 5000; const allowedOrigins = ['http://localhost:3000', 'http://34.16.120.118:3000', 'https://dev1.aptivaai.com']; app.disable('x-powered-by'); app.use(bodyParser.json()); app.use(express.json()); // Enable CORS with dynamic origin checking app.use( cors({ origin: (origin, callback) => { if (!origin || allowedOrigins.includes(origin)) { callback(null, true); } else { console.error('Blocked by CORS:', origin); callback(new Error('Not allowed by CORS')); } }, methods: ['GET', 'POST', 'OPTIONS'], allowedHeaders: ['Authorization', 'Content-Type', 'Accept', 'Origin', 'X-Requested-With'], credentials: true, }) ); // Handle preflight requests explicitly app.options('*', (req, res) => { res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); res.setHeader('Access-Control-Allow-Headers', 'Authorization, Content-Type, Accept, Origin, X-Requested-With'); res.status(200).end(); }); // Add HTTP headers for security and caching app.use((req, res, next) => { res.setHeader('X-Content-Type-Options', 'nosniff'); res.setHeader('X-Frame-Options', 'DENY'); res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains'); res.setHeader('Content-Security-Policy', "default-src 'self';"); res.removeHeader('X-Powered-By'); next(); }); // Route for user registration app.post('/api/register', async (req, res) => { const { userId, username, password } = req.body; if (!userId || !username || !password) { return res.status(400).json({ error: 'All fields are required' }); } try { const hashedPassword = await bcrypt.hash(password, 10); // Hash the password // Step 1: Insert into user_auth const authQuery = ` INSERT INTO user_auth (username, hashed_password) VALUES (?, ?) `; db.run(authQuery, [username, hashedPassword], function (err) { if (err) { console.error('Error inserting into user_auth:', err.message); if (err.message.includes('UNIQUE constraint failed')) { return res.status(400).json({ error: 'Username already exists' }); } return res.status(500).json({ error: 'Failed to register user' }); } const user_id = this.lastID; // Retrieve the auto-generated id from user_auth // Step 2: Insert into user_profile const profileQuery = ` INSERT INTO user_profile (id, user_id, firstname, lastname, email, zipcode, state, area) VALUES (?, ?, NULL, NULL, NULL, NULL, NULL, NULL) `; db.run(profileQuery, [user_id, user_id], (err) => { if (err) { console.error('Error inserting into user_profile:', err.message); return res.status(500).json({ error: 'Failed to create user profile' }); } // Return success response after both inserts res.status(201).json({ message: 'User registered successfully', user_id }); }); }); } catch (error) { console.error('Error during registration:', error.message); res.status(500).json({ error: 'Internal server error' }); } }); // Route to save or update user profile app.post('/api/user-profile', (req, res) => { const token = req.headers.authorization?.split(' ')[1]; if (!token) { return res.status(401).json({ error: 'Authorization token is required' }); } let userId; try { const decoded = jwt.verify(token, SECRET_KEY); userId = decoded.userId; } catch (error) { return res.status(401).json({ error: 'Invalid or expired token' }); } const { firstName, lastName, email, zipCode, state, area, careerSituation } = req.body; if (!firstName || !lastName || !email || !zipCode || !state || !area) { return res.status(400).json({ error: 'All fields are required' }); } const checkQuery = `SELECT id FROM user_profile WHERE user_id = ?`; db.get(checkQuery, [userId], (err, row) => { if (err) { console.error('Error checking profile:', err.message); return res.status(500).json({ error: 'Database error' }); } const params = [firstName, lastName, email, zipCode, state, area, careerSituation || null, userId]; if (row) { // Profile exists → UPDATE const updateQuery = ` UPDATE user_profile SET firstname = ?, lastname = ?, email = ?, zipcode = ?, state = ?, area = ?, career_situation = ? WHERE user_id = ? `; db.run(updateQuery, params, function (err) { if (err) { console.error('Error updating profile:', err.message); return res.status(500).json({ error: 'Failed to update user profile' }); } res.status(200).json({ message: 'User profile updated successfully' }); }); } else { // Profile doesn't exist → INSERT const insertQuery = ` INSERT INTO user_profile (firstname, lastname, email, zipcode, state, area, career_situation, user_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?) `; db.run(insertQuery, params, function (err) { if (err) { console.error('Error inserting profile:', err.message); return res.status(500).json({ error: 'Failed to create user profile' }); } res.status(201).json({ message: 'User profile created successfully', id: this.lastID }); }); } }); }); // Route for login app.post('/api/login', (req, res) => { const { username, password } = req.body; if (!username || !password) { return res.status(400).json({ error: 'Username and password are required' }); } const query = 'SELECT * FROM user_auth WHERE username = ?'; db.get(query, [username], async (err, row) => { if (err) { console.error('Error fetching user:', err.message); return res.status(500).json({ error: 'Internal server error' }); } if (!row) { return res.status(401).json({ error: 'Invalid username or password' }); } // Verify password const isPasswordValid = await bcrypt.compare(password, row.hashed_password); if (!isPasswordValid) { return res.status(401).json({ error: 'Invalid username or password' }); } const { user_id } = row; // This gets the correct user_id from the row object const token = jwt.sign({ userId: row.user_id }, SECRET_KEY, { expiresIn: '2h' }); res.status(200).json({ token }); }); }); // Route to handle user sign-in (customized) app.post('/api/signin', async (req, res) => { const { username, password } = req.body; if (!username || !password) { return res.status(400).json({ error: 'Both username and password are required' }); } const query = `SELECT user_auth.user_id, user_auth.hashed_password, user_profile.zipcode FROM user_auth LEFT JOIN user_profile ON user_auth.user_id = user_profile.user_id WHERE user_auth.username = ?`; db.get(query, [username], async (err, row) => { if (err) { console.error('Error querying user_auth:', err.message); return res.status(500).json({ error: 'Failed to query user authentication data' }); } console.log('Row data:', row); // Log the result of the query if (!row) { return res.status(401).json({ error: 'Invalid username or password' }); } // Verify password const isMatch = await bcrypt.compare(password, row.hashed_password); if (!isMatch) { return res.status(401).json({ error: 'Invalid username or password' }); } // Ensure that you're using the correct user_id const { user_id, zipcode } = row; console.log('UserID:', user_id); console.log('ZIP Code:', zipcode); // Log the ZIP code to ensure it's correct // Send correct token with user_id const token = jwt.sign({ userId: user_id }, SECRET_KEY, { expiresIn: '2h' }); // You can optionally return the ZIP code or any other data as part of the response res.status(200).json({ message: 'Login successful', token, userId: user_id, zipcode }); }); }); // Route to fetch user profile app.get('/api/user-profile', (req, res) => { const token = req.headers.authorization?.split(' ')[1]; if (!token) { return res.status(401).json({ error: 'Authorization token is required' }); } try { // Extract the userId (user_id) from the token const { userId } = jwt.verify(token, SECRET_KEY); // Query user_profile using user_id const query = 'SELECT * FROM user_profile WHERE user_id = ?'; db.get(query, [userId], (err, row) => { if (err) { console.error('Error fetching user profile:', err.message); return res.status(500).json({ error: 'Internal server error' }); } if (!row) { return res.status(404).json({ error: 'User profile not found' }); } res.status(200).json(row); // Return the profile row }); } catch (error) { console.error('Error verifying token:', error.message); res.status(401).json({ error: 'Invalid or expired token' }); } }); // Route to fetch areas by state app.get('/api/areas', (req, res) => { const { state } = req.query; if (!state) { return res.status(400).json({ error: 'State parameter is required' }); } const dbPath = path.resolve('/home/jcoakley/aptiva-dev1-app/salary_info.db'); // Path to salary_info.db const db = new sqlite3.Database(dbPath, sqlite3.OPEN_READONLY, (err) => { if (err) { console.error('Error connecting to database:', err.message); return res.status(500).json({ error: 'Failed to connect to database' }); } }); const query = `SELECT DISTINCT AREA_TITLE FROM salary_data WHERE PRIM_STATE = ?`; db.all(query, [state], (err, rows) => { if (err) { console.error('Error executing query:', err.message); return res.status(500).json({ error: 'Failed to fetch areas' }); } const areas = rows.map((row) => row.AREA_TITLE); res.json({ areas }); }); db.close((err) => { if (err) { console.error('Error closing the database:', err.message); } }); }); // Start the server app.listen(PORT, () => { console.log(`Server running on http://localhost:${PORT}`); });