-
Notifications
You must be signed in to change notification settings - Fork 64
Migrate and test project to latest versions #745
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| NODE_ENV=development | ||
| PORT=3000 | ||
| JWT_SECRET=test-secret-key-for-development | ||
|
|
||
| # Database Configuration | ||
| TYPEORM_CONNECTION=mongodb | ||
| TYPEORM_HOST=localhost | ||
| TYPEORM_PORT=27017 | ||
| TYPEORM_USERNAME= | ||
| TYPEORM_PASSWORD= | ||
| TYPEORM_DATABASE=nodejs_boilerplate | ||
|
|
||
| # Agenda Configuration | ||
| AGENDA_DB_COLLECTION=jobs | ||
| AGENDA_POOL_TIME=200 | ||
| AGENDA_CONCURRENCY=10 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| module.exports = { | ||
| parser: '@typescript-eslint/parser', | ||
| extends: [ | ||
| 'eslint:recommended', | ||
| 'plugin:@typescript-eslint/recommended', | ||
| 'prettier', | ||
| ], | ||
| plugins: ['@typescript-eslint', 'prettier'], | ||
| parserOptions: { | ||
| ecmaVersion: 2022, | ||
| sourceType: 'module', | ||
| project: './tsconfig.json', | ||
| }, | ||
| env: { | ||
| node: true, | ||
| es6: true, | ||
| jest: true, | ||
| }, | ||
| rules: { | ||
| 'prettier/prettier': 'error', | ||
| '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }], | ||
| '@typescript-eslint/explicit-function-return-type': 'off', | ||
| '@typescript-eslint/explicit-module-boundary-types': 'off', | ||
| '@typescript-eslint/no-explicit-any': 'warn', | ||
| '@typescript-eslint/no-inferrable-types': 'off', | ||
| 'no-console': ['warn', { allow: ['warn', 'error'] }], | ||
| 'max-len': ['error', { code: 160 }], | ||
| }, | ||
| ignorePatterns: ['dist/', 'node_modules/', '*.js'], | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| module.exports = { | ||
| preset: 'ts-jest', | ||
| testEnvironment: 'node', | ||
| roots: ['<rootDir>/src', '<rootDir>/tests'], | ||
| testMatch: [ | ||
| '**/__tests__/**/*.ts', | ||
| '**/?(*.)+(spec|test).ts' | ||
| ], | ||
| transform: { | ||
| '^.+\\.ts$': 'ts-jest', | ||
| }, | ||
| collectCoverageFrom: [ | ||
| 'src/**/*.ts', | ||
| '!src/**/*.d.ts', | ||
| '!src/main.ts', | ||
| ], | ||
| coverageDirectory: 'coverage', | ||
| coverageReporters: [ | ||
| 'text', | ||
| 'lcov', | ||
| 'html' | ||
| ], | ||
| moduleFileExtensions: ['ts', 'js', 'json'], | ||
| testTimeout: 30000, | ||
| }; | ||
|
Comment on lines
+1
to
+25
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Config uses ts-jest with Jest 30—this combo is risky today. Given the versioning policy, ts-jest 29 targets Jest 29. Either:
This prevents test runner failures. (npmjs.com) 🤖 Prompt for AI Agents |
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -12,45 +12,50 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "format": "prettier --write \"src/**/*.ts\"", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "start:prod": "node dist/main.js", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "start": "nodemon", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "lint": "tslint -p tsconfig.json -c tslint.json", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "test": "jest", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "lint": "eslint src/**/*.ts", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "lint:fix": "eslint src/**/*.ts --fix", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "test": "jest --passWithNoTests", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "test:watch": "jest --watch", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "test:cov": "jest --coverage" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "engines": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "node": ">=8.0.0" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "node": ">=18.0.0" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "dependencies": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "agenda": "^5.0.0", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "argon2": "^0.31.1", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "body-parser": "^1.20.0", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "argon2": "^0.44.0", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "body-parser": "^2.2.0", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "celebrate": "^15.0.1", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "cors": "^2.8.5", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "dotenv": "^16.0.3", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "express": "^4.18.1", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "dotenv": "^17.2.1", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "express": "^5.1.0", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "express-jwt": "^8.4.1", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "helmet": "^7.0.0", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "joi": "^17.6.0", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "helmet": "^8.1.0", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "joi": "^18.0.1", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "jsonwebtoken": "^9.0.2", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "mongodb": "^6.2.0", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "reflect-metadata": "^0.1.13", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "mongodb": "^6.19.0", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "reflect-metadata": "^0.2.2", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "typedi": "^0.10.0", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "typeorm": "^0.3.10", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "typeorm": "^0.3.26", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "typeorm-typedi-extensions": "^0.4.1" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "devDependencies": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "@types/cors": "^2.8.12", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "@types/express": "^4.17.13", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "@types/jest": "^29.5.6", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "@types/morgan": "^1.9.3", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "@types/node": "^20.8.9", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "chai": "^4.3.10", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "jest": "^29.7.0", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "nodemon": "^3.0.1", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "prettier": "^3.0.3", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "ts-jest": "^29.1.1", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "ts-node": "^10.7.0", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "tslint": "^6.1.3", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "typescript": "^5.2.2" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "@types/cors": "^2.8.19", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "@types/express": "^5.0.3", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "@types/jest": "^30.0.0", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "@types/morgan": "^1.9.10", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "@types/node": "^24.3.0", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "@typescript-eslint/eslint-plugin": "^6.21.0", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "@typescript-eslint/parser": "^6.21.0", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "chai": "^6.0.1", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "eslint": "^8.57.0", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "eslint-config-prettier": "^10.1.8", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "eslint-plugin-prettier": "^5.5.4", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "jest": "^30.1.2", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "nodemon": "^3.1.10", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "prettier": "^3.6.2", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "ts-jest": "^29.4.1", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "ts-node": "^10.9.2", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "typescript": "^5.9.2" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+45
to
60
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Version mismatch: Jest 30 with ts-jest 29 is not supported. ts-jest’s major tracks Jest’s major; 29.x is for Jest 29 and is not guaranteed for Jest 30. Either downgrade Jest to 29 or replace ts-jest (e.g., babel-jest).
Minimal fix (stay with ts-jest): - "@types/jest": "^30.0.0",
- "jest": "^30.1.2",
+ "@types/jest": "^29.5.12",
+ "jest": "^29.7.0",
"ts-jest": "^29.4.1",Alternatively, keep Jest 30 and switch transforms to babel-jest; I can draft that if you prefer. 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,10 +1,3 @@ | ||||||||||||||||||
| interface ExtendedErrorInterface { | ||||||||||||||||||
| name: string; | ||||||||||||||||||
| message: string; | ||||||||||||||||||
| code: string; | ||||||||||||||||||
| status: number; | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| /** | ||||||||||||||||||
| * Exception to extend the error class to allow additional properties. | ||||||||||||||||||
| * | ||||||||||||||||||
|
|
@@ -16,7 +9,9 @@ interface ExtendedErrorInterface { | |||||||||||||||||
| * 3. code - Unique error code | ||||||||||||||||||
| * 4. link - Know more link | ||||||||||||||||||
| */ | ||||||||||||||||||
| class Exception extends Error { | ||||||||||||||||||
| export class Exception extends Error { | ||||||||||||||||||
| public status!: number; | ||||||||||||||||||
| public code?: string; | ||||||||||||||||||
| constructor(message: string, status: number = 500, code?: string, link?: string) { | ||||||||||||||||||
|
Comment on lines
+12
to
15
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainPrevent “/undefined” in dev link and reduce duplication. When - if (isDev && link) {
- message = `${message}\n> More details: ${link}/${code}`;
- }
+ if (isDev && link) {
+ const linkSuffix = code ? `/${code}` : '';
+ message = `${message}\n> More details: ${link}${linkSuffix}`;
+ }Also verify imports across the repo now use a named export: Also applies to: 23-25 🏁 Script executed: #!/usr/bin/env bash
rg -nP "import\s+Exception\s+from\s+['\"]\.\/?Exception" -g '!**/dist/**'Length of output: 77 🏁 Script executed: #!/usr/bin/env bash
set -eo pipefail
echo "🔍 Searching for default ESM imports of Exception..."
rg -nP "import\s+Exception\s+from\s+['\"][^'\"]*Exception['\"]" -g '!**/dist/**' || true
echo "🔍 Searching for CommonJS require calls importing Exception..."
rg -nP "require\(['\"][^'\"]*Exception['\"]\)" -g '!**/dist/**' || trueLength of output: 443 Guard against undefined Replace: - if (isDev && link) {
- message = `${message}\n> More details: ${link}/${code}`;
- }
+ if (isDev && link) {
+ const linkSuffix = code ? `/${code}` : '';
+ message = `${message}\n> More details: ${link}${linkSuffix}`;
+ }Default imports of 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||
| super(message); | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
@@ -74,7 +69,7 @@ class Exception extends Error { | |||||||||||||||||
| /** | ||||||||||||||||||
| * Update the stack trace | ||||||||||||||||||
| */ | ||||||||||||||||||
| if (Error.hasOwnProperty('captureStackTrace')) { | ||||||||||||||||||
| if (Object.prototype.hasOwnProperty.call(Error, 'captureStackTrace')) { | ||||||||||||||||||
| Error.captureStackTrace(this, this.constructor); | ||||||||||||||||||
| return; | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
@@ -87,6 +82,3 @@ class Exception extends Error { | |||||||||||||||||
| }); | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| interface Exception extends ExtendedErrorInterface {} | ||||||||||||||||||
| export { Exception }; | ||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,4 +1,4 @@ | ||||||||||||||||||||||||||||||||||||||||||
| import {expressjwt as jwt} from 'express-jwt'; | ||||||||||||||||||||||||||||||||||||||||||
| import { expressjwt as jwt } from 'express-jwt'; | ||||||||||||||||||||||||||||||||||||||||||
| import config from '../../config'; | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -7,21 +7,21 @@ import config from '../../config'; | |||||||||||||||||||||||||||||||||||||||||
| * Authorization: Bearer ${JWT} | ||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||
| const getTokenFromHeader = (req) => { | ||||||||||||||||||||||||||||||||||||||||||
| const getTokenFromHeader = (req: any) => { | ||||||||||||||||||||||||||||||||||||||||||
| const { authorization } = req.headers; | ||||||||||||||||||||||||||||||||||||||||||
| if ( | ||||||||||||||||||||||||||||||||||||||||||
| (authorization && authorization.split(' ')[0] === 'Token') || | ||||||||||||||||||||||||||||||||||||||||||
| (authorization && authorization.split(' ')[0] === 'Bearer') | ||||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||||
| return authorization.split(' ')[1]; | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| // tslint:disable-next-line: no-null-keyword | ||||||||||||||||||||||||||||||||||||||||||
| // eslint-disable-next-line @typescript-eslint/no-null-keyword | ||||||||||||||||||||||||||||||||||||||||||
| return null; | ||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+10
to
19
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Token extractor: return undefined, drop broken ESLint disable, and handle scheme case-insensitively. Prevents the reported rule error and makes parsing more robust. Apply this diff: -const getTokenFromHeader = (req: any) => {
- const { authorization } = req.headers;
- if (
- (authorization && authorization.split(' ')[0] === 'Token') ||
- (authorization && authorization.split(' ')[0] === 'Bearer')
- ) {
- return authorization.split(' ')[1];
- }
- // eslint-disable-next-line @typescript-eslint/no-null-keyword
- return null;
-};
+const getTokenFromHeader = (req: any) => {
+ const { authorization } = req.headers || {};
+ if (!authorization) return undefined;
+ const [scheme, token] = authorization.split(' ');
+ if (!token) return undefined;
+ const s = (scheme || '').toLowerCase();
+ if (s === 'bearer' || s === 'token') return token;
+ return undefined;
+};📝 Committable suggestion
Suggested change
🧰 Tools🪛 ESLint[error] 18-18: Definition for rule '@typescript-eslint/no-null-keyword' was not found. (@typescript-eslint/no-null-keyword) 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| const isAuth = jwt({ | ||||||||||||||||||||||||||||||||||||||||||
| algorithms: ['HS256'], // This is the default algorithm used by jsonwebtoken | ||||||||||||||||||||||||||||||||||||||||||
| secret: config.jwtSecret, // The _secret_ to sign the JWTs | ||||||||||||||||||||||||||||||||||||||||||
| secret: config.jwtSecret || 'default-secret-key', // The _secret_ to sign the JWTs | ||||||||||||||||||||||||||||||||||||||||||
| getToken: getTokenFromHeader, // How to extract the JWT from the request | ||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+24
to
25
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do not ship with a default JWT secret. Fail fast if JWT_SECRET is missing. Hardcoding a fallback like 'default-secret-key' is a critical security risk; tokens become trivially forgeable. Throw at startup unless explicitly running tests. Apply this diff: import { expressjwt as jwt } from 'express-jwt';
import config from '../../config';
+// Ensure JWT secret is configured (fail fast outside tests)
+if (!config.jwtSecret && process.env.NODE_ENV !== 'test') {
+ throw new Error('JWT secret is required. Set JWT_SECRET in the environment.');
+}
+
const isAuth = jwt({
algorithms: ['HS256'], // This is the default algorithm used by jsonwebtoken
- secret: config.jwtSecret || 'default-secret-key', // The _secret_ to sign the JWTs
+ secret: config.jwtSecret as string, // The _secret_ to sign the JWTs
getToken: getTokenFromHeader, // How to extract the JWT from the request
});📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,9 @@ | ||
| import * as dotenv from 'dotenv'; | ||
| import dotenv from 'dotenv'; | ||
|
|
||
| process.env.NODE_ENV = process.env.NODE_ENV || 'development'; | ||
|
|
||
| const envFound = dotenv.config(); | ||
| if (!envFound) { | ||
| if (envFound.error) { | ||
| // Throw generic error | ||
| throw new Error("Couldn't find .env file"); | ||
| } | ||
|
|
@@ -12,12 +12,12 @@ export default { | |
| /** | ||
| * Application port. | ||
| */ | ||
| port: parseInt(process.env.PORT) || 3000, | ||
| port: parseInt(process.env.PORT || '3000'), | ||
|
|
||
| /** | ||
| * JWT Secret | ||
| */ | ||
| jwtSecret: process.env.JWT_SECRET, | ||
| jwtSecret: process.env.JWT_SECRET || 'default-secret-key', | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do not ship a default JWT secret. Default secrets are a security risk. Require it in non-test envs. - jwtSecret: process.env.JWT_SECRET || 'default-secret-key',
+ jwtSecret: process.env.JWT_SECRET as string,Add a guard (outside this hunk): const isTest = process.env.NODE_ENV === 'test';
if (!process.env.JWT_SECRET && !isTest) {
throw new Error('JWT_SECRET is required');
}🤖 Prompt for AI Agents |
||
|
|
||
| /** | ||
| * MongoDB connection options. | ||
|
|
@@ -35,8 +35,8 @@ export default { | |
| /** | ||
| * Database host port. | ||
| */ | ||
| // tslint:disable-next-line: radix | ||
| port: Number.parseInt(process.env.TYPEORM_PORT), | ||
| // eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
| port: Number.parseInt(process.env.TYPEORM_PORT!), | ||
|
Comment on lines
+38
to
+39
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainFail-fast env parsing for DB port; avoid non-null assertion.
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- port: Number.parseInt(process.env.TYPEORM_PORT!),
+ port: process.env.TYPEORM_PORT
+ ? Number.parseInt(process.env.TYPEORM_PORT, 10)
+ : undefined,If port is required, add an explicit check near startup and fail with a descriptive message. 🏁 Script executed: #!/bin/bash
set -e
echo "Occurrences of TYPEORM_PORT in src/config/index.ts:"
rg -nC3 'TYPEORM_PORT' src/config/index.ts
echo -e "\nFirst 100 lines of src/config/index.ts for context:"
sed -n '1,100p' src/config/index.ts
echo -e "\nTypeORM connection initialization sites in src/:"
rg -nC3 'createConnection' src/**/*.ts || trueLength of output: 2967 🏁 Script executed: #!/bin/bash
set -e
echo "=== src/loaders/database.ts (first 200 lines) ==="
sed -n '1,200p' src/loaders/database.tsLength of output: 1047 Add fail-fast validation for TYPEORM_PORT
🤖 Prompt for AI Agents |
||
| /** | ||
| * Database username. | ||
| */ | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,12 +1,12 @@ | ||
| import * as express from 'express'; | ||
| import express from 'express'; | ||
| import 'reflect-metadata'; | ||
| import database from './database'; | ||
| import server from './server'; | ||
|
|
||
| export default async (app: express.Application) => { | ||
| const connection = await database(); | ||
| console.log('DB loaded and connected!'); | ||
| await database(); | ||
| console.warn('DB loaded and connected!'); | ||
|
|
||
| await server(app); | ||
| console.log('Server loaded!'); | ||
| console.warn('Server loaded!'); | ||
| }; |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -17,41 +17,41 @@ export enum Role { | |||||||||||||||||||||||||||||||
| @Entity() | ||||||||||||||||||||||||||||||||
| export class User { | ||||||||||||||||||||||||||||||||
| @ObjectIdColumn() | ||||||||||||||||||||||||||||||||
| public id: ObjectId; | ||||||||||||||||||||||||||||||||
| public id!: ObjectId; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| @Column() | ||||||||||||||||||||||||||||||||
| public name: string; | ||||||||||||||||||||||||||||||||
| public name!: string; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| @Index({ unique: true }) | ||||||||||||||||||||||||||||||||
| @Column({ | ||||||||||||||||||||||||||||||||
| unique: true, | ||||||||||||||||||||||||||||||||
| nullable: false, | ||||||||||||||||||||||||||||||||
| transformer: [lowercase], | ||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||
| public email: string; | ||||||||||||||||||||||||||||||||
| public email!: string; | ||||||||||||||||||||||||||||||||
|
Comment on lines
26
to
+31
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Avoid locale-dependent email lowercasing.
Apply in ValueTransformers.ts (outside this hunk): export const lowercase: ValueTransformer = {
- to: (entityValue: string) => {
- return entityValue.toLocaleLowerCase();
- },
+ to: (entityValue: string) => entityValue?.toLowerCase(),
from: (databaseValue: string) => {
return databaseValue;
},
};📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| @Column({ | ||||||||||||||||||||||||||||||||
| select: false, | ||||||||||||||||||||||||||||||||
| nullable: false, | ||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||
| public password: string; | ||||||||||||||||||||||||||||||||
| public password!: string; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| @Column({ | ||||||||||||||||||||||||||||||||
| select: false, | ||||||||||||||||||||||||||||||||
| nullable: false, | ||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||
| public salt: string; | ||||||||||||||||||||||||||||||||
| public salt!: string; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| @Column({ type: 'enum', enum: Role, default: Role.User }) | ||||||||||||||||||||||||||||||||
| public role: Role; | ||||||||||||||||||||||||||||||||
| public role!: Role; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| @Column() | ||||||||||||||||||||||||||||||||
| @CreateDateColumn() | ||||||||||||||||||||||||||||||||
| public createdAt: Date; | ||||||||||||||||||||||||||||||||
| public createdAt!: Date; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| @Column() | ||||||||||||||||||||||||||||||||
| @UpdateDateColumn() | ||||||||||||||||||||||||||||||||
| public updatedAt: Date; | ||||||||||||||||||||||||||||||||
| public updatedAt!: Date; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| // @AfterLoad() | ||||||||||||||||||||||||||||||||
| // public deletePropertis(): void { | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do not commit .env with secrets; switch to .env.example and gitignore .env.
Even dev secrets get reused; committing them is a recurring incident vector.
Suggested actions:
.envfrom VCS, rotate any leaked secrets..envto.gitignore, keep a.env.example(no secrets).Example
.gitignoreupdate:Example
.env.example:🧰 Tools
🪛 dotenv-linter (3.3.0)
[warning] 3-3: [UnorderedKey] The JWT_SECRET key should go before the NODE_ENV key
(UnorderedKey)
[warning] 10-10: [UnorderedKey] The TYPEORM_PASSWORD key should go before the TYPEORM_PORT key
(UnorderedKey)
[warning] 11-11: [UnorderedKey] The TYPEORM_DATABASE key should go before the TYPEORM_HOST key
(UnorderedKey)
[warning] 16-16: [EndingBlankLine] No blank line at the end of the file
(EndingBlankLine)
[warning] 16-16: [UnorderedKey] The AGENDA_CONCURRENCY key should go before the AGENDA_DB_COLLECTION key
(UnorderedKey)
🤖 Prompt for AI Agents