1package httpserve
2
3import (
4 "github.com/bnema/gordon/internal/httpserve/handlers"
5 "github.com/bnema/gordon/internal/httpserve/middleware"
6 "github.com/bnema/gordon/internal/server"
7 _ "github.com/joho/godotenv/autoload"
8 "github.com/labstack/echo/v4"
9 echomid "github.com/labstack/echo/v4/middleware"
10)
11
12// RegisterRoutes registers all routes and middlewares
13func RegisterRoutes(e *echo.Echo, a *server.App) *echo.Echo {
14 // e.Use(echomid.Logger()) too verbose
15 e.Use(echomid.Recover())
16 // Initiate the session middleware
17 e.Use(middleware.InitSessionMiddleware(a))
18
19 // Language detection
20 e.Use(middleware.LanguageDetection)
21
22 AdminPath := a.Config.Admin.Path
23 bindLoginRoute(e, a, AdminPath)
24 bindAPIEndpoints(e, a)
25
26 // SetCommonDataMiddleware will pass data to the renderer
27 e.Use(middleware.SetCommonDataMiddleware(a))
28
29 // Error handler middleware
30 e.Use(middleware.ErrorHandler)
31
32 // Use middlewares
33 e.Use(middleware.SecureRoutes(a))
34
35 // Color scheme detection for dark/light mode
36 e.Use(middleware.ColorSchemeDetection)
37
38 bindAdminRoute(e, a, AdminPath)
39 bindStaticRoute(e, a, "/*")
40 bindHTMXEndpoints(e, a)
41
42 // Protect the root path with a 403
43 e.GET("/", func(c echo.Context) error {
44 return c.String(403, "403 Forbidden")
45 })
46
47 return e
48}
49
50// bindAPIEndpoints expose /api endpoints and speaks only JSON
51func bindAPIEndpoints(e *echo.Echo, a *server.App) {
52 apiGroup := e.Group("/api")
53
54 // Device flow endpoints (without RequireToken middleware)
55 apiGroup.POST("/device/code", func(c echo.Context) error {
56 return handlers.DeviceCodeRequest(c, a)
57 })
58 apiGroup.POST("/device/token", func(c echo.Context) error {
59 return handlers.DeviceTokenRequest(c, a)
60 })
61
62 // Other API endpoints that require a token
63 protectedApiGroup := apiGroup.Group("", middleware.RequireToken(a))
64 protectedApiGroup.GET("/ping", func(c echo.Context) error {
65 return handlers.GetInfos(c, a)
66 })
67 protectedApiGroup.POST("/deploy", func(c echo.Context) error {
68 return handlers.PostDeploy(c, a)
69 })
70 protectedApiGroup.POST("/push", func(c echo.Context) error {
71 return handlers.PostPush(c, a)
72 })
73 protectedApiGroup.POST("/stop", func(c echo.Context) error {
74 return handlers.PostContainerStop(c, a)
75 })
76 protectedApiGroup.POST("/remove", func(c echo.Context) error {
77 return handlers.PostContainerRemove(c, a)
78 })
79}
80
81// bindStaticRoute bind static path
82func bindStaticRoute(e *echo.Echo, a *server.App, path string) {
83 e.GET(path, func(c echo.Context) error {
84 return handlers.StaticRoute(c, a)
85 }, echomid.Gzip())
86}
87
88// bindLoginRoute binds all login routes
89func bindLoginRoute(e *echo.Echo, a *server.App, adminPath string) {
90 e.GET(adminPath+"/login", func(c echo.Context) error {
91 return handlers.RenderLoginPage(c, a)
92 })
93 e.GET(adminPath+"/login/oauth/github", func(c echo.Context) error {
94 return handlers.StartOAuthGithub(c, a)
95 })
96 e.GET(adminPath+"/login/oauth/callback", func(c echo.Context) error {
97 return handlers.OAuthCallback(c, a)
98 })
99 e.GET(adminPath+"/logout", func(c echo.Context) error {
100 return handlers.Logout(c, a)
101 })
102}
103
104// bindAdminRoute binds all admin routes
105func bindAdminRoute(e *echo.Echo, a *server.App, adminPath string) {
106 adminGroup := e.Group(adminPath)
107 // Since login is behind the admin path, we cannot use group middleware
108 adminGroup.GET("", func(c echo.Context) error {
109 return c.Redirect(302, adminPath+"/manager")
110 }, middleware.RequireLogin(a))
111
112 adminGroup.GET("/manager", func(c echo.Context) error {
113 return handlers.AdminManagerRoute(c, a)
114 }, middleware.RequireLogin(a))
115 adminGroup.GET("/cc/:ID", func(c echo.Context) error {
116 return handlers.CreateContainerFullGET(c, a)
117 }, middleware.RequireLogin(a))
118}
119
120// bindHTMXEndpoints binds all HTMX endpoints
121func bindHTMXEndpoints(e *echo.Echo, a *server.App) {
122 // Create a group for /htmx endpoints
123 htmxGroup := e.Group("/htmx")
124
125 // Apply middleware to the group, so all endpoints are protected
126 htmxGroup.Use(middleware.RequireLogin(a))
127
128 // List all images component
129 htmxGroup.GET("/image-manager", func(c echo.Context) error {
130 return handlers.ImageManagerComponent(c, a)
131 })
132 // Delete an image
133 htmxGroup.DELETE("/image-manager/delete/:ID", func(c echo.Context) error {
134 return handlers.ImageManagerDelete(c, a)
135 })
136
137 // List all containers
138 htmxGroup.GET("/container-manager", func(c echo.Context) error {
139 return handlers.ContainerManagerComponent(c, a)
140 })
141 // Stop a container
142 htmxGroup.POST("/container-manager/stop/:ID", func(c echo.Context) error {
143 return handlers.ContainerManagerStop(c, a)
144 })
145 // Delete a container
146 htmxGroup.DELETE("/container-manager/delete/:ID", func(c echo.Context) error {
147 return handlers.ContainerManagerDelete(c, a)
148 })
149 // Start a container
150 htmxGroup.POST("/container-manager/start/:ID", func(c echo.Context) error {
151 return handlers.ContainerManagerStart(c, a)
152 })
153 // Edit a container view
154 htmxGroup.GET("/container-manager/edit/:ID", func(c echo.Context) error {
155 return handlers.ContainerManagerEditGET(c, a)
156 })
157 // Edit a container action
158 htmxGroup.POST("/container-manager/edit/:ID", func(c echo.Context) error {
159 return handlers.ContainerManagerEditPOST(c, a)
160 })
161
162 // Display upload-image component
163 htmxGroup.GET("/upload-image", func(c echo.Context) error {
164 return handlers.UploadImageGETHandler(c, a)
165 })
166 // Upload image
167 htmxGroup.POST("/upload-image", func(c echo.Context) error {
168 return handlers.UploadImagePOSTHandler(c, a)
169 })
170 // Display create-container component
171 htmxGroup.GET("/create-container/:ID", func(c echo.Context) error {
172 return handlers.CreateContainerGET(c, a)
173 })
174 // Create container
175 htmxGroup.POST("/create-container/:ID", func(c echo.Context) error {
176 return handlers.CreateContainerPOST(c, a)
177 })
178}