| 1 | import * as Config from "effect/Config"; |
| 2 | import * as Context from "effect/Context"; |
| 3 | import * as Effect from "effect/Effect"; |
| 4 | import * as Layer from "effect/Layer"; |
| 5 | import * as Redacted from "effect/Redacted"; |
| 6 | import * as Schema from "effect/Schema"; |
| 7 | |
| 8 | export const ApnsEnvironment = Schema.Literals(["sandbox", "production"]); |
| 9 | export type ApnsEnvironment = typeof ApnsEnvironment.Type; |
| 10 | |
| 11 | export const ManagedEndpointCleanupMode = Schema.Literals(["off", "dry-run", "enabled"]); |
| 12 | export type ManagedEndpointCleanupMode = typeof ManagedEndpointCleanupMode.Type; |
| 13 | const decodeManagedEndpointCleanupMode = Schema.decodeUnknownEffect(ManagedEndpointCleanupMode); |
| 14 | |
| 15 | const RELAY_TUNNEL_CLEANUP_MODE = "RELAY_TUNNEL_CLEANUP_MODE"; |
| 16 | /** Separate switch for tunnels whose host never registered recovery. */ |
| 17 | const RELAY_LEGACY_TUNNEL_CLEANUP_MODE = "RELAY_LEGACY_TUNNEL_CLEANUP_MODE"; |
| 18 | |
| 19 | const cleanupModeConfig = (name: string) => |
| 20 | Config.String(name).pipe( |
| 21 | Config.withDefault("off"), |
| 22 | Config.map((value) => value.trim() || "off"), |
| 23 | Config.mapEffect((value) => |
| 24 | decodeManagedEndpointCleanupMode(value).pipe( |
| 25 | Effect.mapError((error) => new Config.ConfigError(error)), |
| 26 | ), |
| 27 | ), |
| 28 | ); |
| 29 | |
| 30 | export const managedEndpointCleanupModeConfig = cleanupModeConfig(RELAY_TUNNEL_CLEANUP_MODE); |
| 31 | export const legacyManagedEndpointCleanupModeConfig = cleanupModeConfig( |
| 32 | RELAY_LEGACY_TUNNEL_CLEANUP_MODE, |
| 33 | ); |
| 34 | |
| 35 | /** |
| 36 | * Overrides the 7-day legacy grace period, in minutes, so the disposable |
| 37 | * canary stage can exercise legacy cleanup. Ignored on the prod stage. |
| 38 | */ |
| 39 | const RELAY_LEGACY_TUNNEL_GRACE_MINUTES = "RELAY_LEGACY_TUNNEL_GRACE_MINUTES"; |
| 40 | |
| 41 | // A zero or negative override would be ignored at runtime, silently leaving |
| 42 | // the canary on the 7-day grace period, so reject it when the deploy reads it. |
| 43 | export const legacyTunnelGraceMinutesConfig = Config.option( |
| 44 | Config.schema( |
| 45 | Schema.NumberFromString.pipe(Schema.check(Schema.isInt(), Schema.isGreaterThan(0))), |
| 46 | RELAY_LEGACY_TUNNEL_GRACE_MINUTES, |
| 47 | ), |
| 48 | ); |
| 49 | |
| 50 | export interface ApnsCredentials { |
| 51 | readonly teamId: string; |
| 52 | readonly keyId: string; |
| 53 | readonly privateKey: Redacted.Redacted<string>; |
| 54 | readonly bundleId: string; |
| 55 | readonly environment: ApnsEnvironment; |
| 56 | } |
| 57 | |
| 58 | export class RelayConfiguration extends Context.Service< |
| 59 | RelayConfiguration, |
| 60 | { |
| 61 | readonly relayIssuer: string; |
| 62 | readonly apns: ApnsCredentials | null; |
| 63 | readonly fcmServiceAccount?: Redacted.Redacted<string>; |
| 64 | readonly clerkSecretKey: Redacted.Redacted<string>; |
| 65 | readonly clerkPublishableKey: string; |
| 66 | readonly clerkJwtAudience: string; |
| 67 | readonly apnsDeliveryJobSigningSecret: Redacted.Redacted<string>; |
| 68 | readonly cloudMintPrivateKey: Redacted.Redacted<string>; |
| 69 | readonly cloudMintPublicKey: string; |
| 70 | readonly managedEndpointBaseDomain: string | undefined; |
| 71 | readonly managedEndpointNamespace: string | undefined; |
| 72 | readonly managedEndpointCleanupMode?: ManagedEndpointCleanupMode; |
| 73 | readonly legacyManagedEndpointCleanupMode?: ManagedEndpointCleanupMode; |
| 74 | /** Canary-only override of the legacy grace period; ignored on prod. */ |
| 75 | readonly legacyTunnelGraceMinutes?: number; |
| 76 | } |
| 77 | >()("t3code-relay/Config/RelayConfiguration") {} |
| 78 | |
| 79 | export const make = (configuration: RelayConfiguration["Service"]) => |
| 80 | RelayConfiguration.of(configuration); |
| 81 | |
| 82 | export const layer = (configuration: RelayConfiguration["Service"]) => |
| 83 | Layer.succeed(RelayConfiguration, make(configuration)); |