added devcontainer and made a custom cookie workaround for a specific problem

This commit is contained in:
Legonois 2025-03-11 22:15:59 +00:00
parent 98ad968606
commit 5bf86f1daa
12 changed files with 124 additions and 41 deletions

View File

@ -6,6 +6,13 @@
"image": "mcr.microsoft.com/devcontainers/universal:2-linux",
"features": {
"ghcr.io/devcontainers/features/node:1": {}
},
"customizations": {
"vscode": {
"extensions": [
"svelte.svelte-vscode"
]
}
}
// Features to add to the dev container. More info: https://containers.dev/features.

1
.gitignore vendored
View File

@ -4,6 +4,7 @@ desktop.ini
# node
node_modules
.pnpm-store
# static build
build

View File

@ -154,41 +154,43 @@ export const runAPI = async (express, app, __dirname, isPrimary = true) => {
});
app.post('/', (req, res, next) => {
if (!env.sessionEnabled || req.rateLimitKey) {
return next();
}
// if (!env.sessionEnabled || req.rateLimitKey) {
// return next();
// }
try {
const authorization = req.header("Authorization");
if (!authorization) {
return fail(res, "error.api.auth.jwt.missing");
}
// try {
// const authorization = req.header("Authorization");
// if (!authorization) {
// return fail(res, "error.api.auth.jwt.missing");
// }
if (authorization.length >= 256) {
return fail(res, "error.api.auth.jwt.invalid");
}
// if (authorization.length >= 256) {
// return fail(res, "error.api.auth.jwt.invalid");
// }
const [ type, token, ...rest ] = authorization.split(" ");
if (!token || type.toLowerCase() !== 'bearer' || rest.length) {
return fail(res, "error.api.auth.jwt.invalid");
}
// const [ type, token, ...rest ] = authorization.split(" ");
// if (!token || type.toLowerCase() !== 'bearer' || rest.length) {
// return fail(res, "error.api.auth.jwt.invalid");
// }
if (!jwt.verify(token)) {
return fail(res, "error.api.auth.jwt.invalid");
}
// if (!jwt.verify(token)) {
// return fail(res, "error.api.auth.jwt.invalid");
// }
req.rateLimitKey = hashHmac(token, 'rate');
} catch {
return fail(res, "error.api.generic");
}
// req.rateLimitKey = hashHmac(token, 'rate');
// } catch {
// return fail(res, "error.api.generic");
// }
next();
});
app.post('/', apiLimiter);
app.use('/', express.json({ limit: 1024 }));
app.use('/', express.json({ limit: 8192 }));
app.use('/', (err, _, res, next) => {
app.use('/', (err, req, res, next) => {
if (err) {
// console.error('Failed to normalize request:', req);
console.error('error:', err);
const { status, body } = createResponse("error", {
code: "error.api.invalid_body",
});
@ -234,6 +236,8 @@ export const runAPI = async (express, app, __dirname, isPrimary = true) => {
const { success, data: normalizedRequest } = await normalizeRequest(request);
if (!success) {
console.error('Failed to normalize request:', request);
console.log('data:', normalizedRequest);
return fail(res, "error.api.invalid_body");
}
@ -250,15 +254,20 @@ export const runAPI = async (express, app, __dirname, isPrimary = true) => {
return fail(res, `error.api.${parsed.error}`, context);
}
if (!request.cookies)
request.cookies = {"X-nocookies" : "included"};
try {
const result = await match({
host: parsed.host,
patternMatch: parsed.patternMatch,
params: normalizedRequest,
inputCookies: request.cookies,
});
res.status(result.status).json(result.body);
} catch {
} catch (e) {
console.error("Failed to match request", request, e);
fail(res, "error.api.generic");
}
})

View File

@ -29,14 +29,24 @@ import loom from "./services/loom.js";
import facebook from "./services/facebook.js";
import bluesky from "./services/bluesky.js";
import xiaohongshu from "./services/xiaohongshu.js";
import Cookie from "./cookie/cookie.js";
let freebind;
export default async function({ host, patternMatch, params }) {
export default async function({ host, patternMatch, params, inputCookies }) {
const { url } = params;
assert(url instanceof URL);
let dispatcher, requestIP;
console.log('Calling with cookies:', inputCookies);
let cookies = new Cookie({});
for (const [k, v] of Object.entries(inputCookies)) {
cookies.set(k, v);
}
console.log('Calling with cookies:', cookies);
if (env.freebindCIDR) {
if (!freebind) {
freebind = await import('freebind');
@ -133,6 +143,7 @@ export default async function({ host, patternMatch, params }) {
isAudioOnly,
h265: params.tiktokH265,
alwaysProxy: params.alwaysProxy,
cookies: cookies,
});
break;

View File

@ -77,9 +77,10 @@ export function createResponse(responseType, responseData) {
}
export function normalizeRequest(request) {
return apiSchema.safeParseAsync(request).catch(() => (
{ success: false }
));
return apiSchema.safeParseAsync(request).catch((err) => {
console.log('Zod validation error:', JSON.stringify(err, null, 2));
return { success: false };
});
}
export function getIP(req) {

View File

@ -47,5 +47,7 @@ export const apiSchema = z.object({
twitterGif: z.boolean().default(true),
youtubeHLS: z.boolean().default(false),
cookies: z.record(z.string()).default({}),
})
.strict();

View File

@ -8,9 +8,12 @@ import { createStream } from "../../stream/manage.js";
const shortDomain = "https://vt.tiktok.com/";
export default async function(obj) {
const cookie = new Cookie({});
// const cookie = new Cookie({});
const cookie = obj.cookies;
let postId = obj.postId;
console.log("With TikTok using cookies", cookie);
if (!postId) {
let html = await fetch(`${shortDomain}${obj.shortLink}`, {
redirect: "manual",

View File

@ -1,5 +1,6 @@
{
"gotit": "got it",
"signin": "sign into service",
"cancel": "cancel",
"reset": "reset",
"done": "done",

View File

@ -59,7 +59,7 @@
"api.content.post.unavailable": "couldn't find anything about this post. its visibility may be limited or it may not exist. make sure your link works and try again in a few seconds!",
"api.content.post.private": "couldn't get anything about this post because it's from a private account. try a different link!",
"api.content.post.age": "this post is age-restricted and isn't available without logging in. try a different link!",
"api.content.post.age": "this post is age-restricted and isn't available without logging in. Sign in to your account.",
"api.youtube.no_matching_format": "youtube didn't return a valid video + audio format combo, either video or audio is missing. formats for this video may be re-encoding on youtube's side or something went wrong when parsing them. try enabling the hls option in video settings!",
"api.youtube.decipher": "youtube updated its decipher algorithm and i couldn't extract the info about the video. try again in a few seconds, but if this issue sticks, please report it!",

View File

@ -38,7 +38,8 @@
let isDisabled = false;
let isLoading = false;
$: isBotCheckOngoing = $turnstileEnabled && !$turnstileSolved;
// $: isBotCheckOngoing = $turnstileEnabled && !$turnstileSolved;
$: isBotCheckOngoing = false;
const validLink = (url: string) => {
try {
@ -132,7 +133,8 @@
-->
{#if env.DEFAULT_API || (!$page.url.host.endsWith(".cobalt.tools") && $page.url.host !== "cobalt.tools")}
<div id="instance-label">
{$t("save.label.community_instance")}
KDDResearch Instance
<!-- {$t("save.label.community_instance")} -->
</div>
{/if}
@ -155,13 +157,14 @@
autocapitalize="off"
maxlength="512"
placeholder={$t("save.input.placeholder")}
aria-label={isBotCheckOngoing
? $t("a11y.save.link_area.turnstile")
: $t("a11y.save.link_area")}
data-form-type="other"
disabled={isDisabled}
/>
<!-- aria-label={isBotCheckOngoing
? $t("a11y.save.link_area.turnstile")
: $t("a11y.save.link_area")} -->
{#if $link && !isLoading}
<ClearButton click={() => ($link = "")} />
{/if}

View File

@ -77,6 +77,50 @@
if (response.status === "error") {
changeDownloadButton("error");
console.log(response.error.code);
if (response.error.code == "error.api.content.post.age") {
return createDialog({
id: "save-error",
type: "small",
meowbalt: "error",
buttons: [
{
text: $t("button.signin"),
main: true,
action: () => {
// opens a new tab for the user and redirects them to the login page
const website = URL.parse(link);
if (!website)
throw new Error("Invalid URL");
console.log(website);
switch (website.hostname) {
case "www.tiktok.com":
website.pathname = "/login/qrcode";
break;
default:
website.pathname = "/login";
break;
}
window.open(website.href, "_blank");
},
},
{
text: $t("button.cancel"),
main: false,
action: () => {},
},
],
bodyText: $t(response.error.code, response?.error?.context),
});
}
return createDialog({
...defaultErrorPopup,
bodyText: $t(response.error.code, response?.error?.context),

View File

@ -8,10 +8,11 @@ export const turnstileCreated = writable(false);
export const turnstileEnabled = derived(
[settings, cachedInfo],
([$settings, $cachedInfo]) => {
return !!$cachedInfo?.info?.cobalt?.turnstileSitekey &&
!(
$settings.processing.enableCustomApiKey &&
$settings.processing.customApiKey.length > 0
)
// return !!$cachedInfo?.info?.cobalt?.turnstileSitekey &&
// !(
// $settings.processing.enableCustomApiKey &&
// $settings.processing.customApiKey.length > 0
// )
return false;
}
)