Skip to content

add: docker-compose file#9

Merged
naims6 merged 5 commits into
mainfrom
development
May 28, 2026
Merged

add: docker-compose file#9
naims6 merged 5 commits into
mainfrom
development

Conversation

@naims6
Copy link
Copy Markdown
Owner

@naims6 naims6 commented May 25, 2026

Summary by CodeRabbit

  • Chores

    • Reduced Docker build context by excluding common local artifacts
    • Added container build/run configuration and Dockerfile for the app
    • Added local development stack for PostgreSQL, pgAdmin, Redis, and Redis Insight with persistent volumes
    • Moved DB migration out of default build and updated TypeScript/ESM module settings and imports
  • CI/CD

    • Added GitHub Actions workflow to build and publish the Docker image on pushes to main
  • Database

    • Enforced uniqueness so each student can have at most one admission record

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 25, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Migrates internal imports to explicit ESM .js specifiers, updates TypeScript module settings to nodenext, adds Dockerfile/docker-compose and ignore rules for local development, and includes a Prisma migration adding a unique index on Admission.studentId.

Changes

ESM import updates & TS config

Layer / File(s) Summary
TypeScript config
tsconfig.json
Set compilerOptions.module and moduleResolution to nodenext; reformat include array.
App bootstrap, server, and router wiring
src/app.ts, src/server.ts, src/app/routes/routes.ts
Switch relative imports to explicit .js specifiers for app/server/router entry points; adjust server start log typing.
Prisma client and shared types imports
src/app/lib/prisma.ts, src/app/middlewares/globalError.ts, src/utils/pagination.ts
Update config and types imports to reference .js barrel/module entries.
Middleware ESM updates
src/app/middlewares/*
Convert local middleware imports (authorize, isAuthenticated, validateRequest, globalError) to .js specifiers and add express types where shown.
Admission module import updates
src/app/modules/admission/*
Change admission controller/service/repository/routes/interface imports to .js paths, remove an unused Prisma enum import, and annotate Prisma transaction callback types.
Auth module import updates
src/app/modules/auth/*
Convert auth controller/helper/service/routes imports to .js specifiers and remove an unused type import.
Class and student module updates
src/app/modules/class/*, src/app/modules/student/student.validation.ts
Update class module imports and student validation helper imports to .js-suffixed paths.
Utility imports
src/utils/*
Switch sendEmail, sendVerificationEmail, and pagination shared-type imports to explicit .js paths.

Local Development Docker Stack

Layer / File(s) Summary
Docker build context and Compose stack
Dockerfile, docker-compose.yml, .dockerignore, .gitignore, package.json
Add Dockerfile that builds the app with pnpm and runs dist/server.js. Add docker-compose with PostgreSQL, pgAdmin4, Redis, and Redis Insight services and named volumes. Exclude node_modules and dist from Docker/Git contexts; update package.json scripts (move prisma migrate to prisma:deploy).
CI workflow
.github/workflows/deploy.yml
Add GitHub Actions workflow to build and push Docker image to Docker Hub on push to main using repository secrets.

Prisma migration

Layer / File(s) Summary
Admission unique index migration
prisma/migrations/20260526164546_init/migration.sql
Adds unique index Admission_studentId_key on Admission(studentId).

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs:

  • naims6/dcms-backend#8: Both PRs enforce the “one admission per student” constraint by adding uniqueness on Admission.studentId.
  • naims6/dcms-backend#7: Overlaps with admission transaction logic and notifications (OTP/email) changes touching the same admission flow.

"A rabbit hops through code and tree,
I add .js and build with glee,
Compose stacks hum, databases sing,
One-student rule — a migration ring,
Containers ready — let devs be free!"

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title references only the docker-compose file addition, but the PR includes extensive changes to ESM module imports, TypeScript configuration, Dockerfile setup, GitHub Actions deployment, and database migrations. Revise the title to better reflect the scope: something like 'Configure Docker setup and migrate to ESM module imports' would be more accurate and helpful for future reference.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch development

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
docker-compose.yml (1)

43-43: ⚡ Quick win

Pin redis/redisinsight to a fixed tag (avoid :latest).
docker-compose.yml currently uses redis/redisinsight:latest; pin to an immutable, published version tag such as redis/redisinsight:3.4.2 for reproducibility. For maximum immutability, also pin by digest (redis/redisinsight@sha256:...).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docker-compose.yml` at line 43, Replace the floating image tag
"redis/redisinsight:latest" in the docker-compose service definition with a
fixed, immutable tag or digest to ensure reproducible builds; locate the line
containing the image declaration (image: redis/redisinsight:latest) and change
it to a specific version such as "redis/redisinsight:3.4.2" or, for maximum
immutability, the digest form "redis/redisinsight@sha256:<digest>" before
committing.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docker-compose.yml`:
- Line 10: Remove hardcoded credentials from docker-compose.yml by moving values
like POSTGRES_PASSWORD (and the pgAdmin credentials referenced around lines
23-24) into an untracked .env file, add that .env to .gitignore, and update
docker-compose.yml to reference the variables using ${POSTGRES_PASSWORD} (and
${PGADMIN_EMAIL}, ${PGADMIN_PASSWORD} as applicable) so no plaintext secrets
remain in source control.
- Line 15: The postgres volume mount currently maps postgres_data to
/var/lib/postgresql/18/data which doesn't match the postgres:18 image PGDATA
layout; update the docker-compose service volume mapping to mount postgres_data
at /var/lib/postgresql (or at the image's PGDATA path such as
/var/lib/postgresql/18/docker) so the container's expected PGDATA layout is used
instead of /var/lib/postgresql/18/data.

---

Nitpick comments:
In `@docker-compose.yml`:
- Line 43: Replace the floating image tag "redis/redisinsight:latest" in the
docker-compose service definition with a fixed, immutable tag or digest to
ensure reproducible builds; locate the line containing the image declaration
(image: redis/redisinsight:latest) and change it to a specific version such as
"redis/redisinsight:3.4.2" or, for maximum immutability, the digest form
"redis/redisinsight@sha256:<digest>" before committing.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f23a07ca-d20d-470a-b5bc-c9fed5fa2c2f

📥 Commits

Reviewing files that changed from the base of the PR and between 2073c88 and fda7ec4.

📒 Files selected for processing (2)
  • .dockerignore
  • docker-compose.yml

Comment thread docker-compose.yml Outdated
container_name: postgres_dcms
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: naims
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Remove hardcoded service credentials from source-controlled compose config.

Committing DB/pgAdmin credentials in plain text makes accidental credential reuse and leakage much more likely. Move these to an untracked .env file and reference ${...} variables in Compose.

Suggested change
 services:
   dcms_postgres:
     environment:
       POSTGRES_USER: postgres
-      POSTGRES_PASSWORD: naims
+      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
       POSTGRES_DB: dcms
@@
   dcms_pg_admin:
     environment:
       PGADMIN_DEFAULT_EMAIL: admin@admin.com
-      PGADMIN_DEFAULT_PASSWORD: admin
+      PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_DEFAULT_PASSWORD}

Also applies to: 23-24

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docker-compose.yml` at line 10, Remove hardcoded credentials from
docker-compose.yml by moving values like POSTGRES_PASSWORD (and the pgAdmin
credentials referenced around lines 23-24) into an untracked .env file, add that
.env to .gitignore, and update docker-compose.yml to reference the variables
using ${POSTGRES_PASSWORD} (and ${PGADMIN_EMAIL}, ${PGADMIN_PASSWORD} as
applicable) so no plaintext secrets remain in source control.

Comment thread docker-compose.yml Outdated
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/18/data
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

For the official Docker image postgres:18, what is the default data directory (PGDATA) and recommended volume mount path for persistent storage?

💡 Result:

For the official Docker image postgres:18: 1) Default data directory (PGDATA): /var/lib/postgresql/18/docker [1][2] 2) Recommended persistent volume mount path: mount your persistent volume at /var/lib/postgresql (so the image’s default PGDATA subdirectory is used: /var/lib/postgresql/18/docker) [1][2] Note: Docker’s postgres image documentation also explains that for PostgreSQL 17 and below the mount path was /var/lib/postgresql/data, but for 18+ the guidance changes because PGDATA is version-specific and the “VOLUME” is /var/lib/postgresql [1][2].

Citations:


🌐 Web query:

postgres:18 default PGDATA /var/lib/postgresql/18/docker and recommended volume mount path for persistence

💡 Result:

For the official postgres:18 Docker image (PostgreSQL 18.x), the default data directory setting (PGDATA) is: - /var/lib/postgresql/18/docker [1][2][3] For persistence, the recommended container-side volume mount path is: - /var/lib/postgresql [1][2][3] Why this matters (briefly): the image changes the PGDATA default to be version-specific in PostgreSQL 18+ and also changes what directory is declared as the image VOLUME; therefore mounts/volumes should target /var/lib/postgresql (so the data ends up under the expected PGDATA subdirectory) [1][2][3]. Example (Docker): - -v pgdata18:/var/lib/postgresql postgres:18 (or equivalent in docker-compose) [1][2][3] If you instead mount at /var/lib/postgresql/data (the pre-18 convention), that note is only “for PostgreSQL 17 and below” and is called out as not the correct mount target for 18+ persistence behavior [1][2][3].

Citations:


Fix PostgreSQL 18 volume mount to use the container’s expected PGDATA layout

For postgres:18, the image’s default PGDATA is versioned under /var/lib/postgresql/18/docker, and the recommended persistent volume mount path is /var/lib/postgresql (mounting at the pre-18 /var/lib/postgresql/data path is for 17 and below). Mounting /var/lib/postgresql/18/data won’t align with the image’s expected PGDATA directory.

Suggested change
-      - postgres_data:/var/lib/postgresql/18/data
+      - postgres_data:/var/lib/postgresql
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docker-compose.yml` at line 15, The postgres volume mount currently maps
postgres_data to /var/lib/postgresql/18/data which doesn't match the postgres:18
image PGDATA layout; update the docker-compose service volume mapping to mount
postgres_data at /var/lib/postgresql (or at the image's PGDATA path such as
/var/lib/postgresql/18/docker) so the container's expected PGDATA layout is used
instead of /var/lib/postgresql/18/data.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (5)
src/app/middlewares/globalError.ts (1)

2-2: ⚡ Quick win

Use a type-only import for ErrorResponse.

ErrorResponse is only used in type positions; using import type avoids accidental runtime import emission in stricter TS/ESM setups.

Proposed change
-import { ErrorResponse } from "../../types/index.js";
+import type { ErrorResponse } from "../../types/index.js";
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/middlewares/globalError.ts` at line 2, The import of ErrorResponse in
globalError.ts is only used as a type but is imported as a runtime value; change
the import to a type-only import to avoid emitting runtime import code — replace
the current import of ErrorResponse with an `import type { ErrorResponse }`
style import so the compiler treats ErrorResponse purely as a type (locate the
import line that mentions ErrorResponse in the globalError middleware).
src/app/middlewares/isAuthenticated.ts (1)

3-3: ⚡ Quick win

Use a type-only import for TJWTPayload.

TJWTPayload is consumed only as a type; import type keeps runtime output cleaner and avoids edge-case ESM import issues.

Proposed change
-import { TJWTPayload } from "../../types/index.js";
+import type { TJWTPayload } from "../../types/index.js";
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/middlewares/isAuthenticated.ts` at line 3, Replace the runtime import
of the type with a type-only import: change the import statement that currently
brings in TJWTPayload (the symbol TJWTPayload in
src/app/middlewares/isAuthenticated.ts) to use an "import type" so the type is
erased at runtime and avoids ESM import edge cases; keep the same type name
where it's used in function signatures or type annotations (e.g., any references
inside isAuthenticated middleware) and do not alter runtime imports for non-type
symbols.
prisma/migrations/20260526164546_init/migration.sql (1)

201-201: ⚡ Quick win

Remove redundant non-unique index on studentId.

The non-unique index Admission_studentId_idx is redundant because the unique index Admission_studentId_key at line 195 already covers the same column. Unique indexes can be used for all the same query optimizations as regular indexes, so maintaining both wastes storage and adds unnecessary write overhead.

♻️ Proposed fix to remove the redundant index
 -- CreateIndex
 CREATE UNIQUE INDEX "Admission_applicationId_key" ON "Admission"("applicationId");
 
--- CreateIndex
-CREATE INDEX "Admission_studentId_idx" ON "Admission"("studentId");
-
 -- CreateIndex
 CREATE INDEX "Admission_status_idx" ON "Admission"("status");
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@prisma/migrations/20260526164546_init/migration.sql` at line 201, Remove the
redundant non-unique index by deleting the CREATE INDEX statement for
Admission_studentId_idx in the migration; keep the existing unique index
Admission_studentId_key (which already covers studentId and provides the same
query benefits), and ensure no other migration steps or code reference
Admission_studentId_idx so nothing else needs updating.
Dockerfile (2)

1-17: 🏗️ Heavy lift

Consider multi-stage build to reduce image size.

The current single-stage build includes dev dependencies and source files in the final image. A multi-stage approach would copy only production dependencies and the built dist/ folder to a clean final stage, reducing image size and attack surface.

🏗️ Example multi-stage Dockerfile
# Build stage
FROM node:20-alpine AS builder

RUN npm install -g pnpm

WORKDIR /app

COPY package.json pnpm-lock.yaml ./

RUN pnpm install --frozen-lockfile

COPY . .

RUN pnpm run build

# Production stage
FROM node:20-alpine

RUN npm install -g pnpm

WORKDIR /app

COPY package.json pnpm-lock.yaml ./

RUN pnpm install --frozen-lockfile --prod

COPY --from=builder /app/dist ./dist

RUN addgroup -g 1001 -S nodejs && adduser -S nodejs -u 1001
RUN chown -R nodejs:nodejs /app
USER nodejs

EXPOSE 5000

CMD ["node", "dist/server.js"]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Dockerfile` around lines 1 - 17, The Dockerfile currently uses a single-stage
build that leaves dev dependencies and source files in the final image; convert
it to a multi-stage build by adding a builder stage (e.g., FROM node:20-alpine
AS builder) which installs dependencies (RUN pnpm install --frozen-lockfile),
runs the build (RUN pnpm run build) and then use a clean final stage that only
installs production deps (RUN pnpm install --frozen-lockfile --prod), copies the
built artifacts from the builder (COPY --from=builder /app/dist ./dist), and
drops privileges (adduser/addgroup + USER) before exposing port 5000 and running
node dist/server.js so the final image is smaller and contains only production
files.

13-13: Rework Prisma generation guidance for Docker build

  • pnpm run build is prisma migrate deploy && tsc and doesn’t call prisma generate.
  • Since the Dockerfile runs pnpm install --frozen-lockfile without disabling scripts (and @prisma/client is present), Prisma client generation should happen during install; add an explicit prisma generate only if you want deterministic behavior.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Dockerfile` at line 13, The Dockerfile currently runs "pnpm run build" which
executes "prisma migrate deploy && tsc" but does not run "prisma generate";
because the Dockerfile already runs "pnpm install --frozen-lockfile" with
scripts enabled and `@prisma/client` is present, either rely on install-time
generation (no change) or make generation deterministic by adding an explicit
"prisma generate" step after install and before build; update the Dockerfile to:
after the "pnpm install --frozen-lockfile" step ensure Prisma client is
generated (call "prisma generate" or the equivalent pnpm script) or document why
install-time generation is sufficient, referencing the "pnpm install
--frozen-lockfile", "pnpm run build" and "prisma generate"/"`@prisma/client`"
symbols.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Dockerfile`:
- Around line 1-17: The Dockerfile currently leaves the container running as
root; modify it to run the process as a non-root user by creating or using a
non-root user, ensuring the application directory is owned by that user, and
switching to that user before CMD. Specifically, in the Dockerfile add steps
after WORKDIR /app (or after copying files): create a non-root user (or use the
existing node user from the base image), chown -R /app to that user's UID/GID so
pnpm install/build outputs are writable, and add a USER instruction (e.g., USER
node or USER <appuser>) so the final CMD ["node","dist/server.js"] runs without
root privileges. Ensure these changes reference the Dockerfile's WORKDIR /app,
the RUN pnpm install --frozen-lockfile and RUN pnpm run build steps so ownership
is set before build/runtime.

---

Nitpick comments:
In `@Dockerfile`:
- Around line 1-17: The Dockerfile currently uses a single-stage build that
leaves dev dependencies and source files in the final image; convert it to a
multi-stage build by adding a builder stage (e.g., FROM node:20-alpine AS
builder) which installs dependencies (RUN pnpm install --frozen-lockfile), runs
the build (RUN pnpm run build) and then use a clean final stage that only
installs production deps (RUN pnpm install --frozen-lockfile --prod), copies the
built artifacts from the builder (COPY --from=builder /app/dist ./dist), and
drops privileges (adduser/addgroup + USER) before exposing port 5000 and running
node dist/server.js so the final image is smaller and contains only production
files.
- Line 13: The Dockerfile currently runs "pnpm run build" which executes "prisma
migrate deploy && tsc" but does not run "prisma generate"; because the
Dockerfile already runs "pnpm install --frozen-lockfile" with scripts enabled
and `@prisma/client` is present, either rely on install-time generation (no
change) or make generation deterministic by adding an explicit "prisma generate"
step after install and before build; update the Dockerfile to: after the "pnpm
install --frozen-lockfile" step ensure Prisma client is generated (call "prisma
generate" or the equivalent pnpm script) or document why install-time generation
is sufficient, referencing the "pnpm install --frozen-lockfile", "pnpm run
build" and "prisma generate"/"`@prisma/client`" symbols.

In `@prisma/migrations/20260526164546_init/migration.sql`:
- Line 201: Remove the redundant non-unique index by deleting the CREATE INDEX
statement for Admission_studentId_idx in the migration; keep the existing unique
index Admission_studentId_key (which already covers studentId and provides the
same query benefits), and ensure no other migration steps or code reference
Admission_studentId_idx so nothing else needs updating.

In `@src/app/middlewares/globalError.ts`:
- Line 2: The import of ErrorResponse in globalError.ts is only used as a type
but is imported as a runtime value; change the import to a type-only import to
avoid emitting runtime import code — replace the current import of ErrorResponse
with an `import type { ErrorResponse }` style import so the compiler treats
ErrorResponse purely as a type (locate the import line that mentions
ErrorResponse in the globalError middleware).

In `@src/app/middlewares/isAuthenticated.ts`:
- Line 3: Replace the runtime import of the type with a type-only import: change
the import statement that currently brings in TJWTPayload (the symbol
TJWTPayload in src/app/middlewares/isAuthenticated.ts) to use an "import type"
so the type is erased at runtime and avoids ESM import edge cases; keep the same
type name where it's used in function signatures or type annotations (e.g., any
references inside isAuthenticated middleware) and do not alter runtime imports
for non-type symbols.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a380afba-4767-4bff-b0fc-935f1e34f9a8

📥 Commits

Reviewing files that changed from the base of the PR and between fda7ec4 and 7bae7ac.

📒 Files selected for processing (31)
  • .dockerignore
  • .gitignore
  • Dockerfile
  • docker-compose.yml
  • package.json
  • prisma/migrations/20260526164546_init/migration.sql
  • src/app.ts
  • src/app/lib/prisma.ts
  • src/app/middlewares/authorize.ts
  • src/app/middlewares/globalError.ts
  • src/app/middlewares/isAuthenticated.ts
  • src/app/middlewares/validateRequest.ts
  • src/app/modules/admission/admission.controller.ts
  • src/app/modules/admission/admission.interface.ts
  • src/app/modules/admission/admission.repository.ts
  • src/app/modules/admission/admission.routes.ts
  • src/app/modules/admission/admission.service.ts
  • src/app/modules/auth/auth.controller.ts
  • src/app/modules/auth/auth.helper.ts
  • src/app/modules/auth/auth.routes.ts
  • src/app/modules/auth/auth.service.ts
  • src/app/modules/class/class.controller.ts
  • src/app/modules/class/class.route.ts
  • src/app/modules/class/class.service.ts
  • src/app/modules/student/student.validation.ts
  • src/app/routes/routes.ts
  • src/server.ts
  • src/utils/pagination.ts
  • src/utils/sendEmail.ts
  • src/utils/sendVerificationEmail.ts
  • tsconfig.json
💤 Files with no reviewable changes (1)
  • src/app/modules/admission/admission.interface.ts
✅ Files skipped from review due to trivial changes (17)
  • src/utils/sendVerificationEmail.ts
  • .dockerignore
  • src/app/middlewares/authorize.ts
  • src/app/middlewares/validateRequest.ts
  • src/app/modules/class/class.service.ts
  • src/app/modules/admission/admission.routes.ts
  • tsconfig.json
  • src/app/modules/student/student.validation.ts
  • .gitignore
  • package.json
  • src/app.ts
  • src/app/lib/prisma.ts
  • src/app/modules/class/class.controller.ts
  • src/app/modules/class/class.route.ts
  • src/app/modules/auth/auth.helper.ts
  • src/app/modules/admission/admission.repository.ts
  • docker-compose.yml

Comment thread Dockerfile Outdated
Comment on lines +1 to +17
FROM node:20-alpine

RUN npm install -g pnpm

WORKDIR /app

COPY package.json pnpm-lock.yaml ./

RUN pnpm install --frozen-lockfile

COPY . .

RUN pnpm run build

EXPOSE 5000

CMD ["node", "dist/server.js"]
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Security: Container runs as root.

The Dockerfile does not specify a non-root user, meaning the application runs with root privileges. This expands the attack surface if the container is compromised and violates the principle of least privilege.

🔒 Proposed fix to run as non-root user
 FROM node:20-alpine
 
 RUN npm install -g pnpm
 
 WORKDIR /app
 
 COPY package.json pnpm-lock.yaml ./
 
 RUN pnpm install --frozen-lockfile 
 
 COPY . .
 
 RUN pnpm run build
 
+RUN addgroup -g 1001 -S nodejs && adduser -S nodejs -u 1001
+RUN chown -R nodejs:nodejs /app
+USER nodejs
+
 EXPOSE 5000
 
 CMD ["node", "dist/server.js"]

As per static analysis hints: Trivy rule DS-0002 requires at least one USER command with a non-root user.

🧰 Tools
🪛 Trivy (0.69.3)

[error] 1-1: Image user should not be 'root'

Specify at least 1 USER command in Dockerfile with non-root user as argument

Rule: DS-0002

Learn more

(IaC/Dockerfile)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Dockerfile` around lines 1 - 17, The Dockerfile currently leaves the
container running as root; modify it to run the process as a non-root user by
creating or using a non-root user, ensuring the application directory is owned
by that user, and switching to that user before CMD. Specifically, in the
Dockerfile add steps after WORKDIR /app (or after copying files): create a
non-root user (or use the existing node user from the base image), chown -R /app
to that user's UID/GID so pnpm install/build outputs are writable, and add a
USER instruction (e.g., USER node or USER <appuser>) so the final CMD
["node","dist/server.js"] runs without root privileges. Ensure these changes
reference the Dockerfile's WORKDIR /app, the RUN pnpm install --frozen-lockfile
and RUN pnpm run build steps so ownership is set before build/runtime.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docker-compose.yml (1)

37-37: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Move hardcoded email to environment variable.

The personal email address is hardcoded in the source-controlled compose file. Move it to an environment variable like ${PGADMIN_DEFAULT_EMAIL} in .env to avoid exposing personal information and improve configuration flexibility.

🔧 Proposed fix
-      PGADMIN_DEFAULT_EMAIL: naimsorker6@gmail.com
+      PGADMIN_DEFAULT_EMAIL: ${PGADMIN_DEFAULT_EMAIL}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docker-compose.yml` at line 37, Replace the hardcoded PGADMIN_DEFAULT_EMAIL
value in docker-compose.yml with the environment variable reference
${PGADMIN_DEFAULT_EMAIL} and add the corresponding PGADMIN_DEFAULT_EMAIL entry
to the project's .env (or env file used) so the email is not committed; update
any README or env.example to document the new PGADMIN_DEFAULT_EMAIL variable and
ensure the compose file references the variable instead of the literal address.
🧹 Nitpick comments (3)
docker-compose.yml (2)

45-53: Consider Redis authentication for production.

The Redis service has no authentication configured. This is acceptable for local development, but production deployments should use requirepass or ACLs to secure the Redis instance.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docker-compose.yml` around lines 45 - 53, The Redis service (dcms_redis /
container_name redis_dcms using image redis:7-alpine) is running without
authentication; for production, enable Redis auth by either mounting a secured
redis.conf (or adding a command) that sets requirepass or configures ACL users
and passwords, ensure secrets are provided via Docker secrets or environment
variables (not hard-coded), and update any clients to use the configured
password/ACL user; also document or add a volumes entry for the custom config
and ensure the redis_data volume permissions remain correct.

14-16: Add health checks to ensure service readiness.

depends_on only waits for containers to start, not for services to be ready. Since the backend CMD runs migrations immediately, if PostgreSQL isn't accepting connections yet, the migration will fail. Consider adding healthcheck configurations to the database and Redis services, and use depends_on with condition: service_healthy to prevent premature startup failures.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docker-compose.yml` around lines 14 - 16, Add health checks for the
dcms_postgres and dcms_redis services and make the dependent service use
depends_on with condition: service_healthy so startup waits for readiness;
specifically, add a healthcheck block for dcms_postgres that runs a lightweight
psql or pg_isready command and for dcms_redis that runs redis-cli ping,
configure appropriate interval/retries/start_period, and update the service that
currently lists depends_on to use the service_healthy condition referencing
dcms_postgres and dcms_redis so migrations only run after both services report
healthy.
Dockerfile (1)

22-22: Consider the migration-on-startup pattern for production deployments.

Running prisma:deploy in the container CMD works for local development, but in production or when horizontally scaled, multiple containers may race to apply migrations concurrently. If migrations fail, the container exits without retry. Consider running migrations as a separate init container, pre-deployment step, or job in production environments.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Dockerfile` at line 22, The Dockerfile currently runs migrations in the
container CMD ("CMD [\"sh\", \"-c\", \"pnpm run prisma:deploy && node
dist/server.js\"]"), which can cause race conditions and failures in production;
remove "pnpm run prisma:deploy" from the container CMD and instead apply
migrations outside the app container (e.g., as a Kubernetes initContainer, a
CI/CD pre-deploy job, or a dedicated migration job) or implement a safe
migration orchestrator (leader-election/lock + retry) before starting the app,
leaving the CMD to only start the server (node dist/server.js).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@docker-compose.yml`:
- Line 37: Replace the hardcoded PGADMIN_DEFAULT_EMAIL value in
docker-compose.yml with the environment variable reference
${PGADMIN_DEFAULT_EMAIL} and add the corresponding PGADMIN_DEFAULT_EMAIL entry
to the project's .env (or env file used) so the email is not committed; update
any README or env.example to document the new PGADMIN_DEFAULT_EMAIL variable and
ensure the compose file references the variable instead of the literal address.

---

Nitpick comments:
In `@docker-compose.yml`:
- Around line 45-53: The Redis service (dcms_redis / container_name redis_dcms
using image redis:7-alpine) is running without authentication; for production,
enable Redis auth by either mounting a secured redis.conf (or adding a command)
that sets requirepass or configures ACL users and passwords, ensure secrets are
provided via Docker secrets or environment variables (not hard-coded), and
update any clients to use the configured password/ACL user; also document or add
a volumes entry for the custom config and ensure the redis_data volume
permissions remain correct.
- Around line 14-16: Add health checks for the dcms_postgres and dcms_redis
services and make the dependent service use depends_on with condition:
service_healthy so startup waits for readiness; specifically, add a healthcheck
block for dcms_postgres that runs a lightweight psql or pg_isready command and
for dcms_redis that runs redis-cli ping, configure appropriate
interval/retries/start_period, and update the service that currently lists
depends_on to use the service_healthy condition referencing dcms_postgres and
dcms_redis so migrations only run after both services report healthy.

In `@Dockerfile`:
- Line 22: The Dockerfile currently runs migrations in the container CMD ("CMD
[\"sh\", \"-c\", \"pnpm run prisma:deploy && node dist/server.js\"]"), which can
cause race conditions and failures in production; remove "pnpm run
prisma:deploy" from the container CMD and instead apply migrations outside the
app container (e.g., as a Kubernetes initContainer, a CI/CD pre-deploy job, or a
dedicated migration job) or implement a safe migration orchestrator
(leader-election/lock + retry) before starting the app, leaving the CMD to only
start the server (node dist/server.js).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: fd11f8ce-4f02-4ac8-8b1d-ba2ed8186981

📥 Commits

Reviewing files that changed from the base of the PR and between 7bae7ac and 062567e.

📒 Files selected for processing (4)
  • Dockerfile
  • docker-compose.yml
  • package.json
  • src/app/modules/admission/admission.repository.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/app/modules/admission/admission.repository.ts

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 28, 2026

Actionable comments posted: 0

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/deploy.yml:
- Around line 9-10: The build-and-push job currently lacks an explicit
permissions block; update the job definition for build-and-push to add a minimal
permissions stanza (for example set contents: read) so the job uses
least-privilege GITHUB_TOKEN access — locate the build-and-push job and insert a
permissions: section with the desired scopes (e.g., contents: read) directly
under the job declaration.
- Around line 13-14: The checkout step using actions/checkout@v4 currently
persists credentials unnecessarily; update the Checkout Code step that uses
actions/checkout@v4 to include persist-credentials: false so git auth data is
not written to the repo's git config (i.e., add the persist-credentials: false
key under the checkout step configuration).
- Around line 14-23: Pin the mutable action refs to immutable 40-char commit
SHAs by replacing actions/checkout@v4, docker/login-action@v3, and
docker/build-push-action@v5 with their corresponding full-commit SHAs; also add
a top-level permissions block with least-privilege settings (e.g., restrict
contents and id-token/pull/push as needed) and update the actions/checkout step
to include persist-credentials: false to avoid leaking GITHUB_TOKEN to
subsequent steps—locate these in the workflow where actions/checkout,
docker/login-action, and docker/build-push-action are used and add the
permissions stanza at the job or workflow level.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9b38d6d0-e6cf-400b-bb81-6584f3a57fe7

📥 Commits

Reviewing files that changed from the base of the PR and between d1ad9cf and 11170d2.

📒 Files selected for processing (2)
  • .dockerignore
  • .github/workflows/deploy.yml
✅ Files skipped from review due to trivial changes (1)
  • .dockerignore

Comment on lines +9 to +10
build-and-push:
runs-on: ubuntu-latest
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
FILE=".github/workflows/deploy.yml"
if [ ! -f "$FILE" ]; then
  echo "Missing $FILE"
  exit 0
fi
echo "---- $FILE (head -n 80) ----"
sed -n '1,120p' "$FILE" | cat -n
echo "---- Extract jobs.build-and-push block ----"
# Use awk to print from build-and-push job start to next top-level job key (2-space indent) or EOF
python3 - <<'PY'
import re, itertools
path=".github/workflows/deploy.yml"
lines=open(path,'r',encoding='utf-8').read().splitlines()
start=None
for i,l in enumerate(lines):
    if re.match(r'^\s*build-and-push:\s*$', l):
        start=i
        break
if start is None:
    print("build-and-push job not found")
    raise SystemExit(0)
# capture until line that matches '^\s{2}\w'?? Actually jobs are under 'jobs:' at indent 2.
# We'll end when we hit a line with 2 spaces then a word ending with ':' at same level as build-and-push.
pat_end=re.compile(r'^ {2}[A-Za-z0-9_-]+:\s*$')
end=len(lines)
for j in range(start+1,len(lines)):
    if pat_end.match(lines[j]):
        end=j
        break
for k in range(start, end):
    print(f"{k+1:4d}: {lines[k]}")
PY
echo "---- Search for permissions blocks in deploy.yml ----"
rg -n "permissions\s*:" "$FILE" || true

Repository: naims6/dcms-backend

Length of output: 1636


Restrict workflow token permissions explicitly.

The build-and-push job in .github/workflows/deploy.yml has no permissions: block, so it inherits default GITHUB_TOKEN permissions. Add a minimal permissions setting (e.g., contents: read) to follow least privilege.

Suggested hardening diff
 jobs:
   build-and-push:
     runs-on: ubuntu-latest
+    permissions:
+      contents: read
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
build-and-push:
runs-on: ubuntu-latest
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
🧰 Tools
🪛 zizmor (1.25.2)

[warning] 9-27: overly broad permissions (excessive-permissions): default permissions used due to no permissions: block

(excessive-permissions)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/deploy.yml around lines 9 - 10, The build-and-push job
currently lacks an explicit permissions block; update the job definition for
build-and-push to add a minimal permissions stanza (for example set contents:
read) so the job uses least-privilege GITHUB_TOKEN access — locate the
build-and-push job and insert a permissions: section with the desired scopes
(e.g., contents: read) directly under the job declaration.

Comment on lines +13 to +14
- name: Checkout Code
uses: actions/checkout@v4
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the referenced workflow lines
echo "=== deploy.yml (lines 1-120) ==="
nl -ba .github/workflows/deploy.yml | sed -n '1,160p'

echo
echo "=== checkout steps in repo ==="
rg -n "uses:\s*actions/checkout@v" .github/workflows || true

echo
echo "=== git operations in workflows ==="
rg -n "(git (push|pull|fetch)|GIT_|persist-credentials|submodule|actions/checkout)" .github/workflows || true

Repository: naims6/dcms-backend

Length of output: 157


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== deploy.yml (lines 1-200) ==="
# Use cat -n for line numbers (nl may be unavailable)
cat -n .github/workflows/deploy.yml | sed -n '1,200p'

echo
echo "=== checkout steps in workflows ==="
rg -n "uses:\s*actions/checkout@v" .github/workflows || true

echo
echo "=== git/auth related operations in workflows ==="
rg -n "(git (push|pull|fetch)|GIT_|persist-credentials|submodule|token:.*GITHUB_TOKEN|actions/checkout)" .github/workflows || true

Repository: naims6/dcms-backend

Length of output: 1099


Disable persisted checkout credentials

.github/workflows/deploy.yml checks out the repo but performs no authenticated git operations afterward, so actions/checkout@v4 credentials don’t need to be persisted. Add persist-credentials: false to avoid writing auth data to git config.

Suggested hardening diff
       - name: Checkout Code
         uses: actions/checkout@v4
+        with:
+          persist-credentials: false
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Checkout Code
uses: actions/checkout@v4
- name: Checkout Code
uses: actions/checkout@v4
with:
persist-credentials: false
🧰 Tools
🪛 zizmor (1.25.2)

[warning] 13-14: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[error] 14-14: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/deploy.yml around lines 13 - 14, The checkout step using
actions/checkout@v4 currently persists credentials unnecessarily; update the
Checkout Code step that uses actions/checkout@v4 to include persist-credentials:
false so git auth data is not written to the repo's git config (i.e., add the
persist-credentials: false key under the checkout step configuration).

Comment on lines +14 to +23
uses: actions/checkout@v4

- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME}}
password: ${{ secrets.DOCKERHUB_TOKEN}}

- name: Build and Push Docker Image
uses: docker/build-push-action@v5
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
ls -la .github/workflows || true
sed -n '1,160p' .github/workflows/deploy.yml

Repository: naims6/dcms-backend

Length of output: 826


Pin uses: actions to immutable commit SHAs in .github/workflows/deploy.yml

Steps reference mutable tags (actions/checkout@v4, docker/login-action@v3, docker/build-push-action@v5). Pin each to a full 40-char commit SHA. This workflow also lacks a permissions block and checkout doesn’t set persist-credentials: false.

Suggested hardening diff
-      - name: Checkout Code
-        uses: actions/checkout@v4
+      - name: Checkout Code
+        uses: actions/checkout@<FULL_40_CHAR_SHA>

       - name: Log in to Docker Hub
-        uses: docker/login-action@v3
+        uses: docker/login-action@<FULL_40_CHAR_SHA>

       -  name: Build and Push Docker Image
-         uses: docker/build-push-action@v5
+         uses: docker/build-push-action@<FULL_40_CHAR_SHA>
🧰 Tools
🪛 zizmor (1.25.2)

[error] 14-14: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[error] 17-17: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[error] 23-23: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/deploy.yml around lines 14 - 23, Pin the mutable action
refs to immutable 40-char commit SHAs by replacing actions/checkout@v4,
docker/login-action@v3, and docker/build-push-action@v5 with their corresponding
full-commit SHAs; also add a top-level permissions block with least-privilege
settings (e.g., restrict contents and id-token/pull/push as needed) and update
the actions/checkout step to include persist-credentials: false to avoid leaking
GITHUB_TOKEN to subsequent steps—locate these in the workflow where
actions/checkout, docker/login-action, and docker/build-push-action are used and
add the permissions stanza at the job or workflow level.

@naims6 naims6 merged commit c3cfa6c into main May 28, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant