Compare commits

..

5 Commits

5 changed files with 453 additions and 352 deletions
+8 -7
View File
@@ -711,21 +711,22 @@ func (s *Store) UpsertBlockProperties(boardID, blockID string, props models.Bloc
var result sql.Result
var affected int64
err = s.QueryRow(`
assigneeIDs = normalizeBlockAssigneeIDs(props)
tx, owned, err = s.begin()
if err != nil {
return props, err
}
err = tx.QueryRow(`
SELECT bl.id FROM blocks bl
JOIN boards brd ON brd.id = bl.board_id
WHERE brd.public_id = ? AND bl.public_id = ? AND bl.delete_at = 0`,
boardID, blockID).Scan(&internalBlockID)
if err == sql.ErrNoRows {
rollbackIfOwned(tx, owned)
return props, sql.ErrNoRows
}
if err != nil {
return props, err
}
assigneeIDs = normalizeBlockAssigneeIDs(props)
tx, owned, err = s.begin()
if err != nil {
rollbackIfOwned(tx, owned)
return props, err
}
_, err = tx.Exec(`
+14 -8
View File
@@ -131,18 +131,24 @@ func (s *Store) DeleteUserGroup(groupID string) error {
var tx txExecutor
var owned bool
var err error
var group models.UserGroup
group, err = s.GetUserGroup(groupID)
if err != nil { return err }
if NormalizeUserGroupScope(group.Scope) == UserGroupScopeAllUsers {
return errors.New("all users group cannot be deleted")
}
var scope string
tx, owned, err = s.begin()
if err != nil { return err }
err = tx.QueryRow(`SELECT scope FROM user_groups WHERE public_id = ?`, strings.TrimSpace(groupID)).Scan(&scope)
if err != nil {
rollbackIfOwned(tx, owned)
return err
}
// TODO: this part needs to be re-thought through. This is still user created group. If it can't be deleted, that's horrible
// above all creating another user group with this scope gives the unique constraint error... so Creating a user group
// should translate it to human friendly message.
if NormalizeUserGroupScope(scope) == UserGroupScopeAllUsers {
rollbackIfOwned(tx, owned)
return errors.New("all users group cannot be deleted")
}
_, err = tx.Exec(`DELETE FROM project_role_bindings WHERE subject_type = 'group' AND subject_id = (SELECT id FROM user_groups WHERE public_id = ?)`, strings.TrimSpace(groupID))
if err != nil {
rollbackIfOwned(tx, owned)
+28 -27
View File
@@ -43,6 +43,13 @@ type httpStatusReasonHolder struct {
OK bool
}
type responseWriterStoringStatus interface {
http.ResponseWriter
Status() int
FlushTo(dst http.ResponseWriter)
}
type bufferedResponseWriter struct {
header http.Header
body bytes.Buffer
@@ -347,41 +354,41 @@ func newPassThroughStatusWriter(w http.ResponseWriter) *passThroughStatusWriter
return writer
}
func (w *bufferedResponseWriter) Status() int {
return w.status
}
func (w *bufferedResponseWriter) Header() http.Header {
return w.header
}
func (w *bufferedResponseWriter) WriteHeader(status int) {
if w.wroteHeader {
return
}
if w.wroteHeader { return }
w.status = status
w.wroteHeader = true
}
func (w *bufferedResponseWriter) Write(data []byte) (int, error) {
if !w.wroteHeader {
w.WriteHeader(http.StatusOK)
}
if !w.wroteHeader { w.WriteHeader(http.StatusOK) }
return w.body.Write(data)
}
func (w *bufferedResponseWriter) Flush() {
}
func (w *passThroughStatusWriter) Status() int {
return w.status
}
func (w *passThroughStatusWriter) WriteHeader(status int) {
if w.wroteHeader {
return
}
if w.wroteHeader { return }
w.status = status
w.wroteHeader = true
w.ResponseWriter.WriteHeader(status)
}
func (w *passThroughStatusWriter) Write(data []byte) (int, error) {
if !w.wroteHeader {
w.WriteHeader(http.StatusOK)
}
if !w.wroteHeader { w.WriteHeader(http.StatusOK) }
return w.ResponseWriter.Write(data)
}
@@ -390,9 +397,7 @@ func (w *passThroughStatusWriter) Flush() {
var ok bool
flusher, ok = w.ResponseWriter.(http.Flusher)
if ok {
flusher.Flush()
}
if ok { flusher.Flush() }
}
func (w *passThroughStatusWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
@@ -400,9 +405,7 @@ func (w *passThroughStatusWriter) Hijack() (net.Conn, *bufio.ReadWriter, error)
var ok bool
hijacker, ok = w.ResponseWriter.(http.Hijacker)
if !ok {
return nil, nil, fmt.Errorf("response writer does not support hijacking")
}
if !ok { return nil, nil, fmt.Errorf("response writer does not support hijacking") }
return hijacker.Hijack()
}
@@ -411,9 +414,7 @@ func (w *passThroughStatusWriter) Push(target string, opts *http.PushOptions) er
var ok bool
pusher, ok = w.ResponseWriter.(http.Pusher)
if !ok {
return http.ErrNotSupported
}
if !ok { return http.ErrNotSupported }
return pusher.Push(target, opts)
}
@@ -421,16 +422,16 @@ func (w *passThroughStatusWriter) ReadFrom(r io.Reader) (int64, error) {
var readerFrom io.ReaderFrom
var ok bool
if !w.wroteHeader {
w.WriteHeader(http.StatusOK)
}
if !w.wroteHeader { w.WriteHeader(http.StatusOK) }
readerFrom, ok = w.ResponseWriter.(io.ReaderFrom)
if ok {
return readerFrom.ReadFrom(r)
}
if ok { return readerFrom.ReadFrom(r) }
return io.Copy(w.ResponseWriter, r)
}
func (w *passThroughStatusWriter) FlushTo(dst http.ResponseWriter) {
// do nothing. only to meet the interface requirement of responseWriterStoringStatus
}
func (w *bufferedResponseWriter) FlushTo(dst http.ResponseWriter) {
var key string
var values []string
+85
View File
@@ -0,0 +1,85 @@
package middleware
import "net/http"
import "codit/internal/db"
import httpx "codit/internal/http"
// TxMode selects how a route-level transaction is started.
// TxImmediate is meaningful for SQLite, where it starts with BEGIN IMMEDIATE
// to reserve the writer slot early. For non-SQLite drivers, TxImmediate falls
// back to the normal deferred BeginStore, so it behaves like TxDeferred.
type TxMode int
const (
TxDeferred TxMode = iota
TxImmediate
)
type TxOutputMode int
const (
TxBuffered TxOutputMode = iota
TxUnbuffered
)
func TxRoute(store *db.Store, mode TxMode, output TxOutputMode, handler httpx.HandlerFunc) httpx.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request, params httpx.Params) {
var txStore *db.Store
var mit responseWriterStoringStatus
var err error
if isWebSocketUpgrade(r) { // exception for a websocket upgrade call
handler(w, r, params)
return
}
if mode == TxImmediate {
txStore, err = store.BeginImmediateStore(r.Context())
} else {
// all other values are TxDeferred
txStore, err = store.BeginStore(r.Context())
}
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer rollbackIfPanic(txStore)
r = WithStore(r, txStore)
if output == TxUnbuffered {
mit = newPassThroughStatusWriter(w)
handler(mit, r, params)
} else {
mit = newBufferedResponseWriter()
handler(mit, r, params)
}
if mit.Status() >= http.StatusBadRequest { // 4XX, 5XX, etc
_ = txStore.Rollback()
mit.FlushTo(w)
return
}
err = txStore.Commit()
if err != nil {
_ = txStore.Rollback()
if output == TxUnbuffered {
var ptrw *passThroughStatusWriter
var ok bool
ptrw, ok = mit.(*passThroughStatusWriter) // ok retrieval and assertion isn't needed as it can't fail
if ok && !ptrw.wroteHeader { // but keep it for defense
http.Error(w, err.Error(), http.StatusInternalServerError)
}
} else {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
return
}
runAfterCommit(r.Context())
mit.FlushTo(w) // [NOTE] this is no-op for unbuffered output
}
}
+318 -310
View File
@@ -635,6 +635,8 @@ func (app *Server) initHandlers() (*handlers.API, *http.ServeMux, error) {
var api *handlers.API
var router *codit_http.Router
var txRead func(codit_http.HandlerFunc) codit_http.HandlerFunc
var txWrite func(codit_http.HandlerFunc) codit_http.HandlerFunc
api = handlers.NewAPI(handlers.APIOptions{
ServerId: app.serverId,
@@ -651,326 +653,332 @@ func (app *Server) initHandlers() (*handlers.API, *http.ServeMux, error) {
})
router = codit_http.NewRouter()
txRead = func(handler codit_http.HandlerFunc) codit_http.HandlerFunc {
return middleware.TxRoute(store, middleware.TxDeferred, middleware.TxUnbuffered, handler)
}
txWrite = func(handler codit_http.HandlerFunc) codit_http.HandlerFunc {
return middleware.TxRoute(store, middleware.TxImmediate, middleware.TxBuffered, handler)
}
router.Handle("GET", "/api/health", api.Health)
router.Handle("GET", "/api/app-info", api.AppInfo)
router.Handle("POST", "/api/login", api.Login)
router.Handle("POST", "/api/logout", api.Logout)
router.Handle("GET", "/api/auth/providers", api.ListPublicAuthProviders)
router.Handle("GET", "/api/auth/oidc/:id/login", api.OIDCLoginWithProvider)
router.Handle("GET", "/api/auth/oidc/:id/callback", api.OIDCCallbackWithProvider)
router.Handle("GET", "/api/me", api.Me)
router.Handle("PATCH", "/api/me", api.UpdateMe)
router.Handle("POST", "/api/me/avatar", api.UploadMyAvatar)
router.Handle("DELETE", "/api/me/avatar", api.DeleteMyAvatar)
router.Handle("GET", "/api/me/totp", api.GetMyTOTP)
router.Handle("POST", "/api/me/totp/setup", api.SetupMyTOTP)
router.Handle("POST", "/api/me/totp/enable", api.EnableMyTOTP)
router.Handle("POST", "/api/me/totp/verify", api.VerifyMyTOTP)
router.Handle("POST", "/api/me/totp/disable", api.DisableMyTOTP)
router.Handle("GET", "/api/app-info", txRead(api.AppInfo))
router.Handle("POST", "/api/login", txWrite(api.Login))
router.Handle("POST", "/api/logout", txWrite(api.Logout))
router.Handle("GET", "/api/auth/providers", txRead(api.ListPublicAuthProviders))
router.Handle("GET", "/api/auth/oidc/:id/login", txRead(api.OIDCLoginWithProvider))
router.Handle("GET", "/api/auth/oidc/:id/callback", txRead(api.OIDCCallbackWithProvider))
router.Handle("GET", "/api/me", txRead(api.Me))
router.Handle("PATCH", "/api/me", txWrite(api.UpdateMe))
router.Handle("POST", "/api/me/avatar", txWrite(api.UploadMyAvatar))
router.Handle("DELETE", "/api/me/avatar", txWrite(api.DeleteMyAvatar))
router.Handle("GET", "/api/me/totp", txRead(api.GetMyTOTP))
router.Handle("POST", "/api/me/totp/setup", txWrite(api.SetupMyTOTP))
router.Handle("POST", "/api/me/totp/enable", txWrite(api.EnableMyTOTP))
router.Handle("POST", "/api/me/totp/verify", txWrite(api.VerifyMyTOTP))
router.Handle("POST", "/api/me/totp/disable", txWrite(api.DisableMyTOTP))
router.Handle("GET", "/api/users", api.ListUsers)
router.Handle("GET", "/api/users/:id/avatar", api.GetUserAvatar)
router.Handle("POST", "/api/users", api.CreateUser)
router.Handle("PATCH", "/api/users/:id", api.UpdateUser)
router.Handle("DELETE", "/api/users/:id", api.DeleteUser)
router.Handle("POST", "/api/users/:id/disable", api.DisableUser)
router.Handle("POST", "/api/users/:id/enable", api.EnableUser)
router.Handle("POST", "/api/users/:id/totp/reset", api.ResetUserTOTPAdmin)
router.Handle("GET", "/api/admin/user-groups", api.ListUserGroups)
router.Handle("POST", "/api/admin/user-groups", api.CreateUserGroup)
router.Handle("PATCH", "/api/admin/user-groups/:id", api.UpdateUserGroup)
router.Handle("DELETE", "/api/admin/user-groups/:id", api.DeleteUserGroup)
router.Handle("GET", "/api/admin/user-groups/:id/members", api.ListUserGroupMembers)
router.Handle("POST", "/api/admin/user-groups/:id/members", api.AddUserGroupMember)
router.Handle("DELETE", "/api/admin/user-groups/:id/members/:userId", api.RemoveUserGroupMember)
router.Handle("GET", "/api/admin/permissions", api.ListSubjectPermissions)
router.Handle("POST", "/api/admin/permissions", api.CreateSubjectPermissions)
router.Handle("DELETE", "/api/admin/permissions/:permission/:subjectType/:subjectID", api.DeleteSubjectPermission)
router.Handle("GET", "/api/admin/auth-providers", api.ListAuthProviders)
router.Handle("POST", "/api/admin/auth-providers", api.CreateAuthProvider)
router.Handle("GET", "/api/admin/auth-providers/:id", api.GetAuthProvider)
router.Handle("PUT", "/api/admin/auth-providers/:id", api.UpdateAuthProvider)
router.Handle("DELETE", "/api/admin/auth-providers/:id", api.DeleteAuthProvider)
router.Handle("POST", "/api/admin/auth-providers/:id/test", api.TestAuthProvider)
router.Handle("GET", "/api/admin/tls", api.GetTLSSettings)
router.Handle("PATCH", "/api/admin/tls", api.UpdateTLSSettings)
router.Handle("GET", "/api/admin/tls/auth-policies", api.ListTLSAuthPolicies)
router.Handle("POST", "/api/admin/tls/auth-policies", api.CreateTLSAuthPolicy)
router.Handle("PATCH", "/api/admin/tls/auth-policies/:id", api.UpdateTLSAuthPolicy)
router.Handle("DELETE", "/api/admin/tls/auth-policies/:id", api.DeleteTLSAuthPolicy)
router.Handle("GET", "/api/admin/tls/listeners", api.ListTLSListeners)
router.Handle("GET", "/api/admin/tls/listeners/runtime", api.GetTLSListenerRuntimeStatus)
router.Handle("POST", "/api/admin/tls/listeners", api.CreateTLSListener)
router.Handle("PATCH", "/api/admin/tls/listeners/:id", api.UpdateTLSListener)
router.Handle("DELETE", "/api/admin/tls/listeners/:id", api.DeleteTLSListener)
router.Handle("GET", "/api/admin/service-principals", api.ListServicePrincipals)
router.Handle("POST", "/api/admin/service-principals", api.CreateServicePrincipal)
router.Handle("PATCH", "/api/admin/service-principals/:id", api.UpdateServicePrincipal)
router.Handle("DELETE", "/api/admin/service-principals/:id", api.DeleteServicePrincipal)
router.Handle("GET", "/api/admin/service-principals/:id/api-keys", api.ListServicePrincipalAPIKeys)
router.Handle("POST", "/api/admin/service-principals/:id/api-keys", api.CreateServicePrincipalAPIKey)
router.Handle("DELETE", "/api/admin/service-principals/:id/api-keys/:keyId", api.DeleteServicePrincipalAPIKey)
router.Handle("POST", "/api/admin/service-principals/:id/api-keys/:keyId/disable", api.DisableServicePrincipalAPIKey)
router.Handle("POST", "/api/admin/service-principals/:id/api-keys/:keyId/enable", api.EnableServicePrincipalAPIKey)
router.Handle("GET", "/api/admin/service-principals/:id/roles", api.ListPrincipalProjectRoles)
router.Handle("POST", "/api/admin/service-principals/:id/roles", api.UpsertPrincipalProjectRole)
router.Handle("DELETE", "/api/admin/service-principals/:id/roles/:projectId", api.DeletePrincipalProjectRole)
router.Handle("GET", "/api/admin/cert-principal-bindings", api.ListCertPrincipalBindings)
router.Handle("POST", "/api/admin/cert-principal-bindings", api.UpsertCertPrincipalBinding)
router.Handle("DELETE", "/api/admin/cert-principal-bindings/:fingerprint", api.DeleteCertPrincipalBinding)
router.Handle("GET", "/api/users", txRead(api.ListUsers))
router.Handle("GET", "/api/users/:id/avatar", txRead(api.GetUserAvatar))
router.Handle("POST", "/api/users", txWrite(api.CreateUser))
router.Handle("PATCH", "/api/users/:id", txWrite(api.UpdateUser))
router.Handle("DELETE", "/api/users/:id", txWrite(api.DeleteUser))
router.Handle("POST", "/api/users/:id/disable", txWrite(api.DisableUser))
router.Handle("POST", "/api/users/:id/enable", txWrite(api.EnableUser))
router.Handle("POST", "/api/users/:id/totp/reset", txWrite(api.ResetUserTOTPAdmin))
router.Handle("GET", "/api/admin/user-groups", txRead(api.ListUserGroups))
router.Handle("POST", "/api/admin/user-groups", txWrite(api.CreateUserGroup))
router.Handle("PATCH", "/api/admin/user-groups/:id", txWrite(api.UpdateUserGroup))
router.Handle("DELETE", "/api/admin/user-groups/:id", txWrite(api.DeleteUserGroup))
router.Handle("GET", "/api/admin/user-groups/:id/members", txRead(api.ListUserGroupMembers))
router.Handle("POST", "/api/admin/user-groups/:id/members", txWrite(api.AddUserGroupMember))
router.Handle("DELETE", "/api/admin/user-groups/:id/members/:userId", txWrite(api.RemoveUserGroupMember))
router.Handle("GET", "/api/admin/permissions", txRead(api.ListSubjectPermissions))
router.Handle("POST", "/api/admin/permissions", txWrite(api.CreateSubjectPermissions))
router.Handle("DELETE", "/api/admin/permissions/:permission/:subjectType/:subjectID", txWrite(api.DeleteSubjectPermission))
router.Handle("GET", "/api/admin/auth-providers", txRead(api.ListAuthProviders))
router.Handle("POST", "/api/admin/auth-providers", txWrite(api.CreateAuthProvider))
router.Handle("GET", "/api/admin/auth-providers/:id", txRead(api.GetAuthProvider))
router.Handle("PUT", "/api/admin/auth-providers/:id", txWrite(api.UpdateAuthProvider))
router.Handle("DELETE", "/api/admin/auth-providers/:id", txWrite(api.DeleteAuthProvider))
router.Handle("POST", "/api/admin/auth-providers/:id/test", txWrite(api.TestAuthProvider))
router.Handle("GET", "/api/admin/tls", txRead(api.GetTLSSettings))
router.Handle("PATCH", "/api/admin/tls", txWrite(api.UpdateTLSSettings))
router.Handle("GET", "/api/admin/tls/auth-policies", txRead(api.ListTLSAuthPolicies))
router.Handle("POST", "/api/admin/tls/auth-policies", txWrite(api.CreateTLSAuthPolicy))
router.Handle("PATCH", "/api/admin/tls/auth-policies/:id", txWrite(api.UpdateTLSAuthPolicy))
router.Handle("DELETE", "/api/admin/tls/auth-policies/:id", txWrite(api.DeleteTLSAuthPolicy))
router.Handle("GET", "/api/admin/tls/listeners", txRead(api.ListTLSListeners))
router.Handle("GET", "/api/admin/tls/listeners/runtime", txRead(api.GetTLSListenerRuntimeStatus))
router.Handle("POST", "/api/admin/tls/listeners", txWrite(api.CreateTLSListener))
router.Handle("PATCH", "/api/admin/tls/listeners/:id", txWrite(api.UpdateTLSListener))
router.Handle("DELETE", "/api/admin/tls/listeners/:id", txWrite(api.DeleteTLSListener))
router.Handle("GET", "/api/admin/service-principals", txRead(api.ListServicePrincipals))
router.Handle("POST", "/api/admin/service-principals", txWrite(api.CreateServicePrincipal))
router.Handle("PATCH", "/api/admin/service-principals/:id", txWrite(api.UpdateServicePrincipal))
router.Handle("DELETE", "/api/admin/service-principals/:id", txWrite(api.DeleteServicePrincipal))
router.Handle("GET", "/api/admin/service-principals/:id/api-keys", txRead(api.ListServicePrincipalAPIKeys))
router.Handle("POST", "/api/admin/service-principals/:id/api-keys", txWrite(api.CreateServicePrincipalAPIKey))
router.Handle("DELETE", "/api/admin/service-principals/:id/api-keys/:keyId", txWrite(api.DeleteServicePrincipalAPIKey))
router.Handle("POST", "/api/admin/service-principals/:id/api-keys/:keyId/disable", txWrite(api.DisableServicePrincipalAPIKey))
router.Handle("POST", "/api/admin/service-principals/:id/api-keys/:keyId/enable", txWrite(api.EnableServicePrincipalAPIKey))
router.Handle("GET", "/api/admin/service-principals/:id/roles", txRead(api.ListPrincipalProjectRoles))
router.Handle("POST", "/api/admin/service-principals/:id/roles", txWrite(api.UpsertPrincipalProjectRole))
router.Handle("DELETE", "/api/admin/service-principals/:id/roles/:projectId", txWrite(api.DeletePrincipalProjectRole))
router.Handle("GET", "/api/admin/cert-principal-bindings", txRead(api.ListCertPrincipalBindings))
router.Handle("POST", "/api/admin/cert-principal-bindings", txWrite(api.UpsertCertPrincipalBinding))
router.Handle("DELETE", "/api/admin/cert-principal-bindings/:fingerprint", txWrite(api.DeleteCertPrincipalBinding))
router.Handle("GET", "/api/admin/pki/cas", api.ListPKICAs)
router.Handle("GET", "/api/admin/pki/cas/:id", api.GetPKICA)
router.Handle("PATCH", "/api/admin/pki/cas/:id", api.UpdatePKICA)
router.Handle("GET", "/api/admin/pki/cas/:id/bundle", api.DownloadPKICABundle)
router.Handle("POST", "/api/admin/pki/cas/root", api.CreatePKIRootCA)
router.Handle("POST", "/api/admin/pki/cas/intermediate", api.CreatePKIIntermediateCA)
router.Handle("GET", "/api/admin/pki/cas/:id/crl", api.GetPKICRL)
router.Handle("DELETE", "/api/admin/pki/cas/:id", api.DeletePKICA)
router.Handle("GET", "/api/admin/pki/certs", api.ListPKICerts)
router.Handle("GET", "/api/admin/pki/certs/:id", api.GetPKICert)
router.Handle("GET", "/api/admin/pki/certs/:id/inspect", api.GetPKICertInspect)
router.Handle("POST", "/api/admin/pki/certs/:id/renew-acme", api.RenewPKICertWithACME)
router.Handle("GET", "/api/admin/pki/certs/:id/bundle", api.DownloadPKICertBundle)
router.Handle("POST", "/api/admin/pki/certs", api.IssuePKICert)
router.Handle("POST", "/api/admin/pki/certs/import", api.ImportPKICert)
router.Handle("POST", "/api/admin/pki/certs/:id/revoke", api.RevokePKICert)
router.Handle("DELETE", "/api/admin/pki/certs/:id", api.DeletePKICert)
router.Handle("GET", "/api/admin/pki/client/profiles", api.ListPKIClientProfiles)
router.Handle("POST", "/api/admin/pki/client/profiles", api.CreatePKIClientProfile)
router.Handle("PATCH", "/api/admin/pki/client/profiles/:id", api.UpdatePKIClientProfile)
router.Handle("DELETE", "/api/admin/pki/client/profiles/:id", api.DeletePKIClientProfile)
router.Handle("GET", "/api/admin/pki/acme/profiles", api.ListACMEProfiles)
router.Handle("POST", "/api/admin/pki/acme/profiles", api.CreateACMEProfile)
router.Handle("PATCH", "/api/admin/pki/acme/profiles/:id", api.UpdateACMEProfile)
router.Handle("DELETE", "/api/admin/pki/acme/profiles/:id", api.DeleteACMEProfile)
router.Handle("GET", "/api/admin/pki/acme/orders", api.ListACMEOrders)
router.Handle("POST", "/api/admin/pki/acme/orders", api.CreateACMEOrder)
router.Handle("POST", "/api/admin/pki/acme/orders/:id/finalize", api.FinalizeACMEOrder)
router.Handle("DELETE", "/api/admin/pki/acme/orders/:id", api.DeleteACMEOrder)
router.Handle("GET", "/api/admin/rpm/mirrors", api.ListAdminRPMMirrors)
router.Handle("GET", "/api/admin/ssh/user-cas", api.ListSSHUserCAs)
router.Handle("POST", "/api/admin/ssh/user-cas", api.CreateSSHUserCA)
router.Handle("GET", "/api/admin/ssh/user-cas/:id", api.GetSSHUserCA)
router.Handle("PATCH", "/api/admin/ssh/user-cas/:id", api.UpdateSSHUserCA)
router.Handle("DELETE", "/api/admin/ssh/user-cas/:id", api.DeleteSSHUserCA)
router.Handle("GET", "/api/admin/ssh/user-cas/:id/public-key", api.DownloadSSHUserCAPublicKey)
router.Handle("GET", "/api/admin/ssh/user-cas/:id/private-key", api.DownloadSSHUserCAPrivateKey)
router.Handle("POST", "/api/admin/ssh/user-cas/:id/sign", api.SignSSHUserKey)
router.Handle("GET", "/api/admin/ssh/user-cas/:id/issuances", api.ListSSHUserCAIssuances)
router.Handle("GET", "/api/admin/ssh/issuances", api.ListSSHUserCAIssuancesAll)
router.Handle("GET", "/api/admin/ssh/principal-grants", api.ListSSHPrincipalGrants)
router.Handle("GET", "/api/admin/ssh/principal-grants/:id", api.GetSSHPrincipalGrant)
router.Handle("POST", "/api/admin/ssh/principal-grants", api.CreateSSHPrincipalGrant)
router.Handle("PATCH", "/api/admin/ssh/principal-grants/:id", api.UpdateSSHPrincipalGrant)
router.Handle("DELETE", "/api/admin/ssh/principal-grants/:id", api.DeleteSSHPrincipalGrant)
router.Handle("GET", "/api/admin/ssh/servers", api.ListSSHServersAdmin)
router.Handle("POST", "/api/admin/ssh/servers", api.CreateSSHServerAdmin)
router.Handle("GET", "/api/admin/ssh/servers/:id", api.GetSSHServerAdmin)
router.Handle("PATCH", "/api/admin/ssh/servers/:id", api.UpdateSSHServerAdmin)
router.Handle("DELETE", "/api/admin/ssh/servers/:id", api.DeleteSSHServerAdmin)
router.Handle("GET", "/api/admin/ssh/servers/:id/host-keys", api.ListSSHServerHostKeysAdmin)
router.Handle("POST", "/api/admin/ssh/servers/:id/host-keys", api.CreateSSHServerHostKeyAdmin)
router.Handle("POST", "/api/admin/ssh/servers/:id/host-keys/discover", api.DiscoverSSHServerHostKeyAdmin)
router.Handle("DELETE", "/api/admin/ssh/servers/:id/host-keys/:hostKeyId", api.DeleteSSHServerHostKeyAdmin)
router.Handle("GET", "/api/admin/ssh/server-groups", api.ListSSHServerGroupsAdmin)
router.Handle("POST", "/api/admin/ssh/server-groups", api.CreateSSHServerGroupAdmin)
router.Handle("PATCH", "/api/admin/ssh/server-groups/:id", api.UpdateSSHServerGroupAdmin)
router.Handle("DELETE", "/api/admin/ssh/server-groups/:id", api.DeleteSSHServerGroupAdmin)
router.Handle("GET", "/api/admin/ssh/server-groups/:id/servers", api.ListSSHServerGroupMembersAdmin)
router.Handle("POST", "/api/admin/ssh/server-groups/:id/servers", api.AddSSHServerGroupMemberAdmin)
router.Handle("DELETE", "/api/admin/ssh/server-groups/:id/servers/:serverId", api.DeleteSSHServerGroupMemberAdmin)
router.Handle("GET", "/api/admin/ssh/credentials", api.ListSSHCredentialsAdmin)
router.Handle("POST", "/api/admin/ssh/credentials", api.CreateSSHCredentialAdmin)
router.Handle("PATCH", "/api/admin/ssh/credentials/:id", api.UpdateSSHCredentialAdmin)
router.Handle("DELETE", "/api/admin/ssh/credentials/:id", api.DeleteSSHCredentialAdmin)
router.Handle("GET", "/api/admin/ssh/access-profiles", api.ListSSHAccessProfilesAdmin)
router.Handle("POST", "/api/admin/ssh/access-profiles", api.CreateSSHAccessProfileAdmin)
router.Handle("GET", "/api/admin/ssh/access-profiles/:id", api.GetSSHAccessProfileAdmin)
router.Handle("PATCH", "/api/admin/ssh/access-profiles/:id", api.UpdateSSHAccessProfileAdmin)
router.Handle("DELETE", "/api/admin/ssh/access-profiles/:id", api.DeleteSSHAccessProfileAdmin)
router.Handle("GET", "/api/admin/ssh/sessions", api.ListSSHSessionsAdmin)
router.Handle("GET", "/api/admin/ssh/file-transfers", api.ListSSHFileTransfersAdmin)
router.Handle("GET", "/api/admin/ssh/sessions/:id/transcript", api.GetSSHSessionTranscriptAdmin)
router.Handle("GET", "/api/ssh/user-cas", api.ListSSHUserCAsForSelf)
router.Handle("GET", "/api/ssh/user-cas/:id", api.GetSSHUserCAForSelf)
router.Handle("GET", "/api/ssh/user-cas/:id/public-key", api.DownloadSSHUserCAPublicKeyForSelf)
router.Handle("GET", "/api/ssh/principal-grants", api.ListSSHPrincipalGrantsForSelf)
router.Handle("GET", "/api/ssh/servers", api.ListSSHServersForSelf)
router.Handle("GET", "/api/ssh/servers/:id", api.GetSSHServerForSelf)
router.Handle("POST", "/api/ssh/servers", api.CreateSSHServerForSelf)
router.Handle("PATCH", "/api/ssh/servers/:id", api.UpdateSSHServerForSelf)
router.Handle("DELETE", "/api/ssh/servers/:id", api.DeleteSSHServerForSelf)
router.Handle("GET", "/api/ssh/servers/:id/host-keys", api.ListSSHServerHostKeysForSelf)
router.Handle("POST", "/api/ssh/servers/:id/host-keys", api.CreateSSHServerHostKeyForSelf)
router.Handle("POST", "/api/ssh/servers/:id/host-keys/discover", api.DiscoverSSHServerHostKeyForSelf)
router.Handle("DELETE", "/api/ssh/servers/:id/host-keys/:hostKeyId", api.DeleteSSHServerHostKeyForSelf)
router.Handle("GET", "/api/ssh/credentials", api.ListSSHCredentialsForSelf)
router.Handle("POST", "/api/ssh/credentials", api.CreateSSHCredentialForSelf)
router.Handle("PATCH", "/api/ssh/credentials/:id", api.UpdateSSHCredentialForSelf)
router.Handle("DELETE", "/api/ssh/credentials/:id", api.DeleteSSHCredentialForSelf)
router.Handle("GET", "/api/admin/pki/cas", txRead(api.ListPKICAs))
router.Handle("GET", "/api/admin/pki/cas/:id", txRead(api.GetPKICA))
router.Handle("PATCH", "/api/admin/pki/cas/:id", txWrite(api.UpdatePKICA))
router.Handle("GET", "/api/admin/pki/cas/:id/bundle", txRead(api.DownloadPKICABundle))
router.Handle("POST", "/api/admin/pki/cas/root", txWrite(api.CreatePKIRootCA))
router.Handle("POST", "/api/admin/pki/cas/intermediate", txWrite(api.CreatePKIIntermediateCA))
router.Handle("GET", "/api/admin/pki/cas/:id/crl", txRead(api.GetPKICRL))
router.Handle("DELETE", "/api/admin/pki/cas/:id", txWrite(api.DeletePKICA))
router.Handle("GET", "/api/admin/pki/certs", txRead(api.ListPKICerts))
router.Handle("GET", "/api/admin/pki/certs/:id", txRead(api.GetPKICert))
router.Handle("GET", "/api/admin/pki/certs/:id/inspect", txRead(api.GetPKICertInspect))
router.Handle("POST", "/api/admin/pki/certs/:id/renew-acme", txWrite(api.RenewPKICertWithACME))
router.Handle("GET", "/api/admin/pki/certs/:id/bundle", txRead(api.DownloadPKICertBundle))
router.Handle("POST", "/api/admin/pki/certs", txWrite(api.IssuePKICert))
router.Handle("POST", "/api/admin/pki/certs/import", txWrite(api.ImportPKICert))
router.Handle("POST", "/api/admin/pki/certs/:id/revoke", txWrite(api.RevokePKICert))
router.Handle("DELETE", "/api/admin/pki/certs/:id", txWrite(api.DeletePKICert))
router.Handle("GET", "/api/admin/pki/client/profiles", txRead(api.ListPKIClientProfiles))
router.Handle("POST", "/api/admin/pki/client/profiles", txWrite(api.CreatePKIClientProfile))
router.Handle("PATCH", "/api/admin/pki/client/profiles/:id", txWrite(api.UpdatePKIClientProfile))
router.Handle("DELETE", "/api/admin/pki/client/profiles/:id", txWrite(api.DeletePKIClientProfile))
router.Handle("GET", "/api/admin/pki/acme/profiles", txRead(api.ListACMEProfiles))
router.Handle("POST", "/api/admin/pki/acme/profiles", txWrite(api.CreateACMEProfile))
router.Handle("PATCH", "/api/admin/pki/acme/profiles/:id", txWrite(api.UpdateACMEProfile))
router.Handle("DELETE", "/api/admin/pki/acme/profiles/:id", txWrite(api.DeleteACMEProfile))
router.Handle("GET", "/api/admin/pki/acme/orders", txRead(api.ListACMEOrders))
router.Handle("POST", "/api/admin/pki/acme/orders", txWrite(api.CreateACMEOrder))
router.Handle("POST", "/api/admin/pki/acme/orders/:id/finalize", txWrite(api.FinalizeACMEOrder))
router.Handle("DELETE", "/api/admin/pki/acme/orders/:id", txWrite(api.DeleteACMEOrder))
router.Handle("GET", "/api/admin/rpm/mirrors", txRead(api.ListAdminRPMMirrors))
router.Handle("GET", "/api/admin/ssh/user-cas", txRead(api.ListSSHUserCAs))
router.Handle("POST", "/api/admin/ssh/user-cas", txWrite(api.CreateSSHUserCA))
router.Handle("GET", "/api/admin/ssh/user-cas/:id", txRead(api.GetSSHUserCA))
router.Handle("PATCH", "/api/admin/ssh/user-cas/:id", txWrite(api.UpdateSSHUserCA))
router.Handle("DELETE", "/api/admin/ssh/user-cas/:id", txWrite(api.DeleteSSHUserCA))
router.Handle("GET", "/api/admin/ssh/user-cas/:id/public-key", txRead(api.DownloadSSHUserCAPublicKey))
router.Handle("GET", "/api/admin/ssh/user-cas/:id/private-key", txRead(api.DownloadSSHUserCAPrivateKey))
router.Handle("POST", "/api/admin/ssh/user-cas/:id/sign", txWrite(api.SignSSHUserKey))
router.Handle("GET", "/api/admin/ssh/user-cas/:id/issuances", txRead(api.ListSSHUserCAIssuances))
router.Handle("GET", "/api/admin/ssh/issuances", txRead(api.ListSSHUserCAIssuancesAll))
router.Handle("GET", "/api/admin/ssh/principal-grants", txRead(api.ListSSHPrincipalGrants))
router.Handle("GET", "/api/admin/ssh/principal-grants/:id", txRead(api.GetSSHPrincipalGrant))
router.Handle("POST", "/api/admin/ssh/principal-grants", txWrite(api.CreateSSHPrincipalGrant))
router.Handle("PATCH", "/api/admin/ssh/principal-grants/:id", txWrite(api.UpdateSSHPrincipalGrant))
router.Handle("DELETE", "/api/admin/ssh/principal-grants/:id", txWrite(api.DeleteSSHPrincipalGrant))
router.Handle("GET", "/api/admin/ssh/servers", txRead(api.ListSSHServersAdmin))
router.Handle("POST", "/api/admin/ssh/servers", txWrite(api.CreateSSHServerAdmin))
router.Handle("GET", "/api/admin/ssh/servers/:id", txRead(api.GetSSHServerAdmin))
router.Handle("PATCH", "/api/admin/ssh/servers/:id", txWrite(api.UpdateSSHServerAdmin))
router.Handle("DELETE", "/api/admin/ssh/servers/:id", txWrite(api.DeleteSSHServerAdmin))
router.Handle("GET", "/api/admin/ssh/servers/:id/host-keys", txRead(api.ListSSHServerHostKeysAdmin))
router.Handle("POST", "/api/admin/ssh/servers/:id/host-keys", txWrite(api.CreateSSHServerHostKeyAdmin))
router.Handle("POST", "/api/admin/ssh/servers/:id/host-keys/discover", txWrite(api.DiscoverSSHServerHostKeyAdmin))
router.Handle("DELETE", "/api/admin/ssh/servers/:id/host-keys/:hostKeyId", txWrite(api.DeleteSSHServerHostKeyAdmin))
router.Handle("GET", "/api/admin/ssh/server-groups", txRead(api.ListSSHServerGroupsAdmin))
router.Handle("POST", "/api/admin/ssh/server-groups", txWrite(api.CreateSSHServerGroupAdmin))
router.Handle("PATCH", "/api/admin/ssh/server-groups/:id", txWrite(api.UpdateSSHServerGroupAdmin))
router.Handle("DELETE", "/api/admin/ssh/server-groups/:id", txWrite(api.DeleteSSHServerGroupAdmin))
router.Handle("GET", "/api/admin/ssh/server-groups/:id/servers", txRead(api.ListSSHServerGroupMembersAdmin))
router.Handle("POST", "/api/admin/ssh/server-groups/:id/servers", txWrite(api.AddSSHServerGroupMemberAdmin))
router.Handle("DELETE", "/api/admin/ssh/server-groups/:id/servers/:serverId", txWrite(api.DeleteSSHServerGroupMemberAdmin))
router.Handle("GET", "/api/admin/ssh/credentials", txRead(api.ListSSHCredentialsAdmin))
router.Handle("POST", "/api/admin/ssh/credentials", txWrite(api.CreateSSHCredentialAdmin))
router.Handle("PATCH", "/api/admin/ssh/credentials/:id", txWrite(api.UpdateSSHCredentialAdmin))
router.Handle("DELETE", "/api/admin/ssh/credentials/:id", txWrite(api.DeleteSSHCredentialAdmin))
router.Handle("GET", "/api/admin/ssh/access-profiles", txRead(api.ListSSHAccessProfilesAdmin))
router.Handle("POST", "/api/admin/ssh/access-profiles", txWrite(api.CreateSSHAccessProfileAdmin))
router.Handle("GET", "/api/admin/ssh/access-profiles/:id", txRead(api.GetSSHAccessProfileAdmin))
router.Handle("PATCH", "/api/admin/ssh/access-profiles/:id", txWrite(api.UpdateSSHAccessProfileAdmin))
router.Handle("DELETE", "/api/admin/ssh/access-profiles/:id", txWrite(api.DeleteSSHAccessProfileAdmin))
router.Handle("GET", "/api/admin/ssh/sessions", txRead(api.ListSSHSessionsAdmin))
router.Handle("GET", "/api/admin/ssh/file-transfers", txRead(api.ListSSHFileTransfersAdmin))
router.Handle("GET", "/api/admin/ssh/sessions/:id/transcript", txRead(api.GetSSHSessionTranscriptAdmin))
router.Handle("GET", "/api/ssh/user-cas", txRead(api.ListSSHUserCAsForSelf))
router.Handle("GET", "/api/ssh/user-cas/:id", txRead(api.GetSSHUserCAForSelf))
router.Handle("GET", "/api/ssh/user-cas/:id/public-key", txRead(api.DownloadSSHUserCAPublicKeyForSelf))
router.Handle("GET", "/api/ssh/principal-grants", txRead(api.ListSSHPrincipalGrantsForSelf))
router.Handle("GET", "/api/ssh/servers", txRead(api.ListSSHServersForSelf))
router.Handle("GET", "/api/ssh/servers/:id", txRead(api.GetSSHServerForSelf))
router.Handle("POST", "/api/ssh/servers", txWrite(api.CreateSSHServerForSelf))
router.Handle("PATCH", "/api/ssh/servers/:id", txWrite(api.UpdateSSHServerForSelf))
router.Handle("DELETE", "/api/ssh/servers/:id", txWrite(api.DeleteSSHServerForSelf))
router.Handle("GET", "/api/ssh/servers/:id/host-keys", txRead(api.ListSSHServerHostKeysForSelf))
router.Handle("POST", "/api/ssh/servers/:id/host-keys", txWrite(api.CreateSSHServerHostKeyForSelf))
router.Handle("POST", "/api/ssh/servers/:id/host-keys/discover", txWrite(api.DiscoverSSHServerHostKeyForSelf))
router.Handle("DELETE", "/api/ssh/servers/:id/host-keys/:hostKeyId", txWrite(api.DeleteSSHServerHostKeyForSelf))
router.Handle("GET", "/api/ssh/credentials", txRead(api.ListSSHCredentialsForSelf))
router.Handle("POST", "/api/ssh/credentials", txWrite(api.CreateSSHCredentialForSelf))
router.Handle("PATCH", "/api/ssh/credentials/:id", txWrite(api.UpdateSSHCredentialForSelf))
router.Handle("DELETE", "/api/ssh/credentials/:id", txWrite(api.DeleteSSHCredentialForSelf))
router.Handle("GET", "/api/ssh/access-profiles", api.ListSSHAccessProfilesForSelf)
router.Handle("POST", "/api/ssh/access-profiles", api.CreateSSHAccessProfileForSelf)
router.Handle("GET", "/api/ssh/access-profiles/:id", api.GetSSHAccessProfileForSelf)
router.Handle("GET", "/api/ssh/access-profiles/:id/credential", api.GetSSHAccessProfileCredentialForSelf)
router.Handle("GET", "/api/ssh/access-profiles/:id/servers", api.ListSSHAccessProfileServersForSelf)
router.Handle("GET", "/api/ssh/access-profiles/:id/servers/:serverId", api.GetSSHAccessProfileServerForSelf)
router.Handle("GET", "/api/ssh/access-profiles/:id/server-groups/:serverGroupId/servers", api.ListSSHAccessProfileServerGroupMembersForSelf)
router.Handle("GET", "/api/ssh/access-profiles", txRead(api.ListSSHAccessProfilesForSelf))
router.Handle("POST", "/api/ssh/access-profiles", txWrite(api.CreateSSHAccessProfileForSelf))
router.Handle("GET", "/api/ssh/access-profiles/:id", txRead(api.GetSSHAccessProfileForSelf))
router.Handle("GET", "/api/ssh/access-profiles/:id/credential", txRead(api.GetSSHAccessProfileCredentialForSelf))
router.Handle("GET", "/api/ssh/access-profiles/:id/servers", txRead(api.ListSSHAccessProfileServersForSelf))
router.Handle("GET", "/api/ssh/access-profiles/:id/servers/:serverId", txRead(api.GetSSHAccessProfileServerForSelf))
router.Handle("GET", "/api/ssh/access-profiles/:id/server-groups/:serverGroupId/servers", txRead(api.ListSSHAccessProfileServerGroupMembersForSelf))
router.Handle("PATCH", "/api/ssh/access-profiles/:id", api.UpdateSSHAccessProfileForSelf)
router.Handle("DELETE", "/api/ssh/access-profiles/:id", api.DeleteSSHAccessProfileForSelf)
router.Handle("POST", "/api/ssh/access-profiles/:id/connect", api.CreateSSHSessionForSelf)
router.Handle("GET", "/api/ssh/sessions", api.ListSSHSessionsForSelf)
router.Handle("GET", "/api/ssh/file-transfers", api.ListSSHFileTransfersForSelf)
router.Handle("GET", "/api/ssh/stream", api.StreamSSHWorkspaceForSelf)
router.Handle("GET", "/api/ssh/sessions/:id", api.GetSSHSessionForSelf)
router.Handle("GET", "/api/ssh/sessions/:id/transcript", api.GetSSHSessionTranscriptForSelf)
router.Handle("POST", "/api/ssh/sessions/:id/disconnect", api.DisconnectSSHSessionForSelf)
router.Handle("POST", "/api/ssh/sessions/:id/files/upload", api.UploadSSHSessionFilesForSelf)
router.Handle("POST", "/api/ssh/sessions/:id/files/download", api.DownloadSSHSessionFilesForSelf)
router.Handle("POST", "/api/ssh/sessions/:id/files/copy-to", api.CopySSHSessionFilesForSelf)
router.Handle("POST", "/api/ssh/cert/inspect", api.InspectSSHCertificate)
router.Handle("POST", "/api/ssh/user-cas/:id/sign", api.SignSSHUserKeyForSelf)
router.Handle("GET", "/api/ssh/issuances", api.ListSSHUserCAIssuancesForSelf)
router.Handle("GET", "/api/pki/cas/:id/crl", api.GetPKICRLForSelf)
router.Handle("GET", "/api/pki/cas/:id", api.GetPKICAForSelf)
router.Handle("GET", "/api/pki/client/profiles", api.ListPKIClientProfilesForSelf)
router.Handle("GET", "/api/pki/client/issuances", api.ListPKIClientIssuancesForSelf)
router.Handle("POST", "/api/pki/client/certs", api.IssuePKIClientCertForSelf)
router.Handle("GET", "/api/pki/client/certs/:id", api.GetPKIClientCertForSelf)
router.Handle("GET", "/api/pki/client/certs/:id/inspect", api.GetPKIClientCertInspectForSelf)
router.Handle("GET", "/api/pki/client/certs/:id/bundle", api.DownloadPKIClientCertBundleForSelf)
router.Handle("POST", "/api/pki/client/certs/:id/revoke", api.RevokePKIClientCertForSelf)
router.Handle("PATCH", "/api/ssh/access-profiles/:id", txWrite(api.UpdateSSHAccessProfileForSelf))
router.Handle("DELETE", "/api/ssh/access-profiles/:id", txWrite(api.DeleteSSHAccessProfileForSelf))
router.Handle("POST", "/api/ssh/access-profiles/:id/connect", txWrite(api.CreateSSHSessionForSelf))
router.Handle("GET", "/api/ssh/sessions", txRead(api.ListSSHSessionsForSelf))
router.Handle("GET", "/api/ssh/file-transfers", txRead(api.ListSSHFileTransfersForSelf))
router.Handle("GET", "/api/ssh/stream", txRead(api.StreamSSHWorkspaceForSelf))
router.Handle("GET", "/api/ssh/sessions/:id", txRead(api.GetSSHSessionForSelf))
router.Handle("GET", "/api/ssh/sessions/:id/transcript", txRead(api.GetSSHSessionTranscriptForSelf))
router.Handle("POST", "/api/ssh/sessions/:id/disconnect", txWrite(api.DisconnectSSHSessionForSelf))
router.Handle("POST", "/api/ssh/sessions/:id/files/upload", txWrite(api.UploadSSHSessionFilesForSelf))
router.Handle("POST", "/api/ssh/sessions/:id/files/download", txWrite(api.DownloadSSHSessionFilesForSelf))
router.Handle("POST", "/api/ssh/sessions/:id/files/copy-to", txWrite(api.CopySSHSessionFilesForSelf))
router.Handle("POST", "/api/ssh/cert/inspect", txWrite(api.InspectSSHCertificate))
router.Handle("POST", "/api/ssh/user-cas/:id/sign", txWrite(api.SignSSHUserKeyForSelf))
router.Handle("GET", "/api/ssh/issuances", txRead(api.ListSSHUserCAIssuancesForSelf))
router.Handle("GET", "/api/pki/cas/:id/crl", txRead(api.GetPKICRLForSelf))
router.Handle("GET", "/api/pki/cas/:id", txRead(api.GetPKICAForSelf))
router.Handle("GET", "/api/pki/client/profiles", txRead(api.ListPKIClientProfilesForSelf))
router.Handle("GET", "/api/pki/client/issuances", txRead(api.ListPKIClientIssuancesForSelf))
router.Handle("POST", "/api/pki/client/certs", txWrite(api.IssuePKIClientCertForSelf))
router.Handle("GET", "/api/pki/client/certs/:id", txRead(api.GetPKIClientCertForSelf))
router.Handle("GET", "/api/pki/client/certs/:id/inspect", txRead(api.GetPKIClientCertInspectForSelf))
router.Handle("GET", "/api/pki/client/certs/:id/bundle", txRead(api.DownloadPKIClientCertBundleForSelf))
router.Handle("POST", "/api/pki/client/certs/:id/revoke", txWrite(api.RevokePKIClientCertForSelf))
router.Handle("GET", "/api/projects", api.ListProjects)
router.Handle("POST", "/api/projects", api.CreateProject)
router.Handle("GET", "/api/projects/:id", api.GetProject)
router.Handle("PATCH", "/api/projects/:id", api.UpdateProject)
router.Handle("DELETE", "/api/projects/:id", api.DeleteProject)
router.Handle("GET", "/api/projects", txRead(api.ListProjects))
router.Handle("POST", "/api/projects", txWrite(api.CreateProject))
router.Handle("GET", "/api/projects/:id", txRead(api.GetProject))
router.Handle("PATCH", "/api/projects/:id", txWrite(api.UpdateProject))
router.Handle("DELETE", "/api/projects/:id", txWrite(api.DeleteProject))
router.Handle("GET", "/api/projects/:projectId/members", api.ListProjectMembers)
router.Handle("GET", "/api/projects/:projectId/member-candidates", api.ListProjectMemberCandidates)
router.Handle("GET", "/api/projects/:projectId/group-candidates", api.ListProjectGroupCandidates)
router.Handle("POST", "/api/projects/:projectId/members", api.AddProjectMember)
router.Handle("PATCH", "/api/projects/:projectId/members", api.UpdateProjectMember)
router.Handle("DELETE", "/api/projects/:projectId/members/:userId", api.RemoveProjectMember)
router.Handle("GET", "/api/projects/:projectId/group-roles", api.ListProjectGroupRoles)
router.Handle("POST", "/api/projects/:projectId/group-roles", api.UpsertProjectGroupRole)
router.Handle("DELETE", "/api/projects/:projectId/group-roles/:groupId", api.RemoveProjectGroupRole)
router.Handle("GET", "/api/projects/:projectId/members", txRead(api.ListProjectMembers))
router.Handle("GET", "/api/projects/:projectId/member-candidates", txRead(api.ListProjectMemberCandidates))
router.Handle("GET", "/api/projects/:projectId/group-candidates", txRead(api.ListProjectGroupCandidates))
router.Handle("POST", "/api/projects/:projectId/members", txWrite(api.AddProjectMember))
router.Handle("PATCH", "/api/projects/:projectId/members", txWrite(api.UpdateProjectMember))
router.Handle("DELETE", "/api/projects/:projectId/members/:userId", txWrite(api.RemoveProjectMember))
router.Handle("GET", "/api/projects/:projectId/group-roles", txRead(api.ListProjectGroupRoles))
router.Handle("POST", "/api/projects/:projectId/group-roles", txWrite(api.UpsertProjectGroupRole))
router.Handle("DELETE", "/api/projects/:projectId/group-roles/:groupId", txWrite(api.RemoveProjectGroupRole))
router.Handle("GET", "/api/projects/:projectId/boards", api.ListBoards)
router.Handle("POST", "/api/projects/:projectId/boards", api.CreateBoard)
router.Handle("GET", "/api/boards", api.ListAllBoards)
router.Handle("GET", "/api/boards/:boardId", api.GetBoard)
router.Handle("PATCH", "/api/boards/:boardId", api.UpdateBoard)
router.Handle("DELETE", "/api/boards/:boardId", api.DeleteBoard)
router.Handle("GET", "/api/boards/:boardId/blocks", api.ListBlocks)
router.Handle("POST", "/api/boards/:boardId/blocks", api.CreateBlock)
router.Handle("PATCH", "/api/boards/:boardId/blocks", api.PatchBlocks)
router.Handle("GET", "/api/boards/:boardId/blocks/:blockId", api.GetBlock)
router.Handle("PATCH", "/api/boards/:boardId/blocks/:blockId", api.UpdateBlock)
router.Handle("DELETE", "/api/boards/:boardId/blocks/:blockId", api.DeleteBlock)
router.Handle("GET", "/api/boards/:boardId/field-values/:field", api.ListBoardFieldValues)
router.Handle("POST", "/api/boards/:boardId/field-values/:field", api.CreateBoardFieldValue)
router.Handle("PUT", "/api/boards/:boardId/field-values/:field/reorder", api.ReorderBoardFieldValues)
router.Handle("PUT", "/api/boards/:boardId/field-values/:field/:valueId", api.UpdateBoardFieldValue)
router.Handle("DELETE", "/api/boards/:boardId/field-values/:field/:valueId", api.DeleteBoardFieldValue)
router.Handle("GET", "/api/boards/:boardId/block-properties", api.ListBoardBlockProperties)
router.Handle("GET", "/api/boards/:boardId/assignable-users", api.ListBoardAssignableUsers)
router.Handle("GET", "/api/boards/:boardId/blocks/:blockId/comments", api.ListBlockComments)
router.Handle("POST", "/api/boards/:boardId/blocks/:blockId/comments", api.CreateBlockComment)
router.Handle("DELETE", "/api/boards/:boardId/blocks/:blockId/comments/:commentId", api.DeleteBlockComment)
router.Handle("GET", "/api/boards/:boardId/blocks/:blockId/checklist", api.ListBlockChecklistItems)
router.Handle("POST", "/api/boards/:boardId/blocks/:blockId/checklist", api.CreateBlockChecklistItem)
router.Handle("PUT", "/api/boards/:boardId/blocks/:blockId/checklist/reorder", api.ReorderBlockChecklistItems)
router.Handle("PUT", "/api/boards/:boardId/blocks/:blockId/checklist/:itemId", api.UpdateBlockChecklistItem)
router.Handle("DELETE", "/api/boards/:boardId/blocks/:blockId/checklist/:itemId", api.DeleteBlockChecklistItem)
router.Handle("GET", "/api/boards/:boardId/blocks/:blockId/attachments", api.ListBlockAttachments)
router.Handle("POST", "/api/boards/:boardId/blocks/:blockId/attachments", api.CreateBlockAttachment)
router.Handle("GET", "/api/boards/:boardId/blocks/:blockId/attachments/:attachmentId/download", api.DownloadBlockAttachment)
router.Handle("DELETE", "/api/boards/:boardId/blocks/:blockId/attachments/:attachmentId", api.DeleteBlockAttachment)
router.Handle("GET", "/api/boards/:boardId/blocks/:blockId/properties", api.GetBlockProperties)
router.Handle("PUT", "/api/boards/:boardId/blocks/:blockId/properties", api.UpsertBlockProperties)
router.Handle("GET", "/api/boards/:boardId/members", api.ListBoardMembers)
router.Handle("POST", "/api/boards/:boardId/members", api.AddBoardMember)
router.Handle("PUT", "/api/boards/:boardId/members/:userId", api.UpdateBoardMember)
router.Handle("DELETE", "/api/boards/:boardId/members/:userId", api.RemoveBoardMember)
router.Handle("GET", "/api/projects/:projectId/boards", txRead(api.ListBoards))
router.Handle("POST", "/api/projects/:projectId/boards", txWrite(api.CreateBoard))
router.Handle("GET", "/api/boards", txRead(api.ListAllBoards))
router.Handle("GET", "/api/boards/:boardId", txRead(api.GetBoard))
router.Handle("PATCH", "/api/boards/:boardId", txWrite(api.UpdateBoard))
router.Handle("DELETE", "/api/boards/:boardId", txWrite(api.DeleteBoard))
router.Handle("GET", "/api/boards/:boardId/blocks", txRead(api.ListBlocks))
router.Handle("POST", "/api/boards/:boardId/blocks", txWrite(api.CreateBlock))
router.Handle("PATCH", "/api/boards/:boardId/blocks", txWrite(api.PatchBlocks))
router.Handle("GET", "/api/boards/:boardId/blocks/:blockId", txRead(api.GetBlock))
router.Handle("PATCH", "/api/boards/:boardId/blocks/:blockId", txWrite(api.UpdateBlock))
router.Handle("DELETE", "/api/boards/:boardId/blocks/:blockId", txWrite(api.DeleteBlock))
router.Handle("GET", "/api/boards/:boardId/field-values/:field", txRead(api.ListBoardFieldValues))
router.Handle("POST", "/api/boards/:boardId/field-values/:field", txWrite(api.CreateBoardFieldValue))
router.Handle("PUT", "/api/boards/:boardId/field-values/:field/reorder", txWrite(api.ReorderBoardFieldValues))
router.Handle("PUT", "/api/boards/:boardId/field-values/:field/:valueId", txWrite(api.UpdateBoardFieldValue))
router.Handle("DELETE", "/api/boards/:boardId/field-values/:field/:valueId", txWrite(api.DeleteBoardFieldValue))
router.Handle("GET", "/api/boards/:boardId/block-properties", txRead(api.ListBoardBlockProperties))
router.Handle("GET", "/api/boards/:boardId/assignable-users", txRead(api.ListBoardAssignableUsers))
router.Handle("GET", "/api/boards/:boardId/blocks/:blockId/comments", txRead(api.ListBlockComments))
router.Handle("POST", "/api/boards/:boardId/blocks/:blockId/comments", txWrite(api.CreateBlockComment))
router.Handle("DELETE", "/api/boards/:boardId/blocks/:blockId/comments/:commentId", txWrite(api.DeleteBlockComment))
router.Handle("GET", "/api/boards/:boardId/blocks/:blockId/checklist", txRead(api.ListBlockChecklistItems))
router.Handle("POST", "/api/boards/:boardId/blocks/:blockId/checklist", txWrite(api.CreateBlockChecklistItem))
router.Handle("PUT", "/api/boards/:boardId/blocks/:blockId/checklist/reorder", txWrite(api.ReorderBlockChecklistItems))
router.Handle("PUT", "/api/boards/:boardId/blocks/:blockId/checklist/:itemId", txWrite(api.UpdateBlockChecklistItem))
router.Handle("DELETE", "/api/boards/:boardId/blocks/:blockId/checklist/:itemId", txWrite(api.DeleteBlockChecklistItem))
router.Handle("GET", "/api/boards/:boardId/blocks/:blockId/attachments", txRead(api.ListBlockAttachments))
router.Handle("POST", "/api/boards/:boardId/blocks/:blockId/attachments", txWrite(api.CreateBlockAttachment))
router.Handle("GET", "/api/boards/:boardId/blocks/:blockId/attachments/:attachmentId/download", txRead(api.DownloadBlockAttachment))
router.Handle("DELETE", "/api/boards/:boardId/blocks/:blockId/attachments/:attachmentId", txWrite(api.DeleteBlockAttachment))
router.Handle("GET", "/api/boards/:boardId/blocks/:blockId/properties", txRead(api.GetBlockProperties))
router.Handle("PUT", "/api/boards/:boardId/blocks/:blockId/properties", txWrite(api.UpsertBlockProperties))
router.Handle("GET", "/api/boards/:boardId/members", txRead(api.ListBoardMembers))
router.Handle("POST", "/api/boards/:boardId/members", txWrite(api.AddBoardMember))
router.Handle("PUT", "/api/boards/:boardId/members/:userId", txWrite(api.UpdateBoardMember))
router.Handle("DELETE", "/api/boards/:boardId/members/:userId", txWrite(api.RemoveBoardMember))
router.Handle("GET", "/api/projects/:projectId/repos", api.ListRepos)
router.Handle("POST", "/api/projects/:projectId/repos", api.CreateRepo)
router.Handle("GET", "/api/repos/types", api.RepoTypes)
router.Handle("GET", "/api/repos", api.ListAllRepos)
router.Handle("GET", "/api/projects/:projectId/foreign-repos/available", api.ListAvailableRepos)
router.Handle("POST", "/api/projects/:projectId/foreign-repos", api.AttachForeignRepo)
router.Handle("DELETE", "/api/projects/:projectId/foreign-repos/:repoId", api.DetachForeignRepo)
router.Handle("GET", "/api/repos/:id", api.GetRepo)
router.Handle("PATCH", "/api/repos/:id", api.UpdateRepo)
router.Handle("DELETE", "/api/repos/:id", api.DeleteRepo)
router.Handle("GET", "/api/repos/:id/branches", api.RepoBranches)
router.Handle("GET", "/api/repos/:id/branches/info", api.RepoBranchesInfo)
router.Handle("PUT", "/api/repos/:id/branches/default", api.RepoSetDefaultBranch)
router.Handle("POST", "/api/repos/:id/branches/rename", api.RepoRenameBranch)
router.Handle("POST", "/api/repos/:id/branches/delete", api.RepoDeleteBranch)
router.Handle("POST", "/api/repos/:id/branches/create", api.RepoCreateBranch)
router.Handle("GET", "/api/repos/:id/commits", api.RepoCommits)
router.Handle("GET", "/api/repos/:id/tree", api.RepoTree)
router.Handle("GET", "/api/repos/:id/blob", api.RepoBlob)
router.Handle("GET", "/api/repos/:id/blob/raw", api.RepoBlobRaw)
router.Handle("GET", "/api/repos/:id/history", api.RepoFileHistory)
router.Handle("GET", "/api/repos/:id/diff", api.RepoFileDiff)
router.Handle("GET", "/api/repos/:id/commit", api.RepoCommitDetail)
router.Handle("GET", "/api/repos/:id/commit/diff", api.RepoCommitDiff)
router.Handle("GET", "/api/repos/:id/compare", api.RepoCompare)
router.Handle("GET", "/api/repos/:id/stats", api.RepoStats)
router.Handle("GET", "/api/repos/:id/rpm/packages", api.RepoRPMPackages)
router.Handle("GET", "/api/repos/:id/rpm/package", api.RepoRPMPackage)
router.Handle("POST", "/api/repos/:id/rpm/subdirs", api.RepoRPMCreateSubdir)
router.Handle("GET", "/api/repos/:id/rpm/subdir", api.RepoRPMGetSubdir)
router.Handle("POST", "/api/repos/:id/rpm/subdir/update", api.RepoRPMRenameSubdir)
router.Handle("POST", "/api/repos/:id/rpm/subdir/rename", api.RepoRPMRenameSubdir)
router.Handle("POST", "/api/repos/:id/rpm/subdir/sync", api.RepoRPMSyncSubdir)
router.Handle("POST", "/api/repos/:id/rpm/subdir/suspend", api.RepoRPMSuspendSubdir)
router.Handle("POST", "/api/repos/:id/rpm/subdir/resume", api.RepoRPMResumeSubdir)
router.Handle("POST", "/api/repos/:id/rpm/subdir/rebuild-metadata", api.RepoRPMRebuildSubdirMetadata)
router.Handle("POST", "/api/repos/:id/rpm/subdir/cancel", api.RepoRPMCancelSubdirSync)
router.Handle("GET", "/api/repos/:id/rpm/subdir/runs", api.RepoRPMMirrorRuns)
router.Handle("DELETE", "/api/repos/:id/rpm/subdir/runs", api.RepoRPMClearMirrorRuns)
router.Handle("DELETE", "/api/repos/:id/rpm/subdir", api.RepoRPMDeleteSubdir)
router.Handle("DELETE", "/api/repos/:id/rpm/file", api.RepoRPMDeleteFile)
router.Handle("GET", "/api/repos/:id/rpm/file", api.RepoRPMFile)
router.Handle("GET", "/api/repos/:id/rpm/tree", api.RepoRPMTree)
router.Handle("POST", "/api/repos/:id/rpm/upload", api.RepoRPMUpload)
router.Handle("GET", "/api/repos/:id/docker/images", api.RepoDockerImages)
router.Handle("GET", "/api/repos/:id/docker/tags", api.RepoDockerTags)
router.Handle("GET", "/api/repos/:id/docker/manifest", api.RepoDockerManifest)
router.Handle("DELETE", "/api/repos/:id/docker/tag", api.RepoDockerDeleteTag)
router.Handle("DELETE", "/api/repos/:id/docker/image", api.RepoDockerDeleteImage)
router.Handle("POST", "/api/repos/:id/docker/tag/rename", api.RepoDockerRenameTag)
router.Handle("POST", "/api/repos/:id/docker/image/rename", api.RepoDockerRenameImage)
router.Handle("GET", "/api/me/keys", api.ListAPIKeys)
router.Handle("POST", "/api/me/keys", api.CreateAPIKey)
router.Handle("DELETE", "/api/me/keys/:id", api.DeleteAPIKey)
router.Handle("POST", "/api/me/keys/:id/disable", api.DisableAPIKey)
router.Handle("POST", "/api/me/keys/:id/enable", api.EnableAPIKey)
router.Handle("GET", "/api/admin/api-keys", api.ListAdminAPIKeys)
router.Handle("DELETE", "/api/admin/api-keys/:id", api.DeleteAdminAPIKey)
router.Handle("POST", "/api/admin/api-keys/:id/disable", api.DisableAdminAPIKey)
router.Handle("POST", "/api/admin/api-keys/:id/enable", api.EnableAdminAPIKey)
router.Handle("GET", "/api/projects/:projectId/repos", txRead(api.ListRepos))
router.Handle("POST", "/api/projects/:projectId/repos", txWrite(api.CreateRepo))
router.Handle("GET", "/api/repos/types", txRead(api.RepoTypes))
router.Handle("GET", "/api/repos", txRead(api.ListAllRepos))
router.Handle("GET", "/api/projects/:projectId/foreign-repos/available", txRead(api.ListAvailableRepos))
router.Handle("POST", "/api/projects/:projectId/foreign-repos", txWrite(api.AttachForeignRepo))
router.Handle("DELETE", "/api/projects/:projectId/foreign-repos/:repoId", txWrite(api.DetachForeignRepo))
router.Handle("GET", "/api/repos/:id", txRead(api.GetRepo))
router.Handle("PATCH", "/api/repos/:id", txWrite(api.UpdateRepo))
router.Handle("DELETE", "/api/repos/:id", txWrite(api.DeleteRepo))
router.Handle("GET", "/api/repos/:id/branches", txRead(api.RepoBranches))
router.Handle("GET", "/api/repos/:id/branches/info", txRead(api.RepoBranchesInfo))
router.Handle("PUT", "/api/repos/:id/branches/default", txWrite(api.RepoSetDefaultBranch))
router.Handle("POST", "/api/repos/:id/branches/rename", txWrite(api.RepoRenameBranch))
router.Handle("POST", "/api/repos/:id/branches/delete", txWrite(api.RepoDeleteBranch))
router.Handle("POST", "/api/repos/:id/branches/create", txWrite(api.RepoCreateBranch))
router.Handle("GET", "/api/repos/:id/commits", txRead(api.RepoCommits))
router.Handle("GET", "/api/repos/:id/tree", txRead(api.RepoTree))
router.Handle("GET", "/api/repos/:id/blob", txRead(api.RepoBlob))
router.Handle("GET", "/api/repos/:id/blob/raw", txRead(api.RepoBlobRaw))
router.Handle("GET", "/api/repos/:id/history", txRead(api.RepoFileHistory))
router.Handle("GET", "/api/repos/:id/diff", txRead(api.RepoFileDiff))
router.Handle("GET", "/api/repos/:id/commit", txRead(api.RepoCommitDetail))
router.Handle("GET", "/api/repos/:id/commit/diff", txRead(api.RepoCommitDiff))
router.Handle("GET", "/api/repos/:id/compare", txRead(api.RepoCompare))
router.Handle("GET", "/api/repos/:id/stats", txRead(api.RepoStats))
router.Handle("GET", "/api/repos/:id/rpm/packages", txRead(api.RepoRPMPackages))
router.Handle("GET", "/api/repos/:id/rpm/package", txRead(api.RepoRPMPackage))
router.Handle("POST", "/api/repos/:id/rpm/subdirs", txWrite(api.RepoRPMCreateSubdir))
router.Handle("GET", "/api/repos/:id/rpm/subdir", txRead(api.RepoRPMGetSubdir))
router.Handle("POST", "/api/repos/:id/rpm/subdir/update", txWrite(api.RepoRPMRenameSubdir))
router.Handle("POST", "/api/repos/:id/rpm/subdir/rename", txWrite(api.RepoRPMRenameSubdir))
router.Handle("POST", "/api/repos/:id/rpm/subdir/sync", txWrite(api.RepoRPMSyncSubdir))
router.Handle("POST", "/api/repos/:id/rpm/subdir/suspend", txWrite(api.RepoRPMSuspendSubdir))
router.Handle("POST", "/api/repos/:id/rpm/subdir/resume", txWrite(api.RepoRPMResumeSubdir))
router.Handle("POST", "/api/repos/:id/rpm/subdir/rebuild-metadata", txWrite(api.RepoRPMRebuildSubdirMetadata))
router.Handle("POST", "/api/repos/:id/rpm/subdir/cancel", txWrite(api.RepoRPMCancelSubdirSync))
router.Handle("GET", "/api/repos/:id/rpm/subdir/runs", txRead(api.RepoRPMMirrorRuns))
router.Handle("DELETE", "/api/repos/:id/rpm/subdir/runs", txWrite(api.RepoRPMClearMirrorRuns))
router.Handle("DELETE", "/api/repos/:id/rpm/subdir", txWrite(api.RepoRPMDeleteSubdir))
router.Handle("DELETE", "/api/repos/:id/rpm/file", txWrite(api.RepoRPMDeleteFile))
router.Handle("GET", "/api/repos/:id/rpm/file", txRead(api.RepoRPMFile))
router.Handle("GET", "/api/repos/:id/rpm/tree", txRead(api.RepoRPMTree))
router.Handle("POST", "/api/repos/:id/rpm/upload", txWrite(api.RepoRPMUpload))
router.Handle("GET", "/api/repos/:id/docker/images", txRead(api.RepoDockerImages))
router.Handle("GET", "/api/repos/:id/docker/tags", txRead(api.RepoDockerTags))
router.Handle("GET", "/api/repos/:id/docker/manifest", txRead(api.RepoDockerManifest))
router.Handle("DELETE", "/api/repos/:id/docker/tag", txWrite(api.RepoDockerDeleteTag))
router.Handle("DELETE", "/api/repos/:id/docker/image", txWrite(api.RepoDockerDeleteImage))
router.Handle("POST", "/api/repos/:id/docker/tag/rename", txWrite(api.RepoDockerRenameTag))
router.Handle("POST", "/api/repos/:id/docker/image/rename", txWrite(api.RepoDockerRenameImage))
router.Handle("GET", "/api/me/keys", txRead(api.ListAPIKeys))
router.Handle("POST", "/api/me/keys", txWrite(api.CreateAPIKey))
router.Handle("DELETE", "/api/me/keys/:id", txWrite(api.DeleteAPIKey))
router.Handle("POST", "/api/me/keys/:id/disable", txWrite(api.DisableAPIKey))
router.Handle("POST", "/api/me/keys/:id/enable", txWrite(api.EnableAPIKey))
router.Handle("GET", "/api/admin/api-keys", txRead(api.ListAdminAPIKeys))
router.Handle("DELETE", "/api/admin/api-keys/:id", txWrite(api.DeleteAdminAPIKey))
router.Handle("POST", "/api/admin/api-keys/:id/disable", txWrite(api.DisableAdminAPIKey))
router.Handle("POST", "/api/admin/api-keys/:id/enable", txWrite(api.EnableAdminAPIKey))
router.Handle("GET", "/api/projects/:projectId/issues", api.ListIssues)
router.Handle("POST", "/api/projects/:projectId/issues", api.CreateIssue)
router.Handle("PATCH", "/api/issues/:id", api.UpdateIssue)
router.Handle("POST", "/api/issues/:id/comments", api.AddIssueComment)
router.Handle("GET", "/api/projects/:projectId/issues", txRead(api.ListIssues))
router.Handle("POST", "/api/projects/:projectId/issues", txWrite(api.CreateIssue))
router.Handle("PATCH", "/api/issues/:id", txWrite(api.UpdateIssue))
router.Handle("POST", "/api/issues/:id/comments", txWrite(api.AddIssueComment))
router.Handle("GET", "/api/projects/:projectId/wiki/pages", api.ListWikiPages)
router.Handle("POST", "/api/projects/:projectId/wiki/pages", api.CreateWikiPage)
router.Handle("PATCH", "/api/wiki/pages/:id", api.UpdateWikiPage)
router.Handle("GET", "/api/projects/:projectId/wiki/pages", txRead(api.ListWikiPages))
router.Handle("POST", "/api/projects/:projectId/wiki/pages", txWrite(api.CreateWikiPage))
router.Handle("PATCH", "/api/wiki/pages/:id", txWrite(api.UpdateWikiPage))
router.Handle("POST", "/api/projects/:projectId/uploads", api.UploadFile)
router.Handle("GET", "/api/projects/:projectId/uploads", api.ListUploads)
router.Handle("GET", "/api/uploads/:id", api.DownloadFile)
router.Handle("POST", "/api/projects/:projectId/uploads", txWrite(api.UploadFile))
router.Handle("GET", "/api/projects/:projectId/uploads", txRead(api.ListUploads))
router.Handle("GET", "/api/uploads/:id", txRead(api.DownloadFile))
// ---------------------------------------------------------------
@@ -1029,7 +1037,6 @@ func (app *Server) initHandlers() (*handlers.API, *http.ServeMux, error) {
apiHandler = withAPIAuth(router, auth_user, store, logger)
apiHandler = middleware.RequireInteractiveTOTP(apiHandler)
apiHandler = middleware.WithUserCookie(store, sessionCookieNameFromServerId(app.serverId), apiHandler)
apiHandler = middleware.WithStoreTransaction(store, apiHandler)
apiHandler = middleware.AccessLog(logger, apiHandler)
apiHandler = middleware.WithRequestStore(store, apiHandler)
apiHandler = middleware.WithCors(apiHandler)
@@ -1038,7 +1045,6 @@ func (app *Server) initHandlers() (*handlers.API, *http.ServeMux, error) {
// apiPublicHandler bypasses the authentication layer. WithAPIAuth is not called.
apiPublicHandler = middleware.WithUserCookie(store, sessionCookieNameFromServerId(app.serverId), router)
apiPublicHandler = middleware.WithStoreTransaction(store, apiPublicHandler)
apiPublicHandler = middleware.AccessLog(logger, apiPublicHandler)
apiPublicHandler = middleware.WithRequestStore(store, apiPublicHandler)
apiPublicHandler = middleware.WithCors(apiPublicHandler)
@@ -1048,7 +1054,9 @@ func (app *Server) initHandlers() (*handlers.API, *http.ServeMux, error) {
mux.Handle("/api/auth/oidc/", apiPublicHandler) // for /api/auth/oidc/:id/login and /api/auth/oidc/:id/callback without authentication
mux.Handle("/api/app-info", apiPublicHandler)
mux.Handle("/", middleware.WithUserCookie(store, sessionCookieNameFromServerId(app.serverId), spaHandler(cfg.FrontendDir, logger, app.serverId, TitleFromServerId(app.serverId), app.siteName)))
mux.Handle("/", middleware.WithUserCookie(
store, sessionCookieNameFromServerId(app.serverId),
spaHandler(cfg.FrontendDir, logger, app.serverId, TitleFromServerId(app.serverId), app.siteName)))
return api, mux, nil
}