1. Front Matter
Title: Secure Asset Delete
Author: Joshua Uriel Tribiana
Reviewers: Joylynne Esportuno (teruterubozuuu)
Creation Date: 2026-06-29
Status: Approved
References:
Issue: [3.7] Secure Asset Delete
2. Introduction & Goals
Problem Summary
Creators need a secure and irreversible method to permanently remove assets from their Asset Library. This is essential for managing storage space, complying with data retention policies, and removing obsolete or incorrect content. To prevent accidental data loss, this destructive action must be protected by a robust confirmation mechanism that cannot be bypassed on the client side.Goals
Provide an API to permanently delete an asset file from Supabase Storage.
Delete the corresponding record from the
asset_metadatatable upon successful file deletion.Implement a "typed challenge" confirmation modal where the user must type "DELETE" to proceed.
Crucially, enforce the typed challenge server-side to prevent malicious or accidental bypass.
Ensure only the asset's owner can perform the deletion.
Log the permanent deletion event for auditing purposes.
Non-Goals
A soft-delete or "trash bin" functionality for assets (this is handled by the "Archive Asset" feature).
The ability to restore a permanently deleted asset.
Bulk deletion of multiple assets in a single request.
Glossary
Asset: Any file (image, video, document) uploaded by a creator to their library.
Asset Library: The UI where creators manage their uploaded assets.
Typed Challenge: A security confirmation where the user must type a specific word (e.g., "DELETE") to authorize a destructive action.
RLS: Row-Level Security in PostgreSQL, used to enforce data access policies at the database layer.
3. High-Level Architecture
System Diagram
+----------------------+| Asset Card |
| (Asset Library) |
+----------------------+
|
v
+----------------------+
| Confirmation Dialog |
| Type "DELETE" |
+----------------------+
|
v
+----------------------+
| Client Validation |
+----------------------+
|
v
+----------------------+
| DELETE API Request |
+----------------------+
|
v
+----------------------+
| Authentication |
+----------------------+
|
v
+----------------------+
| Server Validation |
| Verify "DELETE" |
+----------------------+
|
v
+----------------------+
| Verify Ownership |
+----------------------+
|
v
+----------------------+
| Delete File from |
| Storage |
+----------------------+
|
v
+----------------------+
| Remove Metadata |
| from Database |
+----------------------+
|
v
+----------------------+
| Audit Log |
+----------------------+
|
v
+----------------------+
| Success Response |
| Asset Removed |
+----------------------+
+----------------------+| Confirmation Dialog |
+----------------------+
|
v
+----------------------+
| DELETE API Request |
+----------------------+
|
+-----------------+-----------------+
| | |
v v v
+----------------+ +----------------+ +----------------+
| Invalid Input | | Not Owner | | Valid Request |
| 400 Bad Req. | | 403 Forbidden | | Continue |
+----------------+ +----------------+ +----------------+
|
v
+----------------------+
| Delete Storage File |
+----------------------+
|
v
+----------------------+
| Delete DB Metadata |
+----------------------+
|
v
+----------------------+
| Audit Log |
+----------------------+
|
v
+----------------------+
| 200 OK |
+----------------------+
Technologies Used
Next.js: For API routes.
Clerk: For user authentication.
Supabase:
PostgreSQL: For storing asset metadata in the
asset_metadatatable.Storage: For storing the actual asset files.
Zod: For request body schema validation.
React: For the frontend confirmation modal.
4. Detailed Design & Implementation
Data Model / Schema
This feature performs aDELETEoperation on theasset_metadatatable and its corresponding file in Supabase Storage. No schema changes are required.asset_metadataTable (Relevant Columns):Column
Type
Description
idUUIDPrimary Key for the asset metadata.
creator_idTEXTThe Clerk ID of the user who owns the asset.
file_pathTEXTThe path to the file in Supabase Storage.
deleted_atTIMESTAMPTZUsed for soft-deleting (archiving), not this feature.
API Specification
Endpoint:
DELETE /api/creator/background-assets/purge-assetsAuthentication: Required (Creator role).
Request Body:
{"assetId": "uuid-of-the-asset-to-delete","challengeText": "DELETE"}
Zod Schema (
purgeAssetSchema):typescript
export const purgeAssetSchema = z.object({
assetId: z.string().uuid("Invalid asset ID"),
challengeText: z.literal("DELETE", {
errorMap: () => ({ message: "Confirmation text must be 'DELETE'" }),
}),
});
Success Response (200 OK):
json
{"success": true,"message": "Asset permanently deleted successfully."}
Error Responses:
400 Bad Request:assetIdis missing/invalid orchallengeTextis not "DELETE".401 Unauthorized: User is not authenticated.403 Forbidden: User does not own the asset.404 Not Found: The asset does not exist.500 Internal Server Error: Failed to delete the file from storage or the record from the database.
Logic & Workflows
Server-side Deletion Flow (
.../purge-assets/route.ts):
Authenticate: The user's session is verified via
authenticateUser().Validate Request: The request body is parsed and validated against
purgeAssetSchema. This provides the critical server-side check thatchallengeTextis exactly "DELETE".Fetch Asset Metadata: The API queries the
asset_metadatatable for the givenassetId. The query includes aneq("creator_id", creatorId)clause, which, combined with RLS, ensures the user owns the asset.Handle Not Found: If no record is returned, respond with a
404 Not Founderror.Delete from Storage: The
file_pathfrom the metadata is used to delete the object from Supabase Storage. This is done first to avoid orphaned files if the database delete fails.Delete from Database: If storage deletion is successful, the record is deleted from the
asset_metadatatable.Log and Respond: The action is logged, and a
200 OKsuccess response is returned. If any step fails, an appropriate error is logged and a500response is sent.
Key Files:
app/api/creator/(background-assets)/purge-assets/route.ts: The main API endpoint handler.components/creator/asset-library/DeleteAssetModal.tsx: The frontend modal that implements the typed challenge UI.lib/schemas/asset.schema.ts: Contains thepurgeAssetSchemafor Zod validation.
5. Infrastructure & Operations
Dependencies
Internal: Relies on the existing
asset_metadatatable and Clerk authentication service.External: Supabase (PostgreSQL and Storage).
Monitoring & Alerting
Logging: Successful deletions and, more importantly, any failures during the deletion process (both storage and database steps) should be logged with high severity.
Alerts:
Configure alerts for a high rate of
5xxerrors on theDELETE /.../purge-assetsendpoint, as this could indicate a problem with Supabase Storage or database permissions.Monitor for
403 Forbiddenerrors, which could signify attempts to delete assets not owned by the user.
Deployment Plan
Database Migrations: No schema changes are required.
Rollout: This is a core destructive action. The API endpoint and UI components can be deployed together. Ensure that the server-side validation of the
challengeTextis confirmed to be active before merging to production.
6. Testing & Quality Assurance
Test Strategy
Unit Tests:
Test the
purgeAssetSchemato ensure it correctly validates thechallengeText.
Integration Tests:
Test the API endpoint by mocking Supabase calls.
Verify that a request with an incorrect
challengeText(e.g., "delete", "DELETE ") is rejected with a400error.Verify that a request to delete an asset not owned by the user is rejected with a
403or404error.Verify that a successful request calls the storage
remove()method before the databasedelete()method.
End-to-End (E2E) / QA:
Flow: In the Asset Library, click "Delete Permanently" -> Modal appears -> Type "DELETE" -> Click confirm -> Verify the asset is gone from the library and a success toast appears.
Negative Test: Attempt to confirm deletion by typing an incorrect word; verify the confirm button remains disabled or shows an error.
Security Test: Attempt to bypass the UI by sending a
DELETErequest directly to the API without the correctchallengeText; verify it is rejected.
Known Limitations
As noted in the feature audit, the primary risk was a lack of server-side enforcement. This design explicitly addresses that. Once implemented, there are no other known limitations.
7. Maintenance & Support
Troubleshooting
User reports asset is still visible after deletion:
This likely means the client-side state was not updated. A page refresh should resolve it.
If it persists, check the server logs for errors during the API call. The database
DELETEmight have failed after the storageremovesucceeded, leaving an orphaned metadata record.
API returns
500 Internal Server Error:Check Supabase Storage permissions. The service role key used by the API must have
deletepermissions on the assets bucket.Check database RLS policies and permissions for the
asset_metadatatable.
Changelog
1.0 - Approved, Technical guide created, 2026-06-29