0.4.0 convex及界面修改
This commit is contained in:
@@ -1,14 +1,24 @@
|
||||
import { internalQuery, mutation, query } from "./_generated/server";
|
||||
import { internalMutation, internalQuery, mutation, query } from "./_generated/server";
|
||||
import { v } from "convex/values";
|
||||
import { getAuthUserId } from "@convex-dev/auth/server";
|
||||
import { nowIso } from "./_utils/time";
|
||||
import { enqueueIngestMindmapJob } from "./_utils/ingestJobs";
|
||||
import { internal } from "./_generated/api";
|
||||
|
||||
const defaultMindmapData = {
|
||||
data: { text: "中心主题" },
|
||||
children: [],
|
||||
};
|
||||
|
||||
function resolveGraceSeconds(): number {
|
||||
const raw = process.env.DELETE_GRACE_SECONDS ?? process.env.NEXT_PUBLIC_DELETE_GRACE_SECONDS ?? "600";
|
||||
const parsed = Number(raw);
|
||||
if (!Number.isFinite(parsed) || parsed <= 0) {
|
||||
return 600;
|
||||
}
|
||||
return Math.floor(parsed);
|
||||
}
|
||||
|
||||
async function requireUserId(ctx: any): Promise<string> {
|
||||
const userId = await getAuthUserId(ctx);
|
||||
if (userId === null) {
|
||||
@@ -195,12 +205,71 @@ export const softDelete = mutation({
|
||||
return { ok: true, moved: 0 };
|
||||
}
|
||||
|
||||
if (existing.deleted_at != null) {
|
||||
// 已在垃圾桶:保持幂等,避免重复删除导致 updated_at 抖动/重复调度
|
||||
return { ok: true, moved: 0, deleted_at: existing.deleted_at };
|
||||
}
|
||||
|
||||
const ts = nowIso();
|
||||
await ctx.db.patch(existing._id, { deleted_at: ts, deleted_by: userId, updated_at: ts });
|
||||
|
||||
// 10 分钟宽限(可恢复);到期自动清理(彻底删除)。
|
||||
// 说明:这里用 scheduler.runAfter 做“按条目调度”,无需全局 cron。
|
||||
const graceMs = (resolveGraceSeconds() + 5) * 1000;
|
||||
await ctx.scheduler.runAfter(graceMs, internal.mindmaps.purgeIfExpired, {
|
||||
docId: args.docId,
|
||||
mindmapId,
|
||||
deletedAt: ts,
|
||||
});
|
||||
return { ok: true, moved: 1, deleted_at: ts };
|
||||
},
|
||||
});
|
||||
|
||||
export const purgeIfExpired = internalMutation({
|
||||
args: { docId: v.string(), mindmapId: v.string(), deletedAt: v.string() },
|
||||
handler: async (ctx, args) => {
|
||||
const mindmapId = normalizeMindmapId(args.docId, args.mindmapId);
|
||||
|
||||
const existing = await ctx.db
|
||||
.query("mindmaps")
|
||||
.withIndex("by_doc_mindmap", (q) => q.eq("document_id", args.docId).eq("mindmap_id", mindmapId))
|
||||
.first();
|
||||
|
||||
if (!existing) {
|
||||
return { ok: true, deleted: false, reason: "not_found" as const };
|
||||
}
|
||||
|
||||
if (existing.deleted_at == null) {
|
||||
return { ok: true, deleted: false, reason: "restored" as const };
|
||||
}
|
||||
|
||||
if (existing.deleted_at !== args.deletedAt) {
|
||||
// 删除时间不一致:说明已被恢复/重新删除过,旧调度作废
|
||||
return { ok: true, deleted: false, reason: "changed" as const };
|
||||
}
|
||||
|
||||
const graceSeconds = resolveGraceSeconds();
|
||||
const deletedTs = Date.parse(existing.deleted_at);
|
||||
if (Number.isFinite(deletedTs)) {
|
||||
const elapsedMs = Date.now() - deletedTs;
|
||||
const graceMs = graceSeconds * 1000;
|
||||
if (elapsedMs < graceMs) {
|
||||
// 兜底:调度延迟/时钟偏差时,重新排一次剩余时间
|
||||
const remainingMs = Math.max(1000, graceMs - elapsedMs + 5000);
|
||||
await ctx.scheduler.runAfter(remainingMs, internal.mindmaps.purgeIfExpired, {
|
||||
docId: args.docId,
|
||||
mindmapId,
|
||||
deletedAt: args.deletedAt,
|
||||
});
|
||||
return { ok: true, deleted: false, rescheduled: true, remainingMs };
|
||||
}
|
||||
}
|
||||
|
||||
await ctx.db.delete(existing._id);
|
||||
return { ok: true, deleted: true };
|
||||
},
|
||||
});
|
||||
|
||||
export const restore = mutation({
|
||||
args: { docId: v.string(), mindmapId: v.string() },
|
||||
handler: async (ctx, args) => {
|
||||
|
||||
Reference in New Issue
Block a user