Fixed issues with API URL formatting and added proxy configuration
This commit is contained in:
parent
4b95b92261
commit
85dfd14aa8
@ -4,3 +4,5 @@ ONET_PASSWORD=2296ahq
|
||||
REACT_APP_BLS_API_KEY=80d7a65a809a43f3a306a41ec874d231
|
||||
GOOGLE_MAPS_API_KEY=AIzaSyCTMgjiHUF2Vl3QriQu2kDEuZWz39ZAR20
|
||||
COLLEGE_SCORECARD_KEY = BlZ0tIdmXVGI4G8NxJ9e6dXEiGUfAfnQJyw8bumj
|
||||
|
||||
REACT_APP_API_URL=http://localhost:5001
|
||||
|
@ -53,6 +53,14 @@ app.use(
|
||||
})
|
||||
);
|
||||
|
||||
// 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');
|
||||
|
@ -6,18 +6,18 @@ import dotenv from 'dotenv';
|
||||
import xlsx from 'xlsx'; // Import xlsx to read the Excel file
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url'; // Import fileURLToPath to handle the current file's URL
|
||||
import { open } from 'sqlite'; // Use the open method directly from sqlite package
|
||||
import sqlite3 from 'sqlite3';
|
||||
import { open } from 'sqlite';
|
||||
|
||||
|
||||
dotenv.config({ path: '/home/jcoakley/backend/.env' }); // Load environment variables
|
||||
sqlite3.verbose();
|
||||
dotenv.config({ path: '/home/jcoakley/aptiva-dev1-app/backend/.env' }); // Load environment variables
|
||||
console.log('ONET_USERNAME:', process.env.ONET_USERNAME);
|
||||
console.log('ONET_PASSWORD:', process.env.ONET_PASSWORD);
|
||||
|
||||
// Get the current directory path (workaround for __dirname in ES modules)
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const allowedOrigins = ['http://localhost:3000', 'http://34.16.120.118:3000', 'https://dev.aptivaai.com'];
|
||||
const mappingFilePath = '/home/jcoakley/public/CIP_to_ONET_SOC.xlsx'
|
||||
const mappingFilePath = '/home/jcoakley/aptiva-dev1-app/public/CIP_to_ONET_SOC.xlsx'
|
||||
const app = express();
|
||||
const PORT = process.env.PORT || 5001;
|
||||
|
||||
@ -25,9 +25,10 @@ const PORT = process.env.PORT || 5001;
|
||||
let db;
|
||||
const initDB = async () => {
|
||||
try {
|
||||
// Opening SQLite connection using sqlite's open function and sqlite3 as the driver
|
||||
db = await open({
|
||||
filename: '/home/jcoakley/salary_info.db', // Correct file path
|
||||
driver: sqlite3.Database,
|
||||
filename: '/home/jcoakley/aptiva-dev1-app/salary_info.db', // Path to SQLite DB file
|
||||
driver: sqlite3.Database, // Use sqlite3's Database driver
|
||||
});
|
||||
console.log('Connected to SQLite database.');
|
||||
} catch (error) {
|
||||
@ -131,6 +132,7 @@ app.get('/api/onet/questions', async (req, res) => {
|
||||
},
|
||||
}
|
||||
);
|
||||
console.log('O*Net Response:', response.data);
|
||||
|
||||
// Add questions to the result set
|
||||
if (response.data.question && Array.isArray(response.data.question)) {
|
||||
@ -156,56 +158,6 @@ app.get('/api/onet/questions', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// Route to fetch O*Net Careers list
|
||||
app.get('/api/onet/questions', async (req, res) => {
|
||||
const { start, end } = req.query;
|
||||
|
||||
if (!start || !end) {
|
||||
return res.status(400).json({ error: 'Start and end parameters are required' });
|
||||
}
|
||||
|
||||
try {
|
||||
const questions = [];
|
||||
let currentStart = parseInt(start, 10);
|
||||
let currentEnd = parseInt(end, 10);
|
||||
|
||||
while (currentStart <= currentEnd) {
|
||||
// Fetch questions from O*Net API for the current range
|
||||
const response = await axios.get(
|
||||
`https://services.onetcenter.org/ws/mnm/interestprofiler/questions?start=${currentStart}&end=${Math.min(currentEnd, currentStart + 11)}`,
|
||||
{
|
||||
auth: {
|
||||
username: process.env.ONET_USERNAME,
|
||||
password: process.env.ONET_PASSWORD,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
// Add questions to the result set
|
||||
if (response.data.question && Array.isArray(response.data.question)) {
|
||||
questions.push(...response.data.question);
|
||||
}
|
||||
|
||||
// Check if there's a next page
|
||||
const nextLink = response.data.link?.find((link) => link.rel === 'next');
|
||||
if (nextLink) {
|
||||
// Update start and end based on the "next" link
|
||||
const nextParams = new URLSearchParams(nextLink.href.split('?')[1]);
|
||||
currentStart = parseInt(nextParams.get('start'), 10);
|
||||
currentEnd = parseInt(nextParams.get('end'), 10);
|
||||
} else {
|
||||
break; // Stop if there are no more pages
|
||||
}
|
||||
}
|
||||
|
||||
res.status(200).json({ questions });
|
||||
} catch (error) {
|
||||
console.error('Error fetching O*Net questions:', error.message);
|
||||
res.status(500).json({ error: 'Failed to fetch O*Net questions' });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// New route to handle Google Maps geocoding
|
||||
app.get('/api/maps/distance', async (req, res) => {
|
||||
const { origins, destinations } = req.query;
|
||||
|
17635
package-lock.json
generated
17635
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
15
package.json
15
package.json
@ -16,14 +16,16 @@
|
||||
"react": "^18.2.0",
|
||||
"react-chartjs-2": "^5.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router-dom": "^7.0.2",
|
||||
"react-scripts": "^3.0.1",
|
||||
"react-router-dom": "^7.1.1",
|
||||
"react-scripts": "^5.0.1",
|
||||
"react-spinners": "^0.15.0",
|
||||
"sqlite": "^5.1.1",
|
||||
"sqlite3": "^5.1.7",
|
||||
"web-vitals": "^4.2.4"
|
||||
"web-vitals": "^4.2.4",
|
||||
"xlsx": "^0.18.5"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"start": "react-scripts start --host 0.0.0.0",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
@ -50,5 +52,8 @@
|
||||
"main": "index.js",
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@babel/plugin-proposal-private-property-in-object": "^7.21.11"
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import React, { useState} from 'react';
|
||||
import { Routes, Route, Navigate } from 'react-router-dom';
|
||||
import GettingStarted from './components/GettingStarted.js';
|
||||
import SignIn from './components/SignIn.js';
|
||||
|
@ -4,7 +4,6 @@ import { useNavigate, useLocation } from 'react-router-dom';
|
||||
import { Chart as ChartJS, CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend } from 'chart.js';
|
||||
import { CareerSuggestions } from './CareerSuggestions.js';
|
||||
import PopoutPanel from './PopoutPanel.js';
|
||||
import LoanRepayment from './LoanRepayment.js';
|
||||
import { Bar } from 'react-chartjs-2';
|
||||
import './Dashboard.css';
|
||||
|
||||
@ -24,7 +23,7 @@ function Dashboard() {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState(null);
|
||||
const [cipCode, setCipCode] = useState(null);
|
||||
|
||||
const apiUrl = process.env.REACT_APP_API_URL;
|
||||
|
||||
useEffect(() => {
|
||||
// Redirect if no data is available
|
||||
@ -38,20 +37,8 @@ function Dashboard() {
|
||||
}
|
||||
}, [location.state, navigate]);
|
||||
|
||||
const fetchCipCode = async (socCode) => {
|
||||
try {
|
||||
const response = await fetch(`/api/cip/${socCode}`);
|
||||
const data = await response.json();
|
||||
if (!data.cipCode) throw new Error('No CIP Code returned');
|
||||
setCipCode(data.cipCode.replace('.', '').slice(0, 4));
|
||||
} catch (err) {
|
||||
console.error('Error fetching CIP Code:', err.message);
|
||||
setError('Failed to fetch CIP Code');
|
||||
}
|
||||
};
|
||||
|
||||
const fetchSchools = async (cipCode, userState) => {
|
||||
const response = await axios.get(`/api/CIP_institution_mapping_fixed.json`);
|
||||
const response = await axios.get(`${apiUrl}/api/CIP_institution_mapping_fixed.json`);
|
||||
const schoolsData = response.data;
|
||||
|
||||
const cleanedCipCode = cipCode.replace('.', '').slice(0, 4);
|
||||
@ -82,13 +69,13 @@ function Dashboard() {
|
||||
|
||||
// Step 1: Fetch user profile to get state/area
|
||||
const token = localStorage.getItem('token');
|
||||
const profileResponse = await fetch('/api/user-profile', {
|
||||
const profileResponse = await fetch(`${apiUrl}/api/user-profile`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
const { state: userState, area: userArea } = await profileResponse.json();
|
||||
|
||||
// Step 2: Fetch CIP Code for SOC Code
|
||||
const cipResponse = await fetch(`/api/cip/${socCode}`);
|
||||
const cipResponse = await fetch(`${apiUrl}/api/cip/${socCode}`);
|
||||
const { cipCode } = await cipResponse.json();
|
||||
|
||||
if (!cipCode) throw new Error('Failed to fetch CIP Code');
|
||||
@ -113,7 +100,7 @@ function Dashboard() {
|
||||
});
|
||||
setTuitionData(tuitionResponse.data);
|
||||
|
||||
const salaryResponse = await axios.get('/api/salary', {
|
||||
const salaryResponse = await axios.get(`/api/salary`, {
|
||||
params: { socCode: cleanedSocCode, area: userArea },
|
||||
});
|
||||
setSalaryData(salaryResponse.data);
|
||||
|
@ -5,13 +5,6 @@ import axios from 'axios';
|
||||
function EconomicProjections({ socCode }) {
|
||||
const [projections, setProjections] = useState(null);
|
||||
const [error, setError] = useState(null);
|
||||
const projections = {
|
||||
"2022 Employment": projectionsData?.['2022 Employment'] || 'N/A',
|
||||
"2032 Employment": projectionsData?.['2032 Employment'] || 'N/A',
|
||||
"Total Change": projectionsData?.['Total Change'] || 'N/A',
|
||||
"Annual Openings": projectionsData?.['Annual Openings'] || 'N/A',
|
||||
"Projected Growth": projectionsData?.['Projected Growth'] || 'N/A',
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (socCode) {
|
||||
|
@ -12,8 +12,11 @@ const InterestInventory = () => {
|
||||
const [careerSuggestions, setCareerSuggestions] = useState([]);
|
||||
|
||||
const fetchQuestions = async () => {
|
||||
const url = `/api/onet/questions?start=1&end=60`;
|
||||
const baseUrl = process.env.NODE_ENV === 'production'
|
||||
? '/api/onet/questions' // Production, assuming it's routed by Nginx
|
||||
: 'http://localhost:5001/api/onet/questions'; // Development, backend runs on localhost:5000
|
||||
|
||||
const url = `${baseUrl}?start=1&end=60`;
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
@ -93,7 +96,11 @@ const InterestInventory = () => {
|
||||
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
const response = await fetch('/api/onet/submit_answers', {
|
||||
const baseUrl = process.env.NODE_ENV === 'production'
|
||||
? '/api/onet/submit_answers' // In production, this is proxied by Nginx to server2 (port 5001)
|
||||
: 'http://localhost:5001/api/onet/submit_answers'; // In development, server2 runs on port 5001
|
||||
|
||||
const response = await fetch(baseUrl, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ answers }),
|
||||
|
@ -1,6 +1,4 @@
|
||||
// components/SignIn.js
|
||||
import axios from 'axios';
|
||||
import jwt_decode from 'jwt-decode'; // Optional for decoding the JWT
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import './SignIn.css';
|
||||
|
Loading…
Reference in New Issue
Block a user