* implement runCLI function to handle subcommands * add runRSVPCLI and runPhotosCLI for RSVP and photo management * include usage instructions for CLI commands * integrate CLI execution in main function
172 lines
4.1 KiB
Go
172 lines
4.1 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"wedding-server/internal/store"
|
|
)
|
|
|
|
// runCLI handles admin subcommands (listing/removing bad RSVPs or photo
|
|
// uploads) so fixing a mistake doesn't require touching the database by hand.
|
|
// Returns the process exit code.
|
|
func runCLI(dataDir string, st *store.Store, args []string) int {
|
|
if len(args) == 0 {
|
|
printCLIUsage()
|
|
return 1
|
|
}
|
|
|
|
switch args[0] {
|
|
case "rsvp":
|
|
return runRSVPCLI(st, args[1:])
|
|
case "photos":
|
|
return runPhotosCLI(dataDir, st, args[1:])
|
|
case "help", "-h", "--help":
|
|
printCLIUsage()
|
|
return 0
|
|
default:
|
|
fmt.Fprintf(os.Stderr, "unknown command %q\n\n", args[0])
|
|
printCLIUsage()
|
|
return 1
|
|
}
|
|
}
|
|
|
|
func printCLIUsage() {
|
|
fmt.Println(`Usage:
|
|
wedding-server start the web server
|
|
wedding-server rsvp list list all RSVPs
|
|
wedding-server rsvp delete <id> delete an RSVP and its guests
|
|
wedding-server photos list list all photo uploads
|
|
wedding-server photos delete <id> delete a photo upload and its files`)
|
|
}
|
|
|
|
func runRSVPCLI(st *store.Store, args []string) int {
|
|
if len(args) == 0 {
|
|
printCLIUsage()
|
|
return 1
|
|
}
|
|
|
|
switch args[0] {
|
|
case "list":
|
|
rsvps, err := st.ListRSVPs()
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
|
return 1
|
|
}
|
|
if len(rsvps) == 0 {
|
|
fmt.Println("no RSVPs yet")
|
|
return 0
|
|
}
|
|
for _, r := range rsvps {
|
|
names := make([]string, len(r.Guests))
|
|
for i, g := range r.Guests {
|
|
names[i] = g.Name
|
|
if g.IsChild {
|
|
names[i] += " (child)"
|
|
}
|
|
}
|
|
fmt.Printf("#%d %s %s\n", r.ID, r.CreatedAt.Local().Format("2006-01-02 15:04"), strings.Join(names, ", "))
|
|
if r.Message != "" {
|
|
fmt.Printf(" message: %s\n", r.Message)
|
|
}
|
|
}
|
|
return 0
|
|
|
|
case "delete":
|
|
if len(args) < 2 {
|
|
fmt.Fprintln(os.Stderr, "usage: wedding-server rsvp delete <id>")
|
|
return 1
|
|
}
|
|
id, err := strconv.ParseInt(args[1], 10, 64)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "invalid id %q\n", args[1])
|
|
return 1
|
|
}
|
|
if !confirm(fmt.Sprintf("Delete RSVP #%d? This cannot be undone.", id)) {
|
|
fmt.Println("cancelled")
|
|
return 0
|
|
}
|
|
if err := st.DeleteRSVP(id); err != nil {
|
|
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
|
return 1
|
|
}
|
|
fmt.Printf("deleted RSVP #%d\n", id)
|
|
return 0
|
|
|
|
default:
|
|
printCLIUsage()
|
|
return 1
|
|
}
|
|
}
|
|
|
|
func runPhotosCLI(dataDir string, st *store.Store, args []string) int {
|
|
if len(args) == 0 {
|
|
printCLIUsage()
|
|
return 1
|
|
}
|
|
|
|
switch args[0] {
|
|
case "list":
|
|
uploads, err := st.ListPhotoUploads()
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
|
return 1
|
|
}
|
|
if len(uploads) == 0 {
|
|
fmt.Println("no photo uploads yet")
|
|
return 0
|
|
}
|
|
for _, u := range uploads {
|
|
fmt.Printf("#%d %s %s (%d photo(s))\n", u.ID, u.CreatedAt.Local().Format("2006-01-02 15:04"), u.Name, len(u.Files))
|
|
for _, f := range u.Files {
|
|
fmt.Printf(" %s\n", f.URL)
|
|
}
|
|
}
|
|
return 0
|
|
|
|
case "delete":
|
|
if len(args) < 2 {
|
|
fmt.Fprintln(os.Stderr, "usage: wedding-server photos delete <id>")
|
|
return 1
|
|
}
|
|
id, err := strconv.ParseInt(args[1], 10, 64)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "invalid id %q\n", args[1])
|
|
return 1
|
|
}
|
|
if !confirm(fmt.Sprintf("Delete photo upload #%d and its files? This cannot be undone.", id)) {
|
|
fmt.Println("cancelled")
|
|
return 0
|
|
}
|
|
files, err := st.DeletePhotoUpload(id)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
|
return 1
|
|
}
|
|
for _, f := range files {
|
|
if err := os.Remove(f.Path); err != nil && !os.IsNotExist(err) {
|
|
fmt.Fprintf(os.Stderr, "warning: could not remove %s: %v\n", f.Path, err)
|
|
}
|
|
}
|
|
// Best-effort: only removes the per-upload directory if now empty.
|
|
_ = os.Remove(filepath.Join(dataDir, "photos", strconv.FormatInt(id, 10)))
|
|
fmt.Printf("deleted photo upload #%d (%d file(s))\n", id, len(files))
|
|
return 0
|
|
|
|
default:
|
|
printCLIUsage()
|
|
return 1
|
|
}
|
|
}
|
|
|
|
func confirm(prompt string) bool {
|
|
fmt.Printf("%s [y/N]: ", prompt)
|
|
line, _ := bufio.NewReader(os.Stdin).ReadString('\n')
|
|
line = strings.ToLower(strings.TrimSpace(line))
|
|
return line == "y" || line == "yes"
|
|
}
|