Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
26.8.1
26.12.1
43 changes: 28 additions & 15 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,31 +1,44 @@
FROM node:26.8.1-bookworm-slim AS base
RUN apt-get update && apt-get install -y --no-install-recommends openssl ca-certificates && rm -rf /var/lib/apt/lists/*
RUN npm install --global pnpm@11.21.0
ARG NODE_VERSION=26.12.1
FROM node:${NODE_VERSION}-alpine AS dependencies
RUN apk add --no-cache openssl ca-certificates \
&& npm install --global pnpm@11.21.0
WORKDIR /app
COPY deploy/certs/us-east-1-bundle.pem /app/deploy/certs/us-east-1-bundle.pem
ENV NODE_EXTRA_CA_CERTS=/app/deploy/certs/us-east-1-bundle.pem

FROM base AS dependencies
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
COPY tools ./tools
RUN pnpm install --frozen-lockfile

FROM dependencies AS build
COPY . .
RUN pnpm build

# Separate one-shot migration image: runtime does not ship the Prisma CLI or CMS dev dependencies.
FROM build AS migrate
CMD ["pnpm", "migrate:deploy"]
RUN pnpm lint && pnpm build && pnpm typecheck && pnpm test && pnpm test:dependency-compat

FROM dependencies AS production-dependencies
RUN pnpm prune --prod

FROM base AS runtime
FROM alpine:3.24 AS runtime-base
ARG NODE_VERSION
ENV NODE_ENV=production
COPY --from=production-dependencies --chown=node:node /app/node_modules ./node_modules
COPY --from=build --chown=node:node /app/dist/src ./dist/src
COPY --chown=node:node package.json ./
USER node
WORKDIR /app
RUN apk upgrade --no-cache \
&& apk add --no-cache openssl ca-certificates "nodejs-current~${NODE_VERSION%%.*}" \
&& addgroup -S -g 10001 app \
&& adduser -S -D -u 10001 -G app app
COPY deploy/certs/us-east-1-bundle.pem /app/deploy/certs/us-east-1-bundle.pem
ENV NODE_EXTRA_CA_CERTS=/app/deploy/certs/us-east-1-bundle.pem
USER app

# One-shot migration image; invoke the installed CLI without shipping npm/pnpm.
FROM runtime-base AS migrate
COPY --from=build --chown=app:app /app/node_modules ./node_modules
COPY --chown=app:app package.json prisma.config.ts ./
COPY --chown=app:app prisma ./prisma
CMD ["node", "node_modules/prisma/build/index.js", "migrate", "deploy"]

FROM runtime-base AS runtime
COPY --from=production-dependencies --chown=app:app /app/node_modules ./node_modules
COPY --from=build --chown=app:app /app/dist/src ./dist/src
COPY --chown=app:app package.json ./
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s CMD ["node", "-e", "fetch('http://127.0.0.1:'+(process.env.PORT||3000)+'/v6/health/ready').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"]
CMD ["node", "dist/src/main.js"]
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ All tables and views live in the `forms` schema; dev uses the shared `topcoder-s

## Stack

- Node **26.8.1**, pinned in `.nvmrc` and Docker; current release verified against the [Node release listing](https://nodejs.org/en/download/current) on September 9, 2026.
- Node **26.12.1**, pinned in `.nvmrc` and the Docker build.
- NestJS **11.1.28**, matching the other v6 services and the supported peer range of Nest Throttler.
- TypeScript **5.9.3**, strict checking and decorator metadata.
- Prisma **7.10.0**, generated TypeScript client and `@prisma/adapter-pg`.
Expand Down Expand Up @@ -129,3 +129,9 @@ not retry POSTs automatically. Application retry/outbox behavior remains local.

See the [core migration guide](https://github.com/topcoder-platform/tc-core-library-js/blob/v4.0.0-rc.1/MIGRATION.md)
for the API mapping, compatibility requirements, and rollout scope.

## Security maintenance

The runtime and one-shot migration images use Alpine 3.24's patched Node 26 package and run as UID 10001. The runtime contains only production dependencies and compiled code. Migration tasks invoke the installed Prisma CLI directly with Node; npm and pnpm are not shipped in either final image. The RDS CA bundle and `NODE_EXTRA_CA_CERTS` remain configured in both images.

The lockfile pins patched dependencies and an immutable shared core revision. `pnpm test:dependency-compat` verifies the legacy matcher adapter. Integration fixtures use real RS256 machine tokens with an isolated JWKS endpoint, so the shared verifier's issuer, signature, audience, and machine-identity restrictions remain enabled.
2 changes: 1 addition & 1 deletion deploy/release.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ def main():
container.pop('healthCheck', None)
container['portMappings'] = []
container['secrets'] = [{'name': 'DATABASE_URL', 'valueFrom': settings['ParameterPrefix'] + '/MIGRATION_DATABASE_URL'}]
container['command'] = ['/bin/sh', '-c', 'pnpm migrate:deploy']
container['command'] = ['node', 'node_modules/prisma/build/index.js', 'migrate', 'deploy']
migration_definition = ecs.register_task_definition(**migration)['taskDefinition']['taskDefinitionArn']
result = ecs.run_task(cluster=settings['ClusterName'], taskDefinition=migration_definition,
launchType='FARGATE', networkConfiguration=service['networkConfiguration'],
Expand Down
1 change: 1 addition & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ export default tseslint.config(
},
js.configs.recommended,
...tseslint.configs.recommended,
{ files: ['tools/**/*.cjs'], languageOptions: { sourceType: 'commonjs' }, rules: { '@typescript-eslint/no-require-imports': 'off' } },
{ languageOptions: { globals: { ...globals.node, ...globals.browser } } },
);
27 changes: 14 additions & 13 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,17 @@
"migrate:deploy": "prisma migrate deploy",
"migrate:dev": "prisma migrate dev",
"test": "vitest run",
"test:integration": "vitest run --config vitest.integration.config.mts"
"test:integration": "vitest run --config vitest.integration.config.mts",
"test:dependency-compat": "node --test tools/minimatch-compat/index.test.cjs"
},
"dependencies": {
"@nestjs/common": "11.1.28",
"@nestjs/core": "11.1.28",
"@nestjs/platform-express": "11.1.28",
"@nestjs/common": "11.2.8",
"@nestjs/core": "11.2.8",
"@nestjs/platform-express": "11.2.8",
"@nestjs/swagger": "11.4.6",
"@nestjs/throttler": "6.5.0",
"@prisma/adapter-pg": "7.10.0",
"@prisma/client": "7.10.0",
"@prisma/adapter-pg": "7.10.1",
"@prisma/client": "7.10.1",
"class-transformer": "0.5.1",
"class-validator": "0.14.4",
"dotenv": "17.2.3",
Expand All @@ -42,11 +43,11 @@
"pg": "8.23.0",
"reflect-metadata": "0.2.2",
"rxjs": "7.8.2",
"tc-core-library-js": "github:topcoder-platform/tc-core-library-js#v4.0.0-rc.1"
"tc-core-library-js": "https://codeload.github.com/topcoder-platform/tc-core-library-js/tar.gz/a25576b8b038211d1ba910eca2d900271ba6659d"
},
"devDependencies": {
"@eslint/js": "9.39.2",
"@nestjs/testing": "11.1.28",
"@nestjs/testing": "11.2.8",
"@swc/core": "1.15.11",
"@types/express": "5.0.6",
"@types/jsonwebtoken": "9.0.10",
Expand All @@ -57,19 +58,19 @@
"@types/supertest": "6.0.3",
"eslint": "9.39.2",
"globals": "16.5.0",
"jsdom": "27.4.0",
"jsdom": "30.1.3",
"nock": "14.0.16",
"payload": "3.87.1",
"payload": "3.92.1",
"prettier": "3.8.1",
"prisma": "7.10.0",
"prisma": "7.10.1",
"react": "19.2.8",
"react-dom": "19.2.8",
"supertest": "7.2.2",
"ts-node": "10.9.2",
"tsx": "4.22.4",
"typescript": "5.9.3",
"typescript-eslint": "8.54.0",
"typescript-eslint": "8.72.1",
"unplugin-swc": "1.5.9",
"vitest": "4.0.18"
"vitest": "4.1.12"
}
}
Loading
Loading