Bild Carousel

This commit is contained in:
2026-07-09 16:45:03 +02:00
parent 35f3ed0d0e
commit 2a14c84ad3
8 changed files with 289385 additions and 2 deletions

View File

@@ -52,8 +52,88 @@ export async function getValidTiktokTokens() {
scope: tokens.scope || null,
accessTokenExpiresAt: new Date(now + Number(tokens.expires_in || 0) * 1000),
refreshTokenExpiresAt: tokens.refresh_expires_in
? new Date(now + Number(tokens.refresh_expires_in) * 1000)
? new Date(now + Number(tokens.refresh_expires_in || 0) * 1000)
: null,
},
});
}
const REFRESH_BUFFER_MS = 5 * 60 * 1000;
export async function getLiveTiktokAccessToken() {
const integration = await db.tiktokIntegration.findUnique({
where: { accountKey: TIKTOK_ACCOUNT_KEY },
});
if (!integration) {
const error = new Error('No TikTok account connected.');
error.status = 404;
throw error;
}
if (integration.accessTokenExpiresAt.getTime() - Date.now() > REFRESH_BUFFER_MS) {
return integration;
}
const clientKey = process.env.TIKTOK_CLIENT_KEY;
const clientSecret = process.env.TIKTOK_CLIENT_SECRET;
if (!clientKey || !clientSecret) {
const error = new Error('TikTok client credentials are not configured.');
error.status = 500;
throw error;
}
return getValidTiktokTokens();
}
export async function tiktokApi(url: string, options: RequestInit = {}) {
const tokens = await getLiveTiktokAccessToken();
const accessToken = tokens.accessToken;
const fetchOptions: RequestInit = {
...options,
headers: {
...(options.headers as Record<string, string> | undefined),
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json; charset=UTF-8',
},
};
const res = await fetch(url, fetchOptions);
const text = await res.text();
let data: Record<string, unknown>;
try {
data = JSON.parse(text) as Record<string, unknown>;
} catch {
data = { raw: text };
}
if (!res.ok || (data?.error as Record<string, string> | undefined)?.code !== 'ok') {
const message = (data?.error as Record<string, string> | undefined)?.message || (typeof data?.raw === 'string' ? data.raw : '') || `TikTok API error: ${res.status}`;
const error = new Error(message);
error.status = res.status;
error.body = data;
throw error;
}
return data;
}
export async function uploadBinaryToTiktok(uploadUrl: string, buffer: Buffer, mimeType = 'video/mp4') {
const res = await fetch(uploadUrl, {
method: 'PUT',
headers: {
'Content-Type': mimeType,
'Content-Length': String(buffer.length),
},
body: buffer,
});
const text = await res.text();
if (!res.ok && res.status !== 201) {
const error = new Error(`TikTok binary upload failed: ${res.status} ${text}`);
error.status = res.status;
throw error;
}
return { status: res.status, body: text };
}