Name each database target's archive for its webhook and target (closes #376)
check / check (push) Waiting to run

Each database target now has its own archive file,
archive-WEBHOOKNAME-TARGETNAME-TARGETID.db, instead of one
archive-WEBHOOKID.db per webhook. delivery.ArchiveFileName builds the
name: each name is lowercased, keeps ASCII letters and digits, turns
every other run of characters into one dash, and is cut to 40
characters.

A change of webhook or target name renames its archive files under the
archive writer's lock, before the new name is saved, and back again if
the save fails. A rename never replaces a file: if one already has the
new name, the edit is refused. Deleting a target evicts only that
target's writer. Archive files are never deleted, and nothing looks for
files under the old name.

Model: opus-5-5
This commit is contained in:
2026-10-02 06:41:33 +00:00
committed by sneak
parent cb7bafab17
commit 17e6be34ab
21 changed files with 1438 additions and 663 deletions
+48 -1
View File
@@ -1,6 +1,7 @@
package handlers
import (
"errors"
"net/http"
"github.com/go-chi/chi"
@@ -150,11 +151,42 @@ func (h *Handlers) applyTargetEdit(
target.MaxRetries = retries
}
oldName := target.Name
target.Name = name
target.Config = configJSON
err = h.db.DB().Save(target).Error
// A new name renames the archive file before it is saved (see
// delivery.Engine.Rename). If either step fails, it goes back to
// the name that is still stored.
err = h.renameTargetArchive(target, webhook.Name, oldName, name)
if err == nil {
err = h.db.DB().Save(target).Error
}
if err != nil {
restoreErr := h.renameTargetArchive(
target, webhook.Name, name, oldName,
)
if restoreErr != nil {
h.log.Error(
"failed to rename archive back",
"target_id", target.ID,
"error", restoreErr,
)
}
if errors.Is(err, delivery.ErrArchiveNameTaken) {
http.Error(
w,
"Not saved: "+err.Error()+
". Move that file out of the data directory, "+
"then save again.",
http.StatusConflict,
)
return
}
h.serverError(w, r, "failed to update target", err)
return
@@ -165,6 +197,21 @@ func (h *Handlers) applyTargetEdit(
)
}
// renameTargetArchive renames a database target's archive file from
// the target name oldName to newName. It does nothing when the name
// is unchanged; other target types have no archive.
func (h *Handlers) renameTargetArchive(
target *database.Target,
webhookName, oldName, newName string,
) error {
if h.archives == nil || oldName == newName ||
target.Type != database.TargetTypeDatabase {
return nil
}
return h.archives.Rename(target.ID, webhookName, newName)
}
// renderTargetEdit renders the target edit page with an optional
// error message.
func (h *Handlers) renderTargetEdit(