235 lines · 8.3 KB · TypeScript
RawDownload
import {  bootstrapRemoteBearerSession,  fetchRemoteSessionState,  issueRemoteWebSocketTicket,  RemoteEnvironmentAuthUndeclaredStatusError,  type RemoteEnvironmentAuthError,} from "@t3tools/client-runtime/authorization";import { fetchRemoteEnvironmentDescriptor } from "@t3tools/client-runtime/environment";import {  EnvironmentAuthInvalidError,  DesktopDiscoveredSshHostSchema,  DesktopSshBearerBootstrapInputSchema,  DesktopSshBearerRequestInputSchema,  DesktopSshEnvironmentEnsureInputSchema,  DesktopSshEnvironmentEnsureResultSchema,  DesktopSshEnvironmentTargetSchema,  DesktopSshHttpBaseUrlInputSchema,  DesktopSshPasswordPromptCancelledType,  DesktopSshPasswordPromptResolutionInputSchema,  ExecutionEnvironmentDescriptor,  EnvironmentInternalError,  EnvironmentOperationForbiddenError,  EnvironmentRequestInvalidError,  EnvironmentScopeRequiredError,  AuthAccessTokenResult,  AuthSessionState,  AuthWebSocketTicketResult,} from "@t3tools/contracts";import { SshHttpBridgeError } from "@t3tools/ssh/errors";import { resolveLoopbackSshHttpBaseUrl } from "@t3tools/ssh/tunnel";import * as Data from "effect/Data";import * as Effect from "effect/Effect";import * as Schema from "effect/Schema"; import * as IpcChannels from "../channels.ts";import * as DesktopIpc from "../DesktopIpc.ts";import * as DesktopSshEnvironment from "../../ssh/DesktopSshEnvironment.ts";import * as DesktopSshPasswordPrompts from "../../ssh/DesktopSshPasswordPrompts.ts"; type DesktopSshEnvironmentRequestOperation =  | "fetch-environment-descriptor"  | "bootstrap-bearer-session"  | "fetch-session-state"  | "issue-websocket-ticket"; type DesktopSshEnvironmentRequestCause = RemoteEnvironmentAuthError | SshHttpBridgeError; const isEnvironmentAuthInvalidError = Schema.is(EnvironmentAuthInvalidError);const isEnvironmentInternalError = Schema.is(EnvironmentInternalError);const isEnvironmentOperationForbiddenError = Schema.is(EnvironmentOperationForbiddenError);const isEnvironmentRequestInvalidError = Schema.is(EnvironmentRequestInvalidError);const isEnvironmentScopeRequiredError = Schema.is(EnvironmentScopeRequiredError); function readSshHttpStatus(cause: DesktopSshEnvironmentRequestCause): number | null {  if (    cause instanceof RemoteEnvironmentAuthUndeclaredStatusError ||    cause instanceof SshHttpBridgeError  ) {    return cause.status ?? null;  }  if (isEnvironmentRequestInvalidError(cause)) {    return 400;  }  if (isEnvironmentAuthInvalidError(cause)) {    return 401;  }  if (isEnvironmentScopeRequiredError(cause)) {    return 403;  }  if (isEnvironmentOperationForbiddenError(cause)) {    return 403;  }  if (isEnvironmentInternalError(cause)) {    return 500;  }  return null;} export class DesktopSshEnvironmentRequestError extends Data.TaggedError(  "DesktopSshEnvironmentRequestError",)<{  readonly operation: DesktopSshEnvironmentRequestOperation;  readonly cause: DesktopSshEnvironmentRequestCause;  readonly sshHttpStatus: number | null;}> {  override get message() {    const prefix = this.sshHttpStatus === null ? "" : `[ssh_http:${this.sshHttpStatus}] `;    return `${prefix}SSH remote API request failed during ${this.operation}.`;  }} const withLoopbackSshApi =  <A, R>(    operation: DesktopSshEnvironmentRequestOperation,    use: (httpBaseUrl: string) => Effect.Effect<A, RemoteEnvironmentAuthError, R>,  ) =>  (httpBaseUrl: string): Effect.Effect<A, DesktopSshEnvironmentRequestError, R> =>    resolveLoopbackSshHttpBaseUrl(httpBaseUrl).pipe(      Effect.flatMap(use),      Effect.mapError(        (cause) =>          new DesktopSshEnvironmentRequestError({            operation,            cause,            sshHttpStatus: readSshHttpStatus(cause),          }),      ),    ); export const discoverSshHosts = DesktopIpc.makeIpcMethod({  channel: IpcChannels.DISCOVER_SSH_HOSTS_CHANNEL,  payload: Schema.Void,  result: Schema.Array(DesktopDiscoveredSshHostSchema),  handler: Effect.fn("desktop.ipc.sshEnvironment.discoverHosts")(function* () {    const sshEnvironment = yield* DesktopSshEnvironment.DesktopSshEnvironment;    return yield* sshEnvironment.discoverHosts();  }),}); export const resolveSshHost = DesktopIpc.makeIpcMethod({  channel: IpcChannels.RESOLVE_SSH_HOST_CHANNEL,  payload: Schema.String,  result: DesktopSshEnvironmentTargetSchema,  handler: Effect.fn("desktop.ipc.sshEnvironment.resolveHost")(function* (alias) {    const sshEnvironment = yield* DesktopSshEnvironment.DesktopSshEnvironment;    return yield* sshEnvironment.resolveHost(alias);  }),}); export const ensureSshEnvironment = DesktopIpc.makeIpcMethod({  channel: IpcChannels.ENSURE_SSH_ENVIRONMENT_CHANNEL,  payload: DesktopSshEnvironmentEnsureInputSchema,  result: DesktopSshEnvironmentEnsureResultSchema,  handler: Effect.fn("desktop.ipc.sshEnvironment.ensureEnvironment")(function* ({    target,    options,  }) {    const sshEnvironment = yield* DesktopSshEnvironment.DesktopSshEnvironment;    return yield* sshEnvironment.ensureEnvironment(target, options).pipe(      Effect.catch((error) =>        DesktopSshEnvironment.isDesktopSshPasswordPromptCancellation(error)          ? Effect.succeed({              type: DesktopSshPasswordPromptCancelledType,              message: error.message,            })          : Effect.fail(error),      ),    );  }),}); export const disconnectSshEnvironment = DesktopIpc.makeIpcMethod({  channel: IpcChannels.DISCONNECT_SSH_ENVIRONMENT_CHANNEL,  payload: DesktopSshEnvironmentTargetSchema,  result: Schema.Void,  handler: Effect.fn("desktop.ipc.sshEnvironment.disconnectEnvironment")(function* (target) {    const sshEnvironment = yield* DesktopSshEnvironment.DesktopSshEnvironment;    yield* sshEnvironment.disconnectEnvironment(target);  }),}); export const fetchSshEnvironmentDescriptor = DesktopIpc.makeIpcMethod({  channel: IpcChannels.FETCH_SSH_ENVIRONMENT_DESCRIPTOR_CHANNEL,  payload: DesktopSshHttpBaseUrlInputSchema,  result: ExecutionEnvironmentDescriptor,  handler: Effect.fn("desktop.ipc.sshEnvironment.fetchDescriptor")(function* ({ httpBaseUrl }) {    return yield* withLoopbackSshApi("fetch-environment-descriptor", (resolvedHttpBaseUrl) =>      fetchRemoteEnvironmentDescriptor({ httpBaseUrl: resolvedHttpBaseUrl }),    )(httpBaseUrl);  }),}); export const bootstrapSshBearerSession = DesktopIpc.makeIpcMethod({  channel: IpcChannels.BOOTSTRAP_SSH_BEARER_SESSION_CHANNEL,  payload: DesktopSshBearerBootstrapInputSchema,  result: AuthAccessTokenResult,  handler: Effect.fn("desktop.ipc.sshEnvironment.bootstrapBearerSession")(function* ({    httpBaseUrl,    credential,  }) {    return yield* withLoopbackSshApi("bootstrap-bearer-session", (resolvedHttpBaseUrl) =>      bootstrapRemoteBearerSession({        httpBaseUrl: resolvedHttpBaseUrl,        credential,      }),    )(httpBaseUrl);  }),}); export const fetchSshSessionState = DesktopIpc.makeIpcMethod({  channel: IpcChannels.FETCH_SSH_SESSION_STATE_CHANNEL,  payload: DesktopSshBearerRequestInputSchema,  result: AuthSessionState,  handler: Effect.fn("desktop.ipc.sshEnvironment.fetchSessionState")(function* ({    httpBaseUrl,    bearerToken,  }) {    return yield* withLoopbackSshApi("fetch-session-state", (resolvedHttpBaseUrl) =>      fetchRemoteSessionState({        httpBaseUrl: resolvedHttpBaseUrl,        bearerToken,      }),    )(httpBaseUrl);  }),}); export const issueSshWebSocketTicket = DesktopIpc.makeIpcMethod({  channel: IpcChannels.ISSUE_SSH_WEBSOCKET_TOKEN_CHANNEL,  payload: DesktopSshBearerRequestInputSchema,  result: AuthWebSocketTicketResult,  handler: Effect.fn("desktop.ipc.sshEnvironment.issueWebSocketTicket")(function* ({    httpBaseUrl,    bearerToken,  }) {    return yield* withLoopbackSshApi("issue-websocket-ticket", (resolvedHttpBaseUrl) =>      issueRemoteWebSocketTicket({        httpBaseUrl: resolvedHttpBaseUrl,        bearerToken,      }),    )(httpBaseUrl);  }),}); export const resolveSshPasswordPrompt = DesktopIpc.makeIpcMethod({  channel: IpcChannels.RESOLVE_SSH_PASSWORD_PROMPT_CHANNEL,  payload: DesktopSshPasswordPromptResolutionInputSchema,  result: Schema.Void,  handler: Effect.fn("desktop.ipc.sshEnvironment.resolvePasswordPrompt")(function* ({    requestId,    password,  }) {    const prompts = yield* DesktopSshPasswordPrompts.DesktopSshPasswordPrompts;    yield* prompts.resolve({ requestId, password });  }),});