Skip to content

basic setup with modular monolith & auth#1

Merged
th3hero merged 1 commit into
mainfrom
features/setup
Feb 28, 2026
Merged

basic setup with modular monolith & auth#1
th3hero merged 1 commit into
mainfrom
features/setup

Conversation

@th3hero

@th3hero th3hero commented Feb 28, 2026

Copy link
Copy Markdown
Owner

This pull request introduces a comprehensive boilerplate setup for a Node.js backend application, focusing on modular architecture, robust development tooling, and production readiness. Key changes include the addition of configuration files for Docker, environment variables, code style enforcement, CI/CD, and core backend infrastructure, as well as documentation detailing the stack and usage.

Infrastructure & Environment Setup:

  • Added Dockerfile for multi-stage Node.js builds and production runtime, including health checks and non-root user configuration.
  • Added docker-compose.yml to orchestrate PostgreSQL, Redis, and the backend service with health checks, persistent volumes, and network configuration.
  • Introduced .dockerignore to optimize Docker builds by excluding unnecessary files and directories.
  • Added env.example with detailed environment variable documentation for all major subsystems (database, Redis, JWT, logging, AWS S3, etc.).

Development Tooling & Code Quality:

  • Added ESLint configuration (eslint.config.js) for TypeScript code style enforcement, including custom rules and project-specific restrictions.
  • Added Prettier configuration (.prettierrc.json) and EditorConfig (.editorconfig) for consistent code formatting across editors and CI. [1] [2]
  • Added Husky pre-commit hook (.husky/pre-commit) to automatically lint, fix, and build code before commits.

Continuous Integration:

  • Added GitHub Actions workflow (.github/workflows/ci.yaml) for automated build, lint, and test steps on push and pull requests to the main branch.

Documentation:

  • Overhauled README.md to document the tech stack, architecture, setup instructions, scripts, test users, API routes, and Docker usage for new developers.

Core Backend Implementation:

  • Added initial application setup in src/app.ts with Express middleware chain for security, logging, rate limiting, error handling, and static file serving.
  • Implemented Redis client management in src/app/core/cache/redis.client.ts and exported cache utilities in src/app/core/cache/index.ts. [1] [2]
  • Added Prisma configuration in prisma.config.ts for schema, migrations, and datasource management.
  • Added initial package.json with scripts for build, dev, lint, typecheck, Prisma, and seeding, and declared all dependencies.

These changes lay the foundation for a scalable, maintainable, and production-ready Node.js backend project.

@th3hero th3hero requested a review from Copilot February 28, 2026 09:59
@th3hero th3hero merged commit c965b58 into main Feb 28, 2026
3 checks passed
@th3hero th3hero deleted the features/setup branch February 28, 2026 10:02

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR bootstraps a production-oriented Node.js/TypeScript backend boilerplate with a modular-monolith structure, adding core infrastructure (Docker/CI), configuration, and initial implementations for auth/RBAC, caching/queues, logging, and file storage.

Changes:

  • Added core runtime setup (Express app + clustered entrypoint), environment/config modules, and foundational “core” utilities (errors/constants/types/container/db/cache/queue).
  • Implemented initial feature modules (auth with session caching + BullMQ batching, admin config/roles/permissions, gallery with S3 presigned upload flows).
  • Added production/dev tooling and infra (Docker/Docker Compose, CI workflow, ESLint/Prettier/Husky, Prisma config/schema, updated README).

Reviewed changes

