diff --git a/.env.sample b/.env.sample index 75516e2b97..0b15245a87 100644 --- a/.env.sample +++ b/.env.sample @@ -9,11 +9,11 @@ FRONTEND_PORT=3000 BASE_URI= ## Question service variables -QUESTION_SVC_PORT= +QUESTION_SVC_PORT=8000 QUESTION_SVC_DB_URI= ## User service variables -USER_SVC_PORT= +USER_SVC_PORT=3001 USER_SVC_DB_URI= JWT_SECRET= EMAIL_ADDRESS= @@ -31,4 +31,7 @@ OPENAI_API_KEY= REDIS_PORT=6379 ## Redisinsight variables -REDIS_INSIGHT_PORT=5540 \ No newline at end of file +REDIS_INSIGHT_PORT=5540 + +## API Gateway variables +API_GATEWAY_PORT= diff --git a/api-gateway/nginx.conf b/api-gateway/nginx.conf new file mode 100644 index 0000000000..fc638fe5e6 --- /dev/null +++ b/api-gateway/nginx.conf @@ -0,0 +1,36 @@ +user nginx; +worker_processes auto; + +error_log /var/log/nginx/error.log notice; +pid /var/run/nginx.pid; + + +events { + worker_connections 1024; +} + + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + sendfile on; + #tcp_nopush on; + + keepalive_timeout 65; + + #gzip on; + + map $http_upgrade $connection_upgrade { + default upgrade; + '' close; + } + + include /etc/nginx/conf.d/*.conf; +} diff --git a/api-gateway/templates/api_conf.d/admin_conf.d/matching_service.conf.template b/api-gateway/templates/api_conf.d/admin_conf.d/matching_service.conf.template new file mode 100644 index 0000000000..3099c20c85 --- /dev/null +++ b/api-gateway/templates/api_conf.d/admin_conf.d/matching_service.conf.template @@ -0,0 +1,2 @@ +location /admin/matching-service/ { +} diff --git a/api-gateway/templates/api_conf.d/admin_conf.d/question_service.conf.template b/api-gateway/templates/api_conf.d/admin_conf.d/question_service.conf.template new file mode 100644 index 0000000000..92d08a5a9b --- /dev/null +++ b/api-gateway/templates/api_conf.d/admin_conf.d/question_service.conf.template @@ -0,0 +1,5 @@ +location /admin/question-service/ { + location /admin/question-service/questions { + proxy_pass http://question-service/questions; + } +} diff --git a/api-gateway/templates/api_conf.d/api_backends.conf.template b/api-gateway/templates/api_conf.d/api_backends.conf.template new file mode 100644 index 0000000000..7765e99f01 --- /dev/null +++ b/api-gateway/templates/api_conf.d/api_backends.conf.template @@ -0,0 +1,19 @@ +upstream user-service { + server user-service:$USER_SVC_PORT; +} + +upstream question-service { + server question-service:$QUESTION_SVC_PORT; +} + +upstream matching-service { + server matching-service:$MATCHING_SVC_PORT; +} + +upstream collab-service { + server collab-service:$COLLAB_SVC_PORT; +} + +upstream frontend { + server frontend:$FRONTEND_PORT; +} diff --git a/api-gateway/templates/api_conf.d/auth.conf.template b/api-gateway/templates/api_conf.d/auth.conf.template new file mode 100644 index 0000000000..b0b7572953 --- /dev/null +++ b/api-gateway/templates/api_conf.d/auth.conf.template @@ -0,0 +1,23 @@ +location /verify-token { + internal; + + proxy_pass_request_body off; + proxy_set_header Content-Length ""; + proxy_pass http://user-service/auth/verify-token; +} + +location /verify-owner { + internal; + + proxy_pass_request_body off; + proxy_set_header Content-Length ""; + proxy_pass http://user-service/auth/verify-owner; +} + +location /verify-admin { + internal; + + proxy_pass_request_body off; + proxy_set_header Content-Length ""; + proxy_pass http://user-service/auth/verify-admin; +} diff --git a/api-gateway/templates/api_conf.d/frontend.conf.template b/api-gateway/templates/api_conf.d/frontend.conf.template new file mode 100644 index 0000000000..0e45e0d531 --- /dev/null +++ b/api-gateway/templates/api_conf.d/frontend.conf.template @@ -0,0 +1,3 @@ +location / { + proxy_pass http://frontend; +} diff --git a/api-gateway/templates/api_conf.d/owner_conf.d/matching_service.conf.template b/api-gateway/templates/api_conf.d/owner_conf.d/matching_service.conf.template new file mode 100644 index 0000000000..7d5b43aa18 --- /dev/null +++ b/api-gateway/templates/api_conf.d/owner_conf.d/matching_service.conf.template @@ -0,0 +1,2 @@ +location /owner/matching-service/ { +} diff --git a/api-gateway/templates/api_conf.d/owner_conf.d/question_service.conf.template b/api-gateway/templates/api_conf.d/owner_conf.d/question_service.conf.template new file mode 100644 index 0000000000..6b79d36cac --- /dev/null +++ b/api-gateway/templates/api_conf.d/owner_conf.d/question_service.conf.template @@ -0,0 +1,2 @@ +location /owner/question-service/ { +} diff --git a/api-gateway/templates/api_conf.d/private_conf.d/collab_service.conf.template b/api-gateway/templates/api_conf.d/private_conf.d/collab_service.conf.template new file mode 100644 index 0000000000..2730da2a3c --- /dev/null +++ b/api-gateway/templates/api_conf.d/private_conf.d/collab_service.conf.template @@ -0,0 +1,10 @@ +location /private/collab-service/ { + proxy_pass http://collab-service/; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; +} diff --git a/api-gateway/templates/api_conf.d/private_conf.d/matching_service.conf.template b/api-gateway/templates/api_conf.d/private_conf.d/matching_service.conf.template new file mode 100644 index 0000000000..01406691a8 --- /dev/null +++ b/api-gateway/templates/api_conf.d/private_conf.d/matching_service.conf.template @@ -0,0 +1,5 @@ +location /private/matching-service/ { + location /private/matching-service/match { + proxy_pass http://matching-service/match; + } +} diff --git a/api-gateway/templates/api_conf.d/private_conf.d/question_service.conf.template b/api-gateway/templates/api_conf.d/private_conf.d/question_service.conf.template new file mode 100644 index 0000000000..010dbb8fdb --- /dev/null +++ b/api-gateway/templates/api_conf.d/private_conf.d/question_service.conf.template @@ -0,0 +1,2 @@ +location /private/question-service/ { +} diff --git a/api-gateway/templates/api_conf.d/private_conf.d/user_service.conf.template b/api-gateway/templates/api_conf.d/private_conf.d/user_service.conf.template new file mode 100644 index 0000000000..7b88da2fd1 --- /dev/null +++ b/api-gateway/templates/api_conf.d/private_conf.d/user_service.conf.template @@ -0,0 +1,2 @@ +location /private/user-service/ { +} diff --git a/api-gateway/templates/api_conf.d/public_conf.d/collab_service.conf.template b/api-gateway/templates/api_conf.d/public_conf.d/collab_service.conf.template new file mode 100644 index 0000000000..923c8e27d5 --- /dev/null +++ b/api-gateway/templates/api_conf.d/public_conf.d/collab_service.conf.template @@ -0,0 +1,23 @@ +location /public/collab-service/ { + location /public/collab-service/chat { + proxy_pass http://collab-service/chat; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location /public/collab-service/yjs { + proxy_pass http://collab-service/yjs; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } +} diff --git a/api-gateway/templates/api_conf.d/public_conf.d/matching_service.conf.template b/api-gateway/templates/api_conf.d/public_conf.d/matching_service.conf.template new file mode 100644 index 0000000000..0d6642a155 --- /dev/null +++ b/api-gateway/templates/api_conf.d/public_conf.d/matching_service.conf.template @@ -0,0 +1,14 @@ +location /public/matching-service/ { + location /public/matching-service/match/ { + location /public/matching-service/match/subscribe/ { + proxy_pass http://matching-service/match/subscribe/; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + } +} diff --git a/api-gateway/templates/api_conf.d/public_conf.d/question_service.conf.template b/api-gateway/templates/api_conf.d/public_conf.d/question_service.conf.template new file mode 100644 index 0000000000..ac6f2142ca --- /dev/null +++ b/api-gateway/templates/api_conf.d/public_conf.d/question_service.conf.template @@ -0,0 +1,8 @@ +location /public/question-service/ { + location /public/question-service/questions { + limit_except GET { + deny all; + } + proxy_pass http://question-service/questions; + } +} diff --git a/api-gateway/templates/api_conf.d/public_conf.d/user_service.conf.template b/api-gateway/templates/api_conf.d/public_conf.d/user_service.conf.template new file mode 100644 index 0000000000..eaa4bf1adc --- /dev/null +++ b/api-gateway/templates/api_conf.d/public_conf.d/user_service.conf.template @@ -0,0 +1,9 @@ +location /public/user-service/ { + location /public/user-service/auth { + proxy_pass http://user-service/auth; + } + + location /public/user-service/users { + proxy_pass http://user-service/users; + } +} diff --git a/api-gateway/templates/default.conf.template b/api-gateway/templates/default.conf.template new file mode 100644 index 0000000000..f6fc2f3351 --- /dev/null +++ b/api-gateway/templates/default.conf.template @@ -0,0 +1,31 @@ +include conf.d/api_conf.d/api_backends.conf; + +server { + listen $PORT; + listen [::]:$PORT; + + location /public/ { + include conf.d/api_conf.d/public_conf.d/*.conf; + } + + location /private/ { + auth_request /verify-token; + include conf.d/api_conf.d/private_conf.d/*.conf; + } + + location /owner/ { + auth_request /verify-owner; + include conf.d/api_conf.d/owner_conf.d/*.conf; + } + + location /admin/ { + auth_request /verify-admin; + include conf.d/api_conf.d/admin_conf.d/*.conf; + } + + include conf.d/api_conf.d/auth.conf; + + include conf.d/api_conf.d/frontend.conf; +} + +include conf.d/map_conf.d/*.conf; diff --git a/collab-service/app/server.js b/collab-service/app/server.js index cdb1bae7cd..57bc240923 100644 --- a/collab-service/app/server.js +++ b/collab-service/app/server.js @@ -12,6 +12,7 @@ const server = http.createServer(index); const docs = ywsUtils.docs; const io = new Server(server, { + path: "/chat", cors: { origin: "*", // Allow all origins; replace with specific origin if needed methods: ["GET", "POST"], diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 2022842789..33b3ffe465 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -28,3 +28,13 @@ services: collab-service: build: target: dev + + # access RedisInsight at http://localhost:5540 + # connect to redis on redis insight at redis:6379 + redisinsight: + image: redis/redisinsight:latest + restart: always + ports: + - $REDIS_INSIGHT_PORT:$REDIS_INSIGHT_PORT # Expose RedisInsight UI on port 5540 + depends_on: + - redis diff --git a/docker-compose.yml b/docker-compose.yml index cdb1e1f281..0d991ed432 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,55 +1,69 @@ services: + api-gateway: + image: nginx:1.26 + volumes: + - ./api-gateway/templates:/etc/nginx/templates + - ./api-gateway/nginx.conf:/etc/nginx/nginx.conf + ports: + - $API_GATEWAY_PORT:$API_GATEWAY_PORT + environment: + - PORT=$API_GATEWAY_PORT + - FRONTEND_PORT=$FRONTEND_PORT + - USER_SVC_PORT=$USER_SVC_PORT + - QUESTION_SVC_PORT=$QUESTION_SVC_PORT + - MATCHING_SVC_PORT=$MATCHING_SVC_PORT + - COLLAB_SVC_PORT=$COLLAB_SVC_PORT + depends_on: + - frontend + - user-service + - question-service + - matching-service + - collab-service + frontend: build: context: ./frontend args: - BASE_URI=$BASE_URI - - USER_SVC_PORT=$USER_SVC_PORT - - QUESTION_SVC_PORT=$QUESTION_SVC_PORT - - MATCHING_SVC_PORT=$MATCHING_SVC_PORT - - COLLAB_SVC_PORT=$COLLAB_SVC_PORT - ports: - - $FRONTEND_PORT:$FRONTEND_PORT - depends_on: - - question-service - - user-service - - collab-service + - API_GATEWAY_PORT=$API_GATEWAY_PORT environment: - PORT=$FRONTEND_PORT + expose: + - $FRONTEND_PORT question-service: build: context: ./question-service - ports: - - $QUESTION_SVC_PORT:$QUESTION_SVC_PORT environment: - PORT=$QUESTION_SVC_PORT - DB_URI=$QUESTION_SVC_DB_URI - FRONTEND_PORT=$FRONTEND_PORT + expose: + - $QUESTION_SVC_PORT user-service: build: context: ./user-service - ports: - - $USER_SVC_PORT:$USER_SVC_PORT environment: - PORT=$USER_SVC_PORT - DB_URI=$USER_SVC_DB_URI - JWT_SECRET=$JWT_SECRET - EMAIL_ADDRESS=$EMAIL_ADDRESS - EMAIL_PASSWORD=$EMAIL_PASSWORD + expose: + - $USER_SVC_PORT matching-service: build: context: ./matching-service - ports: - - $MATCHING_SVC_PORT:$MATCHING_SVC_PORT environment: - PORT=$MATCHING_SVC_PORT - REDIS_HOST=redis - REDIS_PORT=$REDIS_PORT - QUESTION_SVC_PORT=$QUESTION_SVC_PORT - COLLAB_SVC_PORT=$COLLAB_SVC_PORT + expose: + - $MATCHING_SVC_PORT depends_on: - redis - question-service @@ -58,26 +72,15 @@ services: redis: image: redis:7.4-alpine restart: always - ports: - - $REDIS_PORT:$REDIS_PORT - - # access RedisInsight at http://localhost:5540 - # connect to redis on redis insight at redis:6379 - redisinsight: - image: redis/redisinsight:latest - restart: always - ports: - - $REDIS_INSIGHT_PORT:$REDIS_INSIGHT_PORT # Expose RedisInsight UI on port 5540 - depends_on: - - redis + expose: + - $REDIS_PORT - collab-service: build: context: ./collab-service - ports: - - $COLLAB_SVC_PORT:$COLLAB_SVC_PORT environment: - PORT=$COLLAB_SVC_PORT - DB_URI=$COLLAB_SVC_DB_URI - - OPENAI_API_KEY=$OPENAI_API_KEY \ No newline at end of file + - OPENAI_API_KEY=$OPENAI_API_KEY + expose: + - $COLLAB_SVC_PORT diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 2252b542aa..77deed4623 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -1,20 +1,14 @@ # Base stage FROM node:20-alpine AS base ARG BASE_URI \ - USER_SVC_PORT \ - QUESTION_SVC_PORT \ - MATCHING_SVC_PORT \ - COLLAB_SVC_PORT + API_GATEWAY_PORT WORKDIR /app COPY package.json . COPY yarn.lock . RUN yarn install --frozen-lockfile ENV NEXT_PUBLIC_BASE_URI=$BASE_URI \ - NEXT_PUBLIC_USER_SVC_PORT=$USER_SVC_PORT \ - NEXT_PUBLIC_QUESTION_SVC_PORT=$QUESTION_SVC_PORT \ - NEXT_PUBLIC_MATCHING_SVC_PORT=$MATCHING_SVC_PORT \ - NEXT_PUBLIC_COLLAB_SVC_PORT=$COLLAB_SVC_PORT - + NEXT_PUBLIC_API_GATEWAY_PORT=$API_GATEWAY_PORT + # Production build stage FROM base AS build COPY . . diff --git a/frontend/app/auth/auth-context.tsx b/frontend/app/auth/auth-context.tsx index c477420e08..3d64ce6aa9 100644 --- a/frontend/app/auth/auth-context.tsx +++ b/frontend/app/auth/auth-context.tsx @@ -1,6 +1,6 @@ "use client"; -import { userServiceUri } from "@/lib/api/api-uri"; +import { AuthType, userServiceUri } from "@/lib/api/api-uri"; import { User, UserSchema } from "@/lib/schemas/user-schema"; import { useRouter } from "next/navigation"; import { @@ -39,12 +39,15 @@ const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) => { } if (token) { setIsLoading(true); - fetch(`${userServiceUri(window.location.hostname)}/auth/verify-token`, { - method: "GET", - headers: { - Authorization: `Bearer ${token}`, - }, - }) + fetch( + `${userServiceUri(window.location.hostname, AuthType.Public)}/auth/verify-token`, + { + method: "GET", + headers: { + Authorization: `Bearer ${token}`, + }, + } + ) .then((res) => { res.json().then((result) => { setUser(result.data); @@ -61,7 +64,7 @@ const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) => { // Login using email and password const login = async (email: string, password: string): Promise => { const response = await fetch( - `${userServiceUri(window.location.hostname)}/auth/login`, + `${userServiceUri(window.location.hostname, AuthType.Public)}/auth/login`, { method: "POST", headers: { diff --git a/frontend/components/admin-user-management/admin-user-management.tsx b/frontend/components/admin-user-management/admin-user-management.tsx index 06f45b688d..4578765ac2 100644 --- a/frontend/components/admin-user-management/admin-user-management.tsx +++ b/frontend/components/admin-user-management/admin-user-management.tsx @@ -17,7 +17,7 @@ import AdminEditUserModal from "@/components/admin-user-management/admin-edit-us import DeleteAccountModal from "@/components/common/delete-account-modal"; import { PencilIcon, Trash2Icon } from "lucide-react"; import { User, UserArraySchema } from "@/lib/schemas/user-schema"; -import { userServiceUri } from "@/lib/api/api-uri"; +import { AuthType, userServiceUri } from "@/lib/api/api-uri"; const fetcher = async (url: string): Promise => { const token = localStorage.getItem("jwtToken"); @@ -45,7 +45,7 @@ export default function AdminUserManagement() { const auth = useAuth(); const { data, isLoading, mutate } = useSWR( - `${userServiceUri(window.location.hostname)}/users`, + `${userServiceUri(window.location.hostname, AuthType.Public)}/users`, fetcher ); @@ -78,7 +78,7 @@ export default function AdminUserManagement() { } const response = await fetch( - `${userServiceUri(window.location.hostname)}/users/${selectedUser?.id}`, + `${userServiceUri(window.location.hostname, AuthType.Public)}/users/${selectedUser?.id}`, { method: "DELETE", headers: { diff --git a/frontend/components/collab/chat.tsx b/frontend/components/collab/chat.tsx index e3bd123d6d..81a222f713 100644 --- a/frontend/components/collab/chat.tsx +++ b/frontend/components/collab/chat.tsx @@ -8,10 +8,16 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { ScrollArea } from "@/components/ui/scroll-area"; import { Send } from "lucide-react"; import { io, Socket } from "socket.io-client"; +import { useToast } from "@/components/hooks/use-toast"; import { useAuth } from "@/app/auth/auth-context"; import LoadingScreen from "@/components/common/loading-screen"; import { sendAiMessage } from "@/lib/api/openai/send-ai-message"; import { getChatHistory } from "@/lib/api/collab-service/get-chat-history"; +import { + AuthType, + baseApiGatewayUri, + constructUriSuffix, +} from "@/lib/api/api-uri"; interface Message { id: string; @@ -30,6 +36,7 @@ interface ChatHistoryMessage { export default function Chat({ roomId }: { roomId: string }) { const auth = useAuth(); + const { toast } = useToast(); const own_user_id = auth?.user?.id; const [socket, setSocket] = useState(null); const [chatTarget, setChatTarget] = useState("partner"); @@ -43,7 +50,16 @@ export default function Chat({ roomId }: { roomId: string }) { useEffect(() => { const fetchChatHistory = async () => { try { - const response = await getChatHistory(roomId); + if (!auth || !auth.token) { + toast({ + title: "Access denied", + description: "No authentication token found", + variant: "destructive", + }); + return; + } + + const response = await getChatHistory(auth?.token, roomId); if (response.ok) { const history: ChatHistoryMessage[] = await response.json(); @@ -77,12 +93,10 @@ export default function Chat({ roomId }: { roomId: string }) { useEffect(() => { if (!auth?.user?.id) return; - const socketInstance = io( - process.env.NEXT_PUBLIC_COLLAB_SERVICE_URL || "http://localhost:3002", - { - auth: { userId: own_user_id }, - } - ); + const socketInstance = io(baseApiGatewayUri(window.location.hostname), { + path: `${constructUriSuffix(AuthType.Public, "collab-service")}/chat`, + auth: { userId: own_user_id }, + }); socketInstance.on("connect", () => { console.log("Connected to Socket.IO"); diff --git a/frontend/components/collab/code-editor.tsx b/frontend/components/collab/code-editor.tsx index c2fee0f63e..aebb53add0 100644 --- a/frontend/components/collab/code-editor.tsx +++ b/frontend/components/collab/code-editor.tsx @@ -15,6 +15,7 @@ import { WebsocketProvider } from "y-websocket"; import { MonacoBinding } from "y-monaco"; import { editor as MonacoEditor } from "monaco-types"; import Editor, { useMonaco } from "@monaco-editor/react"; +import { yjsWebSockUri } from "@/lib/api/api-uri"; interface LanguageEntry { language: string; @@ -52,7 +53,7 @@ export default function CodeEditor({ roomId }: { roomId: string }) { useEffect(() => { const provider = new WebsocketProvider( - "ws://localhost:3002/yjs", + yjsWebSockUri(window.location.hostname), roomId, ydoc ); diff --git a/frontend/components/collab/monaco-editor.tsx b/frontend/components/collab/monaco-editor.tsx index 997bab7a81..0d4bfa0c50 100644 --- a/frontend/components/collab/monaco-editor.tsx +++ b/frontend/components/collab/monaco-editor.tsx @@ -6,6 +6,7 @@ import { editor as MonacoEditor } from "monaco-types"; import React, { useEffect, useMemo, useState } from "react"; import Editor from "@monaco-editor/react"; +import { yjsWebSockUri } from "@/lib/api/api-uri"; export default function MonacoEdit({ roomId }: { roomId: string }) { const ydoc = useMemo(() => new Y.Doc(), []); @@ -16,7 +17,7 @@ export default function MonacoEdit({ roomId }: { roomId: string }) { // this effect manages the lifetime of the Yjs document and the provider useEffect(() => { const provider = new WebsocketProvider( - "ws://localhost:3002/yjs", + yjsWebSockUri(window.location.hostname), roomId, ydoc ); diff --git a/frontend/components/collab/question-display.tsx b/frontend/components/collab/question-display.tsx index d3e4fa9c7d..4a864f19ad 100644 --- a/frontend/components/collab/question-display.tsx +++ b/frontend/components/collab/question-display.tsx @@ -11,6 +11,7 @@ import { import { Badge } from "@/components/ui/badge"; import LoadingScreen from "@/components/common/loading-screen"; import { getQuestion } from "@/lib/api/question-service/get-question"; +import { useToast } from "@/components/hooks/use-toast"; import { useAuth } from "@/app/auth/auth-context"; import { getQuestionId } from "@/lib/api/collab-service/get-questionId"; @@ -37,6 +38,7 @@ export default function QuestionDisplay({ date?: Date; }) { const auth = useAuth(); + const { toast } = useToast(); const token = auth?.token; const [question, setQuestion] = useState(null); const [loading, setLoading] = useState(true); @@ -44,8 +46,18 @@ export default function QuestionDisplay({ useEffect(() => { async function fetchQuestion() { try { + console.log(auth); + if (!auth || !auth.token) { + toast({ + title: "Access denied", + description: "No authentication token found", + variant: "destructive", + }); + return; + } + // Call to the collab microservice to get questionId by roomId - const response = await getQuestionId(roomId); + const response = await getQuestionId(auth.token, roomId); const data = await response.json(); if (data.questionId) { diff --git a/frontend/components/collab/user-rooms.tsx b/frontend/components/collab/user-rooms.tsx index 4b0913ca99..018f5b7ad9 100644 --- a/frontend/components/collab/user-rooms.tsx +++ b/frontend/components/collab/user-rooms.tsx @@ -1,5 +1,6 @@ "use client"; import React, { useEffect, useState } from "react"; +import { useToast } from "@/components/hooks/use-toast"; import { useAuth } from "@/app/auth/auth-context"; import { getRoomsByUser } from "@/lib/api/collab-service/get-rooms-by-users"; import Link from "next/link"; @@ -21,17 +22,22 @@ interface Room { } export default function UserRooms() { const auth = useAuth(); + const { toast } = useToast(); const [loading, setLoading] = useState(true); const [rooms, setRooms] = useState(null); useEffect(() => { async function fetchRoomsByUser() { try { - if (!auth?.user?.id) { - console.error("User is not authenticated"); + if (!auth || !auth.token || !auth?.user?.id) { + toast({ + title: "Access denied", + description: "No authentication token found", + variant: "destructive", + }); return; } - const response = await getRoomsByUser(auth?.user?.id); + const response = await getRoomsByUser(auth?.token, auth?.user?.id); const tempRooms = await response.json(); const sortedRooms = tempRooms.sort((a: Room, b: Room) => { return ( diff --git a/frontend/components/forget-password.tsx b/frontend/components/forget-password.tsx index 77ce09530b..19320550f7 100644 --- a/frontend/components/forget-password.tsx +++ b/frontend/components/forget-password.tsx @@ -15,7 +15,7 @@ import { Label } from "@/components/ui/label"; import { Button } from "@/components/ui/button"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { AlertCircle } from "lucide-react"; -import { userServiceUri } from "@/lib/api/api-uri"; +import { AuthType, userServiceUri } from "@/lib/api/api-uri"; const ForgetPassword: React.FC = () => { const [email, setEmail] = useState(""); @@ -36,7 +36,7 @@ const ForgetPassword: React.FC = () => { try { const response = await fetch( - `${userServiceUri(window.location.hostname)}/users/forget-password`, + `${userServiceUri(window.location.hostname, AuthType.Public)}/users/forget-password`, { method: "POST", headers: { diff --git a/frontend/components/matching/find-match.tsx b/frontend/components/matching/find-match.tsx index 3cbd9030b7..0904515ed5 100644 --- a/frontend/components/matching/find-match.tsx +++ b/frontend/components/matching/find-match.tsx @@ -5,9 +5,9 @@ import { SearchProgress } from "@/components/matching/search-progress"; import { SelectionSummary } from "@/components/matching/selection-summary"; import { useToast } from "@/components/hooks/use-toast"; import { useAuth } from "@/app/auth/auth-context"; -import { joinMatchQueue } from "@/lib/join-match-queue"; -import { leaveMatchQueue } from "@/lib/leave-match-queue"; -import { subscribeMatch } from "@/lib/subscribe-match"; +import { joinMatchQueue } from "@/lib/api/matching-service/join-match-queue"; +import { leaveMatchQueue } from "@/lib/api/matching-service/leave-match-queue"; +import { subscribeMatch } from "@/lib/api/matching-service/subscribe-match"; import { useRouter } from "next/navigation"; export default function FindMatch() { diff --git a/frontend/components/questions/questions-listing.tsx b/frontend/components/questions/questions-listing.tsx index 0cb9563c6b..08dafe5503 100644 --- a/frontend/components/questions/questions-listing.tsx +++ b/frontend/components/questions/questions-listing.tsx @@ -21,8 +21,8 @@ import { CreateQuestionArraySchema, } from "@/lib/schemas/question-schema"; import QuestionFormModal from "./question-form-modal"; +import { AuthType, questionServiceUri } from "@/lib/api/api-uri"; import { updateQuestion } from "@/lib/api/question-service/update-question"; -import { questionServiceUri } from "@/lib/api/api-uri"; import { createQuestion } from "@/lib/api/question-service/create-question"; import { deleteQuestion } from "@/lib/api/question-service/delete-question"; import { bulkCreateQuestion } from "@/lib/api/question-service/bulk-create-question"; @@ -77,7 +77,7 @@ export default function QuestionListing() { }; const { data, isLoading, mutate } = useSWR( - `${questionServiceUri(window.location.hostname)}/questions${getUriParams()}`, + `${questionServiceUri(window.location.hostname, AuthType.Public)}/questions${getUriParams()}`, fetcher, { keepPreviousData: true, diff --git a/frontend/components/user-settings/user-settings.tsx b/frontend/components/user-settings/user-settings.tsx index 510ef54a0b..ac2bea44dc 100644 --- a/frontend/components/user-settings/user-settings.tsx +++ b/frontend/components/user-settings/user-settings.tsx @@ -24,8 +24,8 @@ import LoadingScreen from "@/components/common/loading-screen"; import { useAuth } from "@/app/auth/auth-context"; import { cn } from "@/lib/utils"; import { User, UserSchema } from "@/lib/schemas/user-schema"; +import { AuthType, userServiceUri } from "@/lib/api/api-uri"; import { isPasswordComplex } from "@/lib/password"; -import { userServiceUri } from "@/lib/api/api-uri"; const fetcher = async (url: string): Promise => { // Retrieve the JWT token from localStorage @@ -56,7 +56,7 @@ export default function UserSettings({ userId }: { userId: string }) { const fileInputRef = useRef(null); const { data, error, isLoading, mutate } = useSWR( - `${userServiceUri(window.location.hostname)}/users/${userId}`, + `${userServiceUri(window.location.hostname, AuthType.Public)}/users/${userId}`, fetcher ); const [user, setUser] = useState(null); @@ -142,7 +142,7 @@ export default function UserSettings({ userId }: { userId: string }) { try { const response = await fetch( - `${userServiceUri(window.location.hostname)}/users/${userId}`, + `${userServiceUri(window.location.hostname, AuthType.Public)}/users/${userId}`, { method: "PATCH", headers: { @@ -183,7 +183,7 @@ export default function UserSettings({ userId }: { userId: string }) { try { const response = await fetch( - `${userServiceUri(window.location.hostname)}/users/${userId}`, + `${userServiceUri(window.location.hostname, AuthType.Public)}/users/${userId}`, { method: "DELETE", headers: { @@ -245,7 +245,7 @@ export default function UserSettings({ userId }: { userId: string }) { try { const response = await fetch( - `${userServiceUri(window.location.hostname)}/users/${userId}/change-password`, + `${userServiceUri(window.location.hostname, AuthType.Public)}/users/${userId}/change-password`, { method: "PATCH", headers: { diff --git a/frontend/lib/api/api-uri.ts b/frontend/lib/api/api-uri.ts index 1b83de1586..8f9cc9308a 100644 --- a/frontend/lib/api/api-uri.ts +++ b/frontend/lib/api/api-uri.ts @@ -1,19 +1,62 @@ -const constructUri = (baseUri: string, port: string | undefined) => - `http://${process.env.NEXT_PUBLIC_BASE_URI || baseUri}:${port}`; +export enum AuthType { + Public = "public", + Private = "private", + Owner = "owner", + Admin = "admin", +} -const constructWebSockUri = (baseUri: string, port: string | undefined) => - `ws://${process.env.NEXT_PUBLIC_BASE_URI || baseUri}:${port}`; +export const baseApiGatewayUri: (baseUri: string) => string = (baseUri) => + `http://${process.env.NEXT_PUBLIC_BASE_URI || baseUri}:${process.env.NEXT_PUBLIC_API_GATEWAY_PORT}`; -export const userServiceUri: (baseUri: string) => string = (baseUri) => - constructUri(baseUri, process.env.NEXT_PUBLIC_USER_SVC_PORT); -export const questionServiceUri: (baseUri: string) => string = (baseUri) => - constructUri(baseUri, process.env.NEXT_PUBLIC_QUESTION_SVC_PORT); -export const matchingServiceUri: (baseUri: string) => string = (baseUri) => - constructUri(baseUri, process.env.NEXT_PUBLIC_MATCHING_SVC_PORT); +export const constructUriSuffix: ( + authType: AuthType, + serviceName: string +) => string = (authType: AuthType, serviceName: string) => + `/${authType}/${serviceName}`; -export const matchingServiceWebSockUri: (baseUri: string) => string = ( - baseUri -) => constructWebSockUri(baseUri, process.env.NEXT_PUBLIC_MATCHING_SVC_PORT); +const constructUri: ( + baseUri: string, + authType: AuthType, + serviceName: string +) => string = (baseUri: string, authType: AuthType, serviceName: string) => + `${baseApiGatewayUri(baseUri)}/${constructUriSuffix(authType, serviceName)}`; -export const collabServiceUri: (baseUri: string) => string = (baseUri) => - constructUri(baseUri, process.env.NEXT_PUBLIC_COLLAB_SVC_PORT); +export const userServiceUri: (baseUri: string, authType: AuthType) => string = ( + baseUri: string, + authType: AuthType +) => constructUri(baseUri, authType, "user-service"); +export const questionServiceUri: ( + baseUri: string, + authType: AuthType +) => string = (baseUri, authType) => + constructUri(baseUri, authType, "question-service"); +export const matchingServiceUri: ( + baseUri: string, + authType: AuthType +) => string = (baseUri, authType) => + constructUri(baseUri, authType, "matching-service"); +export const collabServiceUri: ( + baseUri: string, + authType: AuthType +) => string = (baseUri, authType) => + constructUri(baseUri, authType, "collab-service"); + +const constructWebSockUri = ( + baseUri: string, + authType: AuthType, + serviceName: string +) => + `ws://${process.env.NEXT_PUBLIC_BASE_URI || baseUri}:${process.env.NEXT_PUBLIC_API_GATEWAY_PORT}/${authType}/${serviceName}`; + +export const matchingServiceWebSockUri: ( + baseUri: string, + authType: AuthType +) => string = (baseUri, authType) => + constructWebSockUri(baseUri, authType, "matching-service"); +export const collabServiceWebSockUri: ( + baseUri: string, + authType: AuthType +) => string = (baseUri, authType) => + constructWebSockUri(baseUri, authType, "collab-service"); +export const yjsWebSockUri: (baseUri: string) => string = (baseUri) => + `${collabServiceWebSockUri(baseUri, AuthType.Public)}/yjs`; diff --git a/frontend/lib/api/collab-service/get-chat-history.ts b/frontend/lib/api/collab-service/get-chat-history.ts index 0777af36f9..e6e18ee4b6 100644 --- a/frontend/lib/api/collab-service/get-chat-history.ts +++ b/frontend/lib/api/collab-service/get-chat-history.ts @@ -1,11 +1,12 @@ -import { collabServiceUri } from "@/lib/api/api-uri"; +import { AuthType, collabServiceUri } from "@/lib/api/api-uri"; -export const getChatHistory = async (roomId: string) => { +export const getChatHistory = async (jwtToken: string, roomId: string) => { const response = await fetch( - `${collabServiceUri(window.location.hostname)}/collab/chat-history/${roomId}`, + `${collabServiceUri(window.location.hostname, AuthType.Private)}/collab/chat-history/${roomId}`, { method: "GET", headers: { + Authorization: `Bearer ${jwtToken}`, "Content-Type": "application/json", }, } diff --git a/frontend/lib/api/collab-service/get-questionId.ts b/frontend/lib/api/collab-service/get-questionId.ts index ad36cc1058..b1a7f5b2fa 100644 --- a/frontend/lib/api/collab-service/get-questionId.ts +++ b/frontend/lib/api/collab-service/get-questionId.ts @@ -1,11 +1,12 @@ -import { collabServiceUri } from "@/lib/api/api-uri"; +import { AuthType, collabServiceUri } from "@/lib/api/api-uri"; -export const getQuestionId = async (roomId: string) => { +export const getQuestionId = async (jwtToken: string, roomId: string) => { const response = await fetch( - `${collabServiceUri(window.location.hostname)}/collab/rooms/${roomId}/questionId`, + `${collabServiceUri(window.location.hostname, AuthType.Private)}/collab/rooms/${roomId}/questionId`, { method: "GET", headers: { + Authorization: `Bearer ${jwtToken}`, "Content-Type": "application/json", }, } diff --git a/frontend/lib/api/collab-service/get-rooms-by-users.ts b/frontend/lib/api/collab-service/get-rooms-by-users.ts index e61e8a9e1e..3cf9a21a45 100644 --- a/frontend/lib/api/collab-service/get-rooms-by-users.ts +++ b/frontend/lib/api/collab-service/get-rooms-by-users.ts @@ -1,11 +1,12 @@ -import { collabServiceUri } from "@/lib/api/api-uri"; +import { AuthType, collabServiceUri } from "@/lib/api/api-uri"; -export const getRoomsByUser = async (userId: string) => { +export const getRoomsByUser = async (jwtToken: string, userId: string) => { const response = await fetch( - `${collabServiceUri(window.location.hostname)}/collab/rooms/${userId}`, + `${collabServiceUri(window.location.hostname, AuthType.Private)}/collab/rooms/${userId}`, { method: "GET", headers: { + Authorization: `Bearer ${jwtToken}`, "Content-Type": "application/json", }, } diff --git a/frontend/lib/join-match-queue.ts b/frontend/lib/api/matching-service/join-match-queue.ts similarity index 71% rename from frontend/lib/join-match-queue.ts rename to frontend/lib/api/matching-service/join-match-queue.ts index ae97ebaf57..fef70c937f 100644 --- a/frontend/lib/join-match-queue.ts +++ b/frontend/lib/api/matching-service/join-match-queue.ts @@ -1,4 +1,4 @@ -import { matchingServiceUri } from "@/lib/api/api-uri"; +import { AuthType, matchingServiceUri } from "@/lib/api/api-uri"; export const joinMatchQueue = async ( jwtToken: string, @@ -11,7 +11,7 @@ export const joinMatchQueue = async ( difficulty: complexity, }).toString(); const response = await fetch( - `${matchingServiceUri(window.location.hostname)}/match/queue/${userId}?${params}`, + `${matchingServiceUri(window.location.hostname, AuthType.Private)}/match/queue/${userId}?${params}`, { method: "POST", headers: { diff --git a/frontend/lib/leave-match-queue.ts b/frontend/lib/api/matching-service/leave-match-queue.ts similarity index 72% rename from frontend/lib/leave-match-queue.ts rename to frontend/lib/api/matching-service/leave-match-queue.ts index c91e7edb9f..62919901d6 100644 --- a/frontend/lib/leave-match-queue.ts +++ b/frontend/lib/api/matching-service/leave-match-queue.ts @@ -1,4 +1,4 @@ -import { matchingServiceUri } from "@/lib/api/api-uri"; +import { AuthType, matchingServiceUri } from "@/lib/api/api-uri"; export const leaveMatchQueue = async ( jwtToken: string, @@ -11,7 +11,7 @@ export const leaveMatchQueue = async ( difficulty: complexity, }).toString(); const response = await fetch( - `${matchingServiceUri(window.location.hostname)}/match/queue/${userId}?${params}`, + `${matchingServiceUri(window.location.hostname, AuthType.Private)}/match/queue/${userId}?${params}`, { method: "DELETE", headers: { diff --git a/frontend/lib/subscribe-match.ts b/frontend/lib/api/matching-service/subscribe-match.ts similarity index 55% rename from frontend/lib/subscribe-match.ts rename to frontend/lib/api/matching-service/subscribe-match.ts index 6ea193908b..94acdbdea6 100644 --- a/frontend/lib/subscribe-match.ts +++ b/frontend/lib/api/matching-service/subscribe-match.ts @@ -1,4 +1,4 @@ -import { matchingServiceWebSockUri } from "@/lib/api/api-uri"; +import { AuthType, matchingServiceWebSockUri } from "@/lib/api/api-uri"; export const subscribeMatch = async ( userId: string, @@ -10,6 +10,6 @@ export const subscribeMatch = async ( difficulty: complexity, }); return new WebSocket( - `${matchingServiceWebSockUri(window.location.hostname)}/match/subscribe/${userId}?${params}` + `${matchingServiceWebSockUri(window.location.hostname, AuthType.Public)}/match/subscribe/${userId}?${params}` ); }; diff --git a/frontend/lib/api/openai/send-ai-message.ts b/frontend/lib/api/openai/send-ai-message.ts index 67af8f0330..059e0da96a 100644 --- a/frontend/lib/api/openai/send-ai-message.ts +++ b/frontend/lib/api/openai/send-ai-message.ts @@ -1,10 +1,8 @@ -import { collabServiceUri } from "@/lib/api/api-uri"; +import { AuthType, collabServiceUri } from "@/lib/api/api-uri"; export const sendAiMessage = async (message: string) => { - console.log(`{collabServiceUri(window.location.hostname)}/send-ai-message`); - console.log(message); const response = await fetch( - `${collabServiceUri(window.location.hostname)}/collab/send-ai-message`, + `${collabServiceUri(window.location.hostname, AuthType.Private)}/collab/send-ai-message`, { method: "POST", headers: { diff --git a/frontend/lib/api/question-service/bulk-create-question.ts b/frontend/lib/api/question-service/bulk-create-question.ts index 1c77e84fdc..cacddb3c1a 100644 --- a/frontend/lib/api/question-service/bulk-create-question.ts +++ b/frontend/lib/api/question-service/bulk-create-question.ts @@ -1,12 +1,12 @@ import { CreateQuestion } from "@/lib/schemas/question-schema"; -import { questionServiceUri } from "../api-uri"; +import { AuthType, questionServiceUri } from "../api-uri"; export const bulkCreateQuestion = async ( jwtToken: string, questions: CreateQuestion[] ) => { const response = await fetch( - `${questionServiceUri(window.location.hostname)}/questions/batch-upload`, + `${questionServiceUri(window.location.hostname, AuthType.Admin)}/questions/batch-upload`, { method: "POST", headers: { diff --git a/frontend/lib/api/question-service/create-question.ts b/frontend/lib/api/question-service/create-question.ts index 681a7d3736..abdbdfd240 100644 --- a/frontend/lib/api/question-service/create-question.ts +++ b/frontend/lib/api/question-service/create-question.ts @@ -1,4 +1,4 @@ -import { questionServiceUri } from "@/lib/api/api-uri"; +import { AuthType, questionServiceUri } from "@/lib/api/api-uri"; import { CreateQuestion } from "@/lib/schemas/question-schema"; export const createQuestion = async ( @@ -6,7 +6,7 @@ export const createQuestion = async ( question: CreateQuestion ) => { const response = await fetch( - `${questionServiceUri(window.location.hostname)}/questions`, + `${questionServiceUri(window.location.hostname, AuthType.Admin)}/questions`, { method: "POST", headers: { diff --git a/frontend/lib/api/question-service/delete-question.ts b/frontend/lib/api/question-service/delete-question.ts index 30beeb2cf7..f2ab1d462a 100644 --- a/frontend/lib/api/question-service/delete-question.ts +++ b/frontend/lib/api/question-service/delete-question.ts @@ -1,8 +1,8 @@ -import { questionServiceUri } from "@/lib/api/api-uri"; +import { AuthType, questionServiceUri } from "@/lib/api/api-uri"; export const deleteQuestion = async (jwtToken: string, question_id: string) => { const response = await fetch( - `${questionServiceUri(window.location.hostname)}/questions/${question_id}`, + `${questionServiceUri(window.location.hostname, AuthType.Admin)}/questions/${question_id}`, { method: "DELETE", headers: { diff --git a/frontend/lib/api/question-service/get-question.ts b/frontend/lib/api/question-service/get-question.ts index 5ff59a8eef..5aec1d8315 100644 --- a/frontend/lib/api/question-service/get-question.ts +++ b/frontend/lib/api/question-service/get-question.ts @@ -1,8 +1,8 @@ -import { questionServiceUri } from "@/lib/api/api-uri"; +import { AuthType, questionServiceUri } from "@/lib/api/api-uri"; export const getQuestion = async (jwtToken: string, questionId: string) => { const response = await fetch( - `${questionServiceUri(window.location.hostname)}/questions/${questionId}`, + `${questionServiceUri(window.location.hostname, AuthType.Public)}/questions/${questionId}`, { method: "GET", headers: { diff --git a/frontend/lib/api/question-service/update-question.ts b/frontend/lib/api/question-service/update-question.ts index 7245715273..a532f99298 100644 --- a/frontend/lib/api/question-service/update-question.ts +++ b/frontend/lib/api/question-service/update-question.ts @@ -1,9 +1,9 @@ -import { questionServiceUri } from "@/lib/api/api-uri"; +import { AuthType, questionServiceUri } from "@/lib/api/api-uri"; import { Question } from "@/lib/schemas/question-schema"; export const updateQuestion = async (jwtToken: string, question: Question) => { const response = await fetch( - `${questionServiceUri(window.location.hostname)}/questions/${question.id}`, + `${questionServiceUri(window.location.hostname, AuthType.Admin)}/questions/${question.id}`, { method: "PUT", headers: { diff --git a/frontend/lib/api/user-service/reset-password.ts b/frontend/lib/api/user-service/reset-password.ts index 79f1423fa3..64961bb604 100644 --- a/frontend/lib/api/user-service/reset-password.ts +++ b/frontend/lib/api/user-service/reset-password.ts @@ -1,8 +1,8 @@ -import { userServiceUri } from "@/lib/api/api-uri"; +import { AuthType, userServiceUri } from "@/lib/api/api-uri"; export const resetPassword = async (token: string, password: string) => { const response = await fetch( - `${userServiceUri(window.location.hostname)}/users/reset-password`, + `${userServiceUri(window.location.hostname, AuthType.Public)}/users/reset-password`, { method: "POST", headers: { diff --git a/frontend/lib/api/user-service/signup.ts b/frontend/lib/api/user-service/signup.ts index 0d3bf3f488..140f209ed8 100644 --- a/frontend/lib/api/user-service/signup.ts +++ b/frontend/lib/api/user-service/signup.ts @@ -1,4 +1,4 @@ -import { userServiceUri } from "@/lib/api/api-uri"; +import { AuthType, userServiceUri } from "@/lib/api/api-uri"; export const signUp = async ( username: string, @@ -6,7 +6,7 @@ export const signUp = async ( password: string ) => { const response = await fetch( - `${userServiceUri(window.location.hostname)}/users`, + `${userServiceUri(window.location.hostname, AuthType.Public)}/users`, { method: "POST", headers: { diff --git a/frontend/lib/api/user-service/update-user-privilege.ts b/frontend/lib/api/user-service/update-user-privilege.ts index 091420f6f8..4ad95ce9fe 100644 --- a/frontend/lib/api/user-service/update-user-privilege.ts +++ b/frontend/lib/api/user-service/update-user-privilege.ts @@ -1,4 +1,4 @@ -import { userServiceUri } from "@/lib/api/api-uri"; +import { AuthType, userServiceUri } from "@/lib/api/api-uri"; export const updateUserPrivilege = async ( jwtToken: string, @@ -8,7 +8,7 @@ export const updateUserPrivilege = async ( const body = { isAdmin }; const response = await fetch( - `${userServiceUri(window.location.hostname)}/users/${id}/privilege`, + `${userServiceUri(window.location.hostname, AuthType.Public)}/users/${id}/privilege`, { method: "PATCH", headers: { diff --git a/frontend/lib/api/user-service/update-user.ts b/frontend/lib/api/user-service/update-user.ts index 1740365b71..e0fbf7b786 100644 --- a/frontend/lib/api/user-service/update-user.ts +++ b/frontend/lib/api/user-service/update-user.ts @@ -1,4 +1,4 @@ -import { userServiceUri } from "@/lib/api/api-uri"; +import { AuthType, userServiceUri } from "@/lib/api/api-uri"; export const updateUser = async ( jwtToken: string, @@ -15,7 +15,7 @@ export const updateUser = async ( const body = { username, email, password, skillLevel }; const response = await fetch( - `${userServiceUri(window.location.hostname)}/users/${id}`, + `${userServiceUri(window.location.hostname, AuthType.Public)}/users/${id}`, { method: "PATCH", headers: { diff --git a/user-service/app/routes/auth-routes.js b/user-service/app/routes/auth-routes.js index 8690cbe1f6..d37775299e 100644 --- a/user-service/app/routes/auth-routes.js +++ b/user-service/app/routes/auth-routes.js @@ -1,7 +1,7 @@ import express from "express"; import { handleLogin, handleVerifyToken } from "../controller/auth-controller.js"; -import { verifyAccessToken } from "../middleware/basic-access-control.js"; +import { verifyAccessToken, verifyIsAdmin, verifyIsOwnerOrAdmin } from "../middleware/basic-access-control.js"; const router = express.Router(); @@ -9,4 +9,8 @@ router.post("/login", handleLogin); router.get("/verify-token", verifyAccessToken, handleVerifyToken); +router.get("/verify-owner", verifyAccessToken, verifyIsOwnerOrAdmin, handleVerifyToken); + +router.get("/verify-admin", verifyAccessToken, verifyIsAdmin, handleVerifyToken); + export default router;