From 7040f2e86f8a4ac7de130951a4ded81d663ccdda Mon Sep 17 00:00:00 2001 From: Ibrahim Suleiman Date: Sat, 23 May 2026 16:41:53 +0100 Subject: [PATCH 1/3] chore: update GitHub Actions workflow to build and push separate Docker images for API and Web services --- .github/workflows/deploy.yml | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index c91c014..2b9fd90 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -21,15 +21,26 @@ jobs: - name: Login to GHCR run: echo ${{ secrets.GHCR_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin - # 3. Build image - - name: Build Docker image + # 3. Build images + - name: Build API image run: | - docker build -t ghcr.io/${{ github.repository }}:${{ github.sha }} . + docker build \ + -f docker/api.Dockerfile \ + -t ghcr.io/${{ github.repository }}-api:${{ github.sha }} \ + . - # 4. Push image - - name: Push image + - name: Build Web image run: | - docker push ghcr.io/${{ github.repository }}:${{ github.sha }} + docker build \ + -f docker/web.Dockerfile \ + -t ghcr.io/${{ github.repository }}-web:${{ github.sha }} \ + . + + # 4. Push images + - name: Push images + run: | + docker push ghcr.io/${{ github.repository }}-api:${{ github.sha }} + docker push ghcr.io/${{ github.repository }}-web:${{ github.sha }} # 5. Deploy on VPS - name: Deploy via SSH @@ -39,8 +50,9 @@ jobs: username: ${{ secrets.VPS_USER }} key: ${{ secrets.VPS_SSH_KEY }} script: | - IMAGE=ghcr.io/${{ github.repository }}:${{ github.sha }} + API_IMAGE=ghcr.io/${{ github.repository }}-api:${{ github.sha }} + WEB_IMAGE=ghcr.io/${{ github.repository }}-web:${{ github.sha }} echo ${{ secrets.GHCR_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin - cd ~/deployed_apps/future_self - docker compose pull $IMAGE - IMAGE=$IMAGE docker compose up -d --no-deps --force-recreate \ No newline at end of file + cd ~/deployed_apps/futureself-mono + docker compose pull + API_IMAGE=$API_IMAGE WEB_IMAGE=$WEB_IMAGE docker compose up -d --no-deps --force-recreate \ No newline at end of file From b8877313bd6ce80578e2abf4d38e08b07af5be96 Mon Sep 17 00:00:00 2001 From: Ibrahim Suleiman Date: Sat, 23 May 2026 16:52:49 +0100 Subject: [PATCH 2/3] refac: improve code formatting and consistency across multiple files; update exports and imports for better readability --- .github/workflows/build.yml | 40 +++++++++++++++++++ apps/api/src/auth/auth.module.ts | 2 +- apps/api/src/auth/dto/reset-password.dto.ts | 9 ++++- apps/api/src/commons/interfaces/env.ts | 6 +-- apps/api/src/email/email.service.ts | 12 ++++-- apps/api/src/redis/redis.module.ts | 2 +- apps/api/src/redis/redis.service.ts | 2 +- apps/api/src/user/dto/create-user.dto.ts | 8 +++- .../utils/filters/global-exception.filter.ts | 2 +- apps/api/src/utils/string.utils.ts | 6 +-- apps/api/src/utils/time.util.spec.ts | 9 ++--- .../is-future-time-string.validator.ts | 2 +- 12 files changed, 76 insertions(+), 24 deletions(-) create mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..75338d7 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,40 @@ +name: Build Check + +on: + pull_request: + branches: + - main + types: + - opened + - synchronize + - reopened + +jobs: + build: + runs-on: ubuntu-latest + + steps: + # 1. Checkout code + - name: Checkout + uses: actions/checkout@v4 + + # 2. Setup pnpm + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 11 + + # 3. Setup Node + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: 'pnpm' + + # 4. Install dependencies + - name: Install dependencies + run: pnpm install --frozen-lockfile + + # 5. Build + - name: Build + run: pnpm build \ No newline at end of file diff --git a/apps/api/src/auth/auth.module.ts b/apps/api/src/auth/auth.module.ts index dbba0f3..51a6cc1 100644 --- a/apps/api/src/auth/auth.module.ts +++ b/apps/api/src/auth/auth.module.ts @@ -13,6 +13,6 @@ import { HttpRequestsUtil } from 'src/utils/http.util'; imports: [TypeOrmModule.forFeature([AuthSession, User]), HttpModule], controllers: [AuthController], providers: [AuthService, AuthSessionService, HttpRequestsUtil, EmailService], - exports: [AuthSessionService] + exports: [AuthSessionService], }) export class AuthModule {} diff --git a/apps/api/src/auth/dto/reset-password.dto.ts b/apps/api/src/auth/dto/reset-password.dto.ts index 9ac5611..7daaa1f 100644 --- a/apps/api/src/auth/dto/reset-password.dto.ts +++ b/apps/api/src/auth/dto/reset-password.dto.ts @@ -1,4 +1,11 @@ -import { IsEmail, IsNotEmpty, isString, IsString, MaxLength, MinLength } from 'class-validator'; +import { + IsEmail, + IsNotEmpty, + isString, + IsString, + MaxLength, + MinLength, +} from 'class-validator'; export class ResetPasswordDto { @IsNotEmpty() diff --git a/apps/api/src/commons/interfaces/env.ts b/apps/api/src/commons/interfaces/env.ts index f1807ac..8d9e14b 100644 --- a/apps/api/src/commons/interfaces/env.ts +++ b/apps/api/src/commons/interfaces/env.ts @@ -1,10 +1,6 @@ interface IENV { // App - NODE_ENVIRONMENT: - | 'local' - | 'docker' - | 'staging' - | 'production'; + NODE_ENVIRONMENT: 'local' | 'docker' | 'staging' | 'production'; APP_PORT: number; APP_KEY: string; ENABLE_RATE_LIMITING: string; diff --git a/apps/api/src/email/email.service.ts b/apps/api/src/email/email.service.ts index 7eb96c7..5e9e32f 100644 --- a/apps/api/src/email/email.service.ts +++ b/apps/api/src/email/email.service.ts @@ -22,13 +22,17 @@ export class EmailService { }, ); - constructor( - private readonly httpRequests: HttpRequestsUtil - ) {} + constructor(private readonly httpRequests: HttpRequestsUtil) {} async send(data: ISendEmail) { try { - const { recipientEmail: email, emailData, emailSubject: subject, emailTemplate: template, recipientName: name } = data; + const { + recipientEmail: email, + emailData, + emailSubject: subject, + emailTemplate: template, + recipientName: name, + } = data; const payload = { sender: { diff --git a/apps/api/src/redis/redis.module.ts b/apps/api/src/redis/redis.module.ts index 2653cda..b9cfabf 100644 --- a/apps/api/src/redis/redis.module.ts +++ b/apps/api/src/redis/redis.module.ts @@ -6,4 +6,4 @@ import { RedisService } from './redis.service'; providers: [RedisService], exports: [RedisService], }) -export class RedisModule {} \ No newline at end of file +export class RedisModule {} diff --git a/apps/api/src/redis/redis.service.ts b/apps/api/src/redis/redis.service.ts index b78e8cf..dd3313b 100644 --- a/apps/api/src/redis/redis.service.ts +++ b/apps/api/src/redis/redis.service.ts @@ -15,7 +15,7 @@ export class RedisService { const redisPassword = this.configService.get('REDIS_PASSWORD'); const redisHost = this.configService.get('REDIS_HOST'); const redisPort = this.configService.get('REDIS_PORT'); - + if (redisUser && redisPassword) { this.redisUrl = `redis://${redisUser}:${redisPassword}@${redisHost}:${redisPort}`; } else { diff --git a/apps/api/src/user/dto/create-user.dto.ts b/apps/api/src/user/dto/create-user.dto.ts index 5d8b73b..9bb6843 100644 --- a/apps/api/src/user/dto/create-user.dto.ts +++ b/apps/api/src/user/dto/create-user.dto.ts @@ -1,4 +1,10 @@ -import { IsNotEmpty, IsOptional, IsString, MaxLength, MinLength } from 'class-validator'; +import { + IsNotEmpty, + IsOptional, + IsString, + MaxLength, + MinLength, +} from 'class-validator'; export class CreateUserDto { @IsString() diff --git a/apps/api/src/utils/filters/global-exception.filter.ts b/apps/api/src/utils/filters/global-exception.filter.ts index 00fb375..9b387be 100644 --- a/apps/api/src/utils/filters/global-exception.filter.ts +++ b/apps/api/src/utils/filters/global-exception.filter.ts @@ -21,7 +21,7 @@ export default class GlobalExceptionsHandler implements ExceptionFilter { return response.status(status).json({ message: message.toString(), - error: exception?.response?.error + error: exception?.response?.error, }); } } diff --git a/apps/api/src/utils/string.utils.ts b/apps/api/src/utils/string.utils.ts index 137fba5..b4422f0 100644 --- a/apps/api/src/utils/string.utils.ts +++ b/apps/api/src/utils/string.utils.ts @@ -11,6 +11,6 @@ export async function compareStringAndHash(string: string, hash: string) { return result; } -export function generateUUID(): string { - return crypto.randomUUID() -} \ No newline at end of file +export function generateUUID(): string { + return crypto.randomUUID(); +} diff --git a/apps/api/src/utils/time.util.spec.ts b/apps/api/src/utils/time.util.spec.ts index e27c528..f241545 100644 --- a/apps/api/src/utils/time.util.spec.ts +++ b/apps/api/src/utils/time.util.spec.ts @@ -1,7 +1,4 @@ -import { - getFutureTimeWithoutLeap, - parseToTimestamp, -} from './time.util'; +import { getFutureTimeWithoutLeap, parseToTimestamp } from './time.util'; describe('parseToTimestamp', () => { afterEach(() => { @@ -59,7 +56,9 @@ describe('getFutureTimeWithoutLeap', () => { const calendar = parseToTimestamp('1y'); expect(withoutLeap).toBe(Date.now() + 365 * 24 * 60 * 60 * 1000); - expect(new Date(withoutLeap).toISOString()).toBe('2024-02-29T12:00:00.000Z'); + expect(new Date(withoutLeap).toISOString()).toBe( + '2024-02-29T12:00:00.000Z', + ); expect(calendar - withoutLeap).toBe(24 * 60 * 60 * 1000); }); }); diff --git a/apps/api/src/utils/validators/is-future-time-string.validator.ts b/apps/api/src/utils/validators/is-future-time-string.validator.ts index 4448b89..3870d92 100644 --- a/apps/api/src/utils/validators/is-future-time-string.validator.ts +++ b/apps/api/src/utils/validators/is-future-time-string.validator.ts @@ -6,7 +6,7 @@ import { import * as dayjs from 'dayjs'; export function IsFutureTimeString(validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { + return function (object: object, propertyName: string) { registerDecorator({ name: 'isFutureTimeString', target: object.constructor, From f57b9eac613cff77a59399a6489f4d7b902eeeba Mon Sep 17 00:00:00 2001 From: Ibrahim Suleiman Date: Sat, 23 May 2026 16:54:53 +0100 Subject: [PATCH 3/3] chore: remove specific pnpm version setup in GitHub Actions workflow to use default version --- .github/workflows/build.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 75338d7..b297e9c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -21,8 +21,6 @@ jobs: # 2. Setup pnpm - name: Setup pnpm uses: pnpm/action-setup@v4 - with: - version: 11 # 3. Setup Node - name: Setup Node