Files
lislgosms/api/src/prisma/prisma.service.ts
T

87 lines
3.5 KiB
TypeScript

import { Injectable, OnModuleDestroy } from '@nestjs/common';
import { PrismaPg } from '@prisma/adapter-pg';
import { PrismaClient } from '@prisma/client';
import { Pool } from 'pg';
import { requestContext } from '../common/request-context';
@Injectable()
export class PrismaService extends PrismaClient implements OnModuleDestroy {
private readonly databasePool: Pool;
private readonly databasePoolMax: number;
constructor() {
const processRole = process.env.CMPP_PROCESS_ROLE?.trim() || 'api';
const workerRole = processRole === 'worker';
const outboxRole = processRole === 'outbox';
const callbackRole = processRole === 'callback';
const protocolLogRole = processRole === 'protocol-log-worker';
const databaseUrl = protocolLogRole
? process.env.API_PROTOCOL_LOG_DATABASE_URL || process.env.DATABASE_URL
: outboxRole
? process.env.API_OUTBOX_DATABASE_URL || process.env.DATABASE_URL
: callbackRole
? process.env.API_CALLBACK_DATABASE_URL || process.env.DATABASE_URL
: workerRole
? process.env.API_WORKER_DATABASE_URL || process.env.DATABASE_URL
: process.env.DATABASE_URL;
const configuredPoolMax = Number(protocolLogRole
? process.env.API_PROTOCOL_LOG_DB_POOL_MAX ?? 4
: outboxRole
? process.env.API_OUTBOX_DB_POOL_MAX ?? 6
: callbackRole
? process.env.API_CALLBACK_DB_POOL_MAX ?? 16
: workerRole
? process.env.API_WORKER_DB_POOL_MAX ?? 8
: process.env.API_DB_POOL_MAX ?? 32);
const poolMax = Number.isInteger(configuredPoolMax) && configuredPoolMax > 0
? configuredPoolMax
: protocolLogRole ? 4 : outboxRole ? 6 : callbackRole ? 16 : workerRole ? 8 : 32;
const allowDevelopmentDefault = process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test';
if (!databaseUrl && !allowDevelopmentDefault) {
throw new Error(`DATABASE_URL is required for CMPP process role ${processRole}`);
}
const databasePool = new Pool({
connectionString: databaseUrl
?? 'postgresql://cmpp:cmpp_password@localhost:5432/cmpp_platform?schema=public',
// API capacity must be reserved independently from the heavier Worker
// transactions; explicit bounds also protect PostgreSQL max_connections.
max: poolMax,
});
super({ adapter: new PrismaPg(databasePool, { disposeExternalPool: true }) });
this.databasePool = databasePool;
this.databasePoolMax = poolMax;
const operationLog = this.operationLog;
Object.defineProperty(this, 'operationLog', {
value: new Proxy(operationLog, {
get(target, property, receiver) {
if (property === 'create') {
return (args: { data: Record<string, unknown> }) => {
const ipAddress = requestContext.getStore()?.ipAddress;
return (target.create as (input: unknown) => unknown)({
...args,
data: { ...args.data, ipAddress: typeof args.data.ipAddress === 'string' ? args.data.ipAddress : ipAddress },
});
};
}
const value = Reflect.get(target, property, receiver);
return typeof value === 'function' ? value.bind(target) : value;
},
}),
configurable: true,
});
}
getPoolState() {
return {
max: this.databasePoolMax,
total: this.databasePool.totalCount,
idle: this.databasePool.idleCount,
waiting: this.databasePool.waitingCount,
};
}
async onModuleDestroy() {
await this.$disconnect();
}
}