Skip to content

Commit

Permalink
Ensure codebase is production-ready (CS3219-AY2425S1#68)
Browse files Browse the repository at this point in the history
* Frontend: Refactor config to env

Make use of angular environments to ensure it serves the correct URLs on
production.

* Ensure production consistent URLs

AWS load balancers are unable to trim off path (/api/server). Let's update the services themselves to contain these path prefixes.

* User: Add logging

* User: Modify health check route

* Standardise server.ts across services

* Reorder compose.yml
  • Loading branch information
samuelim01 authored Nov 1, 2024
1 parent f85a63b commit 097b6b7
Show file tree
Hide file tree
Showing 31 changed files with 218 additions and 161 deletions.
30 changes: 15 additions & 15 deletions compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,21 @@ services:
networks:
- gateway-network
restart: always

broker:
container_name: broker
hostname: broker
image: rabbitmq:4.0.2
user: rabbitmq
networks:
- gateway-network
healthcheck:
test: rabbitmq-diagnostics check_port_connectivity
interval: 30s
timeout: 30s
retries: 10
start_period: 30s
restart: always

question:
container_name: question
Expand Down Expand Up @@ -123,21 +138,6 @@ services:
- match-db-network
restart: always

broker:
container_name: broker
hostname: broker
image: rabbitmq:4.0.2
user: rabbitmq
networks:
- gateway-network
healthcheck:
test: rabbitmq-diagnostics check_port_connectivity
interval: 30s
timeout: 30s
retries: 10
start_period: 30s
restart: always

collaboration:
container_name: collaboration
image: collaboration
Expand Down
8 changes: 7 additions & 1 deletion frontend/angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,13 @@
"development": {
"optimization": false,
"extractLicenses": false,
"sourceMap": true
"sourceMap": true,
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.development.ts"
}
]
}
},
"defaultConfiguration": "production"
Expand Down
10 changes: 5 additions & 5 deletions frontend/src/_interceptors/jwt.interceptor.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { HttpTestingController, provideHttpClientTesting } from '@angular/common
import { HTTP_INTERCEPTORS, HttpClient } from '@angular/common/http';
import { JwtInterceptor } from './jwt.interceptor';
import { AuthenticationService } from '../_services/authentication.service';
import { API_CONFIG } from '../app/api.config';
import { environment } from '../environments/environment.development';

