lol
This commit is contained in:
parent
91d308485b
commit
0535cd1aad
1 changed files with 132 additions and 27 deletions
159
main.go
159
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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue