Compare commits

...

2 Commits

Author SHA1 Message Date
luxick ddfd29ea0e Prevent page deletion on save 2026-07-21 17:26:31 +02:00
luxick 3f2beafd94 Adjust header spacing 2026-07-21 17:26:15 +02:00
2 changed files with 31 additions and 24 deletions
+1 -1
View File
@@ -141,7 +141,7 @@ header {
padding: var(--space-3) var(--space-4);
border-bottom: var(--border-dashed);
display: grid;
grid-template-columns: 1fr minmax(0, 60rem) 1fr;
grid-template-columns: 1fr minmax(0, 50rem) 1fr;
grid-template-areas: "crumbs search actions";
align-items: center;
gap: var(--space-2);
+13 -6
View File
@@ -426,9 +426,14 @@ func (h *handler) handlePost(w http.ResponseWriter, r *http.Request, urlPath, fs
}
rawMD, _ := os.ReadFile(indexPath)
sections := splitSections(rawMD)
if sectionIndex < len(sections) {
sections[sectionIndex] = []byte(content)
// Out of range means the file changed under the editor (or the index
// never matched it). Writing back the untouched file would swallow the
// edit silently, so refuse and keep the editor's content in the browser.
if sectionIndex >= len(sections) {
http.Error(w, "section no longer exists — the page changed since you opened the editor", http.StatusConflict)
return
}
sections[sectionIndex] = []byte(content)
content = string(joinSections(sections))
// Section index ≥ 1 is a heading-anchored section. Redirect to its
// anchor so the user lands on the section they just saved, even if
@@ -441,12 +446,15 @@ func (h *handler) handlePost(w http.ResponseWriter, r *http.Request, urlPath, fs
}
}
// A save must never remove a page. An empty POST — a truncated mobile
// request, a lost `section` field, an editor that came up blank — is
// indistinguishable from "clear this page", and deleting index.md on that
// signal loses the whole file even though the user only edited one section.
// Removing a page is the explicit ?delete action's job (moves.go).
if strings.TrimSpace(content) == "" {
if err := os.Remove(indexPath); err != nil && !os.IsNotExist(err) {
http.Error(w, "delete failed: "+err.Error(), http.StatusInternalServerError)
http.Error(w, "refusing to save empty content — use DELETE to remove this page", http.StatusBadRequest)
return
}
} else {
// Stat first so we know whether MkdirAll actually created the folder
// — if it did, the search index needs a new entry.
_, statErr := os.Stat(fsPath)
@@ -464,7 +472,6 @@ func (h *handler) handlePost(w http.ResponseWriter, r *http.Request, urlPath, fs
folderIndexAdd(filepath.ToSlash(rel))
}
}
}
// The editor saves via fetch so the save and its result share one history
// entry (see assets/history-nav.js). Hand it the target instead of a 303:
// the browser would follow the redirect into a second entry, and fetch