Initial commit: Setup Bun, Elysia, Vite, React, TanStack Router, Mantine, and Biome
This commit is contained in:
233
src/api/apikey.ts
Normal file
233
src/api/apikey.ts
Normal file
@@ -0,0 +1,233 @@
|
||||
/** 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",
|
||||
},
|
||||
},
|
||||
);
|
||||
Reference in New Issue
Block a user