a tool for shared writing and social publishing

don't allow users to delete drafts they don't own

Changed files
+60 -6
actions
app
(home-pages)
home
LeafletList
+46
actions/deleteLeaflet.ts
··· 16 16 export async function deleteLeaflet(permission_token: PermissionToken) { 17 17 const client = await pool.connect(); 18 18 const db = drizzle(client); 19 + 20 + // Get the current user's identity 21 + let identity = await getIdentityData(); 22 + 23 + // Check publication and document ownership in one query 24 + let { data: tokenData } = await supabaseServerClient 25 + .from("permission_tokens") 26 + .select(` 27 + id, 28 + leaflets_in_publications(publication, publications!inner(identity_did)), 29 + leaflets_to_documents(document, documents!inner(uri)) 30 + `) 31 + .eq("id", permission_token.id) 32 + .single(); 33 + 34 + if (tokenData) { 35 + // Check if leaflet is in a publication 36 + const leafletInPubs = tokenData.leaflets_in_publications || []; 37 + if (leafletInPubs.length > 0) { 38 + if (!identity) { 39 + throw new Error("Unauthorized: You must be logged in to delete a leaflet in a publication"); 40 + } 41 + const isOwner = leafletInPubs.some( 42 + (pub: any) => pub.publications.identity_did === identity.atp_did 43 + ); 44 + if (!isOwner) { 45 + throw new Error("Unauthorized: You must own the publication to delete this leaflet"); 46 + } 47 + } 48 + 49 + // Check if there's a standalone published document 50 + const leafletDocs = tokenData.leaflets_to_documents || []; 51 + if (leafletDocs.length > 0) { 52 + if (!identity) { 53 + throw new Error("Unauthorized: You must be logged in to delete a published leaflet"); 54 + } 55 + for (let leafletDoc of leafletDocs) { 56 + const docUri = leafletDoc.documents?.uri; 57 + // Extract the DID from the document URI (format: at://did:plc:xxx/...) 58 + if (docUri && !docUri.includes(identity.atp_did)) { 59 + throw new Error("Unauthorized: You must own the published document to delete this leaflet"); 60 + } 61 + } 62 + } 63 + } 64 + 19 65 await db.transaction(async (tx) => { 20 66 let [token] = await tx 21 67 .select()
+14 -6
app/(home-pages)/home/LeafletList/LeafletOptions.tsx
··· 86 86 const pubStatus = useLeafletPublicationStatus(); 87 87 const toaster = useToaster(); 88 88 const { setArchived } = useArchiveMutations(); 89 + const { identity } = useIdentityData(); 89 90 const tokenId = pubStatus?.token.id; 90 91 const itemType = pubStatus?.draftInPublication ? "Draft" : "Leaflet"; 92 + 93 + // Check if this is a published post/document and if user is the owner 94 + const isPublishedPostOwner = 95 + !!identity?.atp_did && !!pubStatus?.documentUri?.includes(identity.atp_did); 96 + const canDelete = !pubStatus?.documentUri || isPublishedPostOwner; 91 97 92 98 return ( 93 99 <> ··· 133 139 <ArchiveSmall /> 134 140 {!props.archived ? " Archive" : "Unarchive"} {itemType} 135 141 </MenuItem> 136 - <DeleteForeverMenuItem 137 - onSelect={(e) => { 138 - e.preventDefault(); 139 - props.setState("areYouSure"); 140 - }} 141 - /> 142 + {canDelete && ( 143 + <DeleteForeverMenuItem 144 + onSelect={(e) => { 145 + e.preventDefault(); 146 + props.setState("areYouSure"); 147 + }} 148 + /> 149 + )} 142 150 </> 143 151 ); 144 152 };