Files
image-apiv3/app/api/v1/api-keys/route.ts
Beyhan Oğur 031582ea2c first commit
2026-04-26 22:11:03 +03:00

122 lines
3.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { NextRequest, NextResponse } from "next/server";
import { nanoid } from "nanoid";
import { db } from "@/db";
import { apiKeys } from "@/db/schema";
import { eq, desc } from "drizzle-orm";
import { authenticateWebOrAPIRequest } from "@/app/lib/api-auth";
import { generateAPIKey, maskApiKey } from "@/app/lib/jwt";
import {
MAX_API_KEY_NAME_LEN,
expiresAtFromDays,
getDaysRemaining,
getExpiryRemainingLabel,
parseExpiresInDaysOptional,
} from "@/app/lib/api-key-utils";
/**
* GET /api/v1/api-keys — Oturum veya Bearer ile: kendi API anahtarlarını listele (tam key dönmez)
*/
export async function GET(request: NextRequest) {
const auth = await authenticateWebOrAPIRequest(request);
if (!auth.authenticated || !auth.userId) {
return NextResponse.json({ error: auth.error ?? "Yetkisiz" }, { status: 401 });
}
const rows = await db
.select({
id: apiKeys.id,
name: apiKeys.name,
key: apiKeys.key,
expiresAt: apiKeys.expiresAt,
lastUsedAt: apiKeys.lastUsedAt,
isActive: apiKeys.isActive,
createdAt: apiKeys.createdAt,
})
.from(apiKeys)
.where(eq(apiKeys.userId, auth.userId))
.orderBy(desc(apiKeys.createdAt));
return NextResponse.json({
success: true,
data: {
keys: rows.map((r) => {
const exp = r.expiresAt ?? null;
return {
id: r.id,
name: r.name,
keyPreview: maskApiKey(r.key),
expiresAt: exp?.toISOString() ?? null,
daysRemaining: getDaysRemaining(exp),
remainingLabel: getExpiryRemainingLabel(exp),
lastUsedAt: r.lastUsedAt?.toISOString() ?? null,
isActive: r.isActive,
createdAt: r.createdAt.toISOString(),
};
}),
},
});
}
/**
* POST /api/v1/api-keys — Yeni API anahtarı oluştur (tam key yalnızca bu yanıtta bir kez)
*
* Body: { "name": string, "expiresInDays"?: number | null }
* — expiresInDays yok/null/0: süresiz; 13650: bugünden itibaren o kadar gün
*/
export async function POST(request: NextRequest) {
const auth = await authenticateWebOrAPIRequest(request);
if (!auth.authenticated || !auth.userId) {
return NextResponse.json({ error: auth.error ?? "Yetkisiz" }, { status: 401 });
}
let body: { name?: unknown; expiresInDays?: unknown };
try {
body = await request.json();
} catch {
return NextResponse.json({ error: "Geçersiz JSON" }, { status: 400 });
}
const name = typeof body.name === "string" ? body.name.trim() : "";
if (!name || name.length > MAX_API_KEY_NAME_LEN) {
return NextResponse.json(
{
error: `name zorunludur ve en fazla ${MAX_API_KEY_NAME_LEN} karakter olabilir.`,
},
{ status: 400 }
);
}
const parsed = parseExpiresInDaysOptional(body.expiresInDays);
if (!parsed.ok) {
return NextResponse.json({ error: parsed.error }, { status: 400 });
}
const expiresAt =
parsed.value === null ? null : expiresAtFromDays(parsed.value);
const plainKey = generateAPIKey();
const id = nanoid();
await db.insert(apiKeys).values({
id,
userId: auth.userId,
name,
key: plainKey,
expiresAt,
isActive: true,
});
return NextResponse.json({
success: true,
message:
"API anahtarı oluşturuldu. Tam değeri yalnızca bu yanıtta saklayın; bir daha gösterilmez.",
data: {
id,
name,
key: plainKey,
expiresAt: expiresAt?.toISOString() ?? null,
daysRemaining: getDaysRemaining(expiresAt),
remainingLabel: getExpiryRemainingLabel(expiresAt),
},
});
}