aboutsummaryrefslogtreecommitdiffstats
path: root/packages/trpc/models/webhooks.ts
diff options
context:
space:
mode:
authorMohamedBassem <me@mbassem.com>2025-08-02 21:41:59 -0700
committerMohamedBassem <me@mbassem.com>2025-08-02 21:41:59 -0700
commit99653566f73187631d30cb52a66a982c455c1f9a (patch)
treead7d276d88677deea6848821132116e191ad6dff /packages/trpc/models/webhooks.ts
parent2493ccf08e4a4e96c6be8f3e5ee80f7db7284dfe (diff)
downloadkarakeep-99653566f73187631d30cb52a66a982c455c1f9a.tar.zst
refactor: Move webhook, users and tags into models
Diffstat (limited to 'packages/trpc/models/webhooks.ts')
-rw-r--r--packages/trpc/models/webhooks.ts123
1 files changed, 123 insertions, 0 deletions
diff --git a/packages/trpc/models/webhooks.ts b/packages/trpc/models/webhooks.ts
new file mode 100644
index 00000000..3a8c7bab
--- /dev/null
+++ b/packages/trpc/models/webhooks.ts
@@ -0,0 +1,123 @@
+import { TRPCError } from "@trpc/server";
+import { and, eq } from "drizzle-orm";
+import { z } from "zod";
+
+import { webhooksTable } from "@karakeep/db/schema";
+import {
+ zNewWebhookSchema,
+ zUpdateWebhookSchema,
+ zWebhookSchema,
+} from "@karakeep/shared/types/webhooks";
+
+import { AuthedContext } from "..";
+import { PrivacyAware } from "./privacy";
+
+export class Webhook implements PrivacyAware {
+ constructor(
+ protected ctx: AuthedContext,
+ public webhook: typeof webhooksTable.$inferSelect,
+ ) {}
+
+ static async fromId(ctx: AuthedContext, id: string): Promise<Webhook> {
+ const webhook = await ctx.db.query.webhooksTable.findFirst({
+ where: eq(webhooksTable.id, id),
+ });
+
+ if (!webhook) {
+ throw new TRPCError({
+ code: "NOT_FOUND",
+ message: "Webhook not found",
+ });
+ }
+
+ // If it exists but belongs to another user, throw forbidden error
+ if (webhook.userId !== ctx.user.id) {
+ throw new TRPCError({
+ code: "FORBIDDEN",
+ message: "User is not allowed to access resource",
+ });
+ }
+
+ return new Webhook(ctx, webhook);
+ }
+
+ static async create(
+ ctx: AuthedContext,
+ input: z.infer<typeof zNewWebhookSchema>,
+ ): Promise<Webhook> {
+ const [result] = await ctx.db
+ .insert(webhooksTable)
+ .values({
+ url: input.url,
+ events: input.events,
+ token: input.token ?? null,
+ userId: ctx.user.id,
+ })
+ .returning();
+
+ return new Webhook(ctx, result);
+ }
+
+ static async getAll(ctx: AuthedContext): Promise<Webhook[]> {
+ const webhooks = await ctx.db.query.webhooksTable.findMany({
+ where: eq(webhooksTable.userId, ctx.user.id),
+ });
+
+ return webhooks.map((w) => new Webhook(ctx, w));
+ }
+
+ ensureCanAccess(ctx: AuthedContext): void {
+ if (this.webhook.userId !== ctx.user.id) {
+ throw new TRPCError({
+ code: "FORBIDDEN",
+ message: "User is not allowed to access resource",
+ });
+ }
+ }
+
+ async delete(): Promise<void> {
+ const res = await this.ctx.db
+ .delete(webhooksTable)
+ .where(
+ and(
+ eq(webhooksTable.id, this.webhook.id),
+ eq(webhooksTable.userId, this.ctx.user.id),
+ ),
+ );
+
+ if (res.changes === 0) {
+ throw new TRPCError({ code: "NOT_FOUND" });
+ }
+ }
+
+ async update(input: z.infer<typeof zUpdateWebhookSchema>): Promise<void> {
+ const result = await this.ctx.db
+ .update(webhooksTable)
+ .set({
+ url: input.url,
+ events: input.events,
+ token: input.token,
+ })
+ .where(
+ and(
+ eq(webhooksTable.id, this.webhook.id),
+ eq(webhooksTable.userId, this.ctx.user.id),
+ ),
+ )
+ .returning();
+
+ if (result.length === 0) {
+ throw new TRPCError({ code: "NOT_FOUND" });
+ }
+
+ this.webhook = result[0];
+ }
+
+ asPublicWebhook(): z.infer<typeof zWebhookSchema> {
+ const { token, ...rest } = this.webhook;
+ return {
+ ...rest,
+ hasToken: token !== null,
+ };
+ }
+}