import { randomBytes } from "crypto"; import { hash } from "bcrypt"; import * as ApeKeysDAL from "../../dal/ape-keys"; import MonkeyError from "../../utils/error"; import { MonkeyResponse } from "../../utils/monkey-response"; import { base64UrlEncode, omit } from "../../utils/misc"; import { ObjectId } from "mongodb"; import { AddApeKeyRequest, AddApeKeyResponse, ApeKeyParams, EditApeKeyRequest, GetApeKeyResponse, } from "@monkeytype/contracts/ape-keys"; import { ApeKey } from "@monkeytype/schemas/ape-keys"; import { MonkeyRequest } from "../types"; function cleanApeKey(apeKey: ApeKeysDAL.DBApeKey): ApeKey { return omit(apeKey, ["hash", "_id", "uid", "useCount"]); } export async function getApeKeys( req: MonkeyRequest, ): Promise { const { uid } = req.ctx.decodedToken; const apeKeys = await ApeKeysDAL.getApeKeys(uid); const cleanedKeys: Record = Object.fromEntries( apeKeys.map((item) => [item._id.toHexString(), cleanApeKey(item)]), ); return new MonkeyResponse("ApeKeys retrieved", cleanedKeys); } export async function generateApeKey( req: MonkeyRequest, ): Promise { const { name, enabled } = req.body; const { uid } = req.ctx.decodedToken; const { maxKeysPerUser, apeKeyBytes, apeKeySaltRounds } = req.ctx.configuration.apeKeys; const currentNumberOfApeKeys = await ApeKeysDAL.countApeKeysForUser(uid); if (currentNumberOfApeKeys >= maxKeysPerUser) { throw new MonkeyError(409, "Maximum number of ApeKeys have been generated"); } const apiKey = randomBytes(apeKeyBytes).toString("base64url"); const saltyHash = await hash(apiKey, apeKeySaltRounds); const apeKey: ApeKeysDAL.DBApeKey = { _id: new ObjectId(), name, enabled, uid, hash: saltyHash, createdOn: Date.now(), modifiedOn: Date.now(), lastUsedOn: -1, useCount: 0, }; const apeKeyId = await ApeKeysDAL.addApeKey(apeKey); return new MonkeyResponse("ApeKey generated", { apeKey: base64UrlEncode(`${apeKeyId}.${apiKey}`), apeKeyId, apeKeyDetails: cleanApeKey(apeKey), }); } export async function editApeKey( req: MonkeyRequest, ): Promise { const { apeKeyId } = req.params; const { name, enabled } = req.body; const { uid } = req.ctx.decodedToken; await ApeKeysDAL.editApeKey(uid, apeKeyId, name, enabled); return new MonkeyResponse("ApeKey updated", null); } export async function deleteApeKey( req: MonkeyRequest, ): Promise { const { apeKeyId } = req.params; const { uid } = req.ctx.decodedToken; await ApeKeysDAL.deleteApeKey(uid, apeKeyId); return new MonkeyResponse("ApeKey deleted", null); }