Real web UI: /new editor, /history with live pagination, paste view with gutter and deletion banner, /unlock page; size in public API

This commit is contained in:
2026-09-08 16:50:44 -05:00
parent 9ff4d158ad
commit c6233dbbec
12 changed files with 658 additions and 7 deletions
+17 -6
View File
@@ -66,6 +66,7 @@ type PasteRow struct {
CreatedAt int64
DeletedAt sql.NullInt64
ViewCount int
Size int
DeletionToken sql.NullString
}
@@ -230,7 +231,7 @@ func (s *Store) GetPaste(idOrSlug string) (*PasteRow, error) {
}
func (s *Store) ListPublic(limit, offset int) ([]PasteRow, int, error) {
rows, err := s.db.Query(`SELECT id, custom_slug, content_type, language, title, visibility, created_at, view_count FROM pastes
rows, err := s.db.Query(`SELECT id, custom_slug, content_type, language, title, visibility, created_at, view_count, LENGTH(content) FROM pastes
WHERE visibility='public' AND deleted_at IS NULL AND can_id IS NULL AND (expires_at IS NULL OR expires_at > ?)
ORDER BY created_at DESC LIMIT ? OFFSET ?`, time.Now().Unix(), limit, offset)
if err != nil {
@@ -241,7 +242,7 @@ func (s *Store) ListPublic(limit, offset int) ([]PasteRow, int, error) {
for rows.Next() {
var r PasteRow
var cs, lang, title sql.NullString
if err := rows.Scan(&r.ID, &cs, &r.ContentType, &lang, &title, &r.Visibility, &r.CreatedAt, &r.ViewCount); err != nil {
if err := rows.Scan(&r.ID, &cs, &r.ContentType, &lang, &title, &r.Visibility, &r.CreatedAt, &r.ViewCount, &r.Size); err != nil {
return nil, 0, err
}
r.CustomSlug = cs
@@ -337,9 +338,14 @@ func (a *apiServer) routes() http.Handler {
// raw
r.Get("/raw/{id}", a.handleRaw)
// web (minimal for now)
r.Get("/", a.handleHome)
r.Get("/{id}", a.handlePastePage)
// web pages
r.Get("/", http.RedirectHandler("/history", http.StatusFound).ServeHTTP)
r.Get("/new", a.handleNewPage)
r.Get("/history", a.handleHistoryPage)
r.Handle("/static/*", staticHandler())
r.Get("/unlock/{id}", a.handlePasteView)
r.Post("/unlock/{id}", a.handlePasteView)
r.Get("/{id}", a.handlePasteView)
r.NotFound(func(w http.ResponseWriter, r *http.Request) {
writeErr(w, 404, "not found")
@@ -447,7 +453,7 @@ func (a *apiServer) handleListPublic(w http.ResponseWriter, r *http.Request) {
lang, title := nullStrPtr(row.Language), nullStrPtr(row.Title)
items = append(items, map[string]any{
"id": row.ID, "title": title, "language": lang,
"created_at": row.CreatedAt, "view_count": row.ViewCount,
"created_at": row.CreatedAt, "view_count": row.ViewCount, "size": row.Size,
})
}
writeJSON(w, 200, map[string]any{"total": total, "limit": limit, "offset": offset, "items": items})
@@ -538,6 +544,11 @@ func main() {
}
store.StartSweeper(time.Minute)
ui, err := NewWebUI()
if err != nil {
log.Fatal(err)
}
webUIInstance = ui
srv := &apiServer{store: store, cfg: cfg}
log.Printf("palette listening on %s", cfg.Addr)
log.Fatal(http.ListenAndServe(cfg.Addr, srv.routes()))