| 1 | import { |
| 2 | DEFAULT_API_BASE_URL, |
| 3 | normalizeApiBaseUrl, |
| 4 | } from "@distilled.cloud/fly-io"; |
| 5 | import * as Effect from "effect/Effect"; |
| 6 | import * as Redacted from "effect/Redacted"; |
| 7 | import { getEnv, getEnvRedactedRequired } from "../Auth/Env.ts"; |
| 8 | import { |
| 9 | makeStoredAuthProvider, |
| 10 | storedSecret, |
| 11 | storedValueText, |
| 12 | type StoredAuthConfig, |
| 13 | } from "../Auth/StoredAuthProvider.ts"; |
| 14 | |
| 15 | export const FLY_AUTH_PROVIDER_NAME = "Fly"; |
| 16 | export const FLY_API_TOKEN_ENV = "FLY_API_TOKEN"; |
| 17 | export const FLY_API_HOSTNAME_ENV = "FLY_API_HOSTNAME"; |
| 18 | |
| 19 | export type FlyAuthConfig = StoredAuthConfig; |
| 20 | |
| 21 | export type FlyResolvedCredentials = { |
| 22 | type: "token"; |
| 23 | apiKey: Redacted.Redacted<string>; |
| 24 | apiBaseUrl: string; |
| 25 | source: { type: FlyAuthConfig["method"] | "env"; details?: string }; |
| 26 | }; |
| 27 | |
| 28 | const resolveApiBaseUrl = (explicit?: string) => |
| 29 | getEnv(FLY_API_HOSTNAME_ENV).pipe( |
| 30 | Effect.map((fromEnv) => normalizeApiBaseUrl(explicit ?? fromEnv)), |
| 31 | ); |
| 32 | |
| 33 | const flyAuth = makeStoredAuthProvider<FlyResolvedCredentials>({ |
| 34 | provider: FLY_AUTH_PROVIDER_NAME, |
| 35 | fields: [ |
| 36 | { name: "apiKey", label: "Fly.io API Token", secret: true }, |
| 37 | { |
| 38 | name: "apiBaseUrl", |
| 39 | label: "Fly API hostname", |
| 40 | placeholder: DEFAULT_API_BASE_URL, |
| 41 | optional: true, |
| 42 | }, |
| 43 | ], |
| 44 | toResolved: (values) => ({ |
| 45 | type: "token", |
| 46 | apiKey: storedSecret(values.apiKey) ?? Redacted.make(""), |
| 47 | apiBaseUrl: normalizeApiBaseUrl(storedValueText(values.apiBaseUrl)), |
| 48 | source: { type: "stored" }, |
| 49 | }), |
| 50 | readEnvironment: Effect.gen(function* () { |
| 51 | const apiKey = yield* getEnvRedactedRequired(FLY_API_TOKEN_ENV); |
| 52 | const apiBaseUrl = yield* resolveApiBaseUrl(); |
| 53 | return { |
| 54 | type: "token" as const, |
| 55 | apiKey, |
| 56 | apiBaseUrl, |
| 57 | source: { type: "env" as const, details: FLY_API_TOKEN_ENV }, |
| 58 | }; |
| 59 | }), |
| 60 | environment: [ |
| 61 | { name: FLY_API_TOKEN_ENV, required: true, secret: true }, |
| 62 | { name: FLY_API_HOSTNAME_ENV, required: false }, |
| 63 | ], |
| 64 | }); |
| 65 | |
| 66 | /** |
| 67 | * Layer that registers the Fly {@link AuthProvider} into the |
| 68 | * {@link AuthProviders} registry. Fly uses a stored API token locally and |
| 69 | * `FLY_API_TOKEN` in CI; `FLY_API_HOSTNAME` optionally overrides the API root. |
| 70 | */ |
| 71 | export const FlyAuth = flyAuth.layer; |