Skip to content

Commit

Permalink
Merge pull request #59 from CS3219-AY2425S1/feat/cloud
Browse files Browse the repository at this point in the history
Cloud deployment
  • Loading branch information
bhnuka authored Nov 14, 2024
2 parents 5d46cf7 + 4640b87 commit d8367b1
Show file tree
Hide file tree
Showing 21 changed files with 391 additions and 231 deletions.
128 changes: 71 additions & 57 deletions peerprep/backend/collab-service/src/server.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
const express = require('express');
const WebSocket = require('ws');
const http = require('http');
const https = require('https');
const fs = require('fs');
const StaticServer = require('node-static').Server;
const ywsUtils = require('y-websocket/bin/utils');
const setupWSConnection = ywsUtils.setupWSConnection;
Expand All @@ -11,10 +13,18 @@ const { Server } = require("socket.io");
const cors = require("cors");
const gptRoutes = require('./routes/gptRoutes');
const dotenv = require('dotenv');

dotenv.config();
const app = express();

const server = http.createServer(app);
const wss = new WebSocket.Server({ noServer: true });

const wss = new WebSocket.Server({
server,
verifyClient: (info, done) => {
// Allow connections from any origin
done(true);
}
});

app.use(cors());
app.use(express.json());
Expand All @@ -24,81 +34,85 @@ connectDB();

app.use('/api', gptRoutes);

// Sanity testing endpoint
app.get('/hello', (req, res) => {
res.status(200).json({ message: 'Hello, world!' });
});

