chore: add sslmode support to PrismaService database URL parser (#5671)
This commit is contained in:
parent
d67a85b128
commit
92e3f52b47
2 changed files with 88 additions and 28 deletions
|
|
@ -507,13 +507,9 @@ describe('MockServerService', () => {
|
|||
);
|
||||
|
||||
expect(E.isRight(result)).toBe(true);
|
||||
expect(mockUserCollectionService.createUserCollection).toHaveBeenCalledWith(
|
||||
user,
|
||||
autoCreateInput.name,
|
||||
null,
|
||||
null,
|
||||
'REST',
|
||||
);
|
||||
expect(
|
||||
mockUserCollectionService.createUserCollection,
|
||||
).toHaveBeenCalledWith(user, autoCreateInput.name, null, null, 'REST');
|
||||
expect(mockPrisma.mockServer.create).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
data: expect.objectContaining({
|
||||
|
|
@ -549,7 +545,9 @@ describe('MockServerService', () => {
|
|||
);
|
||||
|
||||
expect(E.isRight(result)).toBe(true);
|
||||
expect(mockUserCollectionService.importCollectionsFromJSON).toHaveBeenCalled();
|
||||
expect(
|
||||
mockUserCollectionService.importCollectionsFromJSON,
|
||||
).toHaveBeenCalled();
|
||||
expect(mockPrisma.mockServer.create).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
data: expect.objectContaining({
|
||||
|
|
@ -629,7 +627,9 @@ describe('MockServerService', () => {
|
|||
);
|
||||
|
||||
expect(E.isRight(result)).toBe(true);
|
||||
expect(mockTeamCollectionService.importCollectionsFromJSON).toHaveBeenCalled();
|
||||
expect(
|
||||
mockTeamCollectionService.importCollectionsFromJSON,
|
||||
).toHaveBeenCalled();
|
||||
expect(mockPrisma.mockServer.create).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
data: expect.objectContaining({
|
||||
|
|
@ -700,7 +700,10 @@ describe('MockServerService', () => {
|
|||
autoCreateRequestExample: false,
|
||||
};
|
||||
|
||||
const createdCollection = { ...userCollection, id: 'rollback-coll-123' };
|
||||
const createdCollection = {
|
||||
...userCollection,
|
||||
id: 'rollback-coll-123',
|
||||
};
|
||||
mockUserCollectionService.createUserCollection.mockResolvedValue(
|
||||
E.right(createdCollection as any),
|
||||
);
|
||||
|
|
@ -717,10 +720,9 @@ describe('MockServerService', () => {
|
|||
);
|
||||
|
||||
expect(E.isLeft(result)).toBe(true);
|
||||
expect(mockUserCollectionService.deleteUserCollection).toHaveBeenCalledWith(
|
||||
'rollback-coll-123',
|
||||
user.uid,
|
||||
);
|
||||
expect(
|
||||
mockUserCollectionService.deleteUserCollection,
|
||||
).toHaveBeenCalledWith('rollback-coll-123', user.uid);
|
||||
});
|
||||
|
||||
test('should rollback team collection on mock server creation failure', async () => {
|
||||
|
|
@ -733,7 +735,10 @@ describe('MockServerService', () => {
|
|||
autoCreateRequestExample: false,
|
||||
};
|
||||
|
||||
const createdTeamColl = { ...teamCollection, id: 'rollback-team-coll-123' };
|
||||
const createdTeamColl = {
|
||||
...teamCollection,
|
||||
id: 'rollback-team-coll-123',
|
||||
};
|
||||
mockPrisma.team.findFirst.mockResolvedValue({ id: 'team123' } as any);
|
||||
mockTeamCollectionService.createCollection.mockResolvedValue(
|
||||
E.right(createdTeamColl as any),
|
||||
|
|
|
|||
|
|
@ -13,57 +13,112 @@ export class PrismaService
|
|||
|
||||
constructor() {
|
||||
const databaseUrl = process.env.DATABASE_URL;
|
||||
|
||||
if (!databaseUrl) {
|
||||
throw new Error('DATABASE_URL environment variable is not set');
|
||||
}
|
||||
|
||||
const { connectionString, schema, connectionLimit, connectTimeout } =
|
||||
PrismaService.parseDatabaseUrl(databaseUrl);
|
||||
const parsed = PrismaService.parseDatabaseUrl(databaseUrl);
|
||||
|
||||
// Generic SSL configuration for all database environments
|
||||
// Supports: AWS Aurora, Docker, local PostgreSQL, managed databases
|
||||
const sslConfig = PrismaService.getSSLConfig(parsed.sslMode);
|
||||
|
||||
const pool = new pg.Pool({
|
||||
connectionString,
|
||||
max: connectionLimit ?? 20,
|
||||
connectionString: parsed.connectionString,
|
||||
max: parsed.connectionLimit ?? 20,
|
||||
idleTimeoutMillis: 30000,
|
||||
connectionTimeoutMillis: connectTimeout ?? 5000,
|
||||
connectionTimeoutMillis: parsed.connectTimeout ?? 10000,
|
||||
ssl: sslConfig,
|
||||
});
|
||||
|
||||
const adapter = new PrismaPg(pool, {
|
||||
schema,
|
||||
schema: parsed.schema,
|
||||
});
|
||||
|
||||
super({
|
||||
adapter,
|
||||
transactionOptions: {
|
||||
maxWait: 5000, // 5 seconds
|
||||
timeout: 10000, // 10 seconds
|
||||
maxWait: 5000,
|
||||
timeout: 10000,
|
||||
},
|
||||
});
|
||||
|
||||
this.pool = pool;
|
||||
}
|
||||
|
||||
/**
|
||||
* --- SSL Configuration ---
|
||||
* Generic SSL handling for various database environments
|
||||
* - Local/Docker: No SSL (sslmode=disable or no sslmode)
|
||||
* - AWS Aurora/RDS: SSL with relaxed validation (common for managed databases)
|
||||
* - Custom: Set sslmode=verify-full for strict certificate validation
|
||||
*/
|
||||
private static getSSLConfig(
|
||||
sslMode?: string,
|
||||
): false | { rejectUnauthorized: boolean } {
|
||||
if (!sslMode || sslMode === 'disable') {
|
||||
// Local PostgreSQL, Docker containers - no SSL
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sslMode === 'require' || sslMode === 'prefer' || sslMode === 'allow') {
|
||||
// AWS Aurora, managed databases - SSL with relaxed validation
|
||||
// This is a pragmatic approach for cloud databases where:
|
||||
// - Connection is encrypted (prevents eavesdropping)
|
||||
// - Network isolation (VPC/firewall) provides additional security
|
||||
// - Certificate validation issues are common with managed services
|
||||
return { rejectUnauthorized: false };
|
||||
}
|
||||
|
||||
if (sslMode === 'verify-ca' || sslMode === 'verify-full') {
|
||||
// Strict certificate validation - requires proper CA certificates
|
||||
// Note: May require additional configuration for Prisma v7 + adapter-pg
|
||||
return { rejectUnauthorized: true };
|
||||
}
|
||||
|
||||
// Default to no SSL for unknown modes
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* --- DATABASE_URL Parser ---
|
||||
* Accepts:
|
||||
* ?schema=custom
|
||||
* ?connection_limit=10
|
||||
* ?connect_timeout=5000
|
||||
* ?sslmode=disable|prefer|require|verify-ca|verify-full
|
||||
*/
|
||||
private static parseDatabaseUrl(databaseUrl: string): {
|
||||
connectionString: string;
|
||||
schema: string;
|
||||
connectionLimit?: number;
|
||||
connectTimeout?: number;
|
||||
sslMode?: string;
|
||||
} {
|
||||
try {
|
||||
const url = new URL(databaseUrl);
|
||||
const schema = url.searchParams.get('schema') || 'public';
|
||||
const connectionLimit = url.searchParams.get('connection_limit');
|
||||
const connectTimeout = url.searchParams.get('connect_timeout');
|
||||
const connectionLimit = parseIntSafe(
|
||||
url.searchParams.get('connection_limit'),
|
||||
);
|
||||
const connectTimeout = parseIntSafe(
|
||||
url.searchParams.get('connect_timeout'),
|
||||
);
|
||||
const sslMode = url.searchParams.get('sslmode');
|
||||
|
||||
// Remove all custom parameters including sslmode
|
||||
// We handle SSL configuration programmatically via the ssl option
|
||||
url.searchParams.delete('schema');
|
||||
url.searchParams.delete('connection_limit');
|
||||
url.searchParams.delete('connect_timeout');
|
||||
url.searchParams.delete('sslmode');
|
||||
|
||||
return {
|
||||
connectionString: url.toString(),
|
||||
schema,
|
||||
connectionLimit: parseIntSafe(connectionLimit),
|
||||
connectTimeout: parseIntSafe(connectTimeout),
|
||||
connectionLimit,
|
||||
connectTimeout,
|
||||
sslMode: sslMode || undefined,
|
||||
};
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
|
|
|
|||
Loading…
Reference in a new issue