├── front └── mymentor-gpt │ ├── README.md │ ├── src │ ├── vite-env.d.ts │ ├── pages │ │ ├── Consult.tsx │ │ ├── Swot.tsx │ │ └── Login.tsx │ ├── index.css │ ├── components │ │ ├── ProtectedRoute.tsx │ │ ├── Notification.tsx │ │ ├── Navbar.tsx │ │ ├── Modal.tsx │ │ ├── Sidebar.tsx │ │ └── InputSuggest.tsx │ ├── main.tsx │ └── assets │ │ └── loading.svg │ ├── .dockerignore │ ├── .env.template │ ├── postcss.config.js │ ├── tsconfig.json │ ├── vite.config.ts │ ├── index.html │ ├── .gitignore │ ├── Dockerfile │ ├── nginx.conf │ ├── tsconfig.node.json │ ├── tailwind.config.js │ ├── deploy.sh │ ├── tsconfig.app.json │ ├── .eslintrc.cjs │ └── package.json ├── back ├── certs │ ├── myCA.srl │ ├── server.ext │ ├── server.csr │ ├── myCA.pem │ ├── generate_certs.sh │ ├── server.crt │ ├── server.key │ └── myCA.key ├── .env.template ├── Dockerfile ├── prompt.js ├── package.json ├── deploy.sh ├── server.js └── package-lock.json ├── .gitignore ├── README.md └── LICENSE /front/mymentor-gpt/README.md: -------------------------------------------------------------------------------- 1 | # MyMenthor FrontEnd 2 | -------------------------------------------------------------------------------- /back/certs/myCA.srl: -------------------------------------------------------------------------------- 1 | 1470D823A2800A25B4DDDF3A195AA5CEB50A0DE1 2 | -------------------------------------------------------------------------------- /front/mymentor-gpt/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /front/mymentor-gpt/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | .dockerignore 4 | Dockerfile 5 | .git 6 | .gitignore 7 | .env 8 | -------------------------------------------------------------------------------- /front/mymentor-gpt/.env.template: -------------------------------------------------------------------------------- 1 | VITE_API_URL=Backend URL 2 | VITE_API_USERNAME=Name of user login 3 | VITE_API_PASSWORD=password 4 | -------------------------------------------------------------------------------- /front/mymentor-gpt/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /back/.env.template: -------------------------------------------------------------------------------- 1 | NAME=Username expected 2 | PASSWORD=password 3 | CLIENTURL=Origin to allow requests from (CORS) 4 | OPENAI_KEY=Key obtained from platform.openai 5 | -------------------------------------------------------------------------------- /front/mymentor-gpt/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [], 3 | "references": [ 4 | { 5 | "path": "./tsconfig.app.json" 6 | }, 7 | { 8 | "path": "./tsconfig.node.json" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /front/mymentor-gpt/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react-swc' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /back/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18-alpine 2 | 3 | ENV NODE_ENV=production 4 | 5 | WORKDIR /usr/src/app 6 | 7 | COPY package*.json ./ 8 | 9 | RUN npm install 10 | 11 | COPY . . 12 | 13 | EXPOSE 8080 14 | 15 | ENTRYPOINT [ "node", "server.js" ] 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | jspm_packages/ 3 | 4 | .npm 5 | 6 | .eslintcache 7 | 8 | .node_repl_history 9 | 10 | logs/ 11 | *.log 12 | 13 | build/ 14 | *.tgz 15 | 16 | .env 17 | .env.test 18 | .env.*.local 19 | .env.* 20 | !.env.template 21 | 22 | *.rc 23 | 24 | dist/ 25 | 26 | *.tsbuildinfo 27 | -------------------------------------------------------------------------------- /front/mymentor-gpt/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | MyMenthor GPT 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /front/mymentor-gpt/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /front/mymentor-gpt/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18-alpine AS build 2 | 3 | WORKDIR /app 4 | 5 | COPY package*.json ./ 6 | 7 | RUN npm install 8 | 9 | COPY . . 10 | 11 | RUN npm run build 12 | 13 | FROM nginx:alpine 14 | 15 | COPY --from=build /app/dist /usr/share/nginx/html 16 | 17 | COPY nginx.conf /etc/nginx/conf.d/default.conf 18 | 19 | EXPOSE 8080 20 | 21 | CMD ["nginx", "-g", "daemon off;"] 22 | -------------------------------------------------------------------------------- /front/mymentor-gpt/nginx.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 8080; 3 | 4 | server_name _; 5 | 6 | root /usr/share/nginx/html; 7 | index index.html index.htm; 8 | 9 | location / { 10 | try_files $uri /index.html; 11 | } 12 | 13 | error_page 404 /index.html; 14 | 15 | access_log /var/log/nginx/react-app.access.log; 16 | error_log /var/log/nginx/react-app.error.log; 17 | } 18 | -------------------------------------------------------------------------------- /front/mymentor-gpt/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", 5 | "skipLibCheck": true, 6 | "module": "ESNext", 7 | "moduleResolution": "bundler", 8 | "allowSyntheticDefaultImports": true, 9 | "strict": true, 10 | "noEmit": true 11 | }, 12 | "include": ["vite.config.ts"] 13 | } 14 | -------------------------------------------------------------------------------- /front/mymentor-gpt/src/pages/Consult.tsx: -------------------------------------------------------------------------------- 1 | import { Outlet } from 'react-router-dom'; 2 | import Navbar from '../components/Navbar'; 3 | import Sidebar from '../components/Sidebar'; 4 | 5 | function ConsultPage() { 6 | return ( 7 | <> 8 |
9 | 10 |
11 | 12 | 13 |
14 |
15 | 16 | ) 17 | } 18 | 19 | export default ConsultPage 20 | -------------------------------------------------------------------------------- /back/prompt.js: -------------------------------------------------------------------------------- 1 | export default ({sector, about}) => (` 2 | You are a helpful assistant that helps teams from businesses of various sectors to brainstorm ideas for the company. 3 | 4 | The current company operates in the sector of ${sector} and the team needs to brainstorm possible ideas to describe the company\`s ${about}. 5 | 6 | The answer must be in JSON format with the key "suggestions" whose value is an array with two suggestions for the team, such as 7 | {"suggestions": ["suggestion1", "suggestion2"]} 8 | `) 9 | -------------------------------------------------------------------------------- /front/mymentor-gpt/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | 3 | const defaultTheme = require('tailwindcss/defaultTheme') 4 | 5 | export default { 6 | content: [ 7 | "./index.html", 8 | "./src/**/*.{js,ts,jsx,tsx}", 9 | ], 10 | theme: { 11 | extend: { 12 | fontFamily: { 13 | "sans": ["Poppins", ...defaultTheme.fontFamily.sans], 14 | "rubik": ["Rubik"] 15 | } 16 | }, 17 | }, 18 | plugins: [], 19 | fontFamily: { 20 | }, 21 | } 22 | 23 | -------------------------------------------------------------------------------- /back/certs/server.ext: -------------------------------------------------------------------------------- 1 | authorityKeyIdentifier=keyid,issuer 2 | basicConstraints=CA:FALSE 3 | keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment 4 | subjectAltName = @alt_names 5 | [alt_names] 6 | DNS.1 = server # Be sure to include the domain name here because Common Name is not so commonly honoured by itself 7 | DNS.2 = bar.server # Optionally, add additional domains (I've added a subdomain here) 8 | IP.1 = 192.168.0.13 # Optionally, add an IP address (if the connection which you have planned requires it) 9 | -------------------------------------------------------------------------------- /back/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "back", 3 | "type": "module", 4 | "version": "1.0.0", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "description": "", 13 | "dependencies": { 14 | "cookie-parser": "^1.4.6", 15 | "cors": "^2.8.5", 16 | "dotenv": "^16.4.5", 17 | "express": "^4.19.2", 18 | "js-cookie": "^3.0.5", 19 | "openai": "^4.52.0", 20 | "react-router-dom": "^6.23.1" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /back/deploy.sh: -------------------------------------------------------------------------------- 1 | LOCATION=us-east1 2 | PROJECT_ID=$(gcloud config list --format 'value(core.project)') 3 | REPOSITORY=${PROJECT_ID} 4 | IMG=mymenthorback 5 | SERVICE_NAME=mymenthorback 6 | 7 | docker build -t ${LOCATION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY}/${IMG}:latest . 8 | 9 | docker push ${LOCATION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY}/${IMG}:latest 10 | 11 | gcloud run deploy ${SERVICE_NAME} \ 12 | --image=${LOCATION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY}/${IMG}:latest \ 13 | --region=${LOCATION} \ 14 | --platform=managed \ 15 | --allow-unauthenticated 16 | -------------------------------------------------------------------------------- /front/mymentor-gpt/deploy.sh: -------------------------------------------------------------------------------- 1 | LOCATION=us-east1 2 | PROJECT_ID=$(gcloud config list --format 'value(core.project)') 3 | REPOSITORY=${PROJECT_ID} 4 | IMG=mymenthor 5 | SERVICE_NAME=mymenthor 6 | 7 | docker build -t ${LOCATION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY}/${IMG}:latest . 8 | 9 | docker push ${LOCATION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY}/${IMG}:latest 10 | 11 | gcloud run deploy ${SERVICE_NAME} \ 12 | --image=${LOCATION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY}/${IMG}:latest \ 13 | --region=${LOCATION} \ 14 | --platform=managed \ 15 | --allow-unauthenticated 16 | -------------------------------------------------------------------------------- /front/mymentor-gpt/src/index.css: -------------------------------------------------------------------------------- 1 | @import url("https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700;800;900&display=swap"); 2 | @import url('https://fonts.googleapis.com/css2?family=Rubik:ital,wght@0,300..900;1,300..900&display=swap'); 3 | 4 | @tailwind base; 5 | @tailwind components; 6 | @tailwind utilities; 7 | 8 | :root { 9 | /* background: linear-gradient(90deg, #e3ffe7 0%, #d9e7ff 100%); */ 10 | 11 | /* background-color: #0093E9; */ 12 | /* background-image: linear-gradient(160deg, #0093E9 0%, #80D0C7 100%); */ 13 | } 14 | 15 | @layer base { 16 | .font-outline-2 { 17 | -webkit-text-stroke: 1px #22d3ee; 18 | } 19 | .font-outline-4 { 20 | -webkit-text-stroke: 4px black; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /front/mymentor-gpt/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", 5 | "target": "ES2020", 6 | "useDefineForClassFields": true, 7 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 8 | "module": "ESNext", 9 | "skipLibCheck": true, 10 | 11 | /* Bundler mode */ 12 | "moduleResolution": "bundler", 13 | "allowImportingTsExtensions": true, 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "moduleDetection": "force", 17 | "noEmit": true, 18 | "jsx": "react-jsx", 19 | 20 | /* Linting */ 21 | "strict": true, 22 | "noUnusedLocals": true, 23 | "noUnusedParameters": true, 24 | "noFallthroughCasesInSwitch": true 25 | }, 26 | "include": ["src"] 27 | } 28 | -------------------------------------------------------------------------------- /front/mymentor-gpt/src/components/ProtectedRoute.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Navigate } from 'react-router-dom'; 3 | 4 | interface ProtectedRouteProps { 5 | component: React.ComponentType; 6 | } 7 | 8 | const ProtectedRoute: React.FC = ({ component: Component }) => { 9 | 10 | const expectedUsername = import.meta.env.VITE_API_USERNAME; 11 | 12 | const expectedPassword = import.meta.env.VITE_API_PASSWORD; 13 | 14 | const name = localStorage.getItem('username'); 15 | const password = localStorage.getItem('password'); 16 | 17 | const isAuthenticated = (name === expectedUsername && password === expectedPassword); 18 | 19 | if (!isAuthenticated) { 20 | return ; 21 | } 22 | return ; 23 | }; 24 | 25 | export default ProtectedRoute; 26 | -------------------------------------------------------------------------------- /front/mymentor-gpt/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import LoginPage from './pages/Login.tsx' 4 | import ConsultPage from './pages/Consult.tsx' 5 | import SwotPage from './pages/Swot.tsx' 6 | import ProtectedRoute from './components/ProtectedRoute.tsx' 7 | import './index.css' 8 | import { BrowserRouter as Router, Route, Routes } from 'react-router-dom'; 9 | 10 | ReactDOM.createRoot(document.getElementById('root')!).render( 11 | 12 | 13 | 14 | } /> 15 | } > 16 | } /> 17 | 18 | 19 | 20 | , 21 | ) 22 | -------------------------------------------------------------------------------- /front/mymentor-gpt/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:@typescript-eslint/recommended-type-checked', 7 | 'plugin:react-hooks/recommended', 8 | 'plugin:@typescript-eslint/stylistic-type-checked', 9 | 'plugin:react/recommended', 10 | 'plugin:react/jsx-runtime' 11 | ], 12 | ignorePatterns: ['dist', '.eslintrc.cjs'], 13 | parser: '@typescript-eslint/parser', 14 | plugins: ['react-refresh'], 15 | rules: { 16 | 'react-refresh/only-export-components': [ 17 | 'warn', 18 | { allowConstantExport: true }, 19 | ], 20 | }, 21 | parserOptions: { 22 | ecmaVersion: 'latest', 23 | sourceType: 'module', 24 | project: ['./tsconfig.json', './tsconfig.node.json'], 25 | tsconfigRootDir: __dirname, 26 | }, 27 | } 28 | -------------------------------------------------------------------------------- /front/mymentor-gpt/src/components/Notification.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | 3 | const Notification: React.FC<{ message: string }> = ({ message }) => { 4 | const [show, setShow] = useState(false); 5 | 6 | useEffect(() => { 7 | setShow(true); 8 | 9 | // Hide the notification after 5 seconds 10 | const timer = setTimeout(() => { 11 | setShow(false); 12 | }, 5000); 13 | 14 | return () => clearTimeout(timer); 15 | }, []); 16 | 17 | return ( 18 |
23 | {message} 24 |
25 | 26 | ); 27 | }; 28 | 29 | export default Notification; 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # experiment-react-gpt 2 | 3 | ## Front 4 | 5 | ### Develop 6 | 7 | In a terminal, simply go to `front/mymentor-gpt/` and run `npm run dev`. It expects to find a `.env` file, make sure to use `.env.template` 8 | as a reference. The username and password should match what the backend sees as correct. 9 | 10 | ### Deploy 11 | 12 | Just run `./deploy.sh`. It expects to already have `gcloud init` executed. Also the artifact registry repository is already expected to be available. 13 | For deploying on cloud run use the file `.env.production`. 14 | 15 | ## Back 16 | 17 | ### Develop 18 | 19 | Go to `back` and then run `node server.js`. It also expects to have the files `.env` properly setup. Use `env.template` as reference. Notice the 20 | username and password must be the same as from the `front` setting. 21 | 22 | 23 | ### Deploy 24 | 25 | Run `./deploy.sh`. For deploying on cloud run use the file `.env.production`. 26 | -------------------------------------------------------------------------------- /back/certs/server.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIICijCCAXICAQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUx 3 | ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcN 4 | AQEBBQADggEPADCCAQoCggEBAKt9YQSO8NS0Mxp43xZD1HYwxLFDtFWWTWqG6MX+ 5 | zyDl3zF/lkqDLu7TjQdNnnW6cB28wrT05kIB23FuMkVLEbf6OR7OXURC8kVg3fOb 6 | XhV9/4oMxQbZgakL2scxgkvxJQDH04fju/VCPd/IK2eKiuY3i2YcB7AS4w6VbERV 7 | zrLfba8ml9mLC2glYWqWV9hmrUH9MHscPnFcocq9zfENoKQw48IP7s+Wzs4/+ZVe 8 | cJclG2200b3JdG1DEUBIxkhWCuBDSeDz/w2xXbQrEEeNTTI/WMqexKDtPO2BC1Po 9 | VpPMnYvVOr9vUNf1HT9YuyvZvdl9mouCbS+hE1clb9yaK1cCAwEAAaAAMA0GCSqG 10 | SIb3DQEBCwUAA4IBAQAx4b7S9plJwIOUHolbt7zlxT8T0bMM7LsF2bbjMr3z926a 11 | ouBG76S+dHwCqsu/mceYSVZxjRF0L9WdxXkdAzplPd8/La+FePb2tiZK6X7cO6Gd 12 | VVlwjCsF/Gz4ztn2Ht6HNvIKszJBj1a6xrTCRnh1p7JttwPzPsfGuloeR6y8qjTw 13 | UUyoiyI7dlzNbf9u8Kh4fwdNGDBQ0Ll4N19A3zGmBPEm9eF+t874P8XJA1Q9zbmF 14 | jyCaBLLZq9TXqHRwjdSZpDZH5hn+S4z+2s1WoeZsE/LIVG+nG7AlBrkYxZEfwV/D 15 | uOpejAfFZBLmoGTCdtH0AnCwPTUFgGTd6rDu6OgX 16 | -----END CERTIFICATE REQUEST----- 17 | -------------------------------------------------------------------------------- /front/mymentor-gpt/src/assets/loading.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 willfuks 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /front/mymentor-gpt/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mymentor-gpt", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc -b && vite build", 9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "js-cookie": "^3.0.5", 14 | "react": "^18.3.1", 15 | "react-dom": "^18.3.1", 16 | "react-icons": "^5.2.1", 17 | "react-router-dom": "^6.23.1" 18 | }, 19 | "devDependencies": { 20 | "@types/js-cookie": "^3.0.6", 21 | "@types/node": "^20.14.7", 22 | "@types/react": "^18.3.3", 23 | "@types/react-dom": "^18.3.0", 24 | "@typescript-eslint/eslint-plugin": "^7.13.1", 25 | "@typescript-eslint/parser": "^7.13.1", 26 | "@vitejs/plugin-react-swc": "^3.5.0", 27 | "autoprefixer": "^10.4.19", 28 | "eslint": "^8.57.0", 29 | "eslint-plugin-react": "^7.34.3", 30 | "eslint-plugin-react-hooks": "^4.6.2", 31 | "eslint-plugin-react-refresh": "^0.4.7", 32 | "postcss": "^8.4.38", 33 | "tailwindcss": "^3.4.4", 34 | "typescript": "^5.2.2", 35 | "vite": "^5.3.1" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /front/mymentor-gpt/src/components/Navbar.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import Modal from './Modal'; 3 | import { TbLogout } from "react-icons/tb"; 4 | import { useLocation } from 'react-router-dom'; 5 | 6 | const Navbar: React.FC = () => { 7 | const [isModalOpen, setIsModalOpen] = useState(false); 8 | 9 | const handleOpenModal = () => { 10 | setIsModalOpen(true); 11 | }; 12 | 13 | const location = useLocation(); 14 | const pathSegments = location.pathname.split('/'); 15 | const lastSegment = pathSegments[pathSegments.length - 1] || ''; 16 | 17 | return ( 18 | 31 | ); 32 | }; 33 | 34 | export default Navbar; 35 | -------------------------------------------------------------------------------- /back/certs/myCA.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDazCCAlOgAwIBAgIUf8RNGe8uiGWB2DB8UnVsLhwSQkcwDQYJKoZIhvcNAQEL 3 | BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM 4 | GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNDA2MjIxNTEzMjlaFw0yNjA5 5 | MjUxNTEzMjlaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw 6 | HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB 7 | AQUAA4IBDwAwggEKAoIBAQCaCHobTBFwRvy+qkMZtxdUJcVXWBxNQKEbFLM2Umj5 8 | ac269KAz831NCcsTnMkVij0rM3qpGXJhJdUD5/j8RDpHlkkUc9/i3fx08hrFZ5pG 9 | OTgrFLvtA+DtnfavYIzMhwZWx312B5QtDTGWV/+YrU6gQiELzsIJRwbut0BXyEKQ 10 | UOqCfFFuujnr/cM4svte/YHTFJ3dTYizruVEm9ljN6EZVmM6vh1GoXzSpqmsv1Ii 11 | txZdvvFobO/vrYIxMwskemrPIsRkR7+ZLrknus6i+0Kwo4BJQy7pWqh8XLCZhTR1 12 | UqbFcX/6Cf357XOgSliufUYJQgeXGkwP6d8rivgw4QaTAgMBAAGjUzBRMB0GA1Ud 13 | DgQWBBSTT4jVnZkQh3yTSjfcOYWKy8NvsDAfBgNVHSMEGDAWgBSTT4jVnZkQh3yT 14 | SjfcOYWKy8NvsDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAd 15 | pyUYpAH54o68p9fhrs17x98xHzeaoz9yYTxGxI0GCSx9JTDrNxLF37MJ0IwL994H 16 | 78NQSZZ6DeVh7+8yOlTl/9eM9FDYbTiGbc5K+mLLgPLsz+ydcZo4E8OZDjnKPrMl 17 | mXMZep/UWDMrNRfHHCiCMUdzIfP2ZOgkA7obrMpU4RLdCUuMlSzmhZnFkmKBV3sQ 18 | 1V1Y6fvydv63tQ6BhdrDEWfLQj18Ylj9Bh70LAT3v5xG8IHVxL8OHPQhrE8swoVy 19 | GJagRI2SLyHLaA3vrT+jFbDj9j+OHphxym39GYlTj/l1JxSE5kmr6+wpUu0d+c6Z 20 | I0VuIr5iaWejAyOL9hWZ 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /back/certs/generate_certs.sh: -------------------------------------------------------------------------------- 1 | ###################### 2 | # Become a Certificate Authority 3 | ###################### 4 | 5 | # Generate private key 6 | openssl genrsa -des3 -out myCA.key 2048 7 | # Generate root certificate 8 | openssl req -x509 -new -nodes -key myCA.key -sha256 -days 825 -out myCA.pem 9 | 10 | ###################### 11 | # Create CA-signed certs 12 | ###################### 13 | 14 | NAME=server 15 | # Generate a private key 16 | openssl genrsa -out $NAME.key 2048 17 | # Create a certificate-signing request 18 | openssl req -new -key $NAME.key -out $NAME.csr 19 | # Create a config file for the extensions 20 | >$NAME.ext cat <<-EOF 21 | authorityKeyIdentifier=keyid,issuer 22 | basicConstraints=CA:FALSE 23 | keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment 24 | subjectAltName = @alt_names 25 | [alt_names] 26 | DNS.1 = $NAME # Be sure to include the domain name here because Common Name is not so commonly honoured by itself 27 | DNS.2 = bar.$NAME # Optionally, add additional domains (I've added a subdomain here) 28 | IP.1 = 192.168.0.13 # Optionally, add an IP address (if the connection which you have planned requires it) 29 | EOF 30 | # Create the signed certificate 31 | openssl x509 -req -in $NAME.csr -CA myCA.pem -CAkey myCA.key -CAcreateserial \ 32 | -out $NAME.crt -days 825 -sha256 -extfile $NAME.ext 33 | -------------------------------------------------------------------------------- /back/certs/server.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDeDCCAmCgAwIBAgIUFHDYI6KACiW03d86GVqlzrUKDeEwDQYJKoZIhvcNAQEL 3 | BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM 4 | GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNDA2MjIxNTEzNDlaFw0yNjA5 5 | MjUxNTEzNDlaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw 6 | HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB 7 | AQUAA4IBDwAwggEKAoIBAQCrfWEEjvDUtDMaeN8WQ9R2MMSxQ7RVlk1qhujF/s8g 8 | 5d8xf5ZKgy7u040HTZ51unAdvMK09OZCAdtxbjJFSxG3+jkezl1EQvJFYN3zm14V 9 | ff+KDMUG2YGpC9rHMYJL8SUAx9OH47v1Qj3fyCtniormN4tmHAewEuMOlWxEVc6y 10 | 322vJpfZiwtoJWFqllfYZq1B/TB7HD5xXKHKvc3xDaCkMOPCD+7Pls7OP/mVXnCX 11 | JRtttNG9yXRtQxFASMZIVgrgQ0ng8/8NsV20KxBHjU0yP1jKnsSg7TztgQtT6FaT 12 | zJ2L1Tq/b1DX9R0/WLsr2b3ZfZqLgm0voRNXJW/cmitXAgMBAAGjYDBeMB8GA1Ud 13 | IwQYMBaAFJNPiNWdmRCHfJNKN9w5hYrLw2+wMAkGA1UdEwQCMAAwCwYDVR0PBAQD 14 | AgTwMCMGA1UdEQQcMBqCBnNlcnZlcoIKYmFyLnNlcnZlcocEwKgADTANBgkqhkiG 15 | 9w0BAQsFAAOCAQEAACS0uujRE3+FZ4UBEwgD2vAN7zLvXz1uJYUyoD+wrrHF7Qfh 16 | 1LLWzXI0e9rALViHqOFJd/fY0rDv770Cp/AYvOUX451pYZ15WvN2zAajJUu/PYJ0 17 | Y0K9byMH+Ekx/sQcZvXJ9Ss5oatWiGjKunrJSywgJBUnRV8+L8HvDrOxw6sj4xXP 18 | Zal0mzk2UkQOckVWjRwsHm5/SAKISlaRHvnghZgKvNlcFFL1OxN74lNbo9ab0GP2 19 | tPlCVpeN1rawfNcb3LL2M2omNuiRRvBrHk8x9GEWDIIuaUCCZ4KdwhUT4OKFIjC0 20 | OHDt24TRARd6A5JGSTCsvgChfWkdruTWEyxY5g== 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /front/mymentor-gpt/src/components/Modal.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useNavigate } from 'react-router-dom'; 3 | import { FiX, FiCheck } from 'react-icons/fi'; 4 | 5 | interface ModalProps { 6 | setIsModalOpen: React.Dispatch>; 7 | } 8 | 9 | const Modal: React.FC = ({ setIsModalOpen }) => { 10 | const navigate = useNavigate(); 11 | 12 | const handleCancel = () => { 13 | setIsModalOpen(false); 14 | }; 15 | 16 | const handleOk = () => { 17 | localStorage.removeItem('username'); 18 | localStorage.removeItem('password'); 19 | document.cookie = 'username=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;'; 20 | document.cookie = 'password=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;'; 21 | navigate('/'); 22 | }; 23 | 24 | return ( 25 |
26 |
27 |