Copilot reviewed 110 out of 114 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
tsconfig.json TypeScript compiler settings + path aliases.
storage/keys/.gitignore Ignores key subfolders under storage.
src/routes/index.ts Root router + versioned module mounting.
src/index.ts Server entrypoint + clustering + graceful shutdown.
src/database/schema.prisma Initial Prisma schema (auth, roles, configs, gallery).
src/database/prisma.client.ts Prisma client singleton using pg adapter/pool.
src/database/README.md Database layer docs and commands.
src/database/.gitignore Ignores migrations and generated Prisma client.
src/config/helmet.config.ts Helmet security headers configuration.
src/config/environment.config.ts Centralized env parsing and defaults.
src/config/cors.config.ts Central CORS configuration.
src/app/http/services/storage/types.ts Storage DTOs/constants and validation categories.
src/app/http/services/storage/storage.service.ts S3 presigned storage manager + operations.
src/app/http/services/storage/index.ts Storage service exports.
src/app/http/services/password.service.ts PBKDF2 password hashing/verification.
src/app/http/services/logger.service.ts Winston logger with request context support.
src/app/http/services/jwt.service.ts RS256 JWT sign/verify helpers.
src/app/http/services/index.ts Aggregated service exports.
src/app/http/services/helpers/health/types.ts Health check result types.
src/app/http/services/helpers/health/redis.service.ts Redis health check implementation.
src/app/http/services/helpers/health/key-file.service.ts Key file existence/access health checks.
src/app/http/services/helpers/health/health.service.ts Aggregated system health snapshot.
src/app/http/services/helpers/health/database.service.ts DB connectivity health check (Prisma raw query).
src/app/http/services/encryption.service.ts RSA encrypt/decrypt helper service.
src/app/http/modules/index.ts HTTP modules router composition.
src/app/http/modules/gallery/validation.ts Gallery request validation rules.
src/app/http/modules/gallery/types.ts Gallery DTOs + response transformer.
src/app/http/modules/gallery/service.ts Gallery business logic for uploads and retrieval.
src/app/http/modules/gallery/routes.ts Gallery endpoints + permission checks.
src/app/http/modules/gallery/repository.ts Gallery persistence methods.
src/app/http/modules/gallery/index.ts Gallery module exports.
src/app/http/modules/gallery/controller.ts Gallery controller endpoints.
src/app/http/modules/auth/validation.ts Auth request validation rules.
src/app/http/modules/auth/types.ts Auth DTOs and related types.
src/app/http/modules/auth/routes.ts Auth routes (OTP/password/session mgmt).
src/app/http/modules/auth/repository.ts Auth data access (users/sessions/OTP/reset).
src/app/http/modules/auth/queue.ts BullMQ queue/worker for session lastUsed batching.
src/app/http/modules/auth/index.ts Auth module exports (routes/cache/queue).
src/app/http/modules/auth/helpers.ts Auth response helpers and account checks.
src/app/http/modules/auth/cache.ts Session cache keying + invalidation in Redis.
src/app/http/modules/admin/roles/validation.ts Role validation rules.
src/app/http/modules/admin/roles/types.ts Role DTOs + response/entity types.
src/app/http/modules/admin/roles/service.ts Role business logic + cache invalidation.
src/app/http/modules/admin/roles/routes.ts Role endpoints + permission checks.
src/app/http/modules/admin/roles/repository.ts Role persistence + permission upsert.
src/app/http/modules/admin/roles/index.ts Roles module exports.
src/app/http/modules/admin/roles/controller.ts Roles controller endpoints.
src/app/http/modules/admin/permissions/types.ts Permissions response types.
src/app/http/modules/admin/permissions/service.ts Permissions viewing logic.
src/app/http/modules/admin/permissions/routes.ts Permissions endpoints.
src/app/http/modules/admin/permissions/repository.ts Permissions data access with role joins.
src/app/http/modules/admin/permissions/index.ts Permissions module exports.
src/app/http/modules/admin/permissions/controller.ts Permissions controller endpoints.
src/app/http/modules/admin/index.ts Admin router composition.
src/app/http/modules/admin/config/validation.ts System config validation rules.
src/app/http/modules/admin/config/types.ts System config DTOs/entities/responses.
src/app/http/modules/admin/config/service.ts Config business logic + caching.
src/app/http/modules/admin/config/routes.ts Config endpoints + permission checks.
src/app/http/modules/admin/config/repository.ts Config persistence methods.
src/app/http/modules/admin/config/index.ts Config module exports.
src/app/http/modules/admin/config/controller.ts Config controller endpoints.
src/app/http/modules/admin/config/cache.ts Redis-backed config cache operations.
src/app/http/middleware/sanitize.middleware.ts Request input sanitization middleware.
src/app/http/middleware/request-context.middleware.ts AsyncLocalStorage request context + request IDs.
src/app/http/middleware/rate-limit.middleware.ts Rate limiting middleware with Redis store support.
src/app/http/middleware/logging.middleware.ts HTTP request logging + error logging middleware.
src/app/http/middleware/index.ts Middleware barrel exports.
src/app/http/middleware/error.middleware.ts Global error + not-found handlers.
src/app/http/middleware/auth.middleware.ts JWT auth + permission enforcement + session caching.
src/app/http/controllers/controller.ts Base controller helpers + validation + responses.
src/app/http/controllers/app/app.controller.ts Landing + health endpoints.
src/app/core/utils/sanitize.utils.ts Log masking + XSS escape/sanitize helpers.
src/app/core/utils/random.utils.ts Crypto-secure OTP/random generators.
src/app/core/utils/path.utils.ts Project root resolution helper.
src/app/core/utils/index.ts Utils barrel exports.
src/app/core/types/index.ts Types barrel exports.
src/app/core/types/express.types.ts Express request augmentation types.
src/app/core/types/common.types.ts Shared types (request context).
src/app/core/types/api.types.ts API response DTO types.
src/app/core/queue/index.ts Queue barrel exports.
src/app/core/queue/bullmq.defaults.ts Default BullMQ job options.
src/app/core/queue/bullmq.connection.ts BullMQ Redis connection factory.
src/app/core/index.ts Core barrel exports + init helpers.
src/app/core/errors/index.ts AppError hierarchy + type guard.
src/app/core/database/prisma.client.ts Core DB connect/disconnect wrappers.
src/app/core/database/index.ts Core database exports.
src/app/core/database/base.repository.ts Base repository with tx/raw query helpers.
src/app/core/container.ts Simple DI container for core dependencies.
src/app/core/constants/roles.constants.ts Role slug constants.
src/app/core/constants/permissions.constants.ts RBAC module/action/scope constants + helpers.
src/app/core/constants/index.ts Constants barrel exports.
src/app/core/constants/http.constants.ts Error codes + rate limits + pagination constants.
src/app/core/constants/auth.constants.ts Auth/JWT constants sourced from env config.
src/app/core/cache/redis.client.ts Redis client singleton + connect/disconnect.
src/app/core/cache/index.ts Cache barrel exports.
src/app.ts Express app middleware chain + routes + error handling.
prisma.config.ts Prisma config (schema/migrations/datasource).
package.json Dependencies, scripts, engine constraints.
eslint.config.js ESLint flat config + TS rules and restrictions.
env.example Documented environment variables.
docker-compose.yml Local infra stack (Postgres/Redis/backend).
README.md Expanded developer documentation and usage.
Dockerfile Multi-stage build + non-root runtime + healthcheck.
.prettierrc.json Prettier formatting config.
.husky/pre-commit Pre-commit hook (lint/build).
.gitignore Repo ignore rules (env, logs, keys, Prisma client).
.github/workflows/ci.yaml CI build + lint workflow.
.editorconfig Editor formatting defaults.
.dockerignore Docker build context exclusions.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +11 to +16
const projectRoot = getProjectRoot();

