package main import ( "log" "net/http" "os" "strings" "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" ) func main() { dbPath := os.Getenv("DB_PATH") if dbPath == "" { dbPath = "./timetracking.db" } db := InitDB(dbPath) defer db.Close() app := &App{DB: db} e := echo.New() e.Use(middleware.Logger()) e.Use(middleware.Recover()) // CORS Configuration allowOrigins := []string{"*"} // Default for development if os.Getenv("ENVIRONMENT") == "production" { origins := os.Getenv("CORS_ALLOWED_ORIGINS") if origins != "" { allowOrigins = strings.Split(origins, ",") } else { log.Println("Warning: ENVIRONMENT is 'production' but CORS_ALLOWED_ORIGINS is not set. Allowing all origins.") } } e.Use(middleware.CORSWithConfig(middleware.CORSConfig{ AllowOrigins: allowOrigins, AllowMethods: []string{http.MethodGet, http.MethodPost, http.MethodPut, http.MethodDelete}, AllowHeaders: []string{echo.HeaderOrigin, echo.HeaderContentType, echo.HeaderAccept, echo.HeaderAuthorization}, })) e.HTTPErrorHandler = customHTTPErrorHandler e.POST("/api/login", app.LoginHandler) protected := e.Group("/api") protected.Use(JWTMiddleware()) { protected.GET("/schedules", app.GetSchedulesHandler) protected.POST("/time-entries", app.CreateTimeEntryHandler) protected.GET("/my-time-entries", app.GetMyTimeEntriesHandler) protected.POST("/time-entries/batch", app.CreateBatchTimeEntriesHandler) protected.DELETE("/my-time-entries/week", app.DeleteWeekEntries) protected.GET("/week-dates", app.GetWeekDates) protected.GET("/week-has-entries", app.CheckWeekHasEntries) protected.GET("/yearly-hours-summary", app.GetYearlyHoursSummaryHandler) protected.GET("/my-info", app.GetMyInfoHandler) protected.GET("/school-year/active", app.GetActiveSchoolYearHandler) } admin := e.Group("/api/admin") admin.Use(JWTMiddleware()) admin.Use(AdminMiddleware()) { admin.POST("/schedules", app.CreateScheduleHandler) admin.DELETE("/schedules/delete", app.DeleteScheduleHandler) admin.POST("/users", app.CreateUserHandler) admin.GET("/users/list", app.GetUsersHandler) admin.DELETE("/users/delete", app.DeleteUserHandler) admin.GET("/time-entries", app.GetAllTimeEntriesHandler) admin.GET("/weekly-hours", app.GetWeeklyHoursHandler) admin.PUT("/users/:id", app.UpdateUserHandler) admin.PUT("/users/:id/reset-password", app.ResetPasswordHandler) admin.PUT("/time-entries/:id", app.UpdateTimeEntryHandler) admin.DELETE("/time-entries/:id", app.DeleteTimeEntryHandler) admin.POST("/time-entry", app.AdminCreateTimeEntryHandler) admin.GET("/school-years", app.GetSchoolYearsHandler) admin.POST("/school-years", app.CreateSchoolYearHandler) admin.DELETE("/school-years/:id", app.DeleteSchoolYearHandler) admin.PUT("/school-years/:id/activate", app.SetActiveSchoolYearHandler) admin.GET("/yearly-summary/pdf", app.GenerateYearlySummaryPDFHandler) } e.Static("/", "./static") port := os.Getenv("PORT") if port == "" { port = "8080" } log.Printf("Server starting on port %s", port) e.Logger.Fatal(e.Start(":" + port)) } func customHTTPErrorHandler(err error, c echo.Context) { code := http.StatusInternalServerError message := "Internal Server Error" if he, ok := err.(*echo.HTTPError); ok { code = he.Code message = he.Message.(string) } if !c.Response().Committed { if c.Request().Method == http.MethodHead { c.NoContent(code) } else { c.JSON(code, map[string]string{ "error": message, }) } } }