Description
When a workspace is deleted (delete_workspace), the handler unpublishes all files and fonts:
- Deletes rows from
published_files
- Sets
files.is_public = FALSE
- Sets
fonts.is_public = FALSE, clears fonts.slug = NULL, fonts.published_at = NULL
However, when a workspace is restored (restore_workspace), only the workspaces table is updated (deleted_at = NULL, new name/slug). The publication state of files and fonts is not restored. All public URLs (slugs) are permanently lost.
Location
backend/src/workspace_handlers.rs:
delete_workspace (lines ~640-680):
conn.execute("DELETE FROM published_files WHERE file_id IN (SELECT id FROM files WHERE workspace_id = ?)", ...)?;
conn.execute("UPDATE files SET is_public = FALSE WHERE workspace_id = ?", ...)?;
conn.execute("UPDATE fonts SET is_public = FALSE, slug = NULL, published_at = NULL WHERE workspace_id = ?", ...)?;
restore_workspace (lines ~730-760):
// Only updates workspaces table — does NOT undo any of the above
conn.execute("UPDATE workspaces SET name = ?, slug = ?, deleted_at = NULL WHERE id = ?", ...)?;
Impact
Data loss (Medium-High): After restoring a workspace:
- All previously published files have lost their public slugs — old URLs return 404 permanently
- All previously published fonts have lost their public slugs
- Users must manually re-publish every file and font after a restore
- The
published_files entries are gone, so there is no way to recover the original slugs
This is particularly painful because workspace deletion is designed to be reversible (soft delete + restore), but the reversal is incomplete.
Suggested Fix
Option A: Soft-delete publications instead of hard-deleting
During delete_workspace, don't delete published_files rows or null out slugs. Instead, rely on the workspace's deleted_at to gate access. Public endpoints already check w.deleted_at IS NULL, so the data would be automatically hidden while archived and restored on un-archive.
Option B: Backup and restore publication state
During deletion, back up publication data (as done for workspace members). During restore, re-insert the backed-up data.
Option C (minimal): Document the limitation
If this is intentional, add a clear warning in the API response when restoring: "Workspace restored, but all file and font publications have been removed. Please re-publish them."
Recommendation: Option A is cleanest. The public tile endpoints already filter on deleted_at IS NULL, so soft-deleting publications is unnecessary if we just keep the rows in place.
Description
When a workspace is deleted (
delete_workspace), the handler unpublishes all files and fonts:published_filesfiles.is_public = FALSEfonts.is_public = FALSE, clearsfonts.slug = NULL,fonts.published_at = NULLHowever, when a workspace is restored (
restore_workspace), only theworkspacestable is updated (deleted_at = NULL, newname/slug). The publication state of files and fonts is not restored. All public URLs (slugs) are permanently lost.Location
backend/src/workspace_handlers.rs:delete_workspace (lines ~640-680):
restore_workspace (lines ~730-760):
Impact
Data loss (Medium-High): After restoring a workspace:
published_filesentries are gone, so there is no way to recover the original slugsThis is particularly painful because workspace deletion is designed to be reversible (soft delete + restore), but the reversal is incomplete.
Suggested Fix
Option A: Soft-delete publications instead of hard-deleting
During
delete_workspace, don't deletepublished_filesrows or null out slugs. Instead, rely on the workspace'sdeleted_atto gate access. Public endpoints already checkw.deleted_at IS NULL, so the data would be automatically hidden while archived and restored on un-archive.Option B: Backup and restore publication state
During deletion, back up publication data (as done for workspace members). During restore, re-insert the backed-up data.
Option C (minimal): Document the limitation
If this is intentional, add a clear warning in the API response when restoring: "Workspace restored, but all file and font publications have been removed. Please re-publish them."
Recommendation: Option A is cleanest. The public tile endpoints already filter on
deleted_at IS NULL, so soft-deleting publications is unnecessary if we just keep the rows in place.