From 0535cd1aadfa18fbf7a6695e24258c6f3f130296 Mon Sep 17 00:00:00 2001 From: lubiana Date: Fri, 25 Jul 2025 15:20:23 +0200 Subject: [PATCH] lol --- main.go | 159 ++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 132 insertions(+), 27 deletions(-) diff --git a/main.go b/main.go index 5819001..311b93c 100644 --- a/main.go +++ b/main.go @@ -46,7 +46,6 @@ type ItemLock struct { // ==== Globals ==== var ( - db *sql.DB sseClients = make(map[string]map[chan string]bool) // checklist uuid → set of client channels sseClientsMutex sync.Mutex itemLocks = make(map[int]*ItemLock) // item ID → lock @@ -55,14 +54,21 @@ var ( // ==== Database ==== -func setupDatabase() error { - var err error - db, err = sql.Open("sqlite3", "data/checklists.db") - if err != nil { - return err +func getChecklistDB(uuid string) (*sql.DB, error) { + // Ensure data directory exists + if err := os.MkdirAll("data", 0755); err != nil { + return nil, err } + + dbPath := fmt.Sprintf("data/%s.db", uuid) + db, err := sql.Open("sqlite3", dbPath) + if err != nil { + return nil, err + } + + // Setup schema for this checklist queries := []string{ - `CREATE TABLE IF NOT EXISTS checklists ( + `CREATE TABLE IF NOT EXISTS checklist_info ( uuid TEXT PRIMARY KEY, name TEXT NOT NULL );`, @@ -71,23 +77,52 @@ func setupDatabase() error { content TEXT NOT NULL, checked INTEGER NOT NULL, parent_id INTEGER, - checklist_uuid TEXT NOT NULL, - FOREIGN KEY(parent_id) REFERENCES items(id), - FOREIGN KEY(checklist_uuid) REFERENCES checklists(uuid) + FOREIGN KEY(parent_id) REFERENCES items(id) );`, } for _, q := range queries { if _, err := db.Exec(q); err != nil { + db.Close() + return nil, err + } + } + return db, nil +} + +// ensureChecklistExists creates a checklist if it doesn't exist +func ensureChecklistExists(uuid string) error { + db, err := getChecklistDB(uuid) + if err != nil { + return err + } + defer db.Close() + + // Check if checklist_info table has any data + var count int + err = db.QueryRow(`SELECT COUNT(*) FROM checklist_info`).Scan(&count) + if err != nil { + return err + } + + // If no checklist exists, create one with a default name + if count == 0 { + _, err = db.Exec(`INSERT INTO checklist_info (uuid, name) VALUES (?, ?)`, uuid, "Untitled Checklist") + if err != nil { return err } } + return nil } func loadChecklistName(uuid string) (string, error) { - rows, err := db.Query( - `SELECT name FROM checklists WHERE uuid = ?`, - uuid) + db, err := getChecklistDB(uuid) + if err != nil { + return "", err + } + defer db.Close() + + rows, err := db.Query(`SELECT name FROM checklist_info WHERE uuid = ?`, uuid) if err != nil { return "", err } @@ -104,9 +139,13 @@ func loadChecklistName(uuid string) (string, error) { } func loadChecklistItems(uuid string) ([]ChecklistItem, error) { - rows, err := db.Query( - `SELECT id, content, checked, parent_id FROM items WHERE checklist_uuid = ?`, - uuid) + db, err := getChecklistDB(uuid) + if err != nil { + return nil, err + } + defer db.Close() + + rows, err := db.Query(`SELECT id, content, checked, parent_id FROM items`) if err != nil { return nil, err } @@ -143,13 +182,25 @@ func loadChecklistItems(uuid string) ([]ChecklistItem, error) { func addChecklist(name string) (string, error) { uuidStr := uuid.New().String() - _, err := db.Exec(`INSERT INTO checklists (uuid, name) VALUES (?, ?)`, uuidStr, name) + db, err := getChecklistDB(uuidStr) + if err != nil { + return "", err + } + defer db.Close() + + _, err = db.Exec(`INSERT INTO checklist_info (uuid, name) VALUES (?, ?)`, uuidStr, name) return uuidStr, err } func addItem(uuid, content string, parentID *int) (ChecklistItem, error) { - res, err := db.Exec(`INSERT INTO items (content, checked, parent_id, checklist_uuid) VALUES (?, 0, ?, ?)`, - content, parentID, uuid) + db, err := getChecklistDB(uuid) + if err != nil { + return ChecklistItem{}, err + } + defer db.Close() + + res, err := db.Exec(`INSERT INTO items (content, checked, parent_id) VALUES (?, 0, ?)`, + content, parentID) if err != nil { return ChecklistItem{}, err } @@ -164,6 +215,12 @@ func addItem(uuid, content string, parentID *int) (ChecklistItem, error) { } func updateItem(uuid string, id int, content *string, checked *bool, parentID *int) (ChecklistItem, error) { + db, err := getChecklistDB(uuid) + if err != nil { + return ChecklistItem{}, err + } + defer db.Close() + q := "UPDATE items SET " args := []interface{}{} set := []string{} @@ -183,15 +240,14 @@ func updateItem(uuid string, id int, content *string, checked *bool, parentID *i set = append(set, "parent_id = ?") args = append(args, *parentID) } - q += strings.Join(set, ", ") + " WHERE id = ? AND checklist_uuid = ?" - args = append(args, id, uuid) - _, err := db.Exec(q, args...) + q += strings.Join(set, ", ") + " WHERE id = ?" + args = append(args, id) + _, err = db.Exec(q, args...) if err != nil { return ChecklistItem{}, err } // Return updated item - rows, err := db.Query( - `SELECT id, content, checked, parent_id FROM items WHERE id = ? AND checklist_uuid = ?`, id, uuid) + rows, err := db.Query(`SELECT id, content, checked, parent_id FROM items WHERE id = ?`, id) if err != nil { return ChecklistItem{}, err } @@ -216,7 +272,13 @@ func updateItem(uuid string, id int, content *string, checked *bool, parentID *i } func deleteItem(uuid string, id int) error { - _, err := db.Exec(`DELETE FROM items WHERE id = ? AND checklist_uuid = ?`, id, uuid) + db, err := getChecklistDB(uuid) + if err != nil { + return err + } + defer db.Close() + + _, err = db.Exec(`DELETE FROM items WHERE id = ?`, id) return err } @@ -240,6 +302,13 @@ func broadcast(uuid string, msg interface{}) { func handleGetItems(w http.ResponseWriter, r *http.Request) { uuid := strings.TrimPrefix(r.URL.Path, "/api/checklists/") uuid = uuid[:36] + + // Ensure checklist exists + if err := ensureChecklistExists(uuid); err != nil { + http.Error(w, "Failed to ensure checklist exists", 500) + return + } + items, err := loadChecklistItems(uuid) if err != nil { http.Error(w, "Failed to load items", 500) @@ -279,6 +348,13 @@ func handleCreateChecklist(w http.ResponseWriter, r *http.Request) { func handleAddItem(w http.ResponseWriter, r *http.Request) { uuid := strings.TrimPrefix(r.URL.Path, "/api/checklists/") uuid = uuid[:36] + + // Ensure checklist exists + if err := ensureChecklistExists(uuid); err != nil { + http.Error(w, "Failed to ensure checklist exists", 500) + return + } + type Req struct { Content string `json:"content"` ParentID *int `json:"parent_id"` @@ -309,6 +385,13 @@ func handleUpdateItem(w http.ResponseWriter, r *http.Request) { uuid := parts[3] id := 0 fmt.Sscanf(parts[5], "%d", &id) + + // Ensure checklist exists + if err := ensureChecklistExists(uuid); err != nil { + http.Error(w, "Failed to ensure checklist exists", 500) + return + } + type Req struct { Content *string `json:"content"` Checked *bool `json:"checked"` @@ -338,6 +421,13 @@ func handleDeleteItem(w http.ResponseWriter, r *http.Request) { uuid := parts[3] id := 0 fmt.Sscanf(parts[5], "%d", &id) + + // Ensure checklist exists + if err := ensureChecklistExists(uuid); err != nil { + http.Error(w, "Failed to ensure checklist exists", 500) + return + } + if err := deleteItem(uuid, id); err != nil { http.Error(w, "Delete failed", 500) return @@ -356,6 +446,13 @@ func handleLockItem(w http.ResponseWriter, r *http.Request) { uuid := parts[3] id := 0 fmt.Sscanf(parts[5], "%d", &id) + + // Ensure checklist exists + if err := ensureChecklistExists(uuid); err != nil { + http.Error(w, "Failed to ensure checklist exists", 500) + return + } + type Req struct { User string `json:"user"` } @@ -419,6 +516,12 @@ func handleSSE(w http.ResponseWriter, r *http.Request) { uuid := strings.TrimPrefix(r.URL.Path, "/api/checklists/") uuid = uuid[:36] + // Ensure checklist exists + if err := ensureChecklistExists(uuid); err != nil { + http.Error(w, "Failed to ensure checklist exists", 500) + return + } + flusher, ok := w.(http.Flusher) if !ok { http.Error(w, "Streaming unsupported", http.StatusInternalServerError) @@ -477,9 +580,11 @@ func handleSSE(w http.ResponseWriter, r *http.Request) { // ==== Main + Routing ==== func main() { - if err := setupDatabase(); err != nil { - log.Fatalf("DB setup: %v", err) + // Ensure data directory exists + if err := os.MkdirAll("data", 0755); err != nil { + log.Fatalf("Failed to create data directory: %v", err) } + go lockExpiryDaemon() // Serve static files from embedded filesystem