From 334eb784d438e8631b2ff10b0d6670b625e72efb Mon Sep 17 00:00:00 2001 From: hazyplebian Date: Mon, 28 Apr 2025 20:11:55 -0400 Subject: [PATCH 1/4] moved a file back to correct folder --- client/{public => background}/codezilla_bkgd.png | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename client/{public => background}/codezilla_bkgd.png (100%) diff --git a/client/public/codezilla_bkgd.png b/client/background/codezilla_bkgd.png similarity index 100% rename from client/public/codezilla_bkgd.png rename to client/background/codezilla_bkgd.png From 44dc81f52c64b517642035b2a8394ece406f2852 Mon Sep 17 00:00:00 2001 From: Treevyy Date: Mon, 28 Apr 2025 19:39:02 -0500 Subject: [PATCH 2/4] updated scripts and fixed bugs on server side --- client/package-lock.json | 2 -- package.json | 2 +- server/package.json | 3 ++- server/src/server.ts | 19 +++++++++++-------- server/src/types/express/index.d.ts | 8 ++++++++ server/src/utils/auth.ts | 29 +++++++++++++++++------------ server/tsconfig.json | 2 +- 7 files changed, 40 insertions(+), 25 deletions(-) create mode 100644 server/src/types/express/index.d.ts diff --git a/client/package-lock.json b/client/package-lock.json index d92ff6c..e03be82 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -11,9 +11,7 @@ "@apollo/client": "^3.13.8", "@radix-ui/react-dialog": "^1.1.11", "graphql": "^16.11.0", - "jwt-decode": "^4.0.0", - "lucide-react": "^0.503.0", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/package.json b/package.json index 7507d76..3c7e013 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "", "main": "index.js", "scripts": { - "start": "node server/server.js", + "start": "cd server && npm run start", "develop": "concurrently \"cd server && npm run watch\" \"cd client && npm run dev\"", "install": "cd server && npm i && cd ../client && npm i", "build": "concurrently \"cd server && npm run build\" \"cd client && npm run build\"", diff --git a/server/package.json b/server/package.json index ac9f345..b555474 100644 --- a/server/package.json +++ b/server/package.json @@ -7,7 +7,8 @@ "start": "node dist/server.js", "build": "tsc", "test": "echo \"Error: no test specified\" && exit 1", - "dev": "nodemon index.ts" + "dev": "nodemon index.ts", + "watch": "nodemon dist/server.js" }, "keywords": [], "author": "", diff --git a/server/src/server.ts b/server/src/server.ts index e527958..ba3ddf7 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -1,10 +1,10 @@ -import express, { Request, Response } from 'express'; -import cors from 'cors'; +import express, { type Request, type Response } from 'express'; + import dotenv from 'dotenv'; import { OpenAI } from 'openai'; -// import fs from 'fs'; -// import path from 'path'; -import { ApolloServer } from 'apollo-server-express'; + +import path from 'path'; +import { ApolloServer } from '@apollo/server'; import { expressMiddleware } from '@apollo/server/express4'; import typeDefs from './schemas/typeDefs'; import resolvers from './schemas/resolvers'; @@ -33,16 +33,19 @@ dotenv.config(); resolvers, }); - await server.start(); + const startApolloServer = async () => { + await server.start(); + await db; + + app.use(express.static('public')); // serve generated mp3 file app.use(express.urlencoded({ extended: false })); app.use(express.json()); - app.use(express.static('public')); // serve generated mp3 file + app.use('/graphql', expressMiddleware(server as any, { context: authenticateToken as any } )); - // if we're in production, serve client/build as static assets if (process.env.NODE_ENV === 'production') { app.use(express.static(path.join(dirname, '../client/build'))); diff --git a/server/src/types/express/index.d.ts b/server/src/types/express/index.d.ts new file mode 100644 index 0000000..e649f26 --- /dev/null +++ b/server/src/types/express/index.d.ts @@ -0,0 +1,8 @@ +declare namespace Express { + interface Request { + user: { + _id: unknown; + username: string; + }; + } +} diff --git a/server/src/utils/auth.ts b/server/src/utils/auth.ts index 01ef3ac..e0efdd3 100644 --- a/server/src/utils/auth.ts +++ b/server/src/utils/auth.ts @@ -11,7 +11,7 @@ interface JwtPayload { } export const authMiddleware = ({ req }: { req: any }) => { - const authHeader = req.headers.authorization; + const authHeader = req.headers.get('authorization'); if (authHeader) { const token = authHeader.split(' ')[1]; @@ -42,20 +42,25 @@ export const authMiddleware = ({ req }: { req: any }) => { return { user: null }; }; -export const authenticateToken = (token: string) => { - const secretKey = process.env.JWT_SECRET_KEY || ''; +export const authenticateToken = async ({ req }: { req: Request }) => { + const authHeader = req.headers.authorization; + let user = null; + console.log('AUTH HEADER', authHeader); - if (!token) { - throw new Error('No token provided'); - } + if (authHeader) { + const token = authHeader.split(' ')[1]; + console.log('TOKEN', token); + const secretKey = process.env.JWT_SECRET_KEY || ''; - try { - const user = jwt.verify(token, secretKey) as JwtPayload; - return user; - } catch (err) { - console.error('Invalid token:', err); - throw new Error('Invalid or expired token'); + try { + user = jwt.verify(token, secretKey) as JwtPayload; + console.log('USER', user); + } catch (err) { + console.error(err); + } } + + return { user }; }; export const signToken = (username: string, email: string, _id: unknown) => { diff --git a/server/tsconfig.json b/server/tsconfig.json index a082141..e3f0e0c 100644 --- a/server/tsconfig.json +++ b/server/tsconfig.json @@ -13,6 +13,6 @@ "@/*": ["src/*"] // Map the '@' alias to the 'src' directory } }, - "include": ["src/**/*"], + "include": ["src/**/*", "src/server.ts"], "exclude": ["node_modules"] } \ No newline at end of file From 8eb8f00c7d8fd3ebe45d9adfbfe93af78b57a07f Mon Sep 17 00:00:00 2001 From: Treevyy Date: Mon, 28 Apr 2025 20:17:10 -0500 Subject: [PATCH 3/4] fixed errors in the server.ts file --- server/src/server.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/src/server.ts b/server/src/server.ts index ba3ddf7..4504434 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -36,7 +36,7 @@ dotenv.config(); const startApolloServer = async () => { await server.start(); await db; - + app.use(express.static('public')); // serve generated mp3 file app.use(express.urlencoded({ extended: false })); app.use(express.json()); @@ -48,10 +48,10 @@ dotenv.config(); )); // if we're in production, serve client/build as static assets if (process.env.NODE_ENV === 'production') { - app.use(express.static(path.join(dirname, '../client/build'))); + app.use(express.static(path.join(__dirname, '../client/build'))); app.get('*', (_req: Request, res: Response) => { - res.sendFile(path.join(dirname, '../client/dist/index.html')); + res.sendFile(path.join(__dirname, '../client/dist/index.html')); }); } From 6792b4a1606b392125a8a5df792ab5b53b523b77 Mon Sep 17 00:00:00 2001 From: Carmen Wheeler Date: Mon, 28 Apr 2025 20:36:24 -0500 Subject: [PATCH 4/4] added queries/mutations/typedefs/resolvers --- client/src/components/AnswerResultModal.tsx | 25 ++++++++++++++ client/src/components/MinionModal.tsx | 23 +++++++++++++ client/src/graphql/queries.ts | 12 +++---- server/src/schemas/resolvers.ts | 38 ++++++++++++++++++++- server/src/schemas/typeDefs.ts | 15 ++++++-- server/src/utils/PromptBuilder.ts | 2 +- 6 files changed, 104 insertions(+), 11 deletions(-) diff --git a/client/src/components/AnswerResultModal.tsx b/client/src/components/AnswerResultModal.tsx index e69de29..4bb7a2c 100644 --- a/client/src/components/AnswerResultModal.tsx +++ b/client/src/components/AnswerResultModal.tsx @@ -0,0 +1,25 @@ +import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog"; + +interface AnswerResultModalProps { + isOpen: boolean; + onClose: () => void; + isCorrect: boolean; + drDanQuote: string; +} + +const AnswerResultModal = ({ isOpen, onClose, isCorrect, drDanQuote }: AnswerResultModalProps) => { + return ( + { if (!open) onClose(); }}> + + + + {isCorrect ? "Correct!" : "Wrong!"} + + +