Confirm logout?

28 |
29 | 36 | 43 |
44 |
45 |
46 | ); 47 | }; 48 | 49 | export default Modal; 50 | -------------------------------------------------------------------------------- /back/certs/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEogIBAAKCAQEAq31hBI7w1LQzGnjfFkPUdjDEsUO0VZZNaoboxf7PIOXfMX+W 3 | SoMu7tONB02edbpwHbzCtPTmQgHbcW4yRUsRt/o5Hs5dRELyRWDd85teFX3/igzF 4 | BtmBqQvaxzGCS/ElAMfTh+O79UI938grZ4qK5jeLZhwHsBLjDpVsRFXOst9tryaX 5 | 2YsLaCVhapZX2GatQf0wexw+cVyhyr3N8Q2gpDDjwg/uz5bOzj/5lV5wlyUbbbTR 6 | vcl0bUMRQEjGSFYK4ENJ4PP/DbFdtCsQR41NMj9Yyp7EoO087YELU+hWk8ydi9U6 7 | v29Q1/UdP1i7K9m92X2ai4JtL6ETVyVv3JorVwIDAQABAoIBADTxg2dN3go9C93r 8 | XSxZiplmmwMDHQSjX4k411pxPW8xqMN2WWbtsyqmg06y5u3/vidgJFiLlx7fxejy 9 | FXERKQo8pS/elCrD5FoI+tz/pX9LI/5xzM49jmsrLsL6hPh/HWZtSuAULe4dIk8Z 10 | VFUmD5XdphrDIph4bEMPXJqIR0UvdbhtVo3XUbYEkLhf/7KG3lSrhCYftR8KzwdJ 11 | LDjNODRPn0HALAYqp2li4aLWh8n+YFPgbOXpqQVuoJr6JGBjjrrABOoOPqVxvVet 12 | 5Kr0agSB4ts0FkVpsjkv5Q1EqI0WzpKYBGwmPZWA/gVtjOLYfmlSl/IVg3amuK8Z 13 | WjUBmXECgYEA4ybw4kgw8IFEBS76IB/2BMnwh5HaBS3whj4r54IRrHwoodFsXxMY 14 | MgaEUrMKi0iv/13e0Rw9HmDjgIzecEqvqPIpeAkQwXehINSr47nIVFo3wkA7rBCW 15 | amYavsJd+BgPEFYwDvm6KCJn9ZuJCjFI2+6X7iXM1lGAiohiATs+pokCgYEAwUTF 16 | Tca45DASdKcy5YKIaKezeVRNLX/nEI2ELaSGPx3F8Cla5oQk3DhGL1VP+meFKWT9 17 | tYvXiAZhYwExr41FOMRtJ2lw7Zmz1qw2ewB21Hf460lafHZgXfZF15fbmd19aD8J 18 | 4q0XB71gRPnXezm8TY43gczSex5S82U6Riznyt8CgYB6coNnd4LqIsSIBsrOhmwY 19 | KMOAUR81q438z4bbRUJBuKwujrXcim6AiQLLkbgdXRGClxeFobNQrOn2YfmGjrwi 20 | VmgETN67a8Fv8TS3jW5wCjIEhJumJUrwEBpaumvtUQrNw6gkannvKJzhyPYWiHq+ 21 | E9/SgI3q+gRQqTQrdcRQAQKBgFsUcrstjJKVCQ2KghThCDHx3kWucdSgcx884e60 22 | m0OBX3fHtXvvCIPzzTefR9qlEH8qhdoZtg/3mcuTk3u84sNW5m3tZffDOJpRfDdJ 23 | R7bndxpC7m67RrzhQ5bPjQEc/W20T49QrQrLt2OqLl93HQOboxL7mLHG36aurODH 24 | ZtEXAoGAH50jVU6j408gjLLYCcyBp25NJzyAyR+TX369XDtb6jQ1A6s4cV8cbuSJ 25 | kkJHPwXDB8GXJpK7ZR4lV1yjwsC1qvHZQw+1fm1SsRNAj7Xjj5evtsr5Dwtbj7KO 26 | D8yqv+sYZzmOR35BoPciefgsKujsQvMQCXLVIX6qwBTHEqeLvxc= 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /front/mymentor-gpt/src/pages/Swot.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import InputSuggest from '../components/InputSuggest'; 3 | import { GiOpenTreasureChest } from "react-icons/gi"; 4 | import { MdOutlineCenterFocusWeak } from "react-icons/md"; 5 | import { GiBiceps } from "react-icons/gi"; 6 | import { GrThreats } from "react-icons/gr"; 7 | 8 | 9 | function SwotPage() { 10 | 11 | const [sector, setSector] = useState(''); 12 | 13 | const handleChange = (event: React.ChangeEvent): void => { 14 | setSector(event.target.value); 15 | }; 16 | 17 | const data = [ 18 | ['SWOT: Strength', ], 19 | ['SWOT: Weakness', ], 20 | ['SWOT: Opportunities', ], 21 | ['SWOT: Threats', ] 22 | ] 23 | 24 | return ( 25 |
26 | 27 |
28 | 32 | 40 |

BrainStorm

41 | 42 | {data.map((e) => ( 43 | 49 | ))} 50 | 51 |
52 | 53 | 54 |
55 | ); 56 | } 57 | 58 | export default SwotPage 59 | -------------------------------------------------------------------------------- /back/certs/myCA.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | Proc-Type: 4,ENCRYPTED 3 | DEK-Info: DES-EDE3-CBC,0EB4F38B5F610B6C 4 | 5 | ltx475pWH3rOtYLjltfTph5KdVCMiAOddAj0wCA5VSRwgCN6o6wz8KdWQ8PHn1qz 6 | BlqRgYQOGarKyQpNl+lUXVaEIf4o9zDdMXhYViV7l9XC2JvCXGXYAzn6/Ss79RM9 7 | Oi4jR4yYBQPAXSXH8pQJiOWu4ay31Q4zHy0AGg4PNNfpVWZqmAo35RMJXegakrV3 8 | TK7RTk3ZAa0x8SP89Gbqvu0thqKuF39iKB3HqErwLt+829g8jZ8YX4YZlclfyO5w 9 | d6H70DEViNPWuoA0Z0arwfwyxlQiaxcOHbbT1n2+uQ+B74nKhGNsDsfHR37BCE8n 10 | Vm7SnAzkIyqJyHGK0OHei4zSspcId3exXXCFFe6UKoa/lnpd5tnPb3M7BjfaY8Uf 11 | zv7tQRWaLqSB+S9AosyqoXZC6H6S07IE5HI23wEaVIkiGAsino/+LFyL84EFfJY8 12 | yf1GbSd4ZhkhkxU4wehg77er9X0zOb6SrWr1MDFllbe3EUKDaMtTpAPENbDFez0G 13 | MffVm0qCoFyjgwbb0/wqjlPyaXRSob6d7mh+cP1vuo9/lIvpyWcsjztENx+cozVg 14 | dvwfZXBPKYzMPMHp+2x8YUz55mov3T36bXnKz2OIQ93btyuquM7S14ikawHoaY7g 15 | XA2Ycv4U/xQg3r/UDbuqeCE65tgSQNWDDoauYzAsQ0pv2LiUyLm64Ou/gK8/gx+k 16 | jELdNJFcx3g7UHKXxwxTtKOm43gdwkXdBhFmpIZN03TXtZ8AUSI1SYpidTdm0iPM 17 | dzA7DJSGeaObvAGEjFwUwpVrOMCi964XTJ3dVTjooUZLwT9BOcTj4OozjAODIAxb 18 | dCznSWDY/hgz9wIkSIJWCZtltOrnPzElAKElrge94nFTKQh4eTKv8CDy/a5IbuEs 19 | hulUUUxktzLl6A0VmlzBfOZcTPXzmAqALUWfgM9ZyGq+I8rEwCgQuGNWvVv+Lko2 20 | 7fLzn6XKi72GHvUsb162Z3vxgFOmGxDl4X0U9lLeslfmST+ckjyN3GCtTw+vIPxD 21 | JGbOc9mgI9y6ZB8GiOScZrdWzOwT4jA6sglGO2mr3SutbEyPZZo8ClV0jgYBfBRu 22 | XGdekI4ZowD4XrrfImepC5k0RUmeJUL9pIv6sJquC6TAsI0pBS41PRbudW1aN3w9 23 | 9JhvXSi+/1Az5QBYaLT65aTdMlUA4Pvq8x2yzdCD607a7S+IGg/DvHzkc07FlFl0 24 | WTw1WtHeKm8gakOczwoKV5V5kn0Feb0NFIxfVnbEHCsy1HYnqYn0vxFxAZlAcBWX 25 | bvFJzsMSTNB2G7bGToGCnkh2KU4wDVZMLkaRiWp0I2sg/IAcRIcaZ6K53Rj0bCds 26 | 6wAltmM3uXamnsS4dfAnzb005VJcxtXzzO9rb+Lman+1HizzZdNWUWPQluuh4Q4O 27 | KlMoeOJH3dffVabFSBMPGIW7vicm5aprsYUr4dZkB6IjU2fI9xmJe5H3lMr/RWK5 28 | otOgKIdMUkp86h2wohA2uzfCkLv2k36bqD1CPTl7kXU3pboqZJIs8X/PphGBfW5X 29 | 3186rON3H3FyX6C51OMXx9VEVF0Ln0PSFBP/3HMR5W0C+pVePqF3iKFn4SHeFpd4 30 | -----END RSA PRIVATE KEY----- 31 | -------------------------------------------------------------------------------- /front/mymentor-gpt/src/components/Sidebar.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import { FaBars, FaTimes } from 'react-icons/fa'; 3 | import { Link } from 'react-router-dom'; 4 | import { AiOutlinePartition } from "react-icons/ai"; 5 | 6 | const Sidebar = () => { 7 | const [isSidebarOpen, setIsSidebarOpen] = useState(false); 8 | const [activeItem, setActiveItem] = useState('slot'); 9 | 10 | const toggleSidebar = () => { 11 | setIsSidebarOpen(!isSidebarOpen); 12 | }; 13 | 14 | const handleMenuItemClick = (item: string) => () => { 15 | setIsSidebarOpen(false); 16 | setActiveItem(item); 17 | } 18 | 19 | return ( 20 |
21 | {!isSidebarOpen && 22 | } 28 |
33 |
34 | 40 | 41 |

Menu

42 |
    43 |
  • 44 | 49 | 50 |

    SWOT

    51 | 52 |
  • 53 |
54 |
55 |
56 | 57 | {isSidebarOpen && ( 58 |
62 | )} 63 |
64 | ); 65 | }; 66 | 67 | export default Sidebar; 68 | -------------------------------------------------------------------------------- /back/server.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import cookieParser from 'cookie-parser'; 3 | import cors from 'cors'; 4 | import dotenv from 'dotenv'; 5 | import fs from 'fs'; 6 | import https from 'https'; 7 | import path from 'path'; 8 | import { fileURLToPath } from 'url'; 9 | import OpenAI from 'openai'; 10 | import templatePrompt from './prompt.js'; 11 | 12 | 13 | const __filename = fileURLToPath(import.meta.url); 14 | const __dirname = path.dirname(__filename); 15 | 16 | const mode = process.env.NODE_ENV || 'development'; 17 | const envFile = mode === 'production' ? '.env.production' : '.env'; 18 | dotenv.config({ path: path.resolve(__dirname, envFile) }); 19 | 20 | const app = express(); 21 | 22 | app.use(express.json()); 23 | app.use(express.urlencoded({ extended: true })); 24 | app.use(cookieParser()); 25 | 26 | const corsOptions = { 27 | origin: process.env.CLIENTURL, 28 | credentials: true 29 | } 30 | 31 | app.use(cors(corsOptions)); 32 | app.options('*', cors(corsOptions)); 33 | 34 | const validUsername = process.env.NAME; 35 | const validPassword = process.env.PASSWORD; 36 | 37 | const openai = new OpenAI({ 38 | apiKey: process.env['OPENAI_KEY'], 39 | timeout: 20 * 1000, // 20 seconds 40 | }); 41 | 42 | function readTemplateFromFile(filePath) { 43 | try { 44 | const template = fs.readFileSync(filePath, 'utf8'); 45 | return template; 46 | } catch (err) { 47 | console.error('Error reading template file:', err); 48 | return null; 49 | } 50 | } 51 | 52 | const checkCookies = (req, res, next) => { 53 | const expectedCookieName1 = 'username'; 54 | const expectedCookieValue1 = validUsername; 55 | 56 | const expectedCookieName2 = 'password'; 57 | const expectedCookieValue2 = validPassword; 58 | 59 | if ( 60 | req.cookies[expectedCookieName1] === expectedCookieValue1 && 61 | req.cookies[expectedCookieName2] === expectedCookieValue2 62 | ) { 63 | return next(); 64 | } 65 | return res.status(403).json({ message: 'Forbidden: Invalid or missing cookies' }); 66 | }; 67 | 68 | app.post('/login', (req, res) => { 69 | const { name, password } = req.body; 70 | 71 | if (name === validUsername && password === validPassword) { 72 | res.cookie('username', name, { httpOnly: false, secure: true, maxAge: 36000000, sameSite: 'None' }); 73 | res.cookie('password', password, { httpOnly: false, secure: true, maxAge: 36000000, sameSite: 'None' }); 74 | return res.status(200).json({ message: 'Login successful' }); 75 | } 76 | res.status(401).json({ message: 'Invalid credentials' }); 77 | }); 78 | 79 | app.post('/suggest', checkCookies, async (req, res) => { 80 | try { 81 | const { sector, about } = req.body; 82 | const prompt = templatePrompt({sector, about}); 83 | 84 | const response = await openai.chat.completions.create({ 85 | messages: [{ role: 'user', content: prompt}], 86 | model: 'gpt-3.5-turbo-1106', 87 | temperature: 1, 88 | response_format: {"type": "json_object"}, 89 | }); 90 | 91 | const json_resp = JSON.parse(response.choices[0].message.content); 92 | return res.status(200).json(json_resp ); 93 | 94 | // const mock = { 95 | // "suggestions": [ 96 | // "Exploring new marketing strategies to reach a wider audience", 97 | // "Developing innovative products to stay ahead of competitors in the market" 98 | // ] 99 | // } 100 | // return res.status(200).json(mock); 101 | } catch (error){ 102 | res.status(500).json({ message: error}); 103 | } 104 | }); 105 | 106 | // const options = { 107 | // key: fs.readFileSync(path.resolve(__dirname, './certs/server.key')), 108 | // cert: fs.readFileSync(path.resolve(__dirname, './certs/server.crt')) 109 | // }; 110 | 111 | const PORT = process.env.PORT || 8080; 112 | // const server = https.createServer(options, app) 113 | // server.listen(PORT, () => { 114 | // console.log(`HTTPS Server is running on port ${PORT}`); 115 | // }); 116 | 117 | app.listen(PORT, () => { 118 | console.log(`Server running on port ${PORT}`); 119 | }); 120 | 121 | 122 | process.on('SIGINT', () => { 123 | console.log('SIGINT received: shutting down gracefully'); 124 | server.close(() => { 125 | console.log('Server closed'); 126 | process.exit(0); 127 | }); 128 | }); 129 | 130 | process.on('SIGTERM', () => { 131 | console.log('SIGTERM received: shutting down gracefully'); 132 | server.close(() => { 133 | console.log('Server closed'); 134 | process.exit(0); 135 | }); 136 | }); 137 | -------------------------------------------------------------------------------- /front/mymentor-gpt/src/pages/Login.tsx: -------------------------------------------------------------------------------- 1 | import { useState, FormEventHandler, useEffect } from 'react'; 2 | import { FaCompass } from "react-icons/fa6"; 3 | import { useNavigate, useLocation } from 'react-router-dom'; 4 | import Notification from '../components/Notification'; 5 | 6 | 7 | const APIURL = import.meta.env.VITE_API_URL; 8 | 9 | function LoginPage() { 10 | 11 | const location = useLocation(); 12 | const [showNotification, setShowNotification] = useState(false); 13 | const [name, setName] = useState(''); 14 | const [password, setPassword] = useState(''); 15 | const [error, setError] = useState(''); 16 | const [loading, setLoading] = useState(false); 17 | 18 | const navigate = useNavigate(); 19 | 20 | useEffect(() => { 21 | if (location.state?.from === '/protected') { 22 | setShowNotification(true); 23 | 24 | navigate(location.pathname, { replace: true, state: {} }); 25 | 26 | setTimeout(() => { 27 | setShowNotification(false); 28 | }, 5000); 29 | } 30 | }, [location.state, location.pathname, navigate]); 31 | 32 | 33 | const handleSubmit: FormEventHandler = async (e) => { 34 | e.preventDefault(); 35 | setError(''); 36 | 37 | if (name === '' || password === '') { 38 | setError('Both fields are required.'); 39 | return; 40 | } 41 | const data = {name, password}; 42 | 43 | try { 44 | const response = await fetch(`${APIURL}/login`, { 45 | method: 'POST', 46 | headers: { 47 | 'Content-Type': 'application/json', 48 | }, 49 | body: JSON.stringify(data), 50 | credentials: 'include', 51 | }); 52 | 53 | if (!response.ok) { 54 | const errorBody = await response.json(); 55 | setError(errorBody.message || 'Something went wrong'); 56 | } else { 57 | const result = await response.json(); 58 | localStorage.setItem('username', name); 59 | localStorage.setItem('password', password); 60 | console.log('Success:', result); 61 | navigate('/consult/swot'); 62 | } 63 | } catch (error) { 64 | console.error('Error:', error); 65 | setError('Failed to login. Please try again.'); 66 | } finally { 67 | setLoading(false); 68 | } 69 | 70 | }; 71 | 72 | 73 | return ( 74 | <> 75 | 76 | {showNotification && ( 77 | 78 | )} 79 |
80 |
81 |

82 | 83 | MyMenthor 84 |

85 |
86 |
87 |

88 | Log In to your account 89 |

90 |
91 |
92 | 93 | setName(e.target.value)} 98 | className="bg-gray-50 border border-gray-300 text-gray-900 rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" 99 | required 100 | /> 101 |
102 |
103 | 104 | setPassword(e.target.value)} 109 | className="bg-gray-50 border border-gray-300 text-gray-900 rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" 110 | required 111 | /> 112 |
113 | {error &&

{error}

} 114 |
115 | 118 |
119 | {loading ? ( 120 |

'Logging In...'

121 | ): ''} 122 |
123 |
124 |
125 |
126 |
127 | 128 | 129 | 130 | 131 | ) 132 | } 133 | 134 | export default LoginPage 135 | -------------------------------------------------------------------------------- /front/mymentor-gpt/src/components/InputSuggest.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import Notification from '../components/Notification'; 3 | import { HiOutlineLightBulb } from "react-icons/hi"; 4 | import { RiDeleteBin5Line } from "react-icons/ri"; 5 | import { FaCheck } from "react-icons/fa"; 6 | import LoadingIcon from '../assets/loading.svg'; 7 | 8 | 9 | const APIURL = import.meta.env.VITE_API_URL; 10 | 11 | interface TooltipProps { 12 | classes?: string, 13 | text: string; 14 | children: React.ReactNode 15 | } 16 | 17 | const Tooltip: React.FC = ({ classes='', children, text }) => { 18 | return ( 19 |
20 | {children} 21 |
22 |
23 | {text} 24 | 29 | 30 | 31 |
32 |
33 |
34 | ); 35 | }; 36 | 37 | const InputSuggest: React.FC<{ sector: string, about: string, icon: React.ReactNode }> = ({ sector, about, icon }) => { 38 | 39 | const [data, setData] = useState([]); 40 | const [isEmptySector, setIsEmptySector] = useState(false); 41 | const [text, setText] = useState(''); 42 | const [showNotification, setShowNotification] = useState(false); 43 | const [error, setError] = useState(''); 44 | const [isLoading, setIsLoading] = useState(false); 45 | 46 | const handleSuggest = (about: string) => async () => { 47 | if (!sector) { 48 | setIsEmptySector(true); 49 | return 50 | } 51 | try { 52 | setIsLoading(true); 53 | const response = await fetch(`${APIURL}/suggest`, { 54 | method: 'POST', 55 | headers: { 56 | 'Content-Type': 'application/json', 57 | }, 58 | body: JSON.stringify({ sector, about }), 59 | credentials: 'include', 60 | }); 61 | 62 | if (!response.ok) { 63 | const errorBody = await response.json(); 64 | console.log('errorBody ', errorBody.message); 65 | setIsLoading(false); 66 | setError(errorBody.message || 'Something went wrong'); 67 | setShowNotification(true) 68 | } else { 69 | setIsLoading(false); 70 | const data = await response.json(); 71 | console.log('Success:', data); 72 | setData(data['suggestions']); 73 | } 74 | } catch (error) { 75 | console.error('And the Error is:', error); 76 | setIsLoading(false); 77 | setError(JSON.stringify(error) || 'Something went wrong'); 78 | setShowNotification(true) 79 | } 80 | }; 81 | 82 | const handleChange = (event: React.ChangeEvent) => { 83 | setText(event.target.value); 84 | }; 85 | 86 | const handleDeleteClick = (i: number) => () => { 87 | setData(data.filter((_, index) => index !== i)); 88 | }; 89 | 90 | const handleSelectClick = (i:number) => () => { 91 | setText(data[i]); 92 | handleDeleteClick(i)(); 93 | }; 94 | 95 | return ( 96 |
97 | {showNotification && ( 98 | 99 | )} 100 | {isEmptySector && ( 101 | 102 | )} 103 |
107 |
108 |

{about}

109 | { icon } 110 |
111 |