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

View File

@@ -0,0 +1,92 @@
import { NextRequest, NextResponse } from 'next/server';
import { db } from '@/lib/db';
import { TIKTOK_ACCOUNT_KEY, TIKTOK_OAUTH_STATE_COOKIE_NAME } from '@/lib/tiktok';
const textResponse = (body: string, status: number) => {
const response = new NextResponse(body, {
status,
headers: { 'Content-Type': 'text/plain; charset=utf-8' },
});
response.cookies.delete(TIKTOK_OAUTH_STATE_COOKIE_NAME);
return response;
};
export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url);
const code = searchParams.get('code');
const state = searchParams.get('state');
const error = searchParams.get('error');
const errorDescription = searchParams.get('error_description');
const savedState = request.cookies.get(TIKTOK_OAUTH_STATE_COOKIE_NAME)?.value;
if (error) {
return textResponse(`TikTok authorization failed: ${errorDescription || error}`, 400);
}
if (!code) {
return textResponse('Missing authorization code.', 400);
}
if (!state || !savedState || state !== savedState) {
return textResponse('Invalid OAuth state. Start over at /api/tiktok/connect.', 403);
}
const clientKey = process.env.TIKTOK_CLIENT_KEY;
const clientSecret = process.env.TIKTOK_CLIENT_SECRET;
if (!clientKey || !clientSecret) {
return textResponse('TikTok client credentials are not configured.', 500);
}
const redirectUri =
process.env.TIKTOK_REDIRECT_URI || `${process.env.NEXT_PUBLIC_APP_URL}/api/tiktok/callback`;
try {
const tokenResponse = 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,
code,
grant_type: 'authorization_code',
redirect_uri: redirectUri,
}),
});
const tokens = await tokenResponse.json();
if (!tokenResponse.ok || tokens.error) {
throw new Error(tokens.error_description || tokens.error || 'TikTok token exchange failed');
}
const now = Date.now();
const accessTokenExpiresAt = new Date(now + Number(tokens.expires_in || 0) * 1000);
const refreshTokenExpiresAt = tokens.refresh_expires_in
? new Date(now + Number(tokens.refresh_expires_in) * 1000)
: null;
await db.tiktokIntegration.upsert({
where: { accountKey: TIKTOK_ACCOUNT_KEY },
create: {
accountKey: TIKTOK_ACCOUNT_KEY,
openId: tokens.open_id,
accessToken: tokens.access_token,
refreshToken: tokens.refresh_token,
scope: tokens.scope || null,
accessTokenExpiresAt,
refreshTokenExpiresAt,
},
update: {
openId: tokens.open_id,
accessToken: tokens.access_token,
refreshToken: tokens.refresh_token,
scope: tokens.scope || null,
accessTokenExpiresAt,
refreshTokenExpiresAt,
},
});
return textResponse('TikTok account connected. You can close this tab.', 200);
} catch (err) {
console.error('TikTok callback error:', err);
const message = err instanceof Error ? err.message : 'Unknown error';
return textResponse(`Failed to connect TikTok account: ${message}`, 502);
}
}