Files
SanatiLeads/convex/dataSources.ts

120 lines
4.0 KiB
TypeScript

import { mutation, query } from "./_generated/server";
import { v } from "convex/values";
import { getAuthUserId } from "@convex-dev/auth/server";
export const getProjectDataSources = query({
args: { projectId: v.id("projects") },
handler: async (ctx, args) => {
const userId = await getAuthUserId(ctx);
if (!userId) return [];
// Verify project ownership
const project = await ctx.db.get(args.projectId);
if (!project || project.userId !== userId) return [];
return await ctx.db
.query("dataSources")
.filter((q) => q.eq(q.field("projectId"), args.projectId))
.collect();
},
});
export const addDataSource = mutation({
args: {
projectId: v.optional(v.id("projects")), // Optional, if not provided, use default
url: v.string(),
name: v.string(),
type: v.literal("website"),
},
handler: async (ctx, args) => {
const userId = await getAuthUserId(ctx);
if (!userId) throw new Error("Unauthorized");
let projectId = args.projectId;
// Use default project if not provided
if (!projectId) {
const defaultProject = await ctx.db
.query("projects")
.filter((q) => q.and(q.eq(q.field("userId"), userId), q.eq(q.field("isDefault"), true)))
.first();
if (!defaultProject) {
// Create a default project if none exists (First onboarding)
projectId = await ctx.db.insert("projects", {
userId,
name: "My Project",
isDefault: true,
dorkingConfig: { selectedSourceIds: [] },
});
} else {
projectId = defaultProject._id;
}
}
const existing = await ctx.db
.query("dataSources")
.withIndex("by_project_url", (q) =>
q.eq("projectId", projectId!).eq("url", args.url)
)
.first();
const sourceId = existing
? existing._id
: await ctx.db.insert("dataSources", {
projectId: projectId!, // Assert exists
type: args.type,
url: args.url,
name: args.name,
analysisStatus: "pending",
lastAnalyzedAt: undefined,
lastError: undefined,
// analysisResults not set initially
});
// Auto-select this source in the project config
const project = await ctx.db.get(projectId!);
if (project) {
const currentSelected = project.dorkingConfig.selectedSourceIds;
if (!currentSelected.includes(sourceId)) {
await ctx.db.patch(projectId!, {
dorkingConfig: { selectedSourceIds: [...currentSelected, sourceId] }
});
}
}
return { sourceId, projectId: projectId! };
},
});
export const updateDataSourceStatus = mutation({
args: {
dataSourceId: v.id("dataSources"),
analysisStatus: v.union(
v.literal("pending"),
v.literal("completed"),
v.literal("failed")
),
lastError: v.optional(v.string()),
lastAnalyzedAt: v.optional(v.number()),
},
handler: async (ctx, args) => {
const userId = await getAuthUserId(ctx);
if (!userId) throw new Error("Unauthorized");
const dataSource = await ctx.db.get(args.dataSourceId);
if (!dataSource) throw new Error("Data source not found");
const project = await ctx.db.get(dataSource.projectId);
if (!project || project.userId !== userId) {
throw new Error("Project not found or unauthorized");
}
await ctx.db.patch(args.dataSourceId, {
analysisStatus: args.analysisStatus,
lastError: args.lastError,
lastAnalyzedAt: args.lastAnalyzedAt,
});
},
});