feat: add api keys (#991)

* feat: add api keys

* chore: address pull request feedback

---------

Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
This commit is contained in:
Manuel
2024-10-05 16:18:31 +02:00
committed by GitHub
parent ee8375756c
commit b14f82b4bb
22 changed files with 3374 additions and 60 deletions

View File

@@ -1,14 +1,59 @@
import { createOpenApiFetchHandler } from "trpc-swagger/build/index.mjs";
import { appRouter, createTRPCContext } from "@homarr/api";
import type { Session } from "@homarr/auth";
import { createSessionAsync } from "@homarr/auth/server";
import { db, eq } from "@homarr/db";
import { apiKeys } from "@homarr/db/schema/sqlite";
import { logger } from "@homarr/log";
const handlerAsync = async (req: Request) => {
const apiKeyHeaderValue = req.headers.get("ApiKey");
const session: Session | null = await getSessionOrDefaultFromHeadersAsync(apiKeyHeaderValue);
const handler = (req: Request) => {
return createOpenApiFetchHandler({
req,
endpoint: "/",
router: appRouter,
createContext: () => createTRPCContext({ session: null, headers: req.headers }),
createContext: () => createTRPCContext({ session, headers: req.headers }),
});
};
export { handler as GET, handler as POST };
const getSessionOrDefaultFromHeadersAsync = async (apiKeyHeaderValue: string | null): Promise<Session | null> => {
logger.info(
`Creating OpenAPI fetch handler for user ${apiKeyHeaderValue ? "with an api key" : "without an api key"}`,
);
if (apiKeyHeaderValue === null) {
return null;
}
const apiKeyFromDb = await db.query.apiKeys.findFirst({
where: eq(apiKeys.apiKey, apiKeyHeaderValue),
columns: {
id: true,
apiKey: false,
salt: false,
},
with: {
user: {
columns: {
id: true,
name: true,
email: true,
emailVerified: true,
},
},
},
});
if (apiKeyFromDb === undefined) {
logger.warn("An attempt to authenticate over API has failed");
return null;
}
logger.info(`Read session from API request and found user ${apiKeyFromDb.user.name} (${apiKeyFromDb.user.id})`);
return await createSessionAsync(db, apiKeyFromDb.user);
};
export { handlerAsync as GET, handlerAsync as POST };