{drDanQuote}

+
+
+ ); +}; + +export default AnswerResultModal; diff --git a/client/src/components/MinionModal.tsx b/client/src/components/MinionModal.tsx index e69de29..b575d2c 100644 --- a/client/src/components/MinionModal.tsx +++ b/client/src/components/MinionModal.tsx @@ -0,0 +1,23 @@ +import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog"; + +interface MinionModalProps { + isOpen: boolean; + onClose: () => void; + minionName: string; + minionQuote: string; +} + +const MinionModal = ({ isOpen, onClose, minionName, minionQuote }: MinionModalProps) => { + return ( + { if (!open) onClose(); }}> + + + {minionName} Appears! + +

{minionQuote}

+
+
+ ); +}; + +export default MinionModal; diff --git a/client/src/graphql/queries.ts b/client/src/graphql/queries.ts index 4b76e20..fec6426 100644 --- a/client/src/graphql/queries.ts +++ b/client/src/graphql/queries.ts @@ -1,13 +1,13 @@ import { gql } from '@apollo/client'; -export const GET_QUESTIONS = gql` - query GetQuestions { +export const GENERATE_QUESTIONS = gql` + query GenerateQuestion($track: String!, level: $level, minion: $minion) { questions { - _id - questionText - options - correctAnswer + question + choices + answer } } `; + diff --git a/server/src/schemas/resolvers.ts b/server/src/schemas/resolvers.ts index 9b4c318..8098940 100644 --- a/server/src/schemas/resolvers.ts +++ b/server/src/schemas/resolvers.ts @@ -1,6 +1,13 @@ import User from '../models/User'; import { signToken } from '../utils/auth'; import { AuthenticationError } from 'apollo-server-express'; +import { OpenAI } from 'openai'; +import { PromptBuilder } from '../utils/PromptBuilder'; +import dotenv from 'dotenv'; + +dotenv.config(); + +const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY }); interface AddUserArgs { input: { @@ -23,7 +30,36 @@ const resolvers = { } throw new AuthenticationError('You need to be logged in!'); }, + generateQuestion: async (_parent: any, args: { track: string; level: string; minion: string }) => { + const { track, level, minion } = args; + const prompt = PromptBuilder.getPrompt(track, level); + + try { + const completion = await openai.chat.completions.create({ + model: 'gpt-3.5-turbo', + messages: [{ role: 'user', content: prompt }], + max_tokens: 250, + }); + + const raw = completion.choices[0].message.content ?? ''; + const [question, ...choicesAndAnswer] = raw.split('\n').filter(Boolean); + const choices = choicesAndAnswer.slice(0, -1); + const answer = choicesAndAnswer[choicesAndAnswer.length - 1]; + + return { question, choices, answer }; + } catch (error) { + console.error('OpenAI failed, falling back to hardcoded question:', error); + const fallback = PromptBuilder.getFallbackQuestion(minion); + + return { + question: fallback.question, + choices: fallback.choices, + answer: fallback.choices[fallback.correctIndex], + }; + } + }, }, + Mutation: { addUser: async (_parent: any, { input }: AddUserArgs) => { const user = await User.create(input) as { username: string; email: string; _id: string }; @@ -49,4 +85,4 @@ const resolvers = { }, }; -export default resolvers; \ No newline at end of file +export default resolvers; diff --git a/server/src/schemas/typeDefs.ts b/server/src/schemas/typeDefs.ts index 545a57c..be89090 100644 --- a/server/src/schemas/typeDefs.ts +++ b/server/src/schemas/typeDefs.ts @@ -1,4 +1,6 @@ -const typeDefs = ` +import { gql } from 'apollo-server-express'; + +const typeDefs = gql` type User { _id: ID username: String @@ -11,16 +13,23 @@ const typeDefs = ` email: String! password: String! } - + type Auth { token: ID! user: User } + type Question { + question: String! + choices: [String!]! + answer: String! + } + type Query { users: [User] user(username: String!): User me: User + generateQuestion(track: String!, level: String!, minion: String!): Question } type Mutation { @@ -29,4 +38,4 @@ const typeDefs = ` } `; -export default typeDefs; \ No newline at end of file +export default typeDefs; diff --git a/server/src/utils/PromptBuilder.ts b/server/src/utils/PromptBuilder.ts index a5c81b1..3198f4a 100644 --- a/server/src/utils/PromptBuilder.ts +++ b/server/src/utils/PromptBuilder.ts @@ -1,4 +1,4 @@ -import { FallbackQuestion, fallbackQuestion } from "@/utils/fallbackQuestions"; +import { FallbackQuestion, fallbackQuestion } from "../utils/fallbackQuestions"; export class PromptBuilder { /**