package main
import (
"encoding/json"
"errors"
"fmt"
"io"
"log"
"net/http"
"os"
"strings"
"sync"
"time"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
Active bool `json:"active"`
CreatedAt time.Time `json:"created_at"`
}
type UserService struct {
mu sync.RWMutex
users map[int]*User
}
func NewUserService() *UserService {
return &UserService{
users: make(map[int]*User),
}
}
func (s *UserService) Add(user *User) error {
s.mu.Lock()
defer s.mu.Unlock()
if user.Name == "" {
return errors.New("name is required")
}
if !strings.Contains(user.Email, "@") {
return errors.New("invalid email")
}
if _, exists := s.users[user.ID]; exists {
return errors.New("user already exists")
}
s.users[user.ID] = user
return nil
}
func (s *UserService) FindByID(id int) (*User, error) {
s.mu.RLock()
defer s.mu.RUnlock()
user, ok := s.users[id]
if !ok {
return nil, fmt.Errorf("user %d not found", id)
}
return user, nil
}
func (s *UserService) FindByEmail(email string) *User {
s.mu.RLock()
defer s.mu.RUnlock()
for _, u := range s.users {
if u.Email == email {
return u
}
}
return nil
}
func (s *UserService) All() []*User {
s.mu.RLock()
defer s.mu.RUnlock()
result := make([]*User, 0, len(s.users))
for _, u := range s.users {
result = append(result, u)
}
return result
}
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
log.Printf("%s %s %s", r.Method, r.URL.Path, r.RemoteAddr)
next.ServeHTTP(w, r)
log.Printf("%s completed in %v", r.URL.Path, time.Since(start))
})
}
func handleUsers(svc *UserService) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
switch r.Method {
case http.MethodGet:
users := svc.All()
json.NewEncoder(w).Encode(users)
case http.MethodPost:
var user User
if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if err := svc.Add(&user); err != nil {
http.Error(w, err.Error(), http.StatusConflict)
return
}
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(user)
default:
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
}
}
}
func main() {
svc := NewUserService()
svc.Add(&User{
ID: 1,
Name: "Alice",
Email: "alice@example.com",
Active: true,
})
mux := http.NewServeMux()
mux.HandleFunc("/users", handleUsers(svc))
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
server := &http.Server{
Addr: ":" + port,
Handler: loggingMiddleware(mux),
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
}
log.Printf("Server starting on :%s", port)
log.Fatal(server.ListenAndServe())
}