1import { DEFAULT_API_BASE_URL } from "@distilled.cloud/hetzner";
2import * as Effect from "effect/Effect";
3import * as Redacted from "effect/Redacted";
4import { getEnv, getEnvRedactedRequired } from "../Auth/Env.ts";
5import {
6 makeStoredAuthProvider,
7 storedSecret,
8 storedValueText,
9 type StoredAuthConfig,
10} from "../Auth/StoredAuthProvider.ts";
11
12export const HETZNER_AUTH_PROVIDER_NAME = "Hetzner";
13export const HCLOUD_TOKEN_ENV = "HCLOUD_TOKEN";
14export const HCLOUD_ENDPOINT_ENV = "HCLOUD_ENDPOINT";
15
16export type HetznerAuthConfig = StoredAuthConfig;
17
18export type HetznerResolvedCredentials = {
19 type: "token";
20 token: Redacted.Redacted<string>;
21 apiBaseUrl: string;
22 source: { type: HetznerAuthConfig["method"] | "env"; details?: string };
23};
24
25const hetznerAuth = makeStoredAuthProvider<HetznerResolvedCredentials>({
26 provider: HETZNER_AUTH_PROVIDER_NAME,
27 fields: [
28 { name: "token", label: "Hetzner Cloud API Token", secret: true },
29 {
30 name: "apiBaseUrl",
31 label: "Hetzner API endpoint",
32 optional: true,
33 placeholder: DEFAULT_API_BASE_URL,
34 },
35 ],
36 toResolved: (values) => ({
37 type: "token",
38 token: storedSecret(values.token) ?? Redacted.make(""),
39 apiBaseUrl: storedValueText(values.apiBaseUrl) ?? DEFAULT_API_BASE_URL,
40 source: { type: "stored" },
41 }),
42 readEnvironment: Effect.all({
43 token: getEnvRedactedRequired(HCLOUD_TOKEN_ENV),
44 apiBaseUrl: getEnv(HCLOUD_ENDPOINT_ENV),
45 }).pipe(
46 Effect.map(({ token, apiBaseUrl }) => ({
47 type: "token" as const,
48 token,
49 apiBaseUrl: apiBaseUrl ?? DEFAULT_API_BASE_URL,
50 source: {
51 type: "env" as const,
52 details: apiBaseUrl
53 ? `${HCLOUD_TOKEN_ENV}, ${HCLOUD_ENDPOINT_ENV}`
54 : HCLOUD_TOKEN_ENV,
55 },
56 })),
57 ),
58 environment: [
59 { name: HCLOUD_TOKEN_ENV, required: true, secret: true },
60 { name: HCLOUD_ENDPOINT_ENV, required: false },
61 ],
62});
63
64/**
65 * Layer that registers the Hetzner {@link AuthProvider} into the
66 * {@link AuthProviders} registry.
67 *
68 * Auth is a Hetzner Cloud API token (`HCLOUD_TOKEN`). An optional
69 * `HCLOUD_ENDPOINT` overrides the API root.
70 */
71export const HetznerAuth = hetznerAuth.layer;