Skip to content

Commit e9f4a4c

Browse files
committed
refactor: extract sendEvent into @repo/event-utils package and apply fixes
Why: - sendEvent was duplicated in webapp and async-job with identical SigV4 signing logic, making maintenance error-prone. - Several minor bugs and code quality issues accumulated across the codebase. - jest v30 has compatibility issues with ts-jest v29; downgrade to stable v29. What: - Extract sendEvent into packages/event-utils shared workspace package, removing duplicate from webapp/lib/events.ts and async-job/src/events.ts. - Update Dockerfiles, package.json, and next.config.ts to reference @repo/event-utils. - Make UserNotCreatedError extend Error with proper name/message. - Add onConflictDoNothing to user insert in auth-callback to handle race conditions. - Stabilize useEventBus callback with useRef to prevent reconnections. - Add env var validation in db client (DSQL_ENDPOINT, AWS_REGION). - Fix dsql-compat REFERENCES regex to handle ON DELETE/UPDATE clauses. - Add ROLLBACK safety net for TS migration error handling. - Add JSDoc for SQL migration statement splitting convention. - Add grantConnect JSDoc with TODO for custom DB role migration. - Convert Header.tsx from client to server component (remove unused useRouter). - Fix Dockerfile ENV to use ARG passthrough pattern for build-time vars. - Fix strict equality check in next.config.ts (== to ===). - Change console.log to console.error for parse errors in async-job. - Remove hardcoded region from TranslateClient; add userId check in translate job query. - Downgrade jest from v30 to v29 for ts-jest compatibility.
1 parent 50e5eb9 commit e9f4a4c

21 files changed

Lines changed: 815 additions & 852 deletions

File tree

apps/async-job/Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ WORKDIR /build
33
COPY package.json pnpm-workspace.yaml pnpm-lock.yaml ./
44
COPY packages/db/package.json packages/db/
55
COPY packages/shared-types/package.json packages/shared-types/
6+
COPY packages/event-utils/package.json packages/event-utils/
67
COPY apps/async-job/package.json apps/async-job/
78
RUN corepack enable && corepack prepare pnpm@10.8.1 --activate && pnpm install --frozen-lockfile
89
COPY packages/ packages/

apps/async-job/package.json

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,10 @@
1212
"check:ci": "pnpm run lint:ci && pnpm run format:ci"
1313
},
1414
"devDependencies": {
15-
"@aws-crypto/sha256-js": "^5",
1615
"@aws-sdk/client-translate": "^3.995.0",
17-
"@aws-sdk/credential-provider-node": "^3",
1816
"@repo/db": "workspace:*",
17+
"@repo/event-utils": "workspace:*",
1918
"@repo/shared-types": "workspace:*",
20-
"@smithy/protocol-http": "^5",
21-
"@smithy/signature-v4": "^5",
2219
"@types/aws-lambda": "^8.10.149",
2320
"@types/node": "^22",
2421
"drizzle-orm": "^0.44",

apps/async-job/src/handler.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import type { Handler } from 'aws-lambda';
55
export const handler: Handler<unknown> = async (event) => {
66
const { data: payload, error } = jobPayloadPropsSchema.safeParse(event);
77
if (error) {
8-
console.log(error);
8+
console.error(error);
99
throw new Error(error.toString());
1010
}
1111

apps/async-job/src/jobs/translate.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,20 @@
1-
import { sendEvent } from '../events';
1+
import { sendEvent } from '@repo/event-utils/send-event';
22
import { db } from '@repo/db/client';
33
import { todoItems } from '@repo/db/schema';
4-
import { eq } from 'drizzle-orm';
4+
import { eq, and } from 'drizzle-orm';
55
import type { TranslateJobPayload } from '@repo/shared-types/job-payload';
66
import { TranslateClient, TranslateTextCommand } from '@aws-sdk/client-translate';
77

88
export async function translateJobHandler(params: TranslateJobPayload) {
99
const todoItem = await db.query.todoItems.findFirst({
10-
where: eq(todoItems.id, params.todoItemId),
10+
where: and(eq(todoItems.id, params.todoItemId), eq(todoItems.userId, params.userId)),
1111
});
1212
if (!todoItem) {
1313
console.log(`item ${params.todoItemId} not found.`);
1414
return;
1515
}
1616

17-
const translateClient = new TranslateClient({
18-
region: process.env.AWS_REGION || 'ap-northeast-1',
19-
});
17+
const translateClient = new TranslateClient({});
2018

2119
const targetLanguage = 'ja';
2220
const translateResult = await translateClient.send(

apps/cdk/lib/constructs/database.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ export class Database extends Construct {
4141
});
4242
}
4343

44+
/**
45+
* Grant DML-only connect permission (dsql:DbConnect) for application workloads.
46+
* Requires a custom database role created via migration. See ADR-004.
47+
* TODO: Migrate webapp/async-job to use this method with a custom DB role.
48+
*/
4449
public grantConnect(grantee: IGrantable): Grant {
4550
return Grant.addToPrincipal({
4651
grantee,

apps/cdk/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
"@types/node": "^22.14.1",
3030
"aws-cdk": "^2.1007.0",
3131
"esbuild": "^0.25.4",
32-
"jest": "^30.1.3",
32+
"jest": "^29.7.0",
3333
"ts-jest": "^29.4.4",
3434
"ts-node": "^10.7.0",
3535
"typescript": "^5.9.2"

apps/webapp/Dockerfile

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ WORKDIR /build
33
COPY package.json pnpm-workspace.yaml pnpm-lock.yaml ./
44
COPY packages/db/package.json packages/db/
55
COPY packages/shared-types/package.json packages/shared-types/
6+
COPY packages/event-utils/package.json packages/event-utils/
67
COPY apps/webapp/package.json apps/webapp/
78
RUN corepack enable && corepack prepare pnpm@10.8.1 --activate && pnpm install --frozen-lockfile
89
COPY packages/ packages/
@@ -12,10 +13,14 @@ ARG SKIP_TS_BUILD=""
1213
ARG ALLOWED_ORIGIN_HOST=""
1314
ARG NEXT_PUBLIC_EVENT_HTTP_ENDPOINT=""
1415
ARG NEXT_PUBLIC_AWS_REGION=""
15-
ENV USER_POOL_CLIENT_ID="dummy"
16-
ENV USER_POOL_ID="dummy"
17-
ENV AMPLIFY_APP_ORIGIN="https://dummy.example.com"
18-
ENV COGNITO_DOMAIN="dummy.example.com"
16+
ARG USER_POOL_CLIENT_ID="dummy"
17+
ARG USER_POOL_ID="dummy"
18+
ARG AMPLIFY_APP_ORIGIN="https://dummy.example.com"
19+
ARG COGNITO_DOMAIN="dummy.example.com"
20+
ENV USER_POOL_CLIENT_ID=$USER_POOL_CLIENT_ID
21+
ENV USER_POOL_ID=$USER_POOL_ID
22+
ENV AMPLIFY_APP_ORIGIN=$AMPLIFY_APP_ORIGIN
23+
ENV COGNITO_DOMAIN=$COGNITO_DOMAIN
1924
RUN cd apps/webapp && pnpm exec next build
2025

2126
FROM public.ecr.aws/lambda/nodejs:22 AS runner

apps/webapp/next.config.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const nextConfig: NextConfig = {
99
output: 'standalone',
1010
// Required for pnpm workspace packages — Next.js standalone output does not
1111
// resolve symlinked workspace dependencies by default.
12-
transpilePackages: ['@repo/db', '@repo/shared-types'],
12+
transpilePackages: ['@repo/db', '@repo/shared-types', '@repo/event-utils'],
1313
experimental: {
1414
webpackBuildWorker: true,
1515
parallelServerBuildTraces: true,
@@ -19,7 +19,7 @@ const nextConfig: NextConfig = {
1919
},
2020
},
2121
typescript: {
22-
ignoreBuildErrors: process.env.SKIP_TS_BUILD == 'true',
22+
ignoreBuildErrors: process.env.SKIP_TS_BUILD === 'true',
2323
},
2424
};
2525

apps/webapp/package.json

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"@aws-sdk/client-lambda": "^3.995.0",
1919
"@aws-sdk/client-ssm": "^3.995.0",
2020
"@repo/db": "workspace:*",
21+
"@repo/event-utils": "workspace:*",
2122
"@repo/shared-types": "workspace:*",
2223
"aws-amplify": "^6.16.2",
2324
"class-variance-authority": "^0.7.1",
@@ -36,12 +37,8 @@
3637
"zod": "^3.24.2"
3738
},
3839
"devDependencies": {
39-
"@aws-crypto/sha256-js": "^5",
40-
"@aws-sdk/credential-provider-node": "^3",
4140
"@hookform/resolvers": "^5.2.2",
4241
"@next-safe-action/adapter-react-hook-form": "^1.0.14",
43-
"@smithy/protocol-http": "^5",
44-
"@smithy/signature-v4": "^5",
4542
"@tailwindcss/postcss": "^4",
4643
"@types/node": "^20",
4744
"@types/react": "^19",

apps/webapp/src/app/auth-callback/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export default async function AuthCallbackPage() {
1313
if (e instanceof UserNotCreatedError) {
1414
const userId = e.userId;
1515
console.log(userId);
16-
await db.insert(users).values({ id: userId });
16+
await db.insert(users).values({ id: userId }).onConflictDoNothing();
1717
} else {
1818
throw e;
1919
}

0 commit comments

Comments
 (0)