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"` Status string `json:"status"` Priority string `json:"priority"` CreatedAt time.Time `json:"createdAt"` UpdatedAt time.Time `json:"updatedAt"` } type TaskStore struct { mu sync.RWMutex tasks map[int]Task nextID int } func NewTaskStore() *TaskStore { store := &TaskStore{ tasks: make(map[int]Task), nextID: 1, } // Add some sample tasks store.Create(Task{Title: "Set up project structure", Description: "Initialize the repository with proper folder structure", Status: "completed", Priority: "high"}) store.Create(Task{Title: "Implement backend API", Description: "Create REST endpoints for task management", Status: "in_progress", Priority: "high"}) store.Create(Task{Title: "Build frontend UI", Description: "Design and implement the user interface with Next.js", Status: "pending", Priority: "medium"}) return store } 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(task Task) Task { s.mu.Lock() defer s.mu.Unlock() task.ID = s.nextID task.CreatedAt = time.Now() task.UpdatedAt = time.Now() if task.Status == "" { task.Status = "pending" } if task.Priority == "" { task.Priority = "medium" } 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 main() { store := NewTaskStore() r := chi.NewRouter() // Middleware r.Use(middleware.Logger) r.Use(middleware.Recoverer) r.Use(cors.Handler(cors.Options{ AllowedOrigins: []string{"http://localhost:3000", "http://frontend:3000", "*"}, AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}, AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token"}, ExposedHeaders: []string{"Link"}, AllowCredentials: true, MaxAge: 300, })) // Health check r.Get("/health", func(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(map[string]string{"status": "ok"}) }) // API routes r.Route("/api", func(r chi.Router) { r.Route("/tasks", func(r chi.Router) { r.Get("/", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") tasks := store.GetAll() json.NewEncoder(w).Encode(APIResponse{Success: true, Data: tasks}) }) r.Post("/", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") var task Task if err := json.NewDecoder(r.Body).Decode(&task); err != nil { w.WriteHeader(http.StatusBadRequest) json.NewEncoder(w).Encode(APIResponse{Success: false, Error: "Invalid request body"}) return } if task.Title == "" { w.WriteHeader(http.StatusBadRequest) json.NewEncoder(w).Encode(APIResponse{Success: false, Error: "Title is required"}) return } created := store.Create(task) w.WriteHeader(http.StatusCreated) json.NewEncoder(w).Encode(APIResponse{Success: true, Data: created}) }) r.Get("/{id}", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") idStr := chi.URLParam(r, "id") id, err := strconv.Atoi(idStr) if err != nil { w.WriteHeader(http.StatusBadRequest) json.NewEncoder(w).Encode(APIResponse{Success: false, Error: "Invalid task ID"}) return } task, ok := store.Get(id) if !ok { w.WriteHeader(http.StatusNotFound) json.NewEncoder(w).Encode(APIResponse{Success: false, Error: "Task not found"}) return } json.NewEncoder(w).Encode(APIResponse{Success: true, Data: task}) }) r.Delete("/{id}", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") idStr := chi.URLParam(r, "id") id, err := strconv.Atoi(idStr) if err != nil { w.WriteHeader(http.StatusBadRequest) json.NewEncoder(w).Encode(APIResponse{Success: false, Error: "Invalid task ID"}) return } if !store.Delete(id) { w.WriteHeader(http.StatusNotFound) json.NewEncoder(w).Encode(APIResponse{Success: false, Error: "Task not found"}) return } json.NewEncoder(w).Encode(APIResponse{Success: true, Data: map[string]string{"message": "Task deleted"}}) }) }) }) log.Println("Backend server starting on :8080") log.Fatal(http.ListenAndServe(":8080", r)) }