mirror of
https://github.com/Pacerino/CaddyProxyManager.git
synced 2026-08-02 18:38:37 -04:00
Route host changes through the selected Caddy provider, add auth routes and the frontend-dir serving toggle, seed a default admin user on first run, and validate space/comma separated domain lists via a custom validator. Includes validator tests.
105 lines
3.2 KiB
Go
105 lines
3.2 KiB
Go
package api
|
|
|
|
import (
|
|
"io/fs"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/Pacerino/CaddyProxyManager/embed"
|
|
"github.com/Pacerino/CaddyProxyManager/internal/api/handler"
|
|
"github.com/Pacerino/CaddyProxyManager/internal/api/middleware"
|
|
"github.com/Pacerino/CaddyProxyManager/internal/config"
|
|
"github.com/Pacerino/CaddyProxyManager/internal/logger"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
"github.com/go-chi/cors"
|
|
)
|
|
|
|
func NewRouter() http.Handler {
|
|
cors := cors.Handler(cors.Options{
|
|
AllowedOrigins: []string{"*"},
|
|
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
|
|
AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "X-Requested-With"},
|
|
AllowCredentials: true,
|
|
MaxAge: 300,
|
|
})
|
|
|
|
r := chi.NewRouter()
|
|
h := handler.NewHandler()
|
|
|
|
r.Use(
|
|
cors,
|
|
middleware.DecodeAuth(),
|
|
)
|
|
|
|
return generateRoutes(r, h)
|
|
}
|
|
|
|
func generateRoutes(r chi.Router, h *handler.Handler) chi.Router {
|
|
r.Route("/api", func(r chi.Router) {
|
|
|
|
//Hosts
|
|
r.With(middleware.Enforce()).Route("/hosts", func(r chi.Router) {
|
|
r.Get("/", h.GetHosts()) // Get List of Hosts
|
|
r.Post("/", h.CreateHost()) // Create Host & save to the DB
|
|
r.Get("/{hostID:[0-9]+}", h.GetHost()) // Get specific Host by ID
|
|
r.Delete("/{hostID:[0-9]+}", h.DeleteHost()) // Delete Host by ID
|
|
r.Put("/", h.UpdateHost()) // Update Host by ID
|
|
})
|
|
|
|
//Auth
|
|
r.Route("/auth", func(r chi.Router) {
|
|
r.Get("/config", h.AuthConfig()) // Which auth mode is active
|
|
r.Get("/oidc/login", h.OIDCLogin()) // Begin OIDC login
|
|
r.Get("/oidc/callback", h.OIDCCallback()) // OIDC provider redirect
|
|
})
|
|
|
|
//User
|
|
r.Route("/users", func(r chi.Router) {
|
|
r.Post("/login", h.UserLogin()) // Login a User
|
|
r.With(middleware.Enforce()).Get("/", h.GetUsers()) // Get a list of Users
|
|
r.With(middleware.Enforce()).Post("/", h.CreateUser()) // Create a User
|
|
r.With(middleware.Enforce()).Get("/{userID:[0-9]+}", h.GetUser()) // Get a User by ID
|
|
r.With(middleware.Enforce()).Delete("/{userID:[0-9]+}", h.DeleteUser()) // Delete User by ID
|
|
r.With(middleware.Enforce()).Put("/", h.UpdateUser()) // Update Host by ID
|
|
})
|
|
})
|
|
fileServer(r)
|
|
return r
|
|
}
|
|
|
|
// frontendFS returns the filesystem used to serve the SPA. When
|
|
// CPM_FRONTENDDIR is set the assets are served from disk, otherwise the
|
|
// assets embedded in the binary are used.
|
|
func frontendFS() http.FileSystem {
|
|
if dir := config.Configuration.FrontendDir; dir != "" {
|
|
logger.Info("Serving frontend from external directory: %s", dir)
|
|
return http.Dir(dir)
|
|
}
|
|
fSys, err := fs.Sub(embed.Assets, "assets")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return http.FS(fSys)
|
|
}
|
|
|
|
func fileServer(r chi.Router) {
|
|
root := frontendFS()
|
|
fileSrv := http.FileServer(root)
|
|
|
|
r.Get("/*", func(w http.ResponseWriter, r *http.Request) {
|
|
// Serve the requested file if it exists, otherwise fall back to
|
|
// index.html so client-side routing works (SPA behaviour).
|
|
upath := strings.TrimPrefix(r.URL.Path, "/")
|
|
if upath == "" {
|
|
upath = "index.html"
|
|
}
|
|
if f, err := root.Open(upath); err != nil {
|
|
r.URL.Path = "/"
|
|
} else {
|
|
f.Close()
|
|
}
|
|
fileSrv.ServeHTTP(w, r)
|
|
})
|
|
}
|