describe('JwtInterceptor', () => {
let httpMock: HttpTestingController;
Expand Down Expand Up @@ -34,9 +34,9 @@ describe('JwtInterceptor', () => {
});

it('should add an Authorization header', () => {
httpClient.get(`${API_CONFIG.baseUrl}/user/test`).subscribe();
httpClient.get(`${environment.apiUrl}/user/test`).subscribe();

const httpRequest = httpMock.expectOne(`${API_CONFIG.baseUrl}/user/test`);
const httpRequest = httpMock.expectOne(`${environment.apiUrl}/user/test`);

expect(httpRequest.request.headers.has('Authorization')).toBeTruthy();
expect(httpRequest.request.headers.get('Authorization')).toBe('Bearer fake-jwt-token');
Expand All @@ -47,9 +47,9 @@ describe('JwtInterceptor', () => {
userValue: {},
});

httpClient.get(`${API_CONFIG.baseUrl}/user/test`).subscribe();
httpClient.get(`${environment.apiUrl}/user/test`).subscribe();

const httpRequest = httpMock.expectOne(`${API_CONFIG.baseUrl}/user/test`);
const httpRequest = httpMock.expectOne(`${environment.apiUrl}/user/test`);

expect(httpRequest.request.headers.has('Authorization')).toBeFalsy();
});
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/_services/api.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { API_CONFIG } from '../app/api.config';
import { environment } from '../environments/environment';

/**
* Abstract class that serves as a base for API services.
Expand All @@ -16,6 +16,6 @@ export abstract class ApiService {
* the specified apiPath.
*/
get apiUrl(): string {
return API_CONFIG.baseUrl + this.apiPath;
return environment.apiUrl + this.apiPath;
}
}
2 changes: 1 addition & 1 deletion frontend/src/_services/question.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { ApiService } from './api.service';
providedIn: 'root',
})
export class QuestionService extends ApiService {
protected apiPath = 'question/questions';
protected apiPath = 'question';

private httpOptions = {
headers: new HttpHeaders({
Expand Down
7 changes: 0 additions & 7 deletions frontend/src/app/api.config.ts

This file was deleted.

4 changes: 2 additions & 2 deletions frontend/src/app/collaboration/editor/editor.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import { yCollab } from 'y-codemirror.next';
import * as prettier from 'prettier';
import * as prettierPluginEstree from 'prettier/plugins/estree';
import { usercolors } from './user-colors';
import { WEBSOCKET_CONFIG } from '../../api.config';
import { AuthenticationService } from '../../../_services/authentication.service';
import { RoomService } from '../room.service';
// The 'prettier-plugin-java' package does not provide TypeScript declaration files.
Expand All @@ -28,6 +27,7 @@ import { SubmitDialogComponent } from '../submit-dialog/submit-dialog.component'
import { ForfeitDialogComponent } from '../forfeit-dialog/forfeit-dialog.component';
import { Router } from '@angular/router';
import { awarenessData } from '../collab.model';
import { environment } from '../../../environments/environment';

enum WebSocketCode {
AUTH_FAILED = 4000,
Expand Down Expand Up @@ -98,7 +98,7 @@ export class EditorComponent implements AfterViewInit, OnInit, OnDestroy {

initConnection() {
this.ydoc = new Y.Doc();
const websocketUrl = WEBSOCKET_CONFIG.baseUrl + 'collaboration/';
const websocketUrl = environment.wsUrl + 'collaboration/';
this.wsProvider = new WebsocketProvider(websocketUrl, this.roomId, this.ydoc, {
params: {
accessToken: this.authService.userValue?.accessToken || '',
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/environments/environment.development.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const environment = {
apiUrl: 'http://localhost:8080/api/',
wsUrl: 'ws://localhost:8080/api/',
};
4 changes: 4 additions & 0 deletions frontend/src/environments/environment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const environment = {
apiUrl: 'https://app.peerprep.org/api/',
wsUrl: 'wss://app.peerprep.org/api/',
};
16 changes: 8 additions & 8 deletions nginx/default.conf
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,23 @@ server {
listen 8080;
server_name localhost;

location /api/question/ {
proxy_pass http://question-api/;
location /api/question {
proxy_pass http://question-api;
proxy_set_header Host $host;
}

location /api/user/ {
proxy_pass http://user-api/;
location /api/user {
proxy_pass http://user-api;
proxy_set_header Host $host;
}

location /api/match/ {
proxy_pass http://match-api/;
location /api/match {
proxy_pass http://match-api;
proxy_set_header Host $host;
}

location /api/collaboration/ {
proxy_pass http://collaboration-api/;
location /api/collaboration {
proxy_pass http://collaboration-api;
proxy_set_header Host $host;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
Expand Down
8 changes: 4 additions & 4 deletions services/collaboration/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ This endpoint retrieves all active room IDs associated with the authenticated us
is `true` will be retrieved.

- **HTTP Method**: `GET`
- **Endpoint**: `/room/user/rooms`
- **Endpoint**: `/api/collaboration/room/user/rooms`

### Authorization

Expand Down Expand Up @@ -126,7 +126,7 @@ curl -X GET http://localhost:8080/api/collaboration/room/user/rooms \
This endpoint retrieves the details of a room by its room ID.
- **HTTP Method**: `GET`
- **Endpoint**: `/room/{roomId}`
- **Endpoint**: `/api/collaboration/room/{roomId}`
### Authorization
Expand Down Expand Up @@ -187,7 +187,7 @@ This endpoint updates the `isForfeit` status of a specified user in a particular
a `isForfeit` field that tracks whether the user has left the room through forfeiting or is still active.

- **HTTP Method**: `PATCH`
- **Endpoint**: `/room/{roomId}/user/isForfeit`
- **Endpoint**: `/api/collaboration/room/{roomId}/user/isForfeit`

### Authorization

Expand Down Expand Up @@ -254,7 +254,7 @@ curl -X PATCH http://localhost:8080/api/collaboration/room/6721a64b0c4d990bc0fee
This endpoint allows a user to close a room (change `room_status` to `false`) and delete the associated Yjs document.

- **HTTP Method**: `PATCH`
- **Endpoint**: `/room/{roomId}/close`
- **Endpoint**: `/api/collaboration/room/{roomId}/close`

### Authorization

Expand Down
9 changes: 4 additions & 5 deletions services/collaboration/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@
"name": "collaboration-service",
"version": "1.0.0",
"description": "Collaboration service using Yjs, WebSocket, and MongoDB.",
"main": "index.js",
"main": "server.js",
"scripts": {
"start": "tsc && node dist/index.js",
"build": "tsc",
"dev": "nodemon --files src/index.ts",
"test": "echo \"No test specified\" && exit 1",
"build": "npx tsc",
"start": "npm run build && node ./dist/server.js",
"dev": "nodemon --files ./src/server.ts",
"lint": "npx eslint .",
"lint:fix": "npx eslint . --fix"
},
Expand Down
2 changes: 1 addition & 1 deletion services/collaboration/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@ app.use(
);

app.use('/', router);
app.use('/room', verifyAccessToken, roomRouter);
app.use('/api/collaboration/room', verifyAccessToken, roomRouter);

export default app;
2 changes: 1 addition & 1 deletion services/collaboration/src/controllers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ import { Request, Response } from 'express';

export const getHealth = async (req: Request, res: Response) => {
res.status(200).json({
message: 'Server is up and running!',
message: 'Collaboration Service is up and running!',
});
};
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,16 @@ import config from './config';
const PORT = config.PORT;

startMongoDB()
.then(() => initializeConsumers())
.then(() => console.log('Consumers are listening'))
.then(() => {
const server = http.createServer(app);
startWebSocketServer(server);
server.listen(PORT, () => {
console.log(`Server (HTTP + WebSocket) running on port ${PORT}`);
console.log(`Collaboration service is listening on port ${PORT}`);
});
})
.then(() => initializeConsumers())
.then(() => console.log('Consumers are listening'))
.catch(error => {
console.error('Failed to start services:', error);
console.error('Failed to start server');
console.error(error);
});
10 changes: 5 additions & 5 deletions services/match/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ docker compose down -v

- This endpoint creates a new match request. The match request will remain valid for 1 minute.
- **HTTP Method**: `POST`
- **Endpoint**: http://localhost:8083/match/request
- **Endpoint**: http://localhost:8083/api/match/request
- **Body**
- `topics` (Required) - The topics associated with the match request.
- `difficulty` (Required) - The difficulty level of the match request.
Expand Down Expand Up @@ -75,10 +75,10 @@ docker compose down -v

- This endpoint deletes the match request.
- **HTTP Method**: `DELETE`
- **Endpoint**: http://localhost:8083/match/request/{requestId}
- **Endpoint**: http://localhost:8083/api/match/request/{requestId}
- **Parameters**
- `requestId` (Required) - The request ID of the match request.
- Example: `http://localhost:8083/match/request/6706b5d706ecde0138ca27a9`
- Example: `http://localhost:8083/api/match/request/6706b5d706ecde0138ca27a9`
- **Headers**
- `Authorization: Bearer <JWT_ACCESS_TOKEN>` (Required)
- The endpoint requires the user to include a JWT (JSON Web Token) in the HTTP Request Header for authentication and authorization. This token is generated during the authentication process (i.e., login) and contains information about the user's identity. The server verifies this token to ensure that the client is authorized to access the data.
Expand Down Expand Up @@ -115,10 +115,10 @@ docker compose down -v

- This endpoint retrieves the match request and its status.
- **HTTP Method**: `GET`
- **Endpoint**: http://localhost:8083/match/request/{requestId}
- **Endpoint**: http://localhost:8083/api/match/request/{requestId}
- **Parameters**
- `requestId` (Required) - The request ID of the match request.
- Example: `http://localhost:8083/match/request/6706b5d706ecde0138ca27a9`
- Example: `http://localhost:8083/api/match/request/6706b5d706ecde0138ca27a9`
- **Headers**
- `Authorization: Bearer <JWT_ACCESS_TOKEN>` (Required)
- The endpoint requires the user to include a JWT (JSON Web Token) in the HTTP Request Header for authentication and authorization. This token is generated during the authentication process (i.e., login) and contains information about the user's identity. The server verifies this token to ensure that the client is authorized to access the data.
Expand Down
6 changes: 3 additions & 3 deletions services/match/package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"name": "match",
"version": "0.0.0",
"main": "index.js",
"main": "server.js",
"scripts": {
"build": "npx tsc",
"start": "npm run build && node ./dist/index.js",
"dev": "nodemon --files ./src/index.ts",
"start": "npm run build && node ./dist/server.js",
"dev": "nodemon --files ./src/server.ts",
"lint": "npx eslint .",
"lint:fix": "npx eslint . --fix"
},
Expand Down
2 changes: 1 addition & 1 deletion services/match/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,6 @@ app.use(

// Routes
app.use('/', router);
app.use('/request', verifyAccessToken, matchRequestRouter);
app.use('/api/match/request', verifyAccessToken, matchRequestRouter);

export default app;
2 changes: 1 addition & 1 deletion services/match/src/controllers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ import { Request, Response } from 'express';

export const getHealth = async (req: Request, res: Response) => {
res.status(200).json({
message: 'Server is up and running!',
message: 'Match Service is up and running!',
});
};
3 changes: 1 addition & 2 deletions services/match/src/index.ts → services/match/src/server.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import app from './app';
import config from './config';
import messageBroker from './events/broker';
import { initializeConsumers } from './events/consumer';
import { connectToDB } from './models/repository';

const port = config.PORT;

connectToDB()
.then(() => console.log('MongoDB connected successfully'))
.then(async () => await messageBroker.connect())
.then(async () => await initializeConsumers())
.then(() => console.log('Consumers are listening'))
.then(() => app.listen(port, () => console.log(`Match service is listening on port ${port}.`)))
.catch(error => {
console.error('Failed to start server');
Expand Down
Loading

0 comments on commit 097b6b7

Please sign in to comment.