@ragrabbit/mcp

by madarco
Verified
"use server"; import { authActionClient } from "@repo/actions"; import { UnauthorizedError } from "@repo/core"; import db from "@repo/db"; import { and, asc, desc, eq, inArray, sql } from "@repo/db/drizzle"; import { Indexed, indexedTable, normalizeUrl } from "@repo/db/schema"; import { generateEmbeddingsDb } from "@repo/rag/indexing/db"; import { scrapeDbItem } from "@repo/rag/scraping/db"; import { revalidatePath } from "next/cache"; import { addCrawlSchema, addIndexSchema, editSingleIndexSchema, removeManyIndexesSchema, runIndexAllSchema, runIndexingSchema, updateCrawlSchema, } from "./actions.schema"; import { jobs } from "./jobs"; /** Authorization check */ async function canRunIndexing(ids: number[], orgId: number) { const indexed = await db .select({ id: indexedTable.id }) .from(indexedTable) .where(and(inArray(indexedTable.id, ids.map(Number)), eq(indexedTable.organizationId, orgId))); if (indexed.length !== ids.length) { throw new UnauthorizedError("Some indexes do not belong to this organization"); } } export const addIndexAction = authActionClient .schema(addIndexSchema) .metadata({ name: "addIndex" }) .action(async ({ parsedInput: { urls }, ctx }) => { const index = await db .insert(indexedTable) .values( urls .filter((url) => url.value) .map((url) => ({ url: url.value, normalizedUrl: normalizeUrl(url.value), organizationId: ctx.session.user.organizationId, })) ) .returning(); await jobs.triggerProcessing( index.map((i) => i.id), ctx.session.user.organizationId ); revalidatePath("/dashboard/indexing"); return { success: true, data: index }; }); export const removeManyIndexesAction = authActionClient .schema(removeManyIndexesSchema) .metadata({ name: "removeManyIndexes" }) .action(async ({ parsedInput: { ids }, ctx }) => { await db .delete(indexedTable) .where(and(inArray(indexedTable.id, ids), eq(indexedTable.organizationId, ctx.session.user.organizationId))); revalidatePath("/dashboard/indexing"); return { success: true }; }); export const runEmbeddingsAction = authActionClient .schema(runIndexingSchema) .metadata({ name: "runEmbeddings" }) .action(async ({ parsedInput: { ids }, ctx }) => { await canRunIndexing(ids, ctx.session.user.organizationId); if (ids.length == 1) { await generateEmbeddingsDb(ids[0]); revalidatePath("/dashboard/indexing/" + ids[0]); revalidatePath("/dashboard/indexing"); } else { await jobs.triggerEmbeddings(ids, ctx.session.user.organizationId); revalidatePath("/dashboard/indexing"); } return { success: true }; }); export const runScrapingAction = authActionClient .schema(runIndexingSchema) .metadata({ name: "runScraping" }) .action(async ({ parsedInput: { ids }, ctx }) => { await canRunIndexing(ids, ctx.session.user.organizationId); if (ids.length == 1) { // Run the code directly for quick result: const result = await scrapeDbItem(ids[0]); revalidatePath("/dashboard/indexing/" + ids[0]); return { success: true, result }; } else { await jobs.triggerGetContent(ids, ctx.session.user.organizationId); revalidatePath("/dashboard/indexing"); return { success: true }; } }); export const runProcessingAction = authActionClient .schema(runIndexingSchema) .metadata({ name: "runProcessing" }) .action(async ({ parsedInput: { ids }, ctx }) => { await canRunIndexing(ids, ctx.session.user.organizationId); await jobs.triggerProcessing(ids, ctx.session.user.organizationId); revalidatePath("/dashboard/indexing"); return { success: true }; }); export const runIndexAllAction = authActionClient .schema(runIndexAllSchema) .metadata({ name: "runIndexAll" }) .action(async ({ parsedInput: { orgId }, ctx }) => { if (orgId !== ctx.session.user.organizationId) { throw new UnauthorizedError("You are not authorized to index all indexes for this organization"); } await jobs.triggerIndexAll(orgId); revalidatePath("/dashboard/indexing"); return { success: true }; }); export const addCrawlAction = authActionClient .schema(addCrawlSchema) .metadata({ name: "addCrawl" }) .action(async ({ parsedInput: { url, isSitemap, scrapeOptions }, ctx }) => { const index = await db .insert(indexedTable) .values({ url, normalizedUrl: normalizeUrl(url), isSitemap, doCrawl: true, organizationId: ctx.session.user.organizationId, scrapeOptions: scrapeOptions, } as any) .returning(); await jobs.triggerProcessing([index[0].id], ctx.session.user.organizationId); revalidatePath("/dashboard/indexing"); return { success: true, data: index }; }); export const getAllIndexes = authActionClient.metadata({ name: "getAllIndexes" }).action(async ({ ctx }) => { return (await db .select() .from(indexedTable) .where(eq(indexedTable.organizationId, ctx.session.user.organizationId)) .orderBy( desc(indexedTable.doCrawl), desc(sql`${indexedTable.foundFromIndexId} IS NULL`), asc(indexedTable.createdAt) )) as Indexed[]; }); export const updateIndexAction = authActionClient .schema(editSingleIndexSchema) .metadata({ name: "updateIndex" }) .action(async ({ parsedInput: { id, url, clearFoundFrom }, ctx }) => { const index = await db .update(indexedTable) .set({ url: url, normalizedUrl: normalizeUrl(url), ...(clearFoundFrom ? { foundFromIndexId: null } : {}), }) .where(and(eq(indexedTable.id, id), eq(indexedTable.organizationId, ctx.session.user.organizationId))) .returning(); if (index.length === 0) { throw new UnauthorizedError("Index not found or not authorized"); } await jobs.triggerProcessing([id], ctx.session.user.organizationId); revalidatePath("/dashboard/indexing"); return { success: true, data: index[0] }; }); export const updateCrawlAction = authActionClient .schema(updateCrawlSchema) .metadata({ name: "updateCrawl" }) .action(async ({ parsedInput: { id, url, isSitemap, scrapeOptions }, ctx }) => { const index = await db .update(indexedTable) .set({ url, normalizedUrl: normalizeUrl(url), isSitemap, scrapeOptions, } as any) .where(and(eq(indexedTable.id, id), eq(indexedTable.organizationId, ctx.session.user.organizationId))) .returning(); if (index.length === 0) { throw new UnauthorizedError("Index not found or not authorized"); } await jobs.triggerProcessing([id], ctx.session.user.organizationId); revalidatePath("/dashboard/indexing"); return { success: true, data: index[0] }; });