Fix some for production

This commit is contained in:
mahmood
2026-09-10 12:17:47 +03:30
parent 78e148a6b5
commit cea3b808d8
121 changed files with 86493 additions and 211 deletions
+4
View File
@@ -0,0 +1,4 @@
*
!release/
!release/**
!Dockerfile
+7 -45
View File
@@ -1,51 +1,13 @@
FROM node:20-bookworm AS base
FROM docker.arvancloud.ir/node:20-alpine
# Server often cannot reach registry.npmjs.org (Cloudflare). Override in Dokploy if needed.
ARG NPM_REGISTRY=https://mirror.kargadan.ir/repository/npm-group/
ENV npm_config_registry=${NPM_REGISTRY}
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
# Skip corepack (it fetches pnpm from npmjs/Cloudflare). Install from mirror instead.
# Pin major only — Iranian mirrors often lag behind exact patch versions (e.g. 2.10.12).
RUN npm install -g pnpm@9 turbo@2 \
&& printf 'registry=%s\n' "${NPM_REGISTRY}" > /root/.npmrc
FROM base AS builder
WORKDIR /app
COPY . .
RUN turbo prune api --docker
FROM base AS installer
# Pre-built on the developer machine:
# pnpm --filter @gold-bank/shared-types build && pnpm --filter api build:release
WORKDIR /app
# First install dependencies (as they change less often)
COPY .gitignore .gitignore
COPY --from=builder /app/out/json/ .
COPY --from=builder /app/out/pnpm-lock.yaml ./pnpm-lock.yaml
RUN pnpm install --frozen-lockfile
ENV NODE_ENV=production
# Build the project and its dependencies
COPY --from=builder /app/out/full/ .
COPY turbo.json turbo.json
COPY tsconfig.base.json tsconfig.base.json
# Re-link workspace after full source copy (pnpm symlinks)
RUN pnpm install --frozen-lockfile --offline || pnpm install --frozen-lockfile
# Generate prisma client before building
RUN pnpm --filter api exec prisma generate
RUN pnpm turbo run build --filter=api...
FROM base AS runner
WORKDIR /app
# Don't run production as root
RUN groupadd --system --gid 1001 nodejs \
&& useradd --system --uid 1001 --gid nodejs nestjs
USER nestjs
COPY --from=installer /app .
WORKDIR /app/apps/api
COPY release/ ./
EXPOSE 3000
CMD ["node", "dist/main.js"]
CMD ["node", "app.js"]
+26
View File
@@ -0,0 +1,26 @@
/**
* esbuild config for Docker release bundle (apps/api/release/app.js).
* Used by: node scripts/bundle-release.cjs
*/
const path = require('path');
module.exports = {
entryPoints: [path.join(__dirname, 'dist/main.js')],
outfile: path.join(__dirname, 'release/app.js'),
bundle: true,
platform: 'node',
target: 'node20',
format: 'cjs',
// Prisma ships native binaries — keep out of the JS bundle and vendor into release/
// Nest optional peers are required dynamically and are not used by this API.
external: [
'@prisma/client',
'.prisma/client',
'@nestjs/microservices',
'@nestjs/microservices/*',
'@nestjs/websockets',
'@nestjs/websockets/*',
],
sourcemap: false,
logLevel: 'info',
};
+2 -1
View File
@@ -3,6 +3,7 @@
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"deleteOutDir": true
"deleteOutDir": true,
"tsConfigPath": "tsconfig.build.json"
}
}
+2
View File
@@ -5,6 +5,7 @@
"scripts": {
"dev": "nest start --watch",
"build": "nest build",
"build:release": "pnpm exec prisma generate && node scripts/clean-dist.cjs && nest build && node scripts/bundle-release.cjs",
"lint": "eslint \"src/**/*.ts\"",
"test": "jest",
"prisma:push": "prisma db push",
@@ -30,6 +31,7 @@
"@nestjs/cli": "^10.4.5",
"@types/express": "^4.17.21",
"@types/node": "^20.14.0",
"esbuild": "^0.25.0",
"prisma": "^5.19.0",
"ts-node": "^10.9.2",
"typescript": "^5.5.0"
+3 -1
View File
@@ -4,7 +4,9 @@
// All money/weight fields use Decimal, never Float, to avoid rounding drift in debt math.
generator client {
provider = "prisma-client-js"
provider = "prisma-client-js"
// native = local Windows/macOS build; linux-musl = Arvan/Alpine Docker runtime
binaryTargets = ["native", "linux-musl-openssl-3.0.x"]
}
datasource db {
File diff suppressed because one or more lines are too long
+78
View File
@@ -0,0 +1,78 @@
/**
* باندل تک‌فایل برای Docker (release/app.js) بعد از nest build.
* Prisma client + engines را کنار app.js کپی می‌کند تا روی Alpine بدون npm کار کند.
*/
const esbuild = require('esbuild');
const {
existsSync,
mkdirSync,
rmSync,
cpSync,
realpathSync,
} = require('fs');
const path = require('path');
const root = path.join(__dirname, '..');
const config = require(path.join(root, 'esbuild.release.config.cjs'));
const releaseDir = path.join(root, 'release');
function resolvePkg(name) {
return path.dirname(require.resolve(`${name}/package.json`, { paths: [root] }));
}
function copyPrismaIntoRelease() {
const prismaClientSrc = resolvePkg('@prisma/client');
const prismaClientDest = path.join(releaseDir, 'node_modules', '@prisma', 'client');
mkdirSync(path.dirname(prismaClientDest), { recursive: true });
cpSync(prismaClientSrc, prismaClientDest, { recursive: true });
// .prisma/client lives next to @prisma/client in pnpm layout (or under node_modules/.prisma)
const candidates = [
path.join(root, 'node_modules', '.prisma', 'client'),
path.join(prismaClientSrc, '..', '.prisma', 'client'),
path.join(path.dirname(prismaClientSrc), '.prisma', 'client'),
];
let prismaEngineSrc = candidates.find((p) => existsSync(p));
if (!prismaEngineSrc) {
// pnpm: @prisma/client -> .../node_modules/@prisma/client ; .prisma is sibling under node_modules
const nm = path.join(prismaClientSrc, '..', '..', '.prisma', 'client');
if (existsSync(nm)) prismaEngineSrc = nm;
}
if (!prismaEngineSrc) {
throw new Error(
'Prisma generated client (.prisma/client) یافت نشد. ابتدا: pnpm --filter api exec prisma generate',
);
}
const prismaEngineDest = path.join(releaseDir, 'node_modules', '.prisma', 'client');
mkdirSync(path.dirname(prismaEngineDest), { recursive: true });
cpSync(realpathSync(prismaEngineSrc), prismaEngineDest, { recursive: true });
}
async function main() {
const entry = config.entryPoints[0];
const fallbackEntry = path.join(root, 'dist', 'src', 'main.js');
if (!existsSync(entry)) {
if (existsSync(fallbackEntry)) {
config.entryPoints = [fallbackEntry];
} else {
console.error('dist/main.js یافت نشد. ابتدا اجرا کنید: pnpm --filter api build');
process.exit(1);
}
}
rmSync(releaseDir, { recursive: true, force: true });
mkdirSync(releaseDir, { recursive: true });
await esbuild.build(config);
copyPrismaIntoRelease();
console.log('نوشت:', config.outfile);
console.log('Prisma client در release/node_modules کپی شد.');
}
main().catch((err) => {
console.error(err);
process.exit(1);
});
+20
View File
@@ -0,0 +1,20 @@
/**
* Wipe Nest emit output + stale TypeScript incremental caches.
* Incremental caches can skip emit after dist/ is deleted (empty dist, exit 0).
*/
const { rmSync, existsSync } = require('fs');
const path = require('path');
const root = path.join(__dirname, '..');
const targets = [
path.join(root, 'dist'),
path.join(root, 'tsconfig.tsbuildinfo'),
path.join(root, 'tsconfig.build.tsbuildinfo'),
];
for (const target of targets) {
if (existsSync(target)) {
rmSync(target, { recursive: true, force: true });
console.log('removed', path.relative(root, target) || 'dist');
}
}
+4
View File
@@ -0,0 +1,4 @@
{
"extends": "./tsconfig.json",
"exclude": ["node_modules", "dist", "prisma", "scripts", "test", "**/*spec.ts"]
}
+4 -2
View File
@@ -3,11 +3,13 @@
"compilerOptions": {
"module": "commonjs",
"outDir": "./dist",
"rootDir": "./src",
"baseUrl": "./",
"incremental": true,
"incremental": false,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"strictNullChecks": true,
"sourceMap": true
}
},
"include": ["src/**/*"]
}