Initial open-source self-hosted Songs2YT edition

This commit is contained in:
Songs2YT
2026-07-17 02:23:59 +02:00
commit 2aa8a84781
134 changed files with 17758 additions and 0 deletions
+105
View File
@@ -0,0 +1,105 @@
import { NextRequest, NextResponse } from "next/server";
import { findUserByApiKey } from "./api-keys";
import { checkApiRateLimit } from "./api-rate-limit";
import { hasProFeatures } from "./edition";
import { getSessionUser } from "./session";
function extractBearerToken(req: NextRequest) {
const auth = req.headers.get("authorization");
if (!auth?.startsWith("Bearer ")) return null;
return auth.slice(7).trim();
}
export async function requirePaidApiUser(req: NextRequest) {
const token = extractBearerToken(req);
if (!token) {
return {
error: NextResponse.json(
{ error: "Missing API key. Use Authorization: Bearer <your_api_key>" },
{ status: 401 },
),
user: null,
};
}
const user = await findUserByApiKey(token);
if (!user) {
return {
error: NextResponse.json({ error: "Invalid API key" }, { status: 401 }),
user: null,
};
}
if (!hasProFeatures(user.plan)) {
return {
error: NextResponse.json(
{ error: "API access requires an active Pro subscription" },
{ status: 403 },
),
user: null,
};
}
if (!user.youtubeConnection) {
return {
error: NextResponse.json(
{ error: "YouTube account not connected. Sign in via the dashboard first." },
{ status: 403 },
),
user: null,
};
}
const rateLimit = await checkApiRateLimit(user.id);
if (!rateLimit.ok) {
return {
error: NextResponse.json(
{
error: "API rate limit exceeded. Try again shortly.",
retryAfterSeconds: rateLimit.retryAfterSeconds,
},
{
status: 429,
headers: { "Retry-After": String(rateLimit.retryAfterSeconds) },
},
),
user: null,
};
}
return { error: null, user };
}
export async function requirePaidUserFromSessionOrApi(req: NextRequest) {
const apiResult = await requirePaidApiUser(req);
if (apiResult.user) return apiResult;
const sessionUser = await getSessionUser();
if (!sessionUser) {
return apiResult.error
? apiResult
: {
error: NextResponse.json({ error: "Unauthorized" }, { status: 401 }),
user: null,
};
}
if (!hasProFeatures(sessionUser.plan)) {
return {
error: NextResponse.json(
{ error: "API access requires an active Pro subscription" },
{ status: 403 },
),
user: null,
};
}
if (!sessionUser.youtubeConnection) {
return {
error: NextResponse.json({ error: "YouTube account not connected" }, { status: 403 }),
user: null,
};
}
return { error: null, user: sessionUser };
}