package main import ( "encoding/json" "log" "net/http" "strconv" "sync" "time" "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" "github.com/go-chi/cors" ) type Task struct { ID int `json:"id"` Title string `json:"title"` Description string `json:"description"` Completed bool `json:"completed"` CreatedAt time.Time `json:"createdAt"` } type TaskStore struct { mu sync.RWMutex tasks map[int]Task nextID int } func NewTaskStore() *TaskStore { return &TaskStore{ tasks: make(map[int]Task), nextID: 1, } } func (s *TaskStore) GetAll() []Task { s.mu.RLock() defer s.mu.RUnlock() tasks := make([]Task, 0, len(s.tasks)) for _, task := range s.tasks { tasks = append(tasks, task) } return tasks } func (s *TaskStore) Get(id int) (Task, bool) { s.mu.RLock() defer s.mu.RUnlock() task, ok := s.tasks[id] return task, ok } func (s *TaskStore) Create(title, description string) Task { s.mu.Lock() defer s.mu.Unlock() task := Task{ ID: s.nextID, Title: title, Description: description, Completed: false, CreatedAt: time.Now(), } s.tasks[s.nextID] = task s.nextID++ return task } func (s *TaskStore) Delete(id int) bool { s.mu.Lock() defer s.mu.Unlock() if _, ok := s.tasks[id]; ok { delete(s.tasks, id) return true } return false } type APIResponse struct { Success bool `json:"success"` Data interface{} `json:"data,omitempty"` Error string `json:"error,omitempty"` } func jsonResponse(w http.ResponseWriter, status int, resp APIResponse) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(status) json.NewEncoder(w).Encode(resp) } func main() { store := NewTaskStore() // Add some sample tasks store.Create("Welcome to Task Manager", "This is your first task. Click on it to see details!") store.Create("Add a new task", "Use the Add Task button to create new tasks") store.Create("Stay organized", "Keep track of all your tasks in one place") r := chi.NewRouter() // Middleware r.Use(middleware.Logger) r.Use(middleware.Recoverer) r.Use(cors.Handler(cors.Options{ AllowedOrigins: []string{"http://localhost:3000", "http://localhost:8080", "*"}, AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}, AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token"}, ExposedHeaders: []string{"Link"}, AllowCredentials: true, MaxAge: 300, })) // Routes r.Route("/api", func(r chi.Router) { r.Get("/tasks", func(w http.ResponseWriter, r *http.Request) { tasks := store.GetAll() jsonResponse(w, http.StatusOK, APIResponse{Success: true, Data: tasks}) }) r.Post("/tasks", func(w http.ResponseWriter, r *http.Request) { var input struct { Title string `json:"title"` Description string `json:"description"` } if err := json.NewDecoder(r.Body).Decode(&input); err != nil { jsonResponse(w, http.StatusBadRequest, APIResponse{Success: false, Error: "Invalid request body"}) return } if input.Title == "" { jsonResponse(w, http.StatusBadRequest, APIResponse{Success: false, Error: "Title is required"}) return } task := store.Create(input.Title, input.Description) jsonResponse(w, http.StatusCreated, APIResponse{Success: true, Data: task}) }) r.Get("/tasks/{id}", func(w http.ResponseWriter, r *http.Request) { idStr := chi.URLParam(r, "id") id, err := strconv.Atoi(idStr) if err != nil { jsonResponse(w, http.StatusBadRequest, APIResponse{Success: false, Error: "Invalid task ID"}) return } task, ok := store.Get(id) if !ok { jsonResponse(w, http.StatusNotFound, APIResponse{Success: false, Error: "Task not found"}) return } jsonResponse(w, http.StatusOK, APIResponse{Success: true, Data: task}) }) r.Delete("/tasks/{id}", func(w http.ResponseWriter, r *http.Request) { idStr := chi.URLParam(r, "id") id, err := strconv.Atoi(idStr) if err != nil { jsonResponse(w, http.StatusBadRequest, APIResponse{Success: false, Error: "Invalid task ID"}) return } if !store.Delete(id) { jsonResponse(w, http.StatusNotFound, APIResponse{Success: false, Error: "Task not found"}) return } jsonResponse(w, http.StatusOK, APIResponse{Success: true, Data: "Task deleted"}) }) }) // Health check r.Get("/health", func(w http.ResponseWriter, r *http.Request) { jsonResponse(w, http.StatusOK, APIResponse{Success: true, Data: "OK"}) }) port := ":8081" log.Printf("Backend server starting on %s", port) if err := http.ListenAndServe(port, r); err != nil { log.Fatal(err) } }