109 lines
3.7 KiB
TypeScript
109 lines
3.7 KiB
TypeScript
import { COOKIENAME } from "../..";
|
|
import { db, fileRouter } from "../../";
|
|
import { authCheck } from "../../lib/Auth";
|
|
import crypto from "crypto";
|
|
|
|
export = new fileRouter.Path("/")
|
|
.http("GET", "/api/account/api-keys", (http) =>
|
|
http.onRequest(async (ctr) => {
|
|
const cookie = ctr.cookies.get(COOKIENAME) || null;
|
|
const apiHeader = (ctr.headers && ctr.headers.get)
|
|
? ctr.headers.get("api-authentication") || null
|
|
: null;
|
|
const auth = await authCheck(cookie, apiHeader);
|
|
if (!auth.state) {
|
|
return ctr.status(ctr.$status.UNAUTHORIZED).print({ error: auth.message });
|
|
}
|
|
|
|
const user = await db.collection("users").findOne({ id: auth.userId });
|
|
if (!user) {
|
|
return ctr.status(ctr.$status.NOT_FOUND).print({ error: "User not found" });
|
|
}
|
|
|
|
const keys = (user.apiKeys || []).map((k: any) => ({
|
|
id: k.id,
|
|
description: k.description,
|
|
createdAt: k.createdAt,
|
|
}));
|
|
|
|
return ctr.print({ success: true, apiKeys: keys });
|
|
})
|
|
)
|
|
.http("POST", "/api/account/api-keys/create", (http) =>
|
|
http.onRequest(async (ctr) => {
|
|
const cookie = ctr.cookies.get(COOKIENAME) || null;
|
|
const apiHeader = (ctr.headers && ctr.headers.get)
|
|
? ctr.headers.get("api-authentication") || null
|
|
: null;
|
|
const auth = await authCheck(cookie, apiHeader);
|
|
if (!auth.state) {
|
|
return ctr.status(ctr.$status.UNAUTHORIZED).print({ error: auth.message });
|
|
}
|
|
|
|
const [data, error] = await ctr.bindBody((z) =>
|
|
z.object({ description: z.string().min(1).max(200) })
|
|
);
|
|
|
|
if (!data) return ctr.status(ctr.$status.BAD_REQUEST).print({ error: error.toString() });
|
|
|
|
const user = await db.collection("users").findOne({ id: auth.userId });
|
|
if (!user) return ctr.status(ctr.$status.NOT_FOUND).print({ error: "User not found" });
|
|
|
|
const existing = user.apiKeys || [];
|
|
if (existing.length >= 4) {
|
|
return ctr.status(ctr.$status.BAD_REQUEST).print({ error: "API key limit reached (max 4)" });
|
|
}
|
|
|
|
const raw = crypto.randomBytes(32).toString("hex");
|
|
const keyHash = crypto.createHash("sha256").update(raw).digest("hex");
|
|
const keyId = crypto.randomUUID();
|
|
|
|
const newKey = {
|
|
id: keyId,
|
|
description: data.description,
|
|
keyHash,
|
|
createdAt: new Date(),
|
|
};
|
|
|
|
const res = await db.collection("users").updateOne(
|
|
{ id: auth.userId },
|
|
{ $push: { apiKeys: newKey } } as any,
|
|
{ upsert: false }
|
|
);
|
|
|
|
if (!res.acknowledged) {
|
|
return ctr.status(ctr.$status.INTERNAL_SERVER_ERROR).print({ error: "Failed to create API key" });
|
|
}
|
|
|
|
return ctr.print({ success: true, apiKey: { id: keyId, description: data.description, createdAt: newKey.createdAt, token: raw } });
|
|
})
|
|
)
|
|
.http("DELETE", "/api/account/api-keys/{id}", (http) =>
|
|
http.onRequest(async (ctr) => {
|
|
const cookie = ctr.cookies.get(COOKIENAME) || null;
|
|
const apiHeader = (ctr.headers && ctr.headers.get)
|
|
? ctr.headers.get("api-authentication") || null
|
|
: null;
|
|
const auth = await authCheck(cookie, apiHeader);
|
|
if (!auth.state) {
|
|
return ctr.status(ctr.$status.UNAUTHORIZED).print({ error: auth.message });
|
|
}
|
|
|
|
const keyId = ctr.params.get("id");
|
|
if (!keyId) return ctr.status(ctr.$status.BAD_REQUEST).print({ error: "Key id required" });
|
|
|
|
const res = await db.collection("users").updateOne(
|
|
{ id: auth.userId },
|
|
{ $pull: { apiKeys: { id: keyId } } } as any
|
|
);
|
|
|
|
if (!res.acknowledged) {
|
|
return ctr.status(ctr.$status.INTERNAL_SERVER_ERROR).print({ error: "Failed to delete API key" });
|
|
}
|
|
|
|
return ctr.print({ success: true });
|
|
})
|
|
);
|
|
|
|
|