From 84a11f7efcd1096cac6231fa3405fa01cb08e8ef Mon Sep 17 00:00:00 2001 From: barsa Date: Thu, 25 Dec 2025 18:34:45 +0900 Subject: [PATCH] Enhance GitHub Workflows and Refactor BFF Code - Added support for push and workflow_dispatch events in pr-checks.yml to improve CI flexibility. - Implemented concurrency control in workflows to manage job execution more effectively. - Updated pnpm setup to include caching and specified version for consistency. - Removed redundant logging code from BFF application to streamline signal handling and improve readability. - Introduced CSRF_SECRET_KEY validation in environment configuration for production hardening. - Refactored logo component to use a fallback mechanism for image loading, enhancing user experience. - Added linting scripts to package.json for improved code quality checks. --- .github/workflows/pr-checks.yml | 45 ++++++------ .github/workflows/security.yml | 12 +-- apps/bff/src/app/bootstrap.ts | 15 ---- apps/bff/src/core/config/env.validation.ts | 7 ++ .../infra/cache/distributed-lock.service.ts | 3 +- apps/bff/src/infra/database/prisma.service.ts | 73 ------------------- apps/bff/src/main.ts | 45 ------------ .../src/modules/support/support.controller.ts | 10 ++- apps/portal/next-env.d.ts | 2 +- apps/portal/src/components/atoms/logo.tsx | 65 +++++++---------- packages/domain/package.json | 3 + 11 files changed, 74 insertions(+), 206 deletions(-) diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml index f83adcc9..7bf8a6d0 100644 --- a/.github/workflows/pr-checks.yml +++ b/.github/workflows/pr-checks.yml @@ -2,9 +2,18 @@ name: Pull Request Checks on: pull_request: + push: branches: - main - master + workflow_dispatch: + +concurrency: + group: pr-checks-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read jobs: quality-checks: @@ -15,42 +24,34 @@ jobs: - name: Checkout code uses: actions/checkout@v4 + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 10.25.0 + - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: "22" - - - name: Setup pnpm - uses: pnpm/action-setup@v4 - - - name: Get pnpm store directory - id: pnpm-cache - shell: bash - run: | - echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT - - - name: Setup pnpm cache - uses: actions/cache@v4 - with: - path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store- + cache: "pnpm" - name: Install dependencies run: pnpm install --frozen-lockfile + - name: Check formatting + run: pnpm format:check + - name: Run linter run: pnpm lint - name: Run type check run: pnpm type-check - - name: Run security audit - run: pnpm security:check - - name: Run tests run: pnpm test - - name: Check formatting - run: pnpm format:check + - name: Build + run: pnpm build + + - name: Verify domain dist is up-to-date + run: pnpm domain:check-dist diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index eaadb1bc..8bf88f33 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -97,11 +97,6 @@ jobs: contents: read security-events: write - strategy: - fail-fast: false - matrix: - language: ["javascript", "typescript"] - steps: - name: Checkout code uses: actions/checkout@v4 @@ -109,16 +104,13 @@ jobs: - name: Initialize CodeQL uses: github/codeql-action/init@v3 with: - languages: ${{ matrix.language }} + languages: javascript-typescript queries: security-and-quality - - name: Autobuild - uses: github/codeql-action/autobuild@v3 - - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v3 with: - category: "/language:${{matrix.language}}" + category: "/language:javascript-typescript" outdated-dependencies: name: Check Outdated Dependencies diff --git a/apps/bff/src/app/bootstrap.ts b/apps/bff/src/app/bootstrap.ts index 34572171..2254f72b 100644 --- a/apps/bff/src/app/bootstrap.ts +++ b/apps/bff/src/app/bootstrap.ts @@ -120,21 +120,6 @@ export async function bootstrap(): Promise { // Rely on Nest's built-in shutdown hooks. External orchestrator will send signals. app.enableShutdownHooks(); - // #region agent log (hypothesis S1) - fetch("http://127.0.0.1:7242/ingest/a683e422-cfe7-4556-a583-809fbfbeeb4a", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - sessionId: "debug-session", - runId: "run1", - hypothesisId: "S1", - location: "apps/bff/src/app/bootstrap.ts:enableShutdownHooks", - message: "Nest shutdown hooks enabled", - data: { pid: process.pid }, - timestamp: Date.now(), - }), - }).catch(() => {}); - // #endregion // API routing prefix is applied via RouterModule in AppModule for clarity and modern routing. diff --git a/apps/bff/src/core/config/env.validation.ts b/apps/bff/src/core/config/env.validation.ts index f2f6bc68..e129505d 100644 --- a/apps/bff/src/core/config/env.validation.ts +++ b/apps/bff/src/core/config/env.validation.ts @@ -243,5 +243,12 @@ export function validate(config: Record): Record {}); - // #endregion } async onModuleInit() { await this.$connect(); this.logger.log("Database connection established"); - - // #region agent log (hypothesis S3) - fetch("http://127.0.0.1:7242/ingest/a683e422-cfe7-4556-a583-809fbfbeeb4a", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - sessionId: "debug-session", - runId: "run1", - hypothesisId: "S3", - location: "apps/bff/src/infra/database/prisma.service.ts:onModuleInit", - message: "PrismaService connected", - data: { instanceTag: this.instanceTag }, - timestamp: Date.now(), - }), - }).catch(() => {}); - // #endregion } async onModuleDestroy() { this.destroyCalls += 1; - // #region agent log (hypothesis S1) - fetch("http://127.0.0.1:7242/ingest/a683e422-cfe7-4556-a583-809fbfbeeb4a", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - sessionId: "debug-session", - runId: "run1", - hypothesisId: "S1", - location: "apps/bff/src/infra/database/prisma.service.ts:onModuleDestroy(entry)", - message: "PrismaService destroy called", - data: { - instanceTag: this.instanceTag, - destroyCalls: this.destroyCalls, - poolEnded: this.poolEnded, - pid: process.pid, - }, - timestamp: Date.now(), - }), - }).catch(() => {}); - // #endregion - // Make shutdown idempotent: Nest can call destroy hooks more than once during restarts. if (this.poolEnded) { this.logger.warn("Database pool already closed; skipping duplicate shutdown"); @@ -117,25 +64,5 @@ export class PrismaService extends PrismaClient implements OnModuleInit, OnModul await this.$disconnect(); await this.pool.end(); this.logger.log("Database connection closed"); - - // #region agent log (hypothesis S1) - fetch("http://127.0.0.1:7242/ingest/a683e422-cfe7-4556-a583-809fbfbeeb4a", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - sessionId: "debug-session", - runId: "run1", - hypothesisId: "S1", - location: "apps/bff/src/infra/database/prisma.service.ts:onModuleDestroy(exit)", - message: "PrismaService destroy completed", - data: { - instanceTag: this.instanceTag, - destroyCalls: this.destroyCalls, - poolEnded: this.poolEnded, - }, - timestamp: Date.now(), - }), - }).catch(() => {}); - // #endregion } } diff --git a/apps/bff/src/main.ts b/apps/bff/src/main.ts index feeb50b0..d0d58a55 100644 --- a/apps/bff/src/main.ts +++ b/apps/bff/src/main.ts @@ -11,21 +11,6 @@ for (const signal of signals) { process.once(signal, () => { void (async () => { logger.log(`Received ${signal}. Closing Nest application...`); - // #region agent log (hypothesis S1) - fetch("http://127.0.0.1:7242/ingest/a683e422-cfe7-4556-a583-809fbfbeeb4a", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - sessionId: "debug-session", - runId: "run1", - hypothesisId: "S1", - location: "apps/bff/src/main.ts:signalHandler(entry)", - message: "Process signal handler invoked", - data: { signal, pid: process.pid, appInitialized: !!app }, - timestamp: Date.now(), - }), - }).catch(() => {}); - // #endregion if (!app) { logger.warn("Nest application not initialized. Exiting immediately."); @@ -34,21 +19,6 @@ for (const signal of signals) { } try { - // #region agent log (hypothesis S1) - fetch("http://127.0.0.1:7242/ingest/a683e422-cfe7-4556-a583-809fbfbeeb4a", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - sessionId: "debug-session", - runId: "run1", - hypothesisId: "S1", - location: "apps/bff/src/main.ts:signalHandler(beforeClose)", - message: "Calling app.close()", - data: { signal, pid: process.pid }, - timestamp: Date.now(), - }), - }).catch(() => {}); - // #endregion await app.close(); logger.log("Nest application closed gracefully."); } catch (error) { @@ -58,21 +28,6 @@ for (const signal of signals) { resolvedError.stack ); } finally { - // #region agent log (hypothesis S1) - fetch("http://127.0.0.1:7242/ingest/a683e422-cfe7-4556-a583-809fbfbeeb4a", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - sessionId: "debug-session", - runId: "run1", - hypothesisId: "S1", - location: "apps/bff/src/main.ts:signalHandler(exit)", - message: "Exiting process after shutdown handler", - data: { signal, pid: process.pid }, - timestamp: Date.now(), - }), - }).catch(() => {}); - // #endregion process.exit(0); } })(); diff --git a/apps/bff/src/modules/support/support.controller.ts b/apps/bff/src/modules/support/support.controller.ts index 1db80d73..c3c62a37 100644 --- a/apps/bff/src/modules/support/support.controller.ts +++ b/apps/bff/src/modules/support/support.controller.ts @@ -15,6 +15,7 @@ import { ZodValidationPipe } from "nestjs-zod"; import { Public } from "@bff/modules/auth/decorators/public.decorator.js"; import { RateLimit, RateLimitGuard } from "@bff/core/rate-limiting/index.js"; import { z } from "zod"; +import { createHash } from "node:crypto"; import { supportCaseFilterSchema, createCaseRequestSchema, @@ -37,6 +38,11 @@ const publicContactSchema = z.object({ type PublicContactRequest = z.infer; +const hashEmailForLogs = (email: string): string => { + const normalized = email.trim().toLowerCase(); + return createHash("sha256").update(normalized).digest("hex").slice(0, 12); +}; + @Controller("support") export class SupportController { constructor( @@ -84,7 +90,7 @@ export class SupportController { @Body(new ZodValidationPipe(publicContactSchema)) body: PublicContactRequest ): Promise<{ success: boolean; message: string }> { - this.logger.log("Public contact form submission", { email: body.email }); + this.logger.log("Public contact form submission", { emailHash: hashEmailForLogs(body.email) }); try { await this.supportService.createPublicContactRequest(body); @@ -96,7 +102,7 @@ export class SupportController { } catch (error) { this.logger.error("Failed to process public contact form", { error: error instanceof Error ? error.message : String(error), - email: body.email, + emailHash: hashEmailForLogs(body.email), }); throw error; } diff --git a/apps/portal/next-env.d.ts b/apps/portal/next-env.d.ts index c4b7818f..9edff1c7 100644 --- a/apps/portal/next-env.d.ts +++ b/apps/portal/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -import "./.next/dev/types/routes.d.ts"; +import "./.next/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/apps/portal/src/components/atoms/logo.tsx b/apps/portal/src/components/atoms/logo.tsx index 02c73e36..44110347 100644 --- a/apps/portal/src/components/atoms/logo.tsx +++ b/apps/portal/src/components/atoms/logo.tsx @@ -9,6 +9,33 @@ interface LogoProps { } export function Logo({ className = "", size = 32 }: LogoProps) { + const [fallback, setFallback] = React.useState(false); + + if (fallback) { + return ( +
+ + {/* Top section - Light blue curved arrows */} + + + + {/* Bottom section - Dark blue curved arrows */} + + + +
+ ); + } + return (
{ - // Fallback to SVG if image fails to load - const target = e.target as HTMLImageElement; - target.style.display = "none"; - const parent = target.parentElement; - if (parent) { - parent.innerHTML = ` - - - - - - - - - - `; - } - }} + onError={() => setFallback(true)} />
); diff --git a/packages/domain/package.json b/packages/domain/package.json index 5610854e..b4ca43eb 100644 --- a/packages/domain/package.json +++ b/packages/domain/package.json @@ -169,6 +169,9 @@ "build": "tsc", "dev": "tsc -w --preserveWatchOutput", "clean": "rm -rf dist", + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "test": "echo 'No tests yet'", "type-check": "NODE_OPTIONS=\"--max-old-space-size=2048 --max-semi-space-size=128\" tsc --project tsconfig.json --noEmit", "typecheck": "pnpm run type-check" },