// internal/server/server.gopackageserverimport("context""fmt""log""net/http""os""os/signal""syscall""time")// Server represents an HTTP servertypeServerstruct{httpServer*http.Serverrouter*http.ServeMuxconfig*Config}// Config holds server configurationtypeConfigstruct{PortstringReadTimeouttime.DurationWriteTimeouttime.DurationIdleTimeouttime.Duration}// NewServer creates a new HTTP serverfuncNewServer(config*Config)*Server{router:=http.NewServeMux()server:=&Server{router:router,config:config,}server.setupRoutes()returnserver}// setupRoutes configures the HTTP routesfunc(s*Server)setupRoutes(){// Health checks.router.HandleFunc("/health",s.healthHandler)// API routess.router.HandleFunc("/api/users",s.usersHandler)s.router.HandleFunc("/api/users/",s.userHandler)// Static filess.router.Handle("/static/",http.StripPrefix("/static/",http.FileServer(http.Dir("static"))))}// Start starts the HTTP serverfunc(s*Server)Start()error{s.httpServer=&http.Server{Addr:s.config.Port,Handler:s.router,ReadTimeout:s.config.ReadTimeout,WriteTimeout:s.config.WriteTimeout,IdleTimeout:s.config.IdleTimeout,}log.Printf("Starting server on %s",s.config.Port)returns.httpServer.ListenAndServe()}// StartTLS starts the HTTPS serverfunc(s*Server)StartTLS(certFile,keyFilestring)error{s.httpServer=&http.Server{Addr:s.config.Port,Handler:s.router,ReadTimeout:s.config.ReadTimeout,WriteTimeout:s.config.WriteTimeout,IdleTimeout:s.config.IdleTimeout,}log.Printf("Starting TLS server on %s",s.config.Port)returns.httpServer.ListenAndServeTLS(certFile,keyFile)}// Stop gracefully stops the serverfunc(s*Server)Stop(ctxcontext.Context)error{log.Println("Shutting down server...")returns.httpServer.Shutdown(ctx)}// healthHandler handles health check requestsfunc(s*Server)healthHandler(whttp.ResponseWriter,r*http.Request){ifr.Method!=http.MethodGet{http.Error(w,"Method not allowed",http.StatusMethodNotAllowed)return}w.Header().Set("Content-Type","application/json")w.WriteHeader(http.StatusOK)fmt.Fprintf(w,`{"status":"healthy","timestamp":"%s"}`,time.Now().UTC().Format(time.RFC3339))}
// internal/server/graceful.gopackageserverimport("context""log""os""os/signal""syscall""time")// RunWithGracefulShutdown runs the server with graceful shutdownfunc(s*Server)RunWithGracefulShutdown(){// Start server in a goroutinegofunc(){iferr:=s.Start();err!=nil&&err!=http.ErrServerClosed{log.Fatalf("Server failed to start: %v",err)}}()// Wait for interrupt signalquit:=make(chanos.Signal,1)signal.Notify(quit,syscall.SIGINT,syscall.SIGTERM)<-quit// Graceful shutdown with timeoutctx,cancel:=context.WithTimeout(context.Background(),30*time.Second)defercancel()iferr:=s.Stop(ctx);err!=nil{log.Fatalf("Server forced to shutdown: %v",err)}log.Println("Server exited")}
// internal/middleware/logging.gopackagemiddlewareimport("log""net/http""time")// LoggingMiddleware logs HTTP requestsfuncLoggingMiddleware(nexthttp.Handler)http.Handler{returnhttp.HandlerFunc(func(whttp.ResponseWriter,r*http.Request){start:=time.Now()// Wrap the ResponseWriter to capture status codewrapped:=&responseWriter{ResponseWriter:w,statusCode:http.StatusOK}next.ServeHTTP(wrapped,r)duration:=time.Since(start)log.Printf("%s %s %d %v",r.Method,r.URL.Path,wrapped.statusCode,duration)})}// responseWriter wraps http.ResponseWriter to capture status codetyperesponseWriterstruct{http.ResponseWriterstatusCodeint}func(rw*responseWriter)WriteHeader(codeint){rw.statusCode=coderw.ResponseWriter.WriteHeader(code)}
// internal/middleware/auth.gopackagemiddlewareimport("context""net/http""strings")// AuthMiddleware handles authenticationfuncAuthMiddleware(nexthttp.Handler)http.Handler{returnhttp.HandlerFunc(func(whttp.ResponseWriter,r*http.Request){authHeader:=r.Header.Get("Authorization")ifauthHeader==""{http.Error(w,"Authorization header required",http.StatusUnauthorized)return}token:=strings.TrimPrefix(authHeader,"Bearer ")iftoken==authHeader{http.Error(w,"Invalid authorization format",http.StatusUnauthorized)return}// Validate token (implement your validation logic)userID,err:=validateToken(token)iferr!=nil{http.Error(w,"Invalid token",http.StatusUnauthorized)return}// Add user ID to contextctx:=context.WithValue(r.Context(),"userID",userID)next.ServeHTTP(w,r.WithContext(ctx))})}// validateToken validates a JWT tokenfuncvalidateToken(tokenstring)(string,error){// Implement JWT validation logic// Return user ID and errorreturn"user123",nil}
// internal/middleware/ratelimit.gopackagemiddlewareimport("net/http""sync""time")// RateLimiter implements token bucket rate limitingtypeRateLimiterstruct{musync.Mutextokensintcapacityintratetime.DurationlastTimetime.Time}// NewRateLimiter creates a new rate limiterfuncNewRateLimiter(capacityint,ratetime.Duration)*RateLimiter{return&RateLimiter{tokens:capacity,capacity:capacity,rate:rate,lastTime:time.Now(),}}// Allow checks if a request is allowedfunc(rl*RateLimiter)Allow()bool{rl.mu.Lock()deferrl.mu.Unlock()now:=time.Now()elapsed:=now.Sub(rl.lastTime)// Add tokens based on elapsed timetokensToAdd:=int(elapsed/rl.rate)iftokensToAdd>0{rl.tokens=min(rl.capacity,rl.tokens+tokensToAdd)rl.lastTime=now}ifrl.tokens>0{rl.tokens--returntrue}returnfalse}// RateLimitMiddleware implements rate limitingfuncRateLimitMiddleware(limiter*RateLimiter)func(http.Handler)http.Handler{returnfunc(nexthttp.Handler)http.Handler{returnhttp.HandlerFunc(func(whttp.ResponseWriter,r*http.Request){if!limiter.Allow(){http.Error(w,"Rate limit exceeded",http.StatusTooManyRequests)return}next.ServeHTTP(w,r)})}}// min returns the minimum of two integersfuncmin(a,bint)int{ifa<b{returna}returnb}
// internal/handler/json.gopackagehandlerimport("encoding/json""net/http")// JSONResponse represents a JSON responsetypeJSONResponsestruct{Datainterface{}`json:"data,omitempty"`Errorstring`json:"error,omitempty"`Messagestring`json:"message,omitempty"`}// WriteJSON writes a JSON responsefuncWriteJSON(whttp.ResponseWriter,statusCodeint,datainterface{}){w.Header().Set("Content-Type","application/json")w.WriteHeader(statusCode)response:=JSONResponse{Data:data}json.NewEncoder(w).Encode(response)}// WriteError writes an error responsefuncWriteError(whttp.ResponseWriter,statusCodeint,messagestring){w.Header().Set("Content-Type","application/json")w.WriteHeader(statusCode)response:=JSONResponse{Error:message}json.NewEncoder(w).Encode(response)}// ReadJSON reads JSON from request bodyfuncReadJSON(r*http.Request,vinterface{})error{ifr.Header.Get("Content-Type")!="application/json"{returnfmt.Errorf("content-type must be application/json")}returnjson.NewDecoder(r.Body).Decode(v)}
// internal/handler/validation.gopackagehandlerimport("fmt""net/http""strings")// Validator represents a request validatortypeValidatorinterface{Validate()error}// UserRequest represents a user creation requesttypeUserRequeststruct{Namestring`json:"name"`Emailstring`json:"email"`}// Validate validates the user requestfunc(ur*UserRequest)Validate()error{ifstrings.TrimSpace(ur.Name)==""{returnfmt.Errorf("name is required")}ifstrings.TrimSpace(ur.Email)==""{returnfmt.Errorf("email is required")}if!isValidEmail(ur.Email){returnfmt.Errorf("invalid email format")}returnnil}// isValidEmail validates email formatfuncisValidEmail(emailstring)bool{returnstrings.Contains(email,"@")&&strings.Contains(email,".")}
// internal/handler/user.gopackagehandlerimport("encoding/json""fmt""net/http""strconv""strings")// UserHandler handles user-related HTTP requeststypeUserHandlerstruct{userServiceUserService}// NewUserHandler creates a new user handlerfuncNewUserHandler(userServiceUserService)*UserHandler{return&UserHandler{userService:userService,}}// CreateUser handles user creationfunc(h*UserHandler)CreateUser(whttp.ResponseWriter,r*http.Request){ifr.Method!=http.MethodPost{WriteError(w,http.StatusMethodNotAllowed,"Method not allowed")return}varreqUserRequestiferr:=ReadJSON(r,&req);err!=nil{WriteError(w,http.StatusBadRequest,"Invalid JSON")return}iferr:=req.Validate();err!=nil{WriteError(w,http.StatusBadRequest,err.Error())return}user,err:=h.userService.CreateUser(&req)iferr!=nil{WriteError(w,http.StatusInternalServerError,"Failed to create user")return}WriteJSON(w,http.StatusCreated,user)}// GetUser handles user retrievalfunc(h*UserHandler)GetUser(whttp.ResponseWriter,r*http.Request){ifr.Method!=http.MethodGet{WriteError(w,http.StatusMethodNotAllowed,"Method not allowed")return}userID:=extractUserID(r.URL.Path)ifuserID==""{WriteError(w,http.StatusBadRequest,"User ID required")return}user,err:=h.userService.GetUser(userID)iferr!=nil{WriteError(w,http.StatusNotFound,"User not found")return}WriteJSON(w,http.StatusOK,user)}// UpdateUser handles user updatesfunc(h*UserHandler)UpdateUser(whttp.ResponseWriter,r*http.Request){ifr.Method!=http.MethodPut{WriteError(w,http.StatusMethodNotAllowed,"Method not allowed")return}userID:=extractUserID(r.URL.Path)ifuserID==""{WriteError(w,http.StatusBadRequest,"User ID required")return}varreqUserRequestiferr:=ReadJSON(r,&req);err!=nil{WriteError(w,http.StatusBadRequest,"Invalid JSON")return}iferr:=req.Validate();err!=nil{WriteError(w,http.StatusBadRequest,err.Error())return}user,err:=h.userService.UpdateUser(userID,&req)iferr!=nil{WriteError(w,http.StatusInternalServerError,"Failed to update user")return}WriteJSON(w,http.StatusOK,user)}// DeleteUser handles user deletionfunc(h*UserHandler)DeleteUser(whttp.ResponseWriter,r*http.Request){ifr.Method!=http.MethodDelete{WriteError(w,http.StatusMethodNotAllowed,"Method not allowed")return}userID:=extractUserID(r.URL.Path)ifuserID==""{WriteError(w,http.StatusBadRequest,"User ID required")return}iferr:=h.userService.DeleteUser(userID);err!=nil{WriteError(w,http.StatusInternalServerError,"Failed to delete user")return}w.WriteHeader(http.StatusNoContent)}// extractUserID extracts user ID from URL pathfuncextractUserID(pathstring)string{parts:=strings.Split(path,"/")iflen(parts)>=3{returnparts[3]}return""}