234 lines
4.9 KiB
TypeScript
234 lines
4.9 KiB
TypeScript
/** biome-ignore-all lint/suspicious/noExplicitAny: <explanation */
|
|
import Elysia, { t } from "elysia";
|
|
import { nanoid } from "nanoid";
|
|
import { prisma } from "../utils/db";
|
|
import logger from "../utils/logger";
|
|
|
|
export const apikey = new Elysia({
|
|
prefix: "/apikey",
|
|
})
|
|
.get(
|
|
"/",
|
|
async (ctx) => {
|
|
const { set, user } = ctx as any;
|
|
try {
|
|
// logger.info({ userId: user?.id }, 'Fetching API keys');
|
|
|
|
if (!user) {
|
|
set.status = 401;
|
|
return { error: "Unauthorized" };
|
|
}
|
|
|
|
const apiKeys = await prisma.apiKey.findMany({
|
|
where: { userId: user.id },
|
|
select: {
|
|
id: true,
|
|
name: true,
|
|
key: true,
|
|
isActive: true,
|
|
expiresAt: true,
|
|
createdAt: true,
|
|
updatedAt: true,
|
|
},
|
|
});
|
|
|
|
logger.info(
|
|
{ count: apiKeys.length, userId: user?.id },
|
|
"Fetched API keys",
|
|
);
|
|
|
|
return { apiKeys };
|
|
} catch (error) {
|
|
logger.error({ error }, "Failed to fetch API keys");
|
|
set.status = 500;
|
|
return { error: "Failed to fetch API keys" };
|
|
}
|
|
},
|
|
{
|
|
detail: {
|
|
summary: "Get all API keys",
|
|
description: "Get all API keys",
|
|
},
|
|
},
|
|
)
|
|
.post(
|
|
"/",
|
|
async (ctx) => {
|
|
const { body, set, user } = ctx as any;
|
|
try {
|
|
const { name, expiresAt } = body;
|
|
|
|
if (!user) {
|
|
set.status = 401;
|
|
return { error: "Unauthorized" };
|
|
}
|
|
|
|
// Generate a unique API key
|
|
const apiKeyValue = `sk-${nanoid(32)}`;
|
|
|
|
const newApiKey = await prisma.apiKey.create({
|
|
data: {
|
|
name,
|
|
key: apiKeyValue,
|
|
userId: user.id,
|
|
isActive: true,
|
|
expiresAt: expiresAt ? new Date(expiresAt) : null,
|
|
},
|
|
select: {
|
|
id: true,
|
|
name: true,
|
|
key: true,
|
|
isActive: true,
|
|
expiresAt: true,
|
|
createdAt: true,
|
|
updatedAt: true,
|
|
},
|
|
});
|
|
|
|
return { apiKey: newApiKey };
|
|
} catch (error) {
|
|
set.status = 500;
|
|
logger.error({ error }, "Failed to create API key");
|
|
return { error: "Failed to create API key" };
|
|
}
|
|
},
|
|
{
|
|
body: t.Object({
|
|
name: t.String(),
|
|
expiresAt: t.Optional(t.String()), // ISO date string
|
|
}),
|
|
detail: {
|
|
summary: "Create a new API key",
|
|
description: "Create a new API key",
|
|
},
|
|
},
|
|
)
|
|
.post(
|
|
"/update",
|
|
async (ctx) => {
|
|
const { body, set, user } = ctx as any;
|
|
try {
|
|
const { id, isActive, expiresAt } = body;
|
|
|
|
logger.info(
|
|
{ id, isActive, expiresAt, userId: user?.id },
|
|
"Patch API key called",
|
|
);
|
|
|
|
if (!user) {
|
|
set.status = 401;
|
|
logger.error(
|
|
{ id, isActive, expiresAt, userId: user?.id },
|
|
"Unauthorized",
|
|
);
|
|
return { error: "Unauthorized" };
|
|
}
|
|
|
|
// Verify that the API key belongs to the user
|
|
const apiKey = await prisma.apiKey.findUnique({
|
|
where: { id },
|
|
});
|
|
|
|
logger.debug({ apiKey }, "Found API key");
|
|
|
|
if (!apiKey || apiKey.userId !== user.id) {
|
|
set.status = 403;
|
|
logger.error({ id, apiKey, userId: user?.id }, "Forbidden");
|
|
return { error: "Forbidden" };
|
|
}
|
|
|
|
const updatedApiKey = await prisma.apiKey.update({
|
|
where: { id },
|
|
data: {
|
|
isActive,
|
|
expiresAt:
|
|
expiresAt !== undefined
|
|
? expiresAt
|
|
? new Date(expiresAt)
|
|
: null
|
|
: undefined,
|
|
},
|
|
select: {
|
|
id: true,
|
|
name: true,
|
|
key: true,
|
|
isActive: true,
|
|
expiresAt: true,
|
|
createdAt: true,
|
|
updatedAt: true,
|
|
},
|
|
});
|
|
|
|
logger.info({ apiKeyId: updatedApiKey.id }, "Updated API key");
|
|
|
|
return { apiKey: updatedApiKey };
|
|
} catch (error) {
|
|
logger.error({ error }, "Error updating API key");
|
|
set.status = 500;
|
|
return { error: "Failed to update API key" };
|
|
}
|
|
},
|
|
{
|
|
body: t.Object({
|
|
id: t.String(),
|
|
isActive: t.Boolean(),
|
|
expiresAt: t.Optional(t.Union([t.String(), t.Null()])), // ISO date string or null
|
|
}),
|
|
detail: {
|
|
summary: "Update an API key",
|
|
description: "Update an API key",
|
|
},
|
|
},
|
|
)
|
|
.post(
|
|
"/delete",
|
|
async (ctx) => {
|
|
const { body, set, user } = ctx as any;
|
|
try {
|
|
const { id } = body;
|
|
|
|
logger.info({ id, userId: user?.id }, "Deleting API key");
|
|
|
|
if (!user) {
|
|
set.status = 401;
|
|
return { error: "Unauthorized" };
|
|
}
|
|
|
|
// Verify that the API key belongs to the user
|
|
const apiKey = await prisma.apiKey.findUnique({
|
|
where: { id },
|
|
});
|
|
|
|
if (!apiKey || apiKey.userId !== user.id) {
|
|
set.status = 403;
|
|
logger.warn(
|
|
{ id, userId: user?.id },
|
|
"Attempt to delete API key from another user",
|
|
);
|
|
return { error: "Forbidden" };
|
|
}
|
|
|
|
await prisma.apiKey.delete({
|
|
where: { id },
|
|
});
|
|
|
|
logger.info({ id }, "Deleted API key");
|
|
|
|
return { success: true };
|
|
} catch (error) {
|
|
logger.error({ error }, "Failed to delete API key");
|
|
set.status = 500;
|
|
return { error: "Failed to delete API key" };
|
|
}
|
|
},
|
|
{
|
|
body: t.Object({
|
|
id: t.String(),
|
|
}),
|
|
detail: {
|
|
summary: "Delete an API key",
|
|
description: "Delete an API key",
|
|
},
|
|
},
|
|
);
|