From 6cc12272887a09670483b5f8e3b04175ba24ec1b Mon Sep 17 00:00:00 2001 From: dumbmoron Date: Wed, 11 Sep 2024 20:23:06 +0000 Subject: [PATCH] api-client: migrate session handler and response types --- packages/api-client/src/session.ts | 59 +++++++++++++++++++ packages/api-client/src/types/errors.ts | 16 ++--- packages/api-client/src/types/response.ts | 71 +++++++++++++++++++++++ 3 files changed, 138 insertions(+), 8 deletions(-) create mode 100644 packages/api-client/src/session.ts create mode 100644 packages/api-client/src/types/response.ts diff --git a/packages/api-client/src/session.ts b/packages/api-client/src/session.ts new file mode 100644 index 00000000..aab7cf86 --- /dev/null +++ b/packages/api-client/src/session.ts @@ -0,0 +1,59 @@ +import { CobaltSession, CobaltResponseType, CobaltSessionResponse } from "./types/response"; +import { CobaltReachabilityError } from "./types/errors"; + +const currentTime = () => Math.floor(new Date().getTime() / 1000); +const EXPIRY_THRESHOLD_SECONDS = 2; + +export default class CobaltSessionHandler { + #baseURL: string; + #session: CobaltSession | undefined; + + constructor(baseURL: string) { + this.#baseURL = baseURL; + } + + async #requestSession(turnstileResponse?: string): Promise { + const endpoint = new URL('/session', this.#baseURL); + const headers: Record = {}; + + if (turnstileResponse) { + headers['cf-turnstile-response'] = turnstileResponse; + } + + const response = await fetch(endpoint, { + method: 'POST', + redirect: 'manual', + signal: AbortSignal.timeout(10000), + headers + }) + .then(r => r.json()) + .catch((e) => { + const timedOut = e?.message?.includes("timed out"); + return { + status: CobaltResponseType.Error, + error: { + code: timedOut + ? CobaltReachabilityError.TimedOut + : CobaltReachabilityError.Unreachable + } + }; + }); + + if ('token' in response && 'exp' in response) { + this.#session = { + token: response.token, + exp: currentTime() + response.exp + } + } + + return response; + } + + async getSession(turnstileResponse?: string): Promise { + if (this.#session && this.#session.exp - EXPIRY_THRESHOLD_SECONDS > currentTime()) { + return this.#session; + } + + return this.#requestSession(turnstileResponse); + } +}; diff --git a/packages/api-client/src/types/errors.ts b/packages/api-client/src/types/errors.ts index 45c92bcc..ef09ce0a 100644 --- a/packages/api-client/src/types/errors.ts +++ b/packages/api-client/src/types/errors.ts @@ -56,11 +56,11 @@ export enum CobaltYouTubeError { TokenExpired = 'error.api.youtube.token_expired' } -export type CobaltAPIError = CobaltAuthError - | CobaltReachabilityError - | CobaltGenericError - | CobaltServiceError - | CobaltLinkError - | CobaltProcessingError - | CobaltContentError - | CobaltYouTubeError; +export type CobaltAPIErrorCode = CobaltAuthError + | CobaltReachabilityError + | CobaltGenericError + | CobaltServiceError + | CobaltLinkError + | CobaltProcessingError + | CobaltContentError + | CobaltYouTubeError; diff --git a/packages/api-client/src/types/response.ts b/packages/api-client/src/types/response.ts new file mode 100644 index 00000000..cebf0060 --- /dev/null +++ b/packages/api-client/src/types/response.ts @@ -0,0 +1,71 @@ +import { CobaltAPIErrorCode } from "./errors"; + +export enum CobaltResponseType { + Error = 'error', + Picker = 'picker', + Redirect = 'redirect', + Tunnel = 'tunnel', +} + +export type CobaltErrorResponse = { + status: CobaltResponseType.Error, + error: { + code: CobaltAPIErrorCode, + context?: { + service?: string, + limit?: number, + } + }, +}; + +type CobaltPartialURLResponse = { + url: string, + filename: string, +} + +type CobaltPickerResponse = { + status: CobaltResponseType.Picker + picker: { + type: 'photo' | 'video' | 'gif', + url: string, + thumb?: string, + }[]; + audio?: string, + audioFilename?: string, +}; + +type CobaltRedirectResponse = { + status: CobaltResponseType.Redirect, +} & CobaltPartialURLResponse; + +type CobaltTunnelResponse = { + status: CobaltResponseType.Tunnel, +} & CobaltPartialURLResponse; + +export type CobaltSession = { + token: string, + exp: number, +} + +export type CobaltServerInfo = { + cobalt: { + version: string, + url: string, + startTime: string, + durationLimit: number, + services: string[] + }, + git: { + branch: string, + commit: string, + remote: string, + } +} + +export type CobaltSessionResponse = CobaltSession | CobaltErrorResponse; +export type CobaltServerInfoResponse = CobaltServerInfo | CobaltErrorResponse; + +export type CobaltAPIResponse = CobaltErrorResponse + | CobaltPickerResponse + | CobaltRedirectResponse + | CobaltTunnelResponse;