package api import ( "flag" "fmt" "log/slog" "net/http" "os" "strings" "git.dubyatp.xyz/chat-api-server/db" "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" "github.com/go-chi/cors" "github.com/go-chi/docgen" "github.com/go-chi/render" ) var routes = flag.Bool("routes", false, "Generate API route documentation") func RequestLog(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { slog.Debug("api: request received", "request_uri", r.RequestURI, "source_ip", r.RemoteAddr, "user_agent", r.UserAgent()) next.ServeHTTP(w, r) }) } func Start() { db.InitScyllaDB() defer db.CloseScyllaDB() flag.Parse() r := chi.NewRouter() r.Use(cors.Handler(cors.Options{ AllowedOrigins: strings.Split(os.Getenv("ALLOWED_ORIGINS"), ","), AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}, AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token"}, ExposedHeaders: []string{"Link"}, AllowCredentials: true, MaxAge: 300, // Maximum value for preflight request cache })) r.Use(middleware.RequestID) r.Use(RequestLog) r.Use(middleware.Recoverer) r.Use(middleware.URLFormat) r.Use(render.SetContentType(render.ContentTypeJSON)) r.Get("/", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("hello world")) }) r.Get("/ping", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("pong")) }) r.Get("/panic", func(w http.ResponseWriter, r *http.Request) { panic("oh no") }) r.Route("/whoami", func(r chi.Router) { r.Use(SessionAuthMiddleware) r.Use(LoginCtx) r.Get("/", Whoami) }) r.Route("/messages", func(r chi.Router) { r.Use(SessionAuthMiddleware) // Protect with authentication r.Get("/", ListMessages) r.Route("/{messageID}", func(r chi.Router) { r.Use(MessageCtx) // Load message r.Get("/", GetMessage) r.Delete("/", DeleteMessage) r.Post("/edit", EditMessage) }) r.Route("/new", func(r chi.Router) { r.Use(LoginCtx) r.Post("/", NewMessage) }) }) r.Route("/users", func(r chi.Router) { r.Use(SessionAuthMiddleware) // Protect with authentication r.Get("/", ListUsers) r.Route("/{userID}", func(r chi.Router) { r.Use(UserCtx) // Load user r.Get("/", GetUser) }) }) r.Route("/login", func(r chi.Router) { r.Post("/", Login) }) r.Route("/logout", func(r chi.Router) { r.Use(SessionAuthMiddleware) r.Post("/", Logout) }) r.Route("/register", func(r chi.Router) { r.Post("/", NewUser) }) if *routes { fmt.Println(docgen.MarkdownRoutesDoc(r, docgen.MarkdownOpts{ ProjectPath: "git.dubyatp.xyz/chat-api-server", Intro: "Welcome to the chat API server. This is a simple API for sending and receiving messages.", })) return } http.ListenAndServe(":3000", r) }