TikTok api

This commit is contained in:
Timo Knuth
2026-07-02 13:06:50 +02:00
parent b0b70640ab
commit 0b9c8d2a8f
12 changed files with 1965 additions and 1712 deletions

59
src/lib/tiktok.ts Normal file
View File

@@ -0,0 +1,59 @@
import { db } from '@/lib/db';
export const TIKTOK_OAUTH_STATE_COOKIE_NAME = 'tiktok_oauth_state';
export const TIKTOK_ACCOUNT_KEY = 'hermes-agent';
// Refresh when the access token expires within this window, so Hermes never
// receives a token that dies mid-upload.
const REFRESH_BUFFER_MS = 5 * 60 * 1000;
export async function getValidTiktokTokens() {
const integration = await db.tiktokIntegration.findUnique({
where: { accountKey: TIKTOK_ACCOUNT_KEY },
});
if (!integration) {
return null;
}
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) {
throw new Error('TikTok client credentials are not configured.');
}
const response = await fetch('https://open.tiktokapis.com/v2/oauth/token/', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
client_key: clientKey,
client_secret: clientSecret,
grant_type: 'refresh_token',
refresh_token: integration.refreshToken,
}),
});
const tokens = await response.json();
if (!response.ok || tokens.error) {
throw new Error(tokens.error_description || tokens.error || 'TikTok token refresh failed');
}
const now = Date.now();
return db.tiktokIntegration.update({
where: { accountKey: TIKTOK_ACCOUNT_KEY },
data: {
openId: tokens.open_id,
accessToken: tokens.access_token,
refreshToken: tokens.refresh_token,
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)
: null,
},
});
}