import { TRPCError } from "@trpc/server"; import { and, eq } from "drizzle-orm"; import { z } from "zod"; import { rssFeedsTable } from "@karakeep/db/schema"; import { zFeedSchema, zNewFeedSchema, zUpdateFeedSchema, } from "@karakeep/shared/types/feeds"; import { AuthedContext } from ".."; export class Feed { constructor( protected ctx: AuthedContext, private feed: typeof rssFeedsTable.$inferSelect, ) {} static async fromId(ctx: AuthedContext, id: string): Promise { const feed = await ctx.db.query.rssFeedsTable.findFirst({ where: eq(rssFeedsTable.id, id), }); if (!feed) { throw new TRPCError({ code: "NOT_FOUND", message: "Feed not found", }); } // If it exists but belongs to another user, throw forbidden error if (feed.userId !== ctx.user.id) { throw new TRPCError({ code: "FORBIDDEN", message: "User is not allowed to access resource", }); } return new Feed(ctx, feed); } static async create( ctx: AuthedContext, input: z.infer, ): Promise { const [result] = await ctx.db .insert(rssFeedsTable) .values({ name: input.name, url: input.url, userId: ctx.user.id, enabled: input.enabled, importTags: input.importTags ?? false, }) .returning(); return new Feed(ctx, result); } static async getAll(ctx: AuthedContext): Promise { const feeds = await ctx.db.query.rssFeedsTable.findMany({ where: eq(rssFeedsTable.userId, ctx.user.id), }); return feeds.map((f) => new Feed(ctx, f)); } async delete(): Promise { const res = await this.ctx.db .delete(rssFeedsTable) .where( and( eq(rssFeedsTable.id, this.feed.id), eq(rssFeedsTable.userId, this.ctx.user.id), ), ); if (res.changes === 0) { throw new TRPCError({ code: "NOT_FOUND" }); } } async update(input: z.infer): Promise { const result = await this.ctx.db .update(rssFeedsTable) .set({ name: input.name, url: input.url, enabled: input.enabled, importTags: input.importTags, }) .where( and( eq(rssFeedsTable.id, this.feed.id), eq(rssFeedsTable.userId, this.ctx.user.id), ), ) .returning(); if (result.length === 0) { throw new TRPCError({ code: "NOT_FOUND" }); } this.feed = result[0]; } asPublicFeed(): z.infer { return this.feed; } }