diff --git a/apps/api/scripts/seed-demo-data-pg.ts b/apps/api/scripts/seed-demo-data-pg.ts index 0fc198cc..99bd6a62 100644 --- a/apps/api/scripts/seed-demo-data-pg.ts +++ b/apps/api/scripts/seed-demo-data-pg.ts @@ -8,13 +8,14 @@ import { Pool } from 'pg'; import { randomUUID } from 'crypto'; +import { ENV_DEFAULT_ID } from '../src/connections/connection.constants'; const STORAGE_URL = process.env.STORAGE_URL || `postgresql://${process.env.STORAGE_POSTGRES_USER || 'betterdb'}:${process.env.STORAGE_POSTGRES_PASSWORD || 'devpassword'}@${process.env.STORAGE_POSTGRES_HOST || 'localhost'}:${process.env.STORAGE_POSTGRES_PORT || '5432'}/${process.env.STORAGE_POSTGRES_DATABASE || 'betterdb'}`; // Auto-detected at runtime from the most recently active connection -let CONNECTION_ID = process.env.CONNECTION_ID || 'env-default'; +let CONNECTION_ID = process.env.CONNECTION_ID || ENV_DEFAULT_ID; let SOURCE_HOST = process.env.SOURCE_HOST || 'localhost'; let SOURCE_PORT = parseInt(process.env.SOURCE_PORT || '6379', 10); @@ -435,11 +436,12 @@ async function main(): Promise { console.log('Connected to PostgreSQL'); // Auto-detect the most recently active connection ID and source info - if (CONNECTION_ID === 'env-default') { + if (CONNECTION_ID === ENV_DEFAULT_ID) { const res = await pool.query( `SELECT connection_id, source_host, source_port FROM command_log_entries - WHERE connection_id <> 'env-default' - ORDER BY captured_at DESC LIMIT 1` + WHERE connection_id <> $1 + ORDER BY captured_at DESC LIMIT 1`, + [ENV_DEFAULT_ID], ); if (res.rows.length > 0) { CONNECTION_ID = res.rows[0].connection_id; diff --git a/apps/api/scripts/seed-demo-data.ts b/apps/api/scripts/seed-demo-data.ts index 138b844e..4ce2156f 100644 --- a/apps/api/scripts/seed-demo-data.ts +++ b/apps/api/scripts/seed-demo-data.ts @@ -10,6 +10,7 @@ import Database from 'better-sqlite3'; import * as path from 'path'; import * as fs from 'fs'; import { randomUUID } from 'crypto'; +import { ENV_DEFAULT_ID } from '../src/connections/connection.constants'; // Configuration const DB_PATH = process.env.SQLITE_PATH || path.join(__dirname, '..', 'data', 'betterdb.sqlite'); @@ -708,7 +709,7 @@ function seedSlowLogEntries(): void { capturedAt: baseTs + randomInt(0, 5000), // captured shortly after sourceHost: SOURCE_HOST, sourcePort: SOURCE_PORT, - connectionId: 'env-default', + connectionId: ENV_DEFAULT_ID, }); } @@ -784,7 +785,7 @@ function seedLatencySnapshots(): void { eventName, latestEventTimestamp: eventTs, maxLatency: baseLatency, // microseconds - connectionId: 'env-default', + connectionId: ENV_DEFAULT_ID, }); } } @@ -856,7 +857,7 @@ function seedLatencyHistograms(): void { id: randomUUID(), timestamp: ts, data: JSON.stringify(data), - connectionId: 'env-default', + connectionId: ENV_DEFAULT_ID, }); } @@ -952,7 +953,7 @@ function seedMemorySnapshots(): void { cpuUser, ioThreadedReads: cumulativeIoReads, ioThreadedWrites: cumulativeIoWrites, - connectionId: 'env-default', + connectionId: ENV_DEFAULT_ID, }); } diff --git a/apps/api/src/common/dto/connections.dto.ts b/apps/api/src/common/dto/connections.dto.ts index 150bae6e..cb080887 100644 --- a/apps/api/src/common/dto/connections.dto.ts +++ b/apps/api/src/common/dto/connections.dto.ts @@ -1,5 +1,6 @@ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { IsString, IsNumber, IsBoolean, IsOptional, Min, Max, MinLength, MaxLength } from 'class-validator'; +import { ENV_DEFAULT_ID } from '../../connections/connection.constants'; import type { ConnectionStatus, ConnectionCapabilities, @@ -31,7 +32,7 @@ export class ConnectionCapabilitiesDto implements ConnectionCapabilities { * DTO for connection status */ export class ConnectionStatusDto implements ConnectionStatus { - @ApiProperty({ description: 'Unique connection identifier', example: 'env-default' }) + @ApiProperty({ description: 'Unique connection identifier', example: ENV_DEFAULT_ID }) id: string; @ApiProperty({ description: 'Human-readable connection name', example: 'Production Redis' }) @@ -138,7 +139,7 @@ export class ConnectionListResponseDto implements ConnectionListResponse { @ApiProperty({ description: 'List of all connections', type: [ConnectionStatusDto] }) connections: ConnectionStatus[]; - @ApiProperty({ description: 'Current default connection ID', nullable: true, example: 'env-default' }) + @ApiProperty({ description: 'Current default connection ID', nullable: true, example: ENV_DEFAULT_ID }) currentId: string | null; } @@ -146,7 +147,7 @@ export class ConnectionListResponseDto implements ConnectionListResponse { * DTO for current connection response */ export class CurrentConnectionResponseDto implements CurrentConnectionResponse { - @ApiProperty({ description: 'Current default connection ID', nullable: true, example: 'env-default' }) + @ApiProperty({ description: 'Current default connection ID', nullable: true, example: ENV_DEFAULT_ID }) id: string | null; } @@ -170,7 +171,7 @@ export class SuccessResponseDto { * DTO for individual connection health in all-connections response */ export class ConnectionHealthDto { - @ApiProperty({ description: 'Connection ID', example: 'env-default' }) + @ApiProperty({ description: 'Connection ID', example: ENV_DEFAULT_ID }) connectionId: string; @ApiProperty({ description: 'Connection name', example: 'Production Redis' }) diff --git a/apps/api/src/connections/__tests__/connection-registry.service.spec.ts b/apps/api/src/connections/__tests__/connection-registry.service.spec.ts index ae240f84..8e70cf93 100644 --- a/apps/api/src/connections/__tests__/connection-registry.service.spec.ts +++ b/apps/api/src/connections/__tests__/connection-registry.service.spec.ts @@ -2,6 +2,7 @@ import { Test, TestingModule } from '@nestjs/testing'; import { ConfigService } from '@nestjs/config'; import { NotFoundException } from '@nestjs/common'; import { ConnectionRegistry } from '../connection-registry.service'; +import { ENV_DEFAULT_ID } from '../connection.constants'; import { RuntimeCapabilityTracker } from '../runtime-capability-tracker.service'; import { StoragePort } from '../../common/interfaces/storage-port.interface'; import { DatabasePort } from '../../common/interfaces/database-port.interface'; @@ -129,14 +130,14 @@ describe('ConnectionRegistry', () => { expect(mockStorage.saveConnection).toHaveBeenCalledWith( expect.objectContaining({ - id: 'env-default', + id: ENV_DEFAULT_ID, name: 'Default', host: 'localhost', port: 6379, isDefault: true, }), ); - expect(registry.getDefaultId()).toBe('env-default'); + expect(registry.getDefaultId()).toBe(ENV_DEFAULT_ID); }); it('should load saved connections on init', async () => { @@ -188,7 +189,7 @@ describe('ConnectionRegistry', () => { }); it('should return specific connection by id', () => { - const conn = registry.get('env-default'); + const conn = registry.get(ENV_DEFAULT_ID); expect(conn).toBeDefined(); }); @@ -228,9 +229,9 @@ describe('ConnectionRegistry', () => { }); it('should return config for specific id', () => { - const config = registry.getConfig('env-default'); + const config = registry.getConfig(ENV_DEFAULT_ID); expect(config).toBeDefined(); - expect(config?.id).toBe('env-default'); + expect(config?.id).toBe(ENV_DEFAULT_ID); }); it('should return null for non-existent id', () => { @@ -289,7 +290,7 @@ describe('ConnectionRegistry', () => { }); it('should not allow removing env-default connection', async () => { - await expect(registry.removeConnection('env-default')).rejects.toThrow( + await expect(registry.removeConnection(ENV_DEFAULT_ID)).rejects.toThrow( 'Cannot remove the default environment connection', ); }); @@ -413,7 +414,7 @@ describe('ConnectionRegistry', () => { expect(list).toHaveLength(1); expect(list[0]).toMatchObject({ - id: 'env-default', + id: ENV_DEFAULT_ID, name: 'Default', host: 'localhost', port: 6379, @@ -438,10 +439,10 @@ describe('ConnectionRegistry', () => { }); it('should reconnect an existing connection', async () => { - await registry.reconnect('env-default'); + await registry.reconnect(ENV_DEFAULT_ID); // Should have created a new adapter and connected - const conn = registry.get('env-default'); + const conn = registry.get(ENV_DEFAULT_ID); expect(conn.isConnected()).toBe(true); }); @@ -459,7 +460,7 @@ describe('ConnectionRegistry', () => { it('should find connection by host and port', () => { const id = registry.findIdByHostPort('localhost', 6379); - expect(id).toBe('env-default'); + expect(id).toBe(ENV_DEFAULT_ID); }); it('should return null when not found', () => { @@ -470,7 +471,7 @@ describe('ConnectionRegistry', () => { describe('isEnvDefault', () => { it('should return true for env-default id', () => { - expect(registry.isEnvDefault('env-default')).toBe(true); + expect(registry.isEnvDefault(ENV_DEFAULT_ID)).toBe(true); }); it('should return false for other ids', () => { diff --git a/apps/api/src/connections/connection-registry.service.ts b/apps/api/src/connections/connection-registry.service.ts index 2b4529b3..f342438b 100644 --- a/apps/api/src/connections/connection-registry.service.ts +++ b/apps/api/src/connections/connection-registry.service.ts @@ -9,8 +9,8 @@ import { EnvelopeEncryptionService, getEncryptionService } from '../common/utils import { RuntimeCapabilityTracker } from './runtime-capability-tracker.service'; import { UsageTelemetryService } from '../telemetry/usage-telemetry.service'; -// TODO: Export and use across the codebase instead of hardcoded 'env-default' strings -export const ENV_DEFAULT_ID = 'env-default'; +export { ENV_DEFAULT_ID } from './connection.constants'; +import { ENV_DEFAULT_ID } from './connection.constants'; @Injectable() export class ConnectionRegistry implements OnModuleInit, OnModuleDestroy { diff --git a/apps/api/src/connections/connection.constants.ts b/apps/api/src/connections/connection.constants.ts new file mode 100644 index 00000000..e73b7cbc --- /dev/null +++ b/apps/api/src/connections/connection.constants.ts @@ -0,0 +1 @@ +export const ENV_DEFAULT_ID = 'env-default';