package main import ( "database/sql" "encoding/json" "fmt" "log" "math/rand" "net/http" "os" "os/exec" "strconv" "strings" "time" _ "modernc.org/sqlite" ) type Item struct { ID int `json:"id"` Name string `json:"name"` } type UserInfo struct { Username string `json:"username"` Name string `json:"name"` Email string `json:"email"` Groups string `json:"groups"` Headers map[string]string `json:"headers"` } var db *sql.DB func initDB() { var err error db, err = sql.Open("sqlite", "items.db") if err != nil { log.Fatal("Failed to open database:", err) } createTable := ` CREATE TABLE IF NOT EXISTS items ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL );` _, err = db.Exec(createTable) if err != nil { log.Fatal("Failed to create table:", err) } } func main() { if len(os.Args) > 1 && os.Args[1] == "--watch" { watchAndRestart() return } // Initialize database initDB() defer db.Close() // Your actual API rng := rand.New(rand.NewSource(time.Now().UnixNano())) http.HandleFunc("/randomnumber", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "%d", rng.Intn(1000)) }) http.HandleFunc("/slowtest", func(w http.ResponseWriter, r *http.Request) { time.Sleep(1 * time.Second) fmt.Fprintf(w, "true") }) http.HandleFunc("/welcome", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "welcome to shinyspace play api testing") }) http.HandleFunc("/echo", func(w http.ResponseWriter, r *http.Request) { if r.Method == "POST" { r.ParseMultipartForm(32 << 20) // 32MB max memory text := r.FormValue("text") fmt.Fprintf(w, "you said %s", text) } else { w.WriteHeader(http.StatusMethodNotAllowed) fmt.Fprintf(w, "Only POST method allowed") } }) // Items API endpoints http.HandleFunc("/items", handleItems) http.HandleFunc("/items/", handleItemDelete) // Auth endpoints http.HandleFunc("/auth/user", handleAuthUser) log.Println("API running on :3847") http.ListenAndServe("127.0.0.1:3847", nil) } func watchAndRestart() { binaryPath, _ := os.Executable() var cmd *exec.Cmd start := func() { if cmd != nil && cmd.Process != nil { cmd.Process.Kill() } cmd = exec.Command(binaryPath) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.Start() } start() // Initial start lastMod := getModTime(binaryPath) for { time.Sleep(1 * time.Second) if mod := getModTime(binaryPath); mod.After(lastMod) { log.Println("Binary changed, restarting...") lastMod = mod start() } } } func getModTime(path string) time.Time { info, _ := os.Stat(path) return info.ModTime() } func handleItems(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") switch r.Method { case "GET": getItems(w, r) case "POST": createItem(w, r) default: w.WriteHeader(http.StatusMethodNotAllowed) json.NewEncoder(w).Encode(map[string]string{"error": "Method not allowed"}) } } func getItems(w http.ResponseWriter, r *http.Request) { rows, err := db.Query("SELECT id, name FROM items ORDER BY id") if err != nil { w.WriteHeader(http.StatusInternalServerError) json.NewEncoder(w).Encode(map[string]string{"error": "Failed to fetch items"}) return } defer rows.Close() var items []Item for rows.Next() { var item Item err := rows.Scan(&item.ID, &item.Name) if err != nil { w.WriteHeader(http.StatusInternalServerError) json.NewEncoder(w).Encode(map[string]string{"error": "Failed to scan items"}) return } items = append(items, item) } // Ensure we always return an array, even if empty if items == nil { items = []Item{} } json.NewEncoder(w).Encode(items) } func createItem(w http.ResponseWriter, r *http.Request) { var item Item err := json.NewDecoder(r.Body).Decode(&item) if err != nil { w.WriteHeader(http.StatusBadRequest) json.NewEncoder(w).Encode(map[string]string{"error": "Invalid JSON"}) return } if item.Name == "" { w.WriteHeader(http.StatusBadRequest) json.NewEncoder(w).Encode(map[string]string{"error": "Name is required"}) return } result, err := db.Exec("INSERT INTO items (name) VALUES (?)", item.Name) if err != nil { w.WriteHeader(http.StatusInternalServerError) json.NewEncoder(w).Encode(map[string]string{"error": "Failed to create item"}) return } id, _ := result.LastInsertId() item.ID = int(id) w.WriteHeader(http.StatusCreated) json.NewEncoder(w).Encode(item) } func handleItemDelete(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") if r.Method != "DELETE" { w.WriteHeader(http.StatusMethodNotAllowed) json.NewEncoder(w).Encode(map[string]string{"error": "Method not allowed"}) return } // Extract ID from URL path /items/{id} path := strings.TrimPrefix(r.URL.Path, "/items/") id, err := strconv.Atoi(path) if err != nil { w.WriteHeader(http.StatusBadRequest) json.NewEncoder(w).Encode(map[string]string{"error": "Invalid item ID"}) return } result, err := db.Exec("DELETE FROM items WHERE id = ?", id) if err != nil { w.WriteHeader(http.StatusInternalServerError) json.NewEncoder(w).Encode(map[string]string{"error": "Failed to delete item"}) return } rowsAffected, _ := result.RowsAffected() if rowsAffected == 0 { w.WriteHeader(http.StatusNotFound) json.NewEncoder(w).Encode(map[string]string{"error": "Item not found"}) return } json.NewEncoder(w).Encode(map[string]string{"message": "Item deleted successfully"}) } func handleAuthUser(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") if r.Method != "GET" { w.WriteHeader(http.StatusMethodNotAllowed) json.NewEncoder(w).Encode(map[string]string{"error": "Method not allowed"}) return } // Collect all headers for debugging headers := make(map[string]string) for name, values := range r.Header { headers[name] = strings.Join(values, ", ") } // Extract common Authelia headers userInfo := UserInfo{ Username: r.Header.Get("Remote-User"), Name: r.Header.Get("Remote-Name"), Email: r.Header.Get("Remote-Email"), Groups: r.Header.Get("Remote-Groups"), Headers: headers, } json.NewEncoder(w).Encode(userInfo) }