// Endpoint to save a document to MongoDB
app.post('/api/saveDocument', async (req, res) => {
try {
const { name, data } = req.body;
await storeDocument(name, Buffer.from(data));
res.status(200).json({ message: 'Document saved successfully' });
} catch (error) {
res.status(500).json({ error: error.message });
}
app.post('/api/saveDocument', async(req, res) => {
try {
const { name, data } = req.body;
await storeDocument(name, Buffer.from(data));
res.status(200).json({ message: 'Document saved successfully' });
} catch (error) {
res.status(500).json({ error: error.message });
}
});

// Endpoint to retrieve a document from MongoDB
app.get('/api/getDocument/:name', async (req, res) => {
try {
const name = req.params.name;
const documentData = await getDocument(name);
res.status(200).json({ data: Array.from(documentData) });
} catch (error) {
res.status(500).json({ error: error.message });
}
app.get('/api/getDocument/:name', async(req, res) => {
try {
const name = req.params.name;
const documentData = await getDocument(name);
res.status(200).json({ data: Array.from(documentData) });
} catch (error) {
res.status(500).json({ error: error.message });
}
});

// Serve static files and set up WebSocket connections for y-websocket
const staticServer = new StaticServer('../', { cache: false, gzip: true });
app.get('*', (req, res) => {
staticServer.serve(req, res);
staticServer.serve(req, res);
});

wss.on('connection', (conn, req) => {
setupWSConnection(conn, req, { gc: req.url.slice(1) !== 'ws/prosemirror-versions' });
setupWSConnection(conn, req, { gc: req.url.slice(1) !== 'ws/prosemirror-versions' });
});

const io = new Server(server, {
cors: {
origin: "http://localhost:5173",
methods: ["GET", "POST"],
},
cors: {
origin: true,
methods: ["GET", "POST"],
},
});

io.on("connection", (socket) => {
console.log(`User Connected: ${socket.id}`);

socket.on("join_room", (room) => {
try {
socket.join(room);
console.log(`User ${socket.id} joined room ${room}`);
} catch (error) {
console.error(`Error joining room: ${error.message}`);
socket.emit("error_message", { message: "Failed to join room. Try again later." });
}
});

socket.on("send_message", (data) => {
try {
socket.in(data.room).emit("receive_message", data);
} catch (error) {
console.error(`Error sending message: ${error.message}`);
socket.emit("error_message", { message: "Failed to send message. Try again later." });
}
});

socket.on("disconnect", (reason) => {
console.log(`User Disconnected: ${socket.id} - Reason: ${reason}`);
if (reason === "io server disconnect") {
socket.connect(); // Reconnect on server-side disconnect
}
});

socket.on("error", (error) => {
console.error(`Socket error: ${error.message}`);
});
console.log(`User Connected: ${socket.id}`);

socket.on("join_room", (room) => {
try {
socket.join(room);
console.log(`User ${socket.id} joined room ${room}`);
} catch (error) {
console.error(`Error joining room: ${error.message}`);
socket.emit("error_message", { message: "Failed to join room. Try again later." });
}
});

socket.on("send_message", (data) => {
try {
socket.in(data.room).emit("receive_message", data);
} catch (error) {
console.error(`Error sending message: ${error.message}`);
socket.emit("error_message", { message: "Failed to send message. Try again later." });
}
});

socket.on("disconnect", (reason) => {
console.log(`User Disconnected: ${socket.id} - Reason: ${reason}`);
if (reason === "io server disconnect") {
socket.connect(); // Reconnect on server-side disconnect
}
});

socket.on("error", (error) => {
console.error(`Socket error: ${error.message}`);
});
});


const PORT = process.env.PORT || 1234;
server.listen(PORT, () => {
console.log(`Server is running at http://localhost:${PORT}`);
console.log(`Server is running at http://localhost:${PORT}`);
});
6 changes: 6 additions & 0 deletions peerprep/backend/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ services:
- "3001:3001"
env_file:
- ./user-service/.env
restart: always

question-service:
build:
Expand All @@ -16,6 +17,7 @@ services:
- "8080:8080"
env_file:
- ./question-service/.env
restart: always

matching-service:
build:
Expand All @@ -25,6 +27,7 @@ services:
- "3000:3000"
env_file:
- ./matching-service/.env
restart: always

collab-service:
build:
Expand All @@ -35,6 +38,9 @@ services:
- "3003:3003"
env_file:
- ./collab-service/.env
restart: always
volumes:
- "/etc/letsencrypt:/etc/letsencrypt"

networks:
my-network:
Expand Down
9 changes: 9 additions & 0 deletions peerprep/frontend/.env.development
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
VITE_AUTH_API_URL=http://localhost:3001/auth
VITE_MATCHING_API_URL=http://localhost:3333/matchingrequest
VITE_USER_API_URL=http://localhost:3001/api/users
VITE_USERS_API_URL=http://localhost:3001/users
VITE_ASSESSCODE_API_URL=http://localhost:1234/api/gpt/assess
VITE_QUESTION_API_URL=http://localhost:8080/api/questions
VITE_TESTCASE_API_URL=http://localhost:8080/api/testcases
VITE_WS_PROVIDER_URL=ws://localhost:1234/
VITE_SOCKET_IO_URL=http://localhost:1234
9 changes: 9 additions & 0 deletions peerprep/frontend/.env.production
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
VITE_AUTH_API_URL=https://user.peerprep.app/auth
VITE_MATCHING_API_URL=https://matching.peerprep.app/matchingrequest
VITE_USER_API_URL=https://user.peerprep.app/api/users
VITE_USERS_API_URL=https://user.peerprep.app/users
VITE_QUESTION_API_URL=https://question.peerprep.app/api/questions
VITE_TESTCASE_API_URL=https://question.peerprep.app/api/testcases
VITE_ASSESSCODE_API_URL=https://collab.peerprep.app/api/gpt/assess
VITE_WS_PROVIDER_URL=wss://collab.peerprep.app/
VITE_SOCKET_IO_URL=https://collab.peerprep.app/
31 changes: 13 additions & 18 deletions peerprep/frontend/src/api/assesscodeApi.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import axios, { AxiosError } from 'axios';

const API_URL = 'http://localhost:1234/api/gpt/assess';
const API_URL = import.meta.env.VITE_ASSESSCODE_API_URL;

// Define a custom error class for API errors
export class ApiError extends Error {
Expand All @@ -10,21 +10,6 @@ export class ApiError extends Error {
}
}

const handleApiError = (error: unknown): never => {
if (axios.isAxiosError(error)) {
const axiosError = error as AxiosError;
if (axiosError.response) {
throw new ApiError(`API error: ${axiosError.response.statusText}`, axiosError.response.status);
} else if (axiosError.request) {
throw new ApiError('API error: No response received from the server');
} else {
throw new ApiError(`API error: ${axiosError.message}`);
}
} else {
throw new ApiError(`API error: An unexpected error occurred ${error}`);
}
};

export const assessCode = async (currentCode: string): Promise<string> => {
try {
console.log('Submitting code to backend API:');
Expand All @@ -38,6 +23,16 @@ export const assessCode = async (currentCode: string): Promise<string> => {
return feedback;
} catch (error) {
console.error('Error executing backend API call:', error);
return "Failed to process code assessment on backend";
if (axios.isAxiosError(error)) {
const axiosError = error as AxiosError;
if (axiosError.response) {
throw new ApiError(`API error: ${axiosError.response.statusText}`, axiosError.response.status);
} else if (axiosError.request) {
throw new ApiError('API error: No response received from the server');
} else {
throw new ApiError(`API error: ${axiosError.message}`);
}
}
throw new ApiError(`API error: An unexpected error occurred ${error}`);
}
};
};
2 changes: 1 addition & 1 deletion peerprep/frontend/src/api/authApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import axios, { AxiosError } from 'axios';
import { User } from '../models/User';

// Base URL for authentication APIs
const API_URL = 'http://localhost:3001/auth';
const API_URL = import.meta.env.VITE_AUTH_API_URL;

class ApiError extends Error {
constructor(message: string, public statusCode?: number) {
Expand Down
13 changes: 4 additions & 9 deletions peerprep/frontend/src/api/matchingApi.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import axios, { AxiosError } from 'axios';
import { Request } from '../models/Request';

// Set up axios instance with base URL
const API_URL = "http://localhost:3000/matchingrequest";
const API_URL = import.meta.env.VITE_MATCHING_API_URL;

export class ApiError extends Error {
constructor(message: string, public statusCode?: number) {
Expand All @@ -11,10 +10,6 @@ export class ApiError extends Error {
}
}

interface ErrorResponseData {
message: string;
}

const handleApiError = (error: unknown): never => {
if (axios.isAxiosError(error)) {
const axiosError = error as AxiosError;
Expand Down Expand Up @@ -88,7 +83,7 @@ export const getMatchStatus = async (userId: string): Promise<MatchingRequestRes
}
}

export const listenToMatchStatus = (userId: string, onUpdate: (data: MatchingRequestResponse) => void, onError: (error: any) => void) => {
export const listenToMatchStatus = (userId: string, onUpdate: (data: MatchingRequestResponse) => void, onError: (error: Error | Event) => void) => {
const eventSource = new EventSource(`${API_URL}/${userId}`);

// Listen for incoming events from the server
Expand All @@ -98,7 +93,7 @@ export const listenToMatchStatus = (userId: string, onUpdate: (data: MatchingReq
onUpdate(data); // Call the update handler with the received data
} catch (error) {
console.error("Error parsing SSE data:", error);
onError(error);
onError(error instanceof Error ? error : new Error("Failed to parse SSE data"));
}
};

Expand Down Expand Up @@ -192,4 +187,4 @@ export const getAllMatchedSessionsByTopicAndDifficulty = async (topic: string, d
} catch (error) {
return handleApiError(error);
}
}
}
2 changes: 1 addition & 1 deletion peerprep/frontend/src/api/questionApi.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import axios, { AxiosError } from 'axios';
import { Question } from '../models/Question';

const API_URL = 'http://localhost:8080/api/questions';
const API_URL = import.meta.env.VITE_QUESTION_API_URL;

export class ApiError extends Error {
constructor(message: string, public statusCode?: number) {
Expand Down
2 changes: 1 addition & 1 deletion peerprep/frontend/src/api/testcaseApi.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import axios from 'axios';

const API_URL = 'http://localhost:8080/api/testcases';
const API_URL = import.meta.env.VITE_TESTCASE_API_URL;

export interface Testcase {
questionId: number;
Expand Down
2 changes: 1 addition & 1 deletion peerprep/frontend/src/api/userApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import axios, { AxiosError } from 'axios';
import { User } from '../models/User';

// Set up axios instance with base URL
const API_URL = "http://localhost:3001/api/users";
const API_URL = import.meta.env.VITE_USER_API_URL;

class ApiError extends Error {
constructor(message: string, public statusCode?: number) {
Expand Down
2 changes: 1 addition & 1 deletion peerprep/frontend/src/api/usersApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import axios, { AxiosError } from 'axios';
import { User } from '../models/User';

// Base URL for user APIs
const API_URL = 'http://localhost:3001/users';
const API_URL = import.meta.env.VITE_USERS_API_URL;

class ApiError extends Error {
constructor(message: string, public statusCode?: number) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ const CollaborationServiceIntegratedView: React.FC = () => {

useEffect(() => {
const ydoc = new Y.Doc();
const provider = new WebsocketProvider('ws://localhost:1234/' + sessionId, 'collaborative-doc', ydoc);
const provider = new WebsocketProvider(import.meta.env.VITE_WS_PROVIDER_URL + sessionId, 'collaborative-doc', ydoc);
const newYText = ydoc.getText('codemirror');
setYText(newYText);

Expand Down
Loading

0 comments on commit d8367b1

Please sign in to comment.