0.3.5 共享功能修复
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { mutation, query } from "./_generated/server";
|
||||
import { v } from "convex/values";
|
||||
import { nowIso } from "./_utils/time";
|
||||
import { enqueueIngestMediaAssetJob } from "./_utils/ingestJobs";
|
||||
import { enqueueExtractMediaAssetTextJob, enqueueIngestMediaAssetJob } from "./_utils/ingestJobs";
|
||||
import type { MutationCtx, QueryCtx } from "./_generated/server";
|
||||
|
||||
async function assertWorkspaceMember(ctx: QueryCtx | MutationCtx, userId: string, workspaceId: string) {
|
||||
@@ -15,6 +15,26 @@ async function assertWorkspaceMember(ctx: QueryCtx | MutationCtx, userId: string
|
||||
return membership;
|
||||
}
|
||||
|
||||
function shouldExtractAttachmentText(args: {
|
||||
assetType?: string | null;
|
||||
mimeType?: string | null;
|
||||
fileName?: string | null;
|
||||
}): boolean {
|
||||
if (String(args.assetType ?? "") !== "file") return false;
|
||||
const mime = String(args.mimeType ?? "").toLowerCase().trim();
|
||||
if (mime === "application/pdf") return true;
|
||||
if (mime === "application/vnd.openxmlformats-officedocument.wordprocessingml.document") return true;
|
||||
if (mime === "application/vnd.openxmlformats-officedocument.presentationml.presentation") return true;
|
||||
if (mime === "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") return true;
|
||||
|
||||
const name = String(args.fileName ?? "").toLowerCase().trim();
|
||||
if (name.endsWith(".pdf")) return true;
|
||||
if (name.endsWith(".docx")) return true;
|
||||
if (name.endsWith(".pptx")) return true;
|
||||
if (name.endsWith(".xlsx")) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
export const getById = query({
|
||||
args: { userId: v.string(), id: v.string() },
|
||||
handler: async (ctx, args) => {
|
||||
@@ -76,6 +96,44 @@ export const listByWorkspace = query({
|
||||
},
|
||||
});
|
||||
|
||||
export const listSearchDataByWorkspace = query({
|
||||
args: {
|
||||
userId: v.string(),
|
||||
workspaceId: v.string(),
|
||||
includeDeleted: v.optional(v.boolean()),
|
||||
limit: v.optional(v.number()),
|
||||
},
|
||||
handler: async (ctx, args) => {
|
||||
await assertWorkspaceMember(ctx, args.userId, args.workspaceId);
|
||||
|
||||
const includeDeleted = Boolean(args.includeDeleted);
|
||||
const limit = Math.max(1, Math.min(5000, Math.floor(args.limit ?? 5000)));
|
||||
|
||||
const rows = await ctx.db
|
||||
.query("media_assets")
|
||||
.withIndex("by_workspace", (q) => q.eq("workspace_id", args.workspaceId))
|
||||
.order("desc")
|
||||
.take(limit * 2);
|
||||
|
||||
return rows
|
||||
.filter((r) => (includeDeleted ? true : !r.deleted_at))
|
||||
.filter((r) => !r.purged_at)
|
||||
.slice(0, limit)
|
||||
.map((r) => ({
|
||||
id: r.id,
|
||||
workspace_id: r.workspace_id,
|
||||
document_id: r.document_id,
|
||||
asset_type: r.asset_type,
|
||||
file_name: r.file_name ?? null,
|
||||
mime_type: r.mime_type ?? null,
|
||||
ocr_text: r.ocr_text ?? null,
|
||||
ocr_status: r.ocr_status ?? null,
|
||||
created_at: r.created_at ?? null,
|
||||
updated_at: r.updated_at ?? null,
|
||||
}));
|
||||
},
|
||||
});
|
||||
|
||||
export const listByDocument = query({
|
||||
args: {
|
||||
userId: v.string(),
|
||||
@@ -162,7 +220,13 @@ export const create = mutation({
|
||||
...args.asset,
|
||||
storage_id: args.asset.storage_id ?? null,
|
||||
ocr_text: null,
|
||||
ocr_status: null,
|
||||
ocr_status: shouldExtractAttachmentText({
|
||||
assetType: args.asset.asset_type,
|
||||
mimeType: args.asset.mime_type,
|
||||
fileName: args.asset.file_name,
|
||||
})
|
||||
? "queued"
|
||||
: null,
|
||||
deleted_at: null,
|
||||
deleted_by: null,
|
||||
purged_at: null,
|
||||
@@ -171,6 +235,16 @@ export const create = mutation({
|
||||
updated_at: ts,
|
||||
});
|
||||
|
||||
if (
|
||||
shouldExtractAttachmentText({
|
||||
assetType: args.asset.asset_type,
|
||||
mimeType: args.asset.mime_type,
|
||||
fileName: args.asset.file_name,
|
||||
})
|
||||
) {
|
||||
await enqueueExtractMediaAssetTextJob(ctx, { userId: args.userId, assetId: args.asset.id, debounceMs: 800 });
|
||||
}
|
||||
|
||||
return args.asset;
|
||||
},
|
||||
});
|
||||
@@ -222,7 +296,13 @@ export const createWithStorage = mutation({
|
||||
mime_type: args.asset.mime_type,
|
||||
|
||||
ocr_text: null,
|
||||
ocr_status: null,
|
||||
ocr_status: shouldExtractAttachmentText({
|
||||
assetType: args.asset.asset_type,
|
||||
mimeType: args.asset.mime_type,
|
||||
fileName: args.asset.file_name,
|
||||
})
|
||||
? "queued"
|
||||
: null,
|
||||
deleted_at: null,
|
||||
deleted_by: null,
|
||||
purged_at: null,
|
||||
@@ -232,6 +312,16 @@ export const createWithStorage = mutation({
|
||||
};
|
||||
|
||||
await ctx.db.insert("media_assets", row);
|
||||
|
||||
if (
|
||||
shouldExtractAttachmentText({
|
||||
assetType: args.asset.asset_type,
|
||||
mimeType: args.asset.mime_type,
|
||||
fileName: args.asset.file_name,
|
||||
})
|
||||
) {
|
||||
await enqueueExtractMediaAssetTextJob(ctx, { userId: args.userId, assetId: args.asset.id, debounceMs: 800 });
|
||||
}
|
||||
return row;
|
||||
},
|
||||
});
|
||||
@@ -312,13 +402,57 @@ export const replaceStorageFromUpload = mutation({
|
||||
thumbnail_url: url,
|
||||
bucket: null,
|
||||
storage_path: null,
|
||||
ocr_text: null,
|
||||
ocr_status: shouldExtractAttachmentText({
|
||||
assetType: row.asset_type,
|
||||
mimeType: row.mime_type ?? null,
|
||||
fileName: row.file_name ?? null,
|
||||
})
|
||||
? "queued"
|
||||
: row.ocr_status ?? null,
|
||||
updated_at: nowIso(),
|
||||
});
|
||||
|
||||
if (
|
||||
shouldExtractAttachmentText({
|
||||
assetType: row.asset_type,
|
||||
mimeType: row.mime_type ?? null,
|
||||
fileName: row.file_name ?? null,
|
||||
})
|
||||
) {
|
||||
await enqueueExtractMediaAssetTextJob(ctx, { userId: args.userId, assetId: args.id, debounceMs: 800 });
|
||||
}
|
||||
|
||||
return { ok: true, fileUrl: url };
|
||||
},
|
||||
});
|
||||
|
||||
export const enqueueExtractText = mutation({
|
||||
args: { userId: v.string(), id: v.string() },
|
||||
handler: async (ctx, args) => {
|
||||
const row = await ctx.db
|
||||
.query("media_assets")
|
||||
.withIndex("by_asset_id", (q) => q.eq("id", args.id))
|
||||
.first();
|
||||
if (!row) throw new Error("资源不存在");
|
||||
await assertWorkspaceMember(ctx, args.userId, row.workspace_id);
|
||||
|
||||
if (
|
||||
!shouldExtractAttachmentText({
|
||||
assetType: row.asset_type,
|
||||
mimeType: row.mime_type ?? null,
|
||||
fileName: row.file_name ?? null,
|
||||
})
|
||||
) {
|
||||
return { ok: false, reason: "unsupported" as const };
|
||||
}
|
||||
|
||||
await ctx.db.patch(row._id, { ocr_status: "queued", updated_at: nowIso() });
|
||||
await enqueueExtractMediaAssetTextJob(ctx, { userId: args.userId, assetId: args.id, debounceMs: 0 });
|
||||
return { ok: true };
|
||||
},
|
||||
});
|
||||
|
||||
export const purgeById = mutation({
|
||||
args: { userId: v.string(), id: v.string(), expiredDeletedAt: v.string() },
|
||||
handler: async (ctx, args) => {
|
||||
|
||||
Reference in New Issue
Block a user