Skip to content

Commit

Permalink
Merge branch 'main' into fix-jwt
Browse files Browse the repository at this point in the history
  • Loading branch information
samuelim01 committed Nov 13, 2024
2 parents 5506901 + a3e960e commit 33785df
Show file tree
Hide file tree
Showing 67 changed files with 2,902 additions and 1,836 deletions.
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,7 @@ docker compose -f compose.yml -f compose.dev.yml up -d
| User Service | 8082 |
| Match Service | 8083 |
| Collaboration & Room Service | 8084 |
| Chat Service | 8085 |
| History Service | 8086 |
| History Service | 8085 |

**Step 4: Stop the Docker containers**

Expand Down
6 changes: 1 addition & 5 deletions compose.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,10 @@ services:
- /app/node_modules
- ./services/collaboration:/app

collaboration-db:
ports:
- 27020:27017

history:
command: npm run dev
ports:
- 8086:8086
- 8085:8085
volumes:
- /app/node_modules
- ./services/history:/app
Expand Down
60 changes: 60 additions & 0 deletions devops/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Backend Deployment

This script allows you to set the desired number of tasks for each backend service.

## Prerequisites

Before using this script, ensure that you have the following:

1. **AWS CLI**: You must have the AWS CLI installed on your machine to interact with AWS services. You may install AWS CLI [here](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html).

2. **Access Keys**: You may obtain the AWS environment variables from the AWS Access Portal.

## Running the Script

1. Open a terminal and navigate to the `devops/` directory where the script is located.

2. Obtain your AWS Access Keys:
- Log in to the **AWS Access Console**.
- Click on **Access keys**.
- Copy the AWS environment variables into your terminal:

```bash
export AWS_ACCESS_KEY_ID=<AccessKeyId>
export AWS_SECRET_ACCESS_KEY=<SecretAccessKey>
export AWS_SESSION_TOKEN=<SessionToken>
```

3. To scale the services to **1 task** (i.e., start the services), run the following command:

```bash
./deploy-backend.sh 1
```

4. To scale the services to **0 tasks** (i.e., stop the services), run the following command:

```bash
./deploy-backend.sh 0
```

## Troubleshooting

**Q: Why doesn't the script work when I try to run it?**

**A:** Ensure you are using the correct IAM user with sufficient permissions to update ECS services.

---

**Q: The script won't run due to a permission error**

**A:** This could be due to the script not having the correct execution permissions. Run the following command to give the script execute permissions:

```bash
chmod +x deploy-backend.sh
```

---

**Q: The script worked before but doesn't work anymore**

**A:** This could be due to the environment variables expiring. Copy a new set of environment variables as shown above.
47 changes: 47 additions & 0 deletions devops/deploy-backend.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#!/bin/bash

# Configuration
AWS_REGION="ap-southeast-1"
ECS_CLUSTER="backend-cluster"
SERVICES=("question" "user" "match" "collaboration" "history")

# Validate the user input for desired number of tasks
if [[ "$1" != "0" && "$1" != "1" ]]; then
echo "Error: The desired number of tasks must be either 0 or 1."
echo "Usage: $0 <desired_tasks>"
echo "Example: $0 1 # Scale each service to 1 task"
echo "Example: $0 0 # Scale each service to 0 tasks"
exit 1
fi

DESIRED_TASKS=$1 # Desired number of tasks (0 or 1)

# Function to update ECS service
update_service() {
local service=$1

echo "Updating ECS service for $service..."

# Update ECS Service to trigger deployment with the desired number of tasks
aws ecs update-service \
--cluster "$ECS_CLUSTER" \
--service "$service-service" \
--desired-count "$DESIRED_TASKS" \
--force-new-deployment \
--region "$AWS_REGION" \
--output text > /dev/null 2>&1

if [[ $? -eq 0 ]]; then
echo "Service $service updated successfully with desired task count: $DESIRED_TASKS"
else
echo "Error updating service $service"
exit 1
fi
}

# Main script execution
for service in "${SERVICES[@]}"; do
update_service "$service"
done

echo "All services have been updated."
8 changes: 4 additions & 4 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"codemirror": "^6.0.1",
"primeflex": "^3.3.1",
"primeicons": "^7.0.0",
"primeng": "^17.18.11",
"primeng": "^17.18.10",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"typeface-poppins": "^1.1.13",
Expand Down
20 changes: 20 additions & 0 deletions frontend/src/_services/authentication.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,26 @@ export class AuthenticationService extends ApiService {
.pipe(switchMap(() => this.login(username, password))); // auto login after registration
}

updateUsernameAndEmail(username: string, email: string, password: string) {
return this.http
.patch<UServRes>(
`${this.apiUrl}/users/username-email/${this.userValue!.id}`,
{ username: username, email: email, password: password },
{ observe: 'response' },
)
.pipe(switchMap(() => this.login(username, password))); // login to update local storage and subject
}

updatePassword(username: string, oldPassword: string, newPassword: string) {
return this.http
.patch<UServRes>(
`${this.apiUrl}/users/password/${this.userValue!.id}`,
{ oldPassword: oldPassword, newPassword: newPassword },
{ observe: 'response' },
)
.pipe(switchMap(() => this.login(username, newPassword))); // login to update local storage and subject
}

logout() {
// remove user from local storage to log user out
localStorage.removeItem('user');
Expand Down
87 changes: 87 additions & 0 deletions frontend/src/_services/form.utils.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { Injectable } from '@angular/core';
import { AbstractControl, FormGroup } from '@angular/forms';
import { PASSWORD_LOWERCASE } from '../app/account/_validators/lowercase-password';
import { PASSWORD_UPPERCASE } from '../app/account/_validators/uppercase-password';
import { PASSWORD_NUMERIC } from '../app/account/_validators/numeric-password';
import { PASSWORD_SPECIAL } from '../app/account/_validators/special-password';
import { PASSWORD_SHORT } from '../app/account/_validators/short-password';
import { PASSWORD_WEAK } from '../app/account/_validators/weak-password.validator';
import { PASSWORD_MISMATCH } from '../app/account/_validators/mismatch-password.validator';
import { USERNAME_INVALID } from '../app/account/_validators/invalid-username.validator';
import { PASSWORD_INVALID } from '../app/account/_validators/invalid-password.validator';

@Injectable({
providedIn: 'root',
})

// This service is used to validate the form fields in the register and profile components
export class FormUtilsService {
get isUsernameInvalid(): (form: FormGroup) => boolean {
return (form: FormGroup) => {
const usernameControl = form.controls['username'];
return usernameControl.dirty && usernameControl.hasError(USERNAME_INVALID);
};
}

get isEmailInvalid(): (form: FormGroup) => boolean {
return (form: FormGroup) => {
const emailControl = form.controls['email'];
return emailControl.dirty && emailControl.invalid;
};
}

get passwordControl(): (form: FormGroup) => AbstractControl {
return (form: FormGroup) => form.controls['password'];
}

get isPasswordControlDirty(): (form: FormGroup) => boolean {
return (form: FormGroup) => this.passwordControl(form).dirty;
}

get passwordHasNoLowercase(): (form: FormGroup) => boolean {
return (form: FormGroup) =>
this.passwordControl(form).pristine || this.passwordControl(form).hasError(PASSWORD_LOWERCASE);
}

get passwordHasNoUppercase(): (form: FormGroup) => boolean {
return (form: FormGroup) =>
this.passwordControl(form).pristine || this.passwordControl(form).hasError(PASSWORD_UPPERCASE);
}

get passwordHasNoNumeric(): (form: FormGroup) => boolean {
return (form: FormGroup) =>
this.passwordControl(form).pristine || this.passwordControl(form).hasError(PASSWORD_NUMERIC);
}

get passwordHasNoSpecial(): (form: FormGroup) => boolean {
return (form: FormGroup) =>
this.passwordControl(form).pristine || this.passwordControl(form).hasError(PASSWORD_SPECIAL);
}

get isPasswordShort(): (form: FormGroup) => boolean {
return (form: FormGroup) =>
this.passwordControl(form).pristine || this.passwordControl(form).hasError(PASSWORD_SHORT);
}

get isPasswordWeak(): (form: FormGroup) => boolean {
return (form: FormGroup) =>
this.passwordControl(form).dirty && this.passwordControl(form).hasError(PASSWORD_WEAK);
}

get isPasswordStrong(): (form: FormGroup) => boolean {
return (form: FormGroup) =>
this.passwordControl(form).dirty && !this.passwordControl(form).hasError(PASSWORD_WEAK);
}

get isPasswordInvalid(): (form: FormGroup) => boolean {
return (form: FormGroup) =>
this.passwordControl(form).dirty && this.passwordControl(form).hasError(PASSWORD_INVALID);
}

get hasPasswordMismatch(): (form: FormGroup) => boolean {
return (form: FormGroup) => {
const confirmPasswordControl = form.controls['confirmPassword'];
return this.passwordControl(form).valid && confirmPasswordControl.dirty && form.hasError(PASSWORD_MISMATCH);
};
}
}
36 changes: 36 additions & 0 deletions frontend/src/_services/history.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { historyResponse, MatchingHistory } from '../app/account/history/history.model';
import { ApiService } from './api.service';

@Injectable({
providedIn: 'root',
})
export class HistoryService extends ApiService {
protected apiPath = 'history/history';

constructor(private http: HttpClient) {
super();
}

getHistories(): Observable<MatchingHistory[]> {
return this.http.get<historyResponse>(`${this.apiUrl}`).pipe(
map(response =>
response.data.map(item => ({
id: item._id,
roomId: item.roomId,
collaborator: item.collaborator.username,
question: item.question,
topics: item.question.topics,
difficulty: item.question.difficulty,
status: item.status,
time: item.createdAt,
language: item.snapshot?.language,
code: item.snapshot?.code,
})),
),
);
}
}
8 changes: 6 additions & 2 deletions frontend/src/app/account/account.component.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { LoginComponent } from './login.component';
import { RegisterComponent } from './register.component';
import { LoginComponent } from './login/login.component';
import { RegisterComponent } from './register/register.component';
import { LayoutComponent } from './layout.component';
import { ProfileComponent } from './profile/profile.component';
import { HistoryComponent } from './history/history.component';

const routes: Routes = [
{
Expand All @@ -13,6 +15,8 @@ const routes: Routes = [
{ path: '', redirectTo: 'login', pathMatch: 'full' },
{ path: 'login', component: LoginComponent },
{ path: 'register', component: RegisterComponent },
{ path: 'profile', component: ProfileComponent },
{ path: 'history', component: HistoryComponent },
],
},
];
Expand Down
8 changes: 6 additions & 2 deletions frontend/src/app/account/account.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ import { NgModule } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';

import { LoginComponent } from './login.component';
import { RegisterComponent } from './register.component';
import { LoginComponent } from './login/login.component';
import { RegisterComponent } from './register/register.component';
import { LayoutComponent } from './layout.component';
import { AccountRoutingModule } from './account.component';
import { ProfileComponent } from './profile/profile.component';
import { HistoryComponent } from './history/history.component';

@NgModule({
imports: [
Expand All @@ -15,6 +17,8 @@ import { AccountRoutingModule } from './account.component';
LayoutComponent,
LoginComponent,
RegisterComponent,
ProfileComponent,
HistoryComponent,
],
})
export class AccountModule {}
Loading

0 comments on commit 33785df

Please sign in to comment.