package main import ( "bookmann/internal/config" "bookmann/internal/database" "bookmann/internal/handlers" "bookmann/templates" "bytes" "log" "net/http" "github.com/go-playground/validator/v10" "github.com/golang-jwt/jwt/v5" "github.com/labstack/echo-jwt/v4" "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" ) // CustomValidator wraps the go-playground validator type CustomValidator struct { validator *validator.Validate } func (cv *CustomValidator) Validate(i interface{}) error { return cv.validator.Struct(i) } func main() { cfg := config.LoadConfig() dbPool, err := database.NewConnection(cfg.DatabaseURL()) if err != nil { log.Fatal("Failed to connect to database:", err) } defer dbPool.Close() queries := database.New(dbPool) authHandler := handlers.NewAuthHandler(queries, cfg.JWTSecret) e := echo.New() // Set up validator e.Validator = &CustomValidator{validator: validator.New()} // Middleware e.Use(middleware.Logger()) e.Use(middleware.Recover()) e.Use(middleware.CORS()) // Auth routes (no auth required) e.POST("/api/auth/register", authHandler.Register) e.POST("/api/auth/login", authHandler.Login) // JWT middleware for protected routes jwtMiddleware := echojwt.WithConfig(echojwt.Config{ SigningKey: []byte(cfg.JWTSecret), ContextKey: "user", SuccessHandler: func(c echo.Context) { token := c.Get("user").(*jwt.Token) claims := token.Claims.(jwt.MapClaims) c.Set("user_id", claims["user_id"]) }, }) // Protected routes protected := e.Group("/api", jwtMiddleware) protected.GET("/auth/profile", authHandler.GetProfile) protected.PUT("/auth/profile", authHandler.UpdateProfile) protected.GET("/auth/users", authHandler.ListUsers) protected.POST("/auth/ebook-folders", authHandler.AddEbookFolder) protected.GET("/auth/ebook-folders", authHandler.GetEbookFolders) protected.DELETE("/auth/ebook-folders", authHandler.DeleteEbookFolder) protected.DELETE("/auth/account", authHandler.DeleteAccount) protected.PUT("/library/scan-settings", authHandler.UpdateScanSettings) protected.GET("/library/scan-settings", authHandler.GetScanSettings) // Auth update routes authGroup := e.Group("/api/auth", jwtMiddleware) authGroup.PUT("/email", authHandler.UpdateEmail) authGroup.PUT("/username", authHandler.UpdateUsername) authGroup.PUT("/password", authHandler.UpdatePassword) authGroup.PUT("/theme", authHandler.UpdateTheme) // force rebuild // Static files e.Static("/static", "static") // Routes handlers.SetupRoutes(protected, queries) dummyUser := templates.User{ID: "", Username: "Admin", Email: "admin@example.com"} // Routes e.GET("/", func(c echo.Context) error { loggedIn := false var buf bytes.Buffer err := templates.Index(loggedIn).Render(c.Request().Context(), &buf) if err != nil { return err } return c.HTML(http.StatusOK, buf.String()) }) e.GET("/login", func(c echo.Context) error { var buf bytes.Buffer err := templates.Login().Render(c.Request().Context(), &buf) if err != nil { return err } return c.HTML(http.StatusOK, buf.String()) }) e.GET("/register", func(c echo.Context) error { var buf bytes.Buffer err := templates.Register().Render(c.Request().Context(), &buf) if err != nil { return err } return c.HTML(http.StatusOK, buf.String()) }) e.GET("/admin", func(c echo.Context) error { var buf bytes.Buffer err := templates.Admin(dummyUser).Render(c.Request().Context(), &buf) if err != nil { return err } return c.HTML(http.StatusOK, buf.String()) }) e.GET("/admin/", func(c echo.Context) error { var buf bytes.Buffer err := templates.Admin(dummyUser).Render(c.Request().Context(), &buf) if err != nil { return err } return c.HTML(http.StatusOK, buf.String()) }) e.GET("/admin/profile", func(c echo.Context) error { var buf bytes.Buffer err := templates.AdminProfile(dummyUser).Render(c.Request().Context(), &buf) if err != nil { return err } return c.HTML(http.StatusOK, buf.String()) }) e.GET("/admin/library", func(c echo.Context) error { var buf bytes.Buffer err := templates.AdminLibrary(dummyUser).Render(c.Request().Context(), &buf) if err != nil { return err } return c.HTML(http.StatusOK, buf.String()) }) // Start server log.Printf("Starting server on port %s", cfg.ServerPort) e.Logger.Fatal(e.Start(":" + cfg.ServerPort)) }