diff --git a/internal/webserver/handlers.go b/internal/webserver/handlers.go index 195729f..f73598b 100644 --- a/internal/webserver/handlers.go +++ b/internal/webserver/handlers.go @@ -248,6 +248,23 @@ func (ws *WebServer) handleCreateShare(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, fmt.Sprintf("/notebooks/%d", id), http.StatusFound) } +func (ws *WebServer) handleDeleteNotebook(w http.ResponseWriter, r *http.Request) { + userID := r.Context().Value(userIDKey).(int64) + id, _ := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64) + + // Verify ownership before deleting. + var ownerID int64 + err := ws.db.QueryRow("SELECT user_id FROM notebooks WHERE id = ?", id).Scan(&ownerID) + if err != nil || ownerID != userID { + http.Error(w, "Not found", http.StatusNotFound) + return + } + + // CASCADE deletes pages, strokes, and share_links. + _, _ = ws.db.Exec("DELETE FROM notebooks WHERE id = ?", id) + http.Redirect(w, r, "/notebooks", http.StatusFound) +} + func (ws *WebServer) handleRevokeShare(w http.ResponseWriter, r *http.Request) { id, _ := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64) token := r.FormValue("token") diff --git a/internal/webserver/server.go b/internal/webserver/server.go index 12b04df..a011b24 100644 --- a/internal/webserver/server.go +++ b/internal/webserver/server.go @@ -112,6 +112,7 @@ func Start(cfg Config) (*http.Server, error) { r.Get("/notebooks/{id}/pages/{num}/svg", ws.handlePageSVG) r.Get("/notebooks/{id}/pages/{num}/jpg", ws.handlePageJPG) r.Get("/notebooks/{id}/pdf", ws.handleNotebookPDF) + r.Post("/notebooks/{id}/delete", ws.handleDeleteNotebook) r.Post("/notebooks/{id}/share", ws.handleCreateShare) r.Post("/notebooks/{id}/share/revoke", ws.handleRevokeShare) r.Get("/logout", ws.handleLogout) diff --git a/web/templates/layout.html b/web/templates/layout.html index 48e7678..ef5591a 100644 --- a/web/templates/layout.html +++ b/web/templates/layout.html @@ -18,9 +18,9 @@ .grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 1rem; } .page-thumb { border: 1px solid #ccc; background: #fff; aspect-ratio: 0.773; display: flex; align-items: center; justify-content: center; } .page-thumb img { width: 100%; height: 100%; object-fit: contain; } - .btn { display: inline-block; padding: 0.5rem 1rem; border: 1px solid #111; border-radius: 4px; text-decoration: none; color: #111; background: #fff; cursor: pointer; } + .btn { display: inline-flex; align-items: center; padding: 0.5rem 1rem; border: 1px solid #111; border-radius: 4px; text-decoration: none; color: #111; background: #fff; cursor: pointer; font: inherit; font-size: 1rem; line-height: 1.5; box-sizing: border-box; } .btn:hover { background: #f0f0f0; } - .actions { display: flex; gap: 0.5rem; margin: 1rem 0; } + .actions { display: flex; align-items: center; gap: 0.5rem; margin: 1rem 0; } input[type="text"], input[type="password"] { padding: 0.5rem; border: 1px solid #ccc; border-radius: 4px; width: 100%; max-width: 300px; } label { display: block; margin-bottom: 0.25rem; font-weight: bold; } .form-group { margin-bottom: 1rem; } diff --git a/web/templates/notebook.html b/web/templates/notebook.html index 088926d..9ac36c9 100644 --- a/web/templates/notebook.html +++ b/web/templates/notebook.html @@ -6,7 +6,11 @@ Download PDF {{if not .Shared}}
+ {{end}}