Fix some for production
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
*
|
||||
!release/
|
||||
!release/**
|
||||
!Dockerfile
|
||||
+7
-45
@@ -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"]
|
||||
|
||||
@@ -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',
|
||||
};
|
||||
@@ -3,6 +3,7 @@
|
||||
"collection": "@nestjs/schematics",
|
||||
"sourceRoot": "src",
|
||||
"compilerOptions": {
|
||||
"deleteOutDir": true
|
||||
"deleteOutDir": true,
|
||||
"tsConfigPath": "tsconfig.build.json"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 {
|
||||
|
||||
+77966
File diff suppressed because one or more lines are too long
@@ -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);
|
||||
});
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"exclude": ["node_modules", "dist", "prisma", "scripts", "test", "**/*spec.ts"]
|
||||
}
|
||||
@@ -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/**/*"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user