mirror of
https://github.com/wukko/cobalt.git
synced 2025-01-23 19:26:26 +01:00
web/api: jwt session token, clean up, move related modules to own dir
This commit is contained in:
parent
16acf62886
commit
4857030933
9 changed files with 255 additions and 145 deletions
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import "@fontsource-variable/noto-sans-mono";
|
||||
|
||||
import API from "$lib/api";
|
||||
import API from "$lib/api/api";
|
||||
import { t } from "$lib/i18n/translations";
|
||||
import { createDialog } from "$lib/dialogs";
|
||||
import { downloadFile } from "$lib/download";
|
||||
|
|
|
@ -1,140 +0,0 @@
|
|||
import { get } from "svelte/store";
|
||||
|
||||
import turnstile from "$lib/turnstile";
|
||||
import env, { apiURL } from "$lib/env";
|
||||
import { t } from "$lib/i18n/translations";
|
||||
import settings, { updateSetting } from "$lib/state/settings";
|
||||
|
||||
import { createDialog } from "$lib/dialogs";
|
||||
|
||||
import type { CobaltAPIResponse } from "$lib/types/api";
|
||||
import type { Optional } from "$lib/types/generic";
|
||||
|
||||
const request = async (url: string) => {
|
||||
const saveSettings = get(settings).save;
|
||||
|
||||
const request = {
|
||||
url,
|
||||
|
||||
downloadMode: saveSettings.downloadMode,
|
||||
|
||||
audioFormat: saveSettings.audioFormat,
|
||||
tiktokFullAudio: saveSettings.tiktokFullAudio,
|
||||
youtubeDubBrowserLang: saveSettings.youtubeDubBrowserLang,
|
||||
|
||||
youtubeVideoCodec: saveSettings.youtubeVideoCodec,
|
||||
videoQuality: saveSettings.videoQuality,
|
||||
|
||||
filenameStyle: saveSettings.filenameStyle,
|
||||
disableMetadata: saveSettings.disableMetadata,
|
||||
|
||||
twitterGif: saveSettings.twitterGif,
|
||||
tiktokH265: saveSettings.tiktokH265,
|
||||
}
|
||||
|
||||
if (env.DEFAULT_API && !get(settings).processing.seenOverrideWarning) {
|
||||
let _actions: {
|
||||
resolve: () => void;
|
||||
reject: () => void;
|
||||
};
|
||||
|
||||
const promise = new Promise<void>(
|
||||
(resolve, reject) => (_actions = { resolve, reject })
|
||||
).catch(() => {
|
||||
return {}
|
||||
});
|
||||
|
||||
createDialog({
|
||||
id: "security-api-override",
|
||||
type: "small",
|
||||
icon: "warn-red",
|
||||
title: get(t)("dialog.api.override.title"),
|
||||
bodyText: get(t)("dialog.api.override.body", { value: env.DEFAULT_API }),
|
||||
dismissable: false,
|
||||
buttons: [
|
||||
{
|
||||
text: get(t)("button.cancel"),
|
||||
main: false,
|
||||
action: () => {
|
||||
_actions.reject();
|
||||
updateSetting({
|
||||
processing: {
|
||||
seenOverrideWarning: true,
|
||||
},
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
text: get(t)("button.continue"),
|
||||
color: "red",
|
||||
main: true,
|
||||
timeout: 5000,
|
||||
action: () => {
|
||||
_actions.resolve();
|
||||
updateSetting({
|
||||
processing: {
|
||||
allowDefaultOverride: true,
|
||||
seenOverrideWarning: true,
|
||||
},
|
||||
})
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
await promise;
|
||||
}
|
||||
|
||||
let api = apiURL;
|
||||
if (env.DEFAULT_API && get(settings).processing.allowDefaultOverride) {
|
||||
api = env.DEFAULT_API;
|
||||
}
|
||||
|
||||
let turnstileHeader = {};
|
||||
if (env.TURNSTILE_KEY) {
|
||||
const turnstileResponse = turnstile.getResponse();
|
||||
if (turnstileResponse) {
|
||||
turnstileHeader = {
|
||||
"cf-turnstile-response": turnstileResponse
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const response: Optional<CobaltAPIResponse> = await fetch(api, {
|
||||
method: "POST",
|
||||
redirect: "manual",
|
||||
signal: AbortSignal.timeout(10000),
|
||||
body: JSON.stringify(request),
|
||||
headers: {
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json",
|
||||
...turnstileHeader,
|
||||
},
|
||||
})
|
||||
.then(r => r.json())
|
||||
.catch((e) => {
|
||||
if (e?.message?.includes("timed out")) {
|
||||
return {
|
||||
status: "error",
|
||||
error: {
|
||||
code: "error.api.timed_out"
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
const probeCobaltStream = async (url: string) => {
|
||||
const request = await fetch(`${url}&p=1`).catch(() => {});
|
||||
if (request?.status === 200) {
|
||||
return request?.status;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
export default {
|
||||
request,
|
||||
probeCobaltStream,
|
||||
}
|
11
web/src/lib/api/api-url.ts
Normal file
11
web/src/lib/api/api-url.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
import { get } from "svelte/store";
|
||||
|
||||
import env, { apiURL } from "$lib/env";
|
||||
import settings from "$lib/state/settings";
|
||||
|
||||
export const currentApiURL = () => {
|
||||
if (env.DEFAULT_API && get(settings).processing.allowDefaultOverride) {
|
||||
return env.DEFAULT_API;
|
||||
}
|
||||
return apiURL;
|
||||
}
|
88
web/src/lib/api/api.ts
Normal file
88
web/src/lib/api/api.ts
Normal file
|
@ -0,0 +1,88 @@
|
|||
import { get } from "svelte/store";
|
||||
|
||||
import settings from "$lib/state/settings";
|
||||
import { getSession } from "$lib/api/session";
|
||||
import { currentApiURL } from "$lib/api/api-url";
|
||||
import { apiOverrideWarning } from "$lib/api/override-warning";
|
||||
|
||||
import type { Optional } from "$lib/types/generic";
|
||||
import type { CobaltAPIResponse, CobaltErrorResponse } from "$lib/types/api";
|
||||
|
||||
const request = async (url: string) => {
|
||||
const saveSettings = get(settings).save;
|
||||
|
||||
const request = {
|
||||
url,
|
||||
|
||||
downloadMode: saveSettings.downloadMode,
|
||||
|
||||
audioFormat: saveSettings.audioFormat,
|
||||
tiktokFullAudio: saveSettings.tiktokFullAudio,
|
||||
youtubeDubBrowserLang: saveSettings.youtubeDubBrowserLang,
|
||||
|
||||
youtubeVideoCodec: saveSettings.youtubeVideoCodec,
|
||||
videoQuality: saveSettings.videoQuality,
|
||||
|
||||
filenameStyle: saveSettings.filenameStyle,
|
||||
disableMetadata: saveSettings.disableMetadata,
|
||||
|
||||
twitterGif: saveSettings.twitterGif,
|
||||
tiktokH265: saveSettings.tiktokH265,
|
||||
}
|
||||
|
||||
await apiOverrideWarning();
|
||||
const api = currentApiURL();
|
||||
const session = await getSession();
|
||||
|
||||
let extraHeaders = {}
|
||||
|
||||
if (session) {
|
||||
if ("error" in session) {
|
||||
if (session.error.code !== "error.api.auth.not_configured") {
|
||||
return session;
|
||||
}
|
||||
} else {
|
||||
extraHeaders = {
|
||||
"Authorization": `Bearer ${session.token}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const response: Optional<CobaltAPIResponse> = await fetch(api, {
|
||||
method: "POST",
|
||||
redirect: "manual",
|
||||
signal: AbortSignal.timeout(10000),
|
||||
body: JSON.stringify(request),
|
||||
headers: {
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json",
|
||||
...extraHeaders,
|
||||
},
|
||||
})
|
||||
.then(r => r.json())
|
||||
.catch((e) => {
|
||||
if (e?.message?.includes("timed out")) {
|
||||
return {
|
||||
status: "error",
|
||||
error: {
|
||||
code: "error.api.timed_out"
|
||||
}
|
||||
} as CobaltErrorResponse;
|
||||
}
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
const probeCobaltStream = async (url: string) => {
|
||||
const request = await fetch(`${url}&p=1`).catch(() => {});
|
||||
if (request?.status === 200) {
|
||||
return request?.status;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
export default {
|
||||
request,
|
||||
probeCobaltStream,
|
||||
}
|
62
web/src/lib/api/override-warning.ts
Normal file
62
web/src/lib/api/override-warning.ts
Normal file
|
@ -0,0 +1,62 @@
|
|||
import { get } from "svelte/store";
|
||||
|
||||
import env from "$lib/env";
|
||||
import { t } from "$lib/i18n/translations";
|
||||
import settings, { updateSetting } from "$lib/state/settings";
|
||||
|
||||
import { createDialog } from "$lib/dialogs";
|
||||
|
||||
export const apiOverrideWarning = async () => {
|
||||
if (env.DEFAULT_API && !get(settings).processing.seenOverrideWarning) {
|
||||
let _actions: {
|
||||
resolve: () => void;
|
||||
reject: () => void;
|
||||
};
|
||||
|
||||
const promise = new Promise<void>(
|
||||
(resolve, reject) => (_actions = { resolve, reject })
|
||||
).catch(() => {
|
||||
return {}
|
||||
});
|
||||
|
||||
createDialog({
|
||||
id: "security-api-override",
|
||||
type: "small",
|
||||
icon: "warn-red",
|
||||
title: get(t)("dialog.api.override.title"),
|
||||
bodyText: get(t)("dialog.api.override.body", { value: env.DEFAULT_API }),
|
||||
dismissable: false,
|
||||
buttons: [
|
||||
{
|
||||
text: get(t)("button.cancel"),
|
||||
main: false,
|
||||
action: () => {
|
||||
_actions.reject();
|
||||
updateSetting({
|
||||
processing: {
|
||||
seenOverrideWarning: true,
|
||||
},
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
text: get(t)("button.continue"),
|
||||
color: "red",
|
||||
main: true,
|
||||
timeout: 5000,
|
||||
action: () => {
|
||||
_actions.resolve();
|
||||
updateSetting({
|
||||
processing: {
|
||||
allowDefaultOverride: true,
|
||||
seenOverrideWarning: true,
|
||||
},
|
||||
})
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
await promise;
|
||||
}
|
||||
}
|
65
web/src/lib/api/session.ts
Normal file
65
web/src/lib/api/session.ts
Normal file
|
@ -0,0 +1,65 @@
|
|||
import { get } from "svelte/store";
|
||||
|
||||
import turnstile from "$lib/api/turnstile";
|
||||
import { currentApiURL } from "$lib/api/api-url";
|
||||
import { cachedSession } from "$lib/state/session";
|
||||
|
||||
import type { CobaltSessionResponse, CobaltErrorResponse } from "$lib/types/api";
|
||||
|
||||
export const requestSession = async() => {
|
||||
const apiEndpoint = `${currentApiURL()}session`;
|
||||
|
||||
let requestHeaders = {};
|
||||
|
||||
const turnstileResponse = turnstile.getResponse();
|
||||
if (turnstileResponse) {
|
||||
requestHeaders = {
|
||||
"cf-turnstile-response": turnstileResponse
|
||||
};
|
||||
}
|
||||
|
||||
const response: CobaltSessionResponse = await fetch(apiEndpoint, {
|
||||
method: "POST",
|
||||
redirect: "manual",
|
||||
signal: AbortSignal.timeout(10000),
|
||||
headers: requestHeaders,
|
||||
})
|
||||
.then(r => r.json())
|
||||
.catch((e) => {
|
||||
if (e?.message?.includes("timed out")) {
|
||||
return {
|
||||
status: "error",
|
||||
error: {
|
||||
code: "error.api.timed_out"
|
||||
}
|
||||
} as CobaltErrorResponse
|
||||
}
|
||||
});
|
||||
|
||||
turnstile.update();
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
export const getSession = async () => {
|
||||
const currentTime = new Date().getTime();
|
||||
const cache = get(cachedSession);
|
||||
|
||||
if (cache?.token && cache?.exp > currentTime) {
|
||||
return cache;
|
||||
}
|
||||
|
||||
const newSession = await requestSession();
|
||||
|
||||
if (!newSession) return {
|
||||
status: "error",
|
||||
error: {
|
||||
code: "error.api.generic"
|
||||
}
|
||||
} as CobaltErrorResponse
|
||||
|
||||
if (!("status" in newSession)) {
|
||||
cachedSession.set(newSession);
|
||||
}
|
||||
return newSession;
|
||||
}
|
|
@ -8,6 +8,17 @@ const getResponse = () => {
|
|||
return null;
|
||||
}
|
||||
|
||||
export default {
|
||||
getResponse
|
||||
const update = () => {
|
||||
const turnstileElement = document.getElementById("turnstile-widget");
|
||||
|
||||
if (turnstileElement) {
|
||||
return window?.turnstile?.reset(turnstileElement);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export default {
|
||||
getResponse,
|
||||
update,
|
||||
}
|
6
web/src/lib/state/session.ts
Normal file
6
web/src/lib/state/session.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
import { writable } from "svelte/store";
|
||||
|
||||
import type { Writable } from "svelte/store";
|
||||
import type { CobaltSession } from "$lib/types/api";
|
||||
|
||||
export const cachedSession: Writable<CobaltSession | null> = writable();
|
|
@ -5,7 +5,7 @@ enum CobaltResponseType {
|
|||
Stream = 'stream',
|
||||
}
|
||||
|
||||
type CobaltErrorResponse = {
|
||||
export type CobaltErrorResponse = {
|
||||
status: CobaltResponseType.Error,
|
||||
error: {
|
||||
code: string,
|
||||
|
@ -13,7 +13,7 @@ type CobaltErrorResponse = {
|
|||
service?: string,
|
||||
limit?: number,
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
type CobaltPartialURLResponse = {
|
||||
|
@ -38,6 +38,13 @@ type CobaltStreamResponse = {
|
|||
status: CobaltResponseType.Stream,
|
||||
} & CobaltPartialURLResponse;
|
||||
|
||||
export type CobaltSession = {
|
||||
token: string,
|
||||
exp: number,
|
||||
}
|
||||
|
||||
export type CobaltSessionResponse = CobaltSession | CobaltErrorResponse;
|
||||
|
||||
export type CobaltAPIResponse = CobaltErrorResponse
|
||||
| CobaltPickerResponse
|
||||
| CobaltRedirectResponse
|
||||
|
|
Loading…
Reference in a new issue