mirror of
https://github.com/narrrl/narl-workspace.git
synced 2026-11-04 08:57:00 +01:00
fix: let a media file belong to more than one entry
media.entry_id modelled ownership as one entry per file, but link_to_entry reassigns it on every save. Embedding the same image in a second entry therefore moved the claim, and deleting that second entry deleted the file out from under the first — a broken image in the entry and a 404 on its share link. A join table replaces the column. Deleting an entry now removes only the files no other entry embeds, and link_to_entry runs in a transaction so an entry is never briefly attached to nothing while its share link is being read.
This commit is contained in:
1 parent
8da0a30497
commit
1e0f2f8792
6 files changed
+94
-31
No files matched your search
@@ -5,8 +5,13 @@
|
||||
import { diary } from './store.svelte'
|
||||
import { formatBytes, formatStamp } from './util'
|
||||
|
||||
async function remove(id: string, filename: string) {
|
||||
if (!confirm(`delete ${filename}? entries that embed it will break.`)) return
|
||||
const usage = (entryIds: number[]) =>
|
||||
entryIds.length === 0 ? 'unattached' : entryIds.map((id) => `entry:${id}`).join(' ')
|
||||
|
||||
async function remove(id: string, filename: string, entryIds: number[]) {
|
||||
const used = entryIds.length === 1 ? '1 entry' : `${entryIds.length} entries`
|
||||
const warning = entryIds.length > 0 ? ` it is embedded in ${used}, which will break.` : ''
|
||||
if (!confirm(`delete ${filename}?${warning}`)) return
|
||||
await diary.guard(async () => {
|
||||
await api.deleteMedia(id)
|
||||
await diary.loadMedia()
|
||||
@@ -36,7 +41,7 @@
|
||||
<span class="name">{file.filename}</span>
|
||||
<span class="faint">
|
||||
{formatBytes(file.size)} · {formatStamp(file.created_at)} ·
|
||||
{file.entry_id ? `entry:${file.entry_id}` : 'unattached'}
|
||||
{usage(file.entry_ids)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -50,7 +55,7 @@
|
||||
>
|
||||
{/if}
|
||||
<button onclick={() => diary.copy(location.origin + file.url)}>copy url</button>
|
||||
<button onclick={() => remove(file.id, file.filename)}>rm</button>
|
||||
<button onclick={() => remove(file.id, file.filename, file.entry_ids)}>rm</button>
|
||||
</div>
|
||||
</li>
|
||||
{:else}
|
||||
|
||||
+2
-1
@@ -33,7 +33,8 @@ export interface MediaFile {
|
||||
size: number
|
||||
created_at: number
|
||||
url: string
|
||||
entry_id: number | null
|
||||
/** Every entry that embeds this file — a file may be used by several. */
|
||||
entry_ids: number[]
|
||||
}
|
||||
|
||||
export class ApiError extends Error {
|
||||
|
||||
Reference in new issue
Block a user