Authorize Issue Edit and Delete
The issueβs creator can edit or delete the issue, but only while its status is todo. Once the issue moves out of To Do, the creator can no longer modify it.
Add assertIssueCreatorAndEditable
Open convex/issues.ts and add a guard at the top, following the same pattern as assertProjectOwner:
import { query, mutation, MutationCtx } from "./_generated/server"; // π
import { ConvexError, v } from "convex/values"; // π
import { getCurrentUser } from "./users";
import type { Doc, Id } from "./_generated/dataModel"; // π
/**
* Guard for operations that only the issue's creator can perform, and
* only while the issue is still in the "todo" column. As soon as the
* project owner moves the issue out of To Do, the creator loses these
* rights β the issue is considered "triaged" and effectively read-only
* from the creator's perspective.
*/
async function assertIssueCreatorAndEditable(
ctx: MutationCtx,
issueId: Id<"issues">,
): Promise<Doc<"issues">> {
const user = await getCurrentUser(ctx);
const issue = await ctx.db.get(issueId);
if (issue === null) {
throw new ConvexError("Issue not found");
}
if (issue.creatorId !== user._id) {
throw new ConvexError("Only the creator can modify this issue");
}
if (issue.status !== "todo") {
throw new ConvexError("This issue can no longer be modified");
}
return issue;
}
Notice I did not export this guard. It is only used inside issues.ts, so there is no need to make it available outside this module.
Add the update Mutation
The app did not have an update operation for issues before. Add it alongside create, updateStatus, and remove:
export const update = mutation({
args: {
id: v.id("issues"),
title: v.string(),
description: v.string(),
},
handler: async (ctx, args) => {
await assertIssueCreatorAndEditable(ctx, args.id);
await ctx.db.patch(args.id, {
title: args.title,
description: args.description,
});
},
});
Same structure as the remove mutation we are about to update. The rule is checked in one place, assertIssueCreatorAndEditable, so the body of the mutation only has to patch the document.
Apply the Same Guard to remove
Replace the existing remove mutation, which has no authorization check, with one that calls the guard:
export const remove = mutation({
args: {
id: v.id("issues"),
},
handler: async (ctx, args) => {
await assertIssueCreatorAndEditable(ctx, args.id);
await ctx.db.delete(args.id);
},
});
Now update and remove share the exact same authorization logic through the guard. If tomorrow we decide creators should also be allowed to edit issues while they are in in-progress, we change the rule once in the helper and both mutations update automatically.
Checkpoint: Commit your progress.
git add .
git commit -m "tracker-10: Authorize issue edit and delete (creator, while in To Do)"
git push