1
1
package api
2
2
3
3
import (
4
- "context"
5
- "fmt"
6
- "log"
7
- "net/http"
8
- "time"
9
-
10
- "github.com/diggerhq/digger/opentaco/internal/analytics"
11
- "github.com/diggerhq/digger/opentaco/internal/tfe"
12
-
13
- "github.com/diggerhq/digger/opentaco/internal/backend "
14
- authpkg "github.com/diggerhq/digger/opentaco/internal/auth "
15
- "github.com/diggerhq/digger/opentaco/internal/middleware"
16
- "github.com/diggerhq/digger/opentaco/internal/rbac "
17
- "github.com/diggerhq/digger/opentaco/internal/s3compat "
18
- unithandlers "github.com/diggerhq/digger/opentaco/internal/unit "
19
- "github.com/diggerhq/digger/opentaco/internal/observability "
20
- "github.com/diggerhq/digger/opentaco/internal/oidc "
21
- "github.com/diggerhq/digger/opentaco/internal/sts"
22
- "github.com/diggerhq/digger/opentaco/internal/storage "
23
- "github.com/labstack/echo/v4"
4
+ "context"
5
+ "fmt"
6
+ "log"
7
+ "net/http"
8
+ "time"
9
+
10
+ "github.com/diggerhq/digger/opentaco/internal/analytics"
11
+ "github.com/diggerhq/digger/opentaco/internal/tfe"
12
+
13
+ authpkg "github.com/diggerhq/digger/opentaco/internal/auth "
14
+ "github.com/diggerhq/digger/opentaco/internal/backend "
15
+ "github.com/diggerhq/digger/opentaco/internal/middleware"
16
+ "github.com/diggerhq/digger/opentaco/internal/observability "
17
+ "github.com/diggerhq/digger/opentaco/internal/oidc "
18
+ "github.com/diggerhq/digger/opentaco/internal/rbac "
19
+ "github.com/diggerhq/digger/opentaco/internal/s3compat "
20
+ "github.com/diggerhq/digger/opentaco/internal/storage "
21
+ "github.com/diggerhq/digger/opentaco/internal/sts"
22
+ unithandlers "github.com/diggerhq/digger/opentaco/internal/unit "
23
+ "github.com/labstack/echo/v4"
24
24
)
25
25
26
26
// RegisterRoutes registers all API routes
@@ -29,15 +29,15 @@ func RegisterRoutes(e *echo.Echo, store storage.UnitStore, authEnabled bool) {
29
29
health := observability .NewHealthHandler ()
30
30
e .GET ("/healthz" , health .Healthz )
31
31
e .GET ("/readyz" , health .Readyz )
32
-
32
+
33
33
// Info endpoint for CLI to detect storage type
34
34
e .GET ("/v1/info" , func (c echo.Context ) error {
35
35
info := map [string ]interface {}{
36
36
"storage" : map [string ]interface {}{
37
37
"type" : "memory" ,
38
38
},
39
39
}
40
-
40
+
41
41
// Check if we're using S3 storage
42
42
if s3Store , ok := store .(storage.S3Store ); ok {
43
43
info ["storage" ] = map [string ]interface {}{
@@ -46,11 +46,10 @@ func RegisterRoutes(e *echo.Echo, store storage.UnitStore, authEnabled bool) {
46
46
"prefix" : s3Store .GetS3Prefix (),
47
47
}
48
48
}
49
-
49
+
50
50
return c .JSON (http .StatusOK , info )
51
51
})
52
52
53
-
54
53
// Prepare auth deps
55
54
signer , err := authpkg .NewSignerFromEnv ()
56
55
if err != nil {
@@ -84,23 +83,23 @@ func RegisterRoutes(e *echo.Echo, store storage.UnitStore, authEnabled bool) {
84
83
}
85
84
return c .String (http .StatusOK , email )
86
85
})
87
-
86
+
88
87
e .POST ("/v1/system-id/user-email" , func (c echo.Context ) error {
89
88
var req struct {
90
89
Email string `json:"email"`
91
90
}
92
91
if err := c .Bind (& req ); err != nil {
93
92
return c .JSON (http .StatusBadRequest , map [string ]string {"error" : "Invalid request" })
94
93
}
95
-
94
+
96
95
// Set user email in analytics system
97
96
ctx , cancel := context .WithTimeout (context .Background (), 5 * time .Second )
98
97
defer cancel ()
99
-
98
+
100
99
if err := analytics .SetUserEmail (ctx , req .Email ); err != nil {
101
100
return c .JSON (http .StatusInternalServerError , map [string ]string {"error" : "Failed to set email" })
102
101
}
103
-
102
+
104
103
return c .JSON (http .StatusOK , map [string ]string {"message" : "Email set successfully" })
105
104
})
106
105
@@ -111,7 +110,6 @@ func RegisterRoutes(e *echo.Echo, store storage.UnitStore, authEnabled bool) {
111
110
e .GET ("/oauth/oidc-callback" , authHandler .OAuthOIDCCallback )
112
111
e .GET ("/oauth/debug" , authHandler .DebugConfig )
113
112
114
-
115
113
// API v1 protected group - JWT tokens only
116
114
v1 := e .Group ("/v1" )
117
115
if authEnabled {
@@ -170,7 +168,7 @@ func RegisterRoutes(e *echo.Echo, store storage.UnitStore, authEnabled bool) {
170
168
v1 .GET ("/backend/*" , middleware .JWTOnlyRBACMiddleware (rbacManager , signer , rbac .ActionUnitRead , "*" )(backendHandler .GetState ))
171
169
v1 .POST ("/backend/*" , middleware .JWTOnlyRBACMiddleware (rbacManager , signer , rbac .ActionUnitWrite , "*" )(backendHandler .UpdateState ))
172
170
v1 .PUT ("/backend/*" , middleware .JWTOnlyRBACMiddleware (rbacManager , signer , rbac .ActionUnitWrite , "*" )(backendHandler .UpdateState ))
173
- // Explicitly wire non-standard HTTP methods used by Terraform backend
171
+ // Explicitly wire non-standard HTTP methods used by Terraform backend
174
172
jwtVerifyFn := middleware .JWTOnlyVerifier (signer )
175
173
e .Add ("LOCK" , "/v1/backend/*" , middleware .RequireAuth (jwtVerifyFn )(middleware .JWTOnlyRBACMiddleware (rbacManager , signer , rbac .ActionUnitLock , "*" )(backendHandler .HandleLockUnlock )))
176
174
e .Add ("UNLOCK" , "/v1/backend/*" , middleware .RequireAuth (jwtVerifyFn )(middleware .JWTOnlyRBACMiddleware (rbacManager , signer , rbac .ActionUnitLock , "*" )(backendHandler .HandleLockUnlock )))
@@ -200,13 +198,13 @@ func RegisterRoutes(e *echo.Echo, store storage.UnitStore, authEnabled bool) {
200
198
// RBAC routes (only available with S3 storage)
201
199
if rbacManager != nil {
202
200
rbacHandler := rbac .NewHandler (rbacManager , signer )
203
-
201
+
204
202
// RBAC initialization (no auth required for init)
205
203
v1 .POST ("/rbac/init" , rbacHandler .Init )
206
-
204
+
207
205
// RBAC user info (handle auth gracefully in handler, like /v1/auth/me)
208
206
e .GET ("/v1/rbac/me" , rbacHandler .Me )
209
-
207
+
210
208
// RBAC management routes (require RBAC manage permission)
211
209
v1 .POST ("/rbac/users/assign" , rbacHandler .AssignRole )
212
210
v1 .POST ("/rbac/users/revoke" , rbacHandler .RevokeRole )
@@ -224,14 +222,14 @@ func RegisterRoutes(e *echo.Echo, store storage.UnitStore, authEnabled bool) {
224
222
// RBAC not available with memory storage - add catch-all route
225
223
v1 .Any ("/rbac/*" , func (c echo.Context ) error {
226
224
return c .JSON (http .StatusBadRequest , map [string ]string {
227
- "error" : "RBAC requires S3 storage" ,
225
+ "error" : "RBAC requires S3 storage" ,
228
226
"message" : "RBAC is only available when using S3 storage. Please configure S3 storage to use RBAC features." ,
229
227
})
230
228
})
231
229
}
232
230
233
231
// TFE api - inject auth handler, storage, and RBAC dependencies
234
- tfeHandler := tfe .NewTFETokenHandler (authHandler , store , rbacManager ) // Pass rbacManager (may be nil)
232
+ tfeHandler := tfe .NewTFETokenHandler (authHandler , store , rbacManager ) // Pass rbacManager (may be nil)
235
233
236
234
// Create protected TFE group - opaque tokens only
237
235
tfeGroup := e .Group ("/tfe/api/v2" )
@@ -261,6 +259,8 @@ func RegisterRoutes(e *echo.Echo, store storage.UnitStore, authEnabled bool) {
261
259
262
260
// Keep discovery endpoints unprotected (needed for terraform login)
263
261
e .GET ("/.well-known/terraform.json" , tfeHandler .GetWellKnownJson )
262
+ e .GET ("/tfe/api/v2/motd" , tfeHandler .MessageOfTheDay )
263
+
264
264
e .GET ("/tfe/app/oauth2/auth" , tfeHandler .AuthLogin )
265
265
e .POST ("/tfe/oauth2/token" , tfeHandler .AuthTokenExchange )
266
266
0 commit comments