// Load RSA keys for data encryption (separate from JWT keys)
const publicKey = fs.readFileSync(path.resolve(projectRoot, 'storage/keys/encryption/public.key'), 'utf8');
const privateKey = fs.readFileSync(path.resolve(projectRoot, 'storage/keys/encryption/private.key'), 'utf8');

Copilot AI Feb 28, 2026

Copy link

Choose a reason for hiding this comment

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

Encryption keys are loaded synchronously at module import time. This will crash the process if key files are missing and makes it impossible to start the server to expose /health diagnostics. Load keys lazily (or validate them during a controlled startup step) and avoid readFileSync at module scope.

Copilot uses AI. Check for mistakes.
Comment on lines +196 to +200
const response: StorageConfirmResponse = {
success: true,
viewUrl: result.viewUrl ?? ''
};
if (result.actualFileSize != null) {

Copilot AI Feb 28, 2026

Copy link

Choose a reason for hiding this comment

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

On successful confirmation, viewUrl is set to result.viewUrl ?? ''. Returning an empty string on a “success” path can propagate an invalid URL to API consumers and hide a storage-provider regression. Consider treating a missing viewUrl as an error (return failure or throw) similarly to how generateUploadUrl/generateViewUrl validate required fields.

Copilot uses AI. Check for mistakes.
Comment on lines +253 to +259
(req as AuthenticatedRequest).session = {
id: sessionId,
userId: user.id,
token: decoded.t,
status: true,
expiresAt: cachedSession ? new Date(cachedSession.expiresAt) : new Date()
};

Copilot AI Feb 28, 2026

Copy link

Choose a reason for hiding this comment

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

expiresAt on the request session is set to new Date() when the session is loaded from DB (cache miss). That loses the real expiry time and can cause downstream logic (TTL calculations, client responses, authorization checks) to treat sessions as effectively expiring immediately or inconsistently. Populate expiresAt from the DB session record (and in the cached path keep using the cached value).

Copilot uses AI. Check for mistakes.
Comment thread README.md
openssl genrsa -out storage/keys/api/private.key 2048
openssl rsa -in storage/keys/api/private.key -pubout -out storage/keys/api/public.key
openssl genrsa -out storage/keys/encryption/private.key 2048
openssl rsa -in storage/keys/encryption/private.key -pubout -out storage/keys/encryption/private.key.pub

Copilot AI Feb 28, 2026

Copy link

Choose a reason for hiding this comment

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

The encryption key generation command writes the public key to storage/keys/encryption/private.key.pub, but the runtime code reads storage/keys/encryption/public.key (see encryption.service.ts). Update the README commands/paths so the generated filenames match what the application expects.

Suggested change
openssl rsa -in storage/keys/encryption/private.key -pubout -out storage/keys/encryption/private.key.pub
openssl rsa -in storage/keys/encryption/private.key -pubout -out storage/keys/encryption/public.key

Copilot uses AI. Check for mistakes.
Comment on lines +11 to +15
const projectRoot = getProjectRoot();

// Read pepper/secret from key file (not stored in DB, adds extra layer of security)
const pepper = fs.readFileSync(path.resolve(projectRoot, 'storage/keys/encryption/private.key'), 'utf8');

Copilot AI Feb 28, 2026

Copy link

Choose a reason for hiding this comment

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

The password pepper is read synchronously from storage/keys/encryption/private.key at module import time. If the file is missing/unreadable, the server will crash on startup (and unit tests/imports will fail) before health checks can run. Prefer lazy loading (or an explicit init step) and surface a clear configuration error; also avoid readFileSync in module scope.

Copilot uses AI. Check for mistakes.
Comment thread .github/workflows/ci.yaml
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '24'

Copilot AI Feb 28, 2026

Copy link

Choose a reason for hiding this comment

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

CI uses Node.js 24 while package.json declares support for >=22 and the Dockerfile builds/runs on Node 22-alpine. Using a different major in CI can mask runtime issues or introduce Node-version-specific behavior. Consider aligning the workflow to the same major as production (e.g., 22) or testing a matrix that explicitly includes the production version.

Suggested change
node-version: '24'
node-version: '22'

Copilot uses AI. Check for mistakes.
Comment on lines +12 to +14
const projectRoot = getProjectRoot();
const logsDir = path.join(projectRoot, 'storage', 'logs');

Copilot AI Feb 28, 2026

Copy link

Choose a reason for hiding this comment

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

logsDir points to storage/logs, but that directory isn’t created here (and isn’t currently present under storage/). In production, winston-daily-rotate-file will fail to write/rotate if the directory doesn’t exist. Create the directory during startup (e.g., fs.mkdirSync(logsDir, { recursive: true })) or ensure it’s shipped/created in the container image at runtime.

Copilot uses AI. Check for mistakes.
Comment thread .dockerignore
Comment on lines +17 to +26
# Logs
*.log
storage/logs/*
!storage/logs/.gitkeep

# Environment files
.env
.env.*
!env.example

Copilot AI Feb 28, 2026

Copy link

Choose a reason for hiding this comment

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

storage/keys/* is not excluded from the Docker build context. If a developer has generated RSA keys locally, COPY . . in the build stage can bake those private keys into the image layers. Add storage/keys/ (or at least storage/keys/**/*.key) to .dockerignore and provide keys to the container via secrets/volumes instead.

Copilot uses AI. Check for mistakes.
Comment thread src/config/cors.config.ts
Comment on lines +15 to +46
origin: environment.cors?.origin,

/**
* Specifies the HTTP methods allowed for CORS requests.
* - Sourced from environment configuration (array of allowed methods).
* - Example: ["GET", "POST", "PUT", "DELETE", "OPTIONS"]
*/
methods: environment.cors?.methods,

/**
* Specifies the headers allowed in the actual request.
* - Allows common browser headers and custom headers.
*/
allowedHeaders: [
'Content-Type',
'Authorization',
'X-Requested-With',
'Accept',
'Origin',
'Referer',
'User-Agent',
'sec-ch-ua',
'sec-ch-ua-mobile',
'sec-ch-ua-platform',
'DNT'
],

/**
* Indicates whether the response to the request can be exposed when credentials are present.
* - true: Allows cookies and credentials to be sent in cross-origin requests.
*/
credentials: true,

Copilot AI Feb 28, 2026

Copy link

Choose a reason for hiding this comment

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

credentials: true combined with a default origin of ['*'] (from environment.config.ts) results in invalid CORS for credentialed requests (browsers will reject Access-Control-Allow-Origin: * when credentials are used). Consider either (a) defaulting origins to an explicit list and keeping credentials: true, or (b) turning off credentials when the origin list contains *, or (c) using an origin function that reflects the request origin when it’s allowed.

Copilot uses AI. Check for mistakes.
Comment on lines +13 to +17
const projectRoot = getProjectRoot();

// Load RSA keys
const privateKey = fs.readFileSync(path.resolve(projectRoot, 'storage/keys/api/private.key'), 'utf8');
const publicKey = fs.readFileSync(path.resolve(projectRoot, 'storage/keys/api/public.key'), 'utf8');

Copilot AI Feb 28, 2026

Copy link

Choose a reason for hiding this comment

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

JWT keys are loaded synchronously at module import time via readFileSync. If the key files are missing/misconfigured, the entire process will crash before the health check can report the issue and before the app can start gracefully. Load keys lazily inside generateTokenPair/verifyToken (or via an init step) with a clear startup-time validation error, and avoid synchronous file reads on the hot path.

Copilot uses AI. Check for mistakes.
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.

2 participants