This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

semble / src / shared / infrastructure / config / EnvironmentConfigService.ts
8.8 kB 317 lines
1import { ATPROTO_NSID } from 'src/shared/constants/atproto'; 2 3export enum Environment { 4 LOCAL = 'local', 5 DEV = 'dev', 6 PROD = 'prod', 7} 8 9export interface EnvironmentConfig { 10 environment: Environment; 11 runtime: { 12 useMockPersistence: boolean; 13 useMockAuth: boolean; 14 useFakePublishers: boolean; 15 useMockVectorDb: boolean; 16 }; 17 database: { 18 url: string; 19 }; 20 auth: { 21 jwtSecret: string; 22 accessTokenExpiresIn: number; 23 refreshTokenExpiresIn: number; 24 }; 25 atproto: { 26 firehoseWebsocket: string; 27 jetstreamWebsocket: string; 28 serviceEndpoint: string; 29 baseUrl: string; 30 baseCollection: string; 31 collections: { 32 card: string; 33 collection: string; 34 collectionLink: string; 35 marginBookmark: string; 36 marginCollection: string; 37 marginCollectionItem: string; 38 collectionLinkRemoval: string; 39 follow: string; 40 connection: string; 41 }; 42 serviceAccount: { 43 identifier: string; 44 appPassword: string; 45 }; 46 }; 47 server: { 48 port: number; 49 host: string; 50 }; 51 app: { 52 appUrl: string; 53 }; 54 iframely: { 55 apiKey: string; 56 }; 57 upstash: { 58 vectorUrl: string; 59 vectorToken: string; 60 }; 61 workers: { 62 redisUrl: string; 63 redisConfig: { 64 host: string; 65 port: number; 66 password?: string; 67 maxRetriesPerRequest: number | null; // Disable automatic retries 68 }; 69 }; 70 sentry: { 71 dsn: string; 72 environment: string; 73 }; 74} 75 76export class EnvironmentConfigService { 77 private config: EnvironmentConfig; 78 79 constructor() { 80 const environment = (process.env.NODE_ENV || 81 Environment.LOCAL) as Environment; 82 83 this.config = { 84 environment, 85 runtime: { 86 useMockPersistence: this.determineMockPersistenceFlag(), 87 useMockAuth: process.env.USE_MOCK_AUTH === 'true', 88 useFakePublishers: process.env.USE_FAKE_PUBLISHERS === 'true', 89 useMockVectorDb: process.env.USE_MOCK_VECTOR_DB === 'true', 90 }, 91 database: { 92 url: 93 process.env.DATABASE_URL || 94 'postgres://postgres:postgres@localhost:5432/annotations', 95 }, 96 auth: { 97 jwtSecret: 98 process.env.JWT_SECRET || 'default-secret-change-in-production', 99 accessTokenExpiresIn: parseInt( 100 process.env.ACCESS_TOKEN_EXPIRES_IN || '3600', 101 ), 102 refreshTokenExpiresIn: parseInt( 103 process.env.REFRESH_TOKEN_EXPIRES_IN || '2592000', 104 ), 105 }, 106 atproto: { 107 firehoseWebsocket: 108 process.env.ATPROTO_FIREHOSE_WEBSOCKET || 109 'wss://relay1.us-west.bsky.network', 110 jetstreamWebsocket: 111 process.env.ATPROTO_JETSTREAM_WEBSOCKET || 112 'wss://jetstream2.us-west.bsky.network', 113 serviceEndpoint: 114 process.env.ATPROTO_SERVICE_ENDPOINT || 'https://bsky.social', 115 baseUrl: process.env.BASE_URL || 'http://127.0.0.1:3000', 116 baseCollection: 117 environment === Environment.PROD 118 ? ATPROTO_NSID.COSMIK.NAMESPACE 119 : `${ATPROTO_NSID.COSMIK.NAMESPACE}.${environment}`, 120 collections: { 121 card: 122 environment === Environment.PROD 123 ? ATPROTO_NSID.COSMIK.CARD 124 : `${ATPROTO_NSID.COSMIK.NAMESPACE}.${environment}.card`, 125 collection: 126 environment === Environment.PROD 127 ? ATPROTO_NSID.COSMIK.COLLECTION 128 : `${ATPROTO_NSID.COSMIK.NAMESPACE}.${environment}.collection`, 129 collectionLink: 130 environment === Environment.PROD 131 ? ATPROTO_NSID.COSMIK.COLLECTION_LINK 132 : `${ATPROTO_NSID.COSMIK.NAMESPACE}.${environment}.collectionLink`, 133 collectionLinkRemoval: 134 environment === Environment.PROD 135 ? ATPROTO_NSID.COSMIK.COLLECTION_LINK_REMOVAL 136 : `${ATPROTO_NSID.COSMIK.NAMESPACE}.${environment}.collectionLinkRemoval`, 137 follow: 138 environment === Environment.PROD 139 ? ATPROTO_NSID.COSMIK.FOLLOW 140 : `${ATPROTO_NSID.COSMIK.NAMESPACE}.${environment}.follow`, 141 connection: 142 environment === Environment.PROD 143 ? ATPROTO_NSID.COSMIK.CONNECTION 144 : `${ATPROTO_NSID.COSMIK.NAMESPACE}.${environment}.connection`, 145 // Margin collections - no environment suffix 146 marginBookmark: ATPROTO_NSID.MARGIN.BOOKMARK, 147 marginCollection: ATPROTO_NSID.MARGIN.COLLECTION, 148 marginCollectionItem: ATPROTO_NSID.MARGIN.COLLECTION_ITEM, 149 }, 150 serviceAccount: { 151 identifier: process.env.BSKY_SERVICE_ACCOUNT_IDENTIFIER || '', 152 appPassword: process.env.BSKY_SERVICE_ACCOUNT_APP_PASSWORD || '', 153 }, 154 }, 155 server: { 156 port: parseInt(process.env.PORT || '3000'), 157 host: process.env.HOST || '127.0.0.1', 158 }, 159 app: { 160 appUrl: process.env.APP_URL || 'http://127.0.0.1:4000', 161 }, 162 iframely: { 163 apiKey: process.env.IFRAMELY_API_KEY || '', 164 }, 165 upstash: { 166 vectorUrl: process.env.UPSTASH_VECTOR_REST_URL || '', 167 vectorToken: process.env.UPSTASH_VECTOR_REST_TOKEN || '', 168 }, 169 workers: { 170 redisConfig: { 171 host: process.env.REDIS_HOST || 'localhost', 172 port: parseInt(process.env.REDIS_PORT || '6379', 10), 173 password: process.env.REDIS_PASSWORD || undefined, 174 maxRetriesPerRequest: null, // Disable automatic retries 175 }, 176 redisUrl: process.env.REDIS_URL || 'redis://localhost:6379', 177 }, 178 sentry: { 179 dsn: process.env.SENTRY_DSN || '', 180 environment: environment, 181 }, 182 }; 183 184 this.applyEnvironmentSpecificConfig(); 185 } 186 187 private applyEnvironmentSpecificConfig(): void { 188 switch (this.config.environment) { 189 case Environment.DEV: 190 // Override defaults with dev-specific values 191 break; 192 case Environment.PROD: 193 // Override defaults with production-specific values 194 if ( 195 this.config.auth.jwtSecret === 'default-secret-change-in-production' 196 ) { 197 throw new Error('JWT secret must be set in production environment'); 198 } 199 break; 200 case Environment.LOCAL: 201 default: 202 // Local development defaults are already set 203 break; 204 } 205 } 206 207 public get(): EnvironmentConfig { 208 return this.config; 209 } 210 211 public getDatabaseConfig() { 212 return this.config.database; 213 } 214 215 public getAuthConfig() { 216 return this.config.auth; 217 } 218 219 public getAtProtoConfig() { 220 return this.config.atproto; 221 } 222 223 public getAtProtoCollections() { 224 return this.config.atproto.collections; 225 } 226 227 public getAtProtoBaseCollection() { 228 return this.config.atproto.baseCollection; 229 } 230 231 public getAtProtoServiceAccount() { 232 return this.config.atproto.serviceAccount; 233 } 234 235 public getServerConfig() { 236 return this.config.server; 237 } 238 public getAppConfig() { 239 return this.config.app; 240 } 241 242 public getIFramelyApiKey(): string { 243 return this.config.iframely.apiKey; 244 } 245 246 public getUpstashConfig() { 247 return this.config.upstash; 248 } 249 250 public getWorkersConfig() { 251 return this.config.workers; 252 } 253 254 public getRedisConfig() { 255 return this.config.workers.redisConfig; 256 } 257 258 public getSentryConfig() { 259 return this.config.sentry; 260 } 261 262 public getRuntimeConfig() { 263 return this.config.runtime; 264 } 265 266 public shouldUseMockPersistence(): boolean { 267 return this.config.runtime.useMockPersistence; 268 } 269 270 public shouldUseMockRepos(): boolean { 271 return this.config.runtime.useMockPersistence; 272 } 273 274 public shouldUseInMemoryEvents(): boolean { 275 return this.config.runtime.useMockPersistence; 276 } 277 278 public shouldUseMockAuth(): boolean { 279 return this.config.runtime.useMockAuth; 280 } 281 282 public shouldUseFakePublishers(): boolean { 283 return this.config.runtime.useFakePublishers; 284 } 285 286 public shouldUseMockVectorDb(): boolean { 287 return this.config.runtime.useMockVectorDb; 288 } 289 290 // Convenience methods for common combinations 291 public isFullyMocked(): boolean { 292 const r = this.config.runtime; 293 return r.useMockPersistence && r.useMockAuth && r.useFakePublishers; 294 } 295 296 public isMockPersistenceEnabled(): boolean { 297 return this.config.runtime.useMockPersistence; 298 } 299 300 private determineMockPersistenceFlag(): boolean { 301 // New unified flag takes precedence 302 if (process.env.USE_MOCK_PERSISTENCE !== undefined) { 303 return process.env.USE_MOCK_PERSISTENCE === 'true'; 304 } 305 306 // Legacy support - if either old flag is false, persistence is disabled 307 if ( 308 process.env.USE_MOCK_REPOS === 'true' || 309 process.env.USE_IN_MEMORY_EVENTS === 'true' 310 ) { 311 return true; 312 } 313 314 // Default to false (use mock persistence) unless explicitly enabled 315 return false; 316 } 317}