177 lines · 6.0 KB · TypeScript
RawDownload
import {  DesktopCloudAuthFetchInputSchema,  DesktopCloudAuthFetchResultSchema,} from "@t3tools/contracts";import {  clerkFrontendApiHostnameFromPublishableKey,  isAllowedClerkFrontendApiHostname,} from "@t3tools/shared/relayAuth";import * as Data from "effect/Data";import * as Effect from "effect/Effect";import { identity } from "effect/Function";import * as Layer from "effect/Layer";import * as Option from "effect/Option";import * as Schema from "effect/Schema";import { FetchHttpClient, HttpClient, HttpClientRequest } from "effect/unstable/http"; import * as DesktopCloudAuth from "../../app/DesktopCloudAuth.ts";import * as DesktopCloudAuthTokenStore from "../../app/DesktopCloudAuthTokenStore.ts";import * as IpcChannels from "../channels.ts";import { makeIpcMethod } from "../DesktopIpc.ts"; declare const __T3CODE_BUILD_CLERK_PUBLISHABLE_KEY__: string | undefined; export class DesktopCloudAuthFetchError extends Data.TaggedError("DesktopCloudAuthFetchError")<{  readonly reason: string;  readonly cause?: unknown;}> {  override get message() {    return this.reason;  }} function configuredClerkFrontendApiHostname(): string | null {  const publishableKey =    process.env.T3CODE_CLERK_PUBLISHABLE_KEY?.trim() ||    (typeof __T3CODE_BUILD_CLERK_PUBLISHABLE_KEY__ === "undefined"      ? ""      : __T3CODE_BUILD_CLERK_PUBLISHABLE_KEY__.trim());  if (!publishableKey) return null;   return clerkFrontendApiHostnameFromPublishableKey(publishableKey);} const allowedClerkFrontendApiHosts = (hostname: string): boolean =>  isAllowedClerkFrontendApiHostname(hostname, configuredClerkFrontendApiHostname()); export function validateClerkFrontendApiUrl(rawUrl: string): URL {  const url = new URL(rawUrl);  if (url.protocol !== "https:" || !allowedClerkFrontendApiHosts(url.hostname)) {    throw new DesktopCloudAuthFetchError({      reason: "Desktop cloud auth fetch is restricted to Clerk Frontend API HTTPS hosts.",    });  }  return url;} function executeCloudAuthFetch(url: URL, input: typeof DesktopCloudAuthFetchInputSchema.Type) {  return Effect.gen(function* () {    const method = (input.method ?? "GET") as "GET" | "POST";    const headers = new Headers(input.headers);    const response = yield* HttpClientRequest.make(method)(url).pipe(      HttpClientRequest.setHeaders(Object.fromEntries(headers.entries())),      input.body === undefined        ? identity        : HttpClientRequest.bodyText(input.body, headers.get("content-type") ?? undefined),      HttpClient.execute,      Effect.mapError(        (cause) =>          new DesktopCloudAuthFetchError({            reason: "Desktop cloud auth fetch failed to execute.",            cause,          }),      ),    );     const body = yield* response.text.pipe(      Effect.mapError(        (cause) =>          new DesktopCloudAuthFetchError({            reason: "Desktop cloud auth fetch response could not be read.",            cause,          }),      ),    );     return {      ok: response.status >= 200 && response.status < 300,      status: response.status,      statusText: "",      headers: response.headers,      body,    };  });} const electronNetFetchLayer = Layer.unwrap(  Effect.gen(function* () {    const electronFetch = yield* Effect.promise(async () => {      const electron = (await import("electron")) as {        readonly net?: { readonly fetch?: typeof globalThis.fetch };      };      return typeof electron.net?.fetch === "function"        ? electron.net.fetch.bind(electron.net)        : null;    }).pipe(Effect.catchCause(() => Effect.succeed(null)));     if (!electronFetch) {      yield* Effect.logWarning(        "electron.net.fetch is not available, falling back to global fetch. This may cause unexpected errors.",      );    }     return FetchHttpClient.layer.pipe(      Layer.provide(Layer.succeed(FetchHttpClient.Fetch, electronFetch ?? globalThis.fetch)),    );  }),); export const createCloudAuthRequest = makeIpcMethod({  channel: IpcChannels.CREATE_CLOUD_AUTH_REQUEST_CHANNEL,  payload: Schema.Void,  result: Schema.String,  handler: Effect.fn("desktop.ipc.cloudAuth.createRequest")(function* () {    const cloudAuth = yield* DesktopCloudAuth.DesktopCloudAuth;    return yield* cloudAuth.createRequest;  }),}); export const getCloudAuthToken = makeIpcMethod({  channel: IpcChannels.GET_CLOUD_AUTH_TOKEN_CHANNEL,  payload: Schema.Void,  result: Schema.NullOr(Schema.String),  handler: Effect.fn("desktop.ipc.cloudAuth.getToken")(function* () {    const tokenStore = yield* DesktopCloudAuthTokenStore.DesktopCloudAuthTokenStore;    return Option.getOrNull(yield* tokenStore.get);  }),}); export const setCloudAuthToken = makeIpcMethod({  channel: IpcChannels.SET_CLOUD_AUTH_TOKEN_CHANNEL,  payload: Schema.String,  result: Schema.Boolean,  handler: Effect.fn("desktop.ipc.cloudAuth.setToken")(function* (token) {    const tokenStore = yield* DesktopCloudAuthTokenStore.DesktopCloudAuthTokenStore;    return yield* tokenStore.set(token);  }),}); export const clearCloudAuthToken = makeIpcMethod({  channel: IpcChannels.CLEAR_CLOUD_AUTH_TOKEN_CHANNEL,  payload: Schema.Void,  result: Schema.Void,  handler: Effect.fn("desktop.ipc.cloudAuth.clearToken")(function* () {    const tokenStore = yield* DesktopCloudAuthTokenStore.DesktopCloudAuthTokenStore;    yield* tokenStore.clear;  }),}); export const fetchCloudAuth = makeIpcMethod({  channel: IpcChannels.FETCH_CLOUD_AUTH_CHANNEL,  payload: DesktopCloudAuthFetchInputSchema,  result: DesktopCloudAuthFetchResultSchema,  handler: Effect.fn("desktop.ipc.cloudAuth.fetch")(function* (input) {    const url = yield* Effect.try({      try: () => validateClerkFrontendApiUrl(input.url),      catch: (cause) =>        cause instanceof DesktopCloudAuthFetchError          ? cause          : new DesktopCloudAuthFetchError({              reason: "Desktop cloud auth fetch received an invalid URL.",              cause,            }),    });     return yield* executeCloudAuthFetch(url, input).pipe(Effect.provide(electronNetFetchLayer));  }),});