2024-12-03 01:18:04 +00:00
|
|
|
package server
|
2024-12-16 00:09:32 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"github.com/labstack/echo/v4"
|
|
|
|
"github.com/thomiceli/opengist/internal/config"
|
|
|
|
"github.com/thomiceli/opengist/internal/index"
|
|
|
|
"github.com/thomiceli/opengist/internal/web/context"
|
2025-01-06 13:18:31 +00:00
|
|
|
"github.com/thomiceli/opengist/internal/web/handlers"
|
2024-12-16 00:09:32 +00:00
|
|
|
"github.com/thomiceli/opengist/public"
|
|
|
|
"net/http"
|
|
|
|
"os"
|
|
|
|
"path"
|
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
2025-01-02 19:55:33 +00:00
|
|
|
func (s *Server) registerRoutes() {
|
2024-12-16 00:09:32 +00:00
|
|
|
r := NewRouter(s.echo.Group(""))
|
|
|
|
|
|
|
|
{
|
2025-01-06 13:18:31 +00:00
|
|
|
r.GET("/", handlers.Create, logged)
|
|
|
|
r.POST("/", handlers.ProcessCreate, logged)
|
|
|
|
r.POST("/preview", handlers.Preview, logged)
|
|
|
|
|
|
|
|
r.GET("/healthcheck", handlers.Healthcheck)
|
|
|
|
r.GET("/metrics", handlers.Metrics)
|
|
|
|
|
|
|
|
r.GET("/register", handlers.Register)
|
|
|
|
r.POST("/register", handlers.ProcessRegister)
|
|
|
|
r.GET("/login", handlers.Login)
|
|
|
|
r.POST("/login", handlers.ProcessLogin)
|
|
|
|
r.GET("/logout", handlers.Logout)
|
|
|
|
r.GET("/oauth/:provider", handlers.Oauth)
|
|
|
|
r.GET("/oauth/:provider/callback", handlers.OauthCallback)
|
|
|
|
r.GET("/oauth/:provider/unlink", handlers.OauthUnlink, logged)
|
|
|
|
r.POST("/webauthn/bind", handlers.BeginWebAuthnBinding, logged)
|
|
|
|
r.POST("/webauthn/bind/finish", handlers.FinishWebAuthnBinding, logged)
|
|
|
|
r.POST("/webauthn/login", handlers.BeginWebAuthnLogin)
|
|
|
|
r.POST("/webauthn/login/finish", handlers.FinishWebAuthnLogin)
|
|
|
|
r.POST("/webauthn/assertion", handlers.BeginWebAuthnAssertion, inMFASession)
|
|
|
|
r.POST("/webauthn/assertion/finish", handlers.FinishWebAuthnAssertion, inMFASession)
|
|
|
|
r.GET("/mfa", handlers.Mfa, inMFASession)
|
|
|
|
r.POST("/mfa/totp/assertion", handlers.AssertTotp, inMFASession)
|
2024-12-16 00:09:32 +00:00
|
|
|
|
2025-01-02 19:55:33 +00:00
|
|
|
sA := r.SubGroup("/settings")
|
|
|
|
{
|
|
|
|
sA.Use(logged)
|
2025-01-06 13:18:31 +00:00
|
|
|
sA.GET("", handlers.UserSettings)
|
|
|
|
sA.POST("/email", handlers.EmailProcess)
|
|
|
|
sA.DELETE("/account", handlers.AccountDeleteProcess)
|
|
|
|
sA.POST("/ssh-keys", handlers.SshKeysProcess)
|
|
|
|
sA.DELETE("/ssh-keys/:id", handlers.SshKeysDelete)
|
|
|
|
sA.DELETE("/passkeys/:id", handlers.PasskeyDelete)
|
|
|
|
sA.PUT("/password", handlers.PasswordProcess)
|
|
|
|
sA.PUT("/username", handlers.UsernameProcess)
|
|
|
|
sA.GET("/totp/generate", handlers.BeginTotp)
|
|
|
|
sA.POST("/totp/generate", handlers.FinishTotp)
|
|
|
|
sA.DELETE("/totp", handlers.DisableTotp)
|
|
|
|
sA.POST("/totp/regenerate", handlers.RegenerateTotpRecoveryCodes)
|
2025-01-02 19:55:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
sB := r.SubGroup("/admin-panel")
|
2024-12-16 00:09:32 +00:00
|
|
|
{
|
2025-01-02 19:55:33 +00:00
|
|
|
sB.Use(adminPermission)
|
2025-01-06 13:18:31 +00:00
|
|
|
sB.GET("", handlers.AdminIndex)
|
|
|
|
sB.GET("/users", handlers.AdminUsers)
|
|
|
|
sB.POST("/users/:user/delete", handlers.AdminUserDelete)
|
|
|
|
sB.GET("/gists", handlers.AdminGists)
|
|
|
|
sB.POST("/gists/:gist/delete", handlers.AdminGistDelete)
|
|
|
|
sB.GET("/invitations", handlers.AdminInvitations)
|
|
|
|
sB.POST("/invitations", handlers.AdminInvitationsCreate)
|
|
|
|
sB.POST("/invitations/:id/delete", handlers.AdminInvitationsDelete)
|
|
|
|
sB.POST("/sync-fs", handlers.AdminSyncReposFromFS)
|
|
|
|
sB.POST("/sync-db", handlers.AdminSyncReposFromDB)
|
|
|
|
sB.POST("/gc-repos", handlers.AdminGcRepos)
|
|
|
|
sB.POST("/sync-previews", handlers.AdminSyncGistPreviews)
|
|
|
|
sB.POST("/reset-hooks", handlers.AdminResetHooks)
|
|
|
|
sB.POST("/index-gists", handlers.AdminIndexGists)
|
|
|
|
sB.GET("/configuration", handlers.AdminConfig)
|
|
|
|
sB.PUT("/set-config", handlers.AdminSetConfig)
|
2024-12-16 00:09:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if config.C.HttpGit {
|
2025-01-06 13:18:31 +00:00
|
|
|
r.Any("/init/*", handlers.GitHttp, gistNewPushSoftInit)
|
2024-12-16 00:09:32 +00:00
|
|
|
}
|
|
|
|
|
2025-01-06 13:18:31 +00:00
|
|
|
r.GET("/all", handlers.AllGists, checkRequireLogin)
|
2024-12-16 00:09:32 +00:00
|
|
|
|
|
|
|
if index.Enabled() {
|
2025-01-06 13:18:31 +00:00
|
|
|
r.GET("/search", handlers.Search, checkRequireLogin)
|
2024-12-16 00:09:32 +00:00
|
|
|
} else {
|
2025-01-06 13:18:31 +00:00
|
|
|
r.GET("/search", handlers.AllGists, checkRequireLogin)
|
2024-12-16 00:09:32 +00:00
|
|
|
}
|
|
|
|
|
2025-01-06 13:18:31 +00:00
|
|
|
r.GET("/:user", handlers.AllGists, checkRequireLogin)
|
|
|
|
r.GET("/:user/liked", handlers.AllGists, checkRequireLogin)
|
|
|
|
r.GET("/:user/forked", handlers.AllGists, checkRequireLogin)
|
2024-12-16 00:09:32 +00:00
|
|
|
|
2025-01-02 19:55:33 +00:00
|
|
|
sC := r.SubGroup("/:user/:gistname")
|
2024-12-16 00:09:32 +00:00
|
|
|
{
|
2025-01-06 13:18:31 +00:00
|
|
|
sC.Use(makeCheckRequireLogin(true), gistInit)
|
|
|
|
sC.GET("", handlers.GistIndex)
|
|
|
|
sC.GET("/rev/:revision", handlers.GistIndex)
|
|
|
|
sC.GET("/revisions", handlers.Revisions)
|
|
|
|
sC.GET("/archive/:revision", handlers.DownloadZip)
|
|
|
|
sC.POST("/visibility", handlers.EditVisibility, logged, writePermission)
|
|
|
|
sC.POST("/delete", handlers.DeleteGist, logged, writePermission)
|
|
|
|
sC.GET("/raw/:revision/:file", handlers.RawFile)
|
|
|
|
sC.GET("/download/:revision/:file", handlers.DownloadFile)
|
|
|
|
sC.GET("/edit", handlers.Edit, logged, writePermission)
|
|
|
|
sC.POST("/edit", handlers.ProcessCreate, logged, writePermission)
|
|
|
|
sC.POST("/like", handlers.Like, logged)
|
|
|
|
sC.GET("/likes", handlers.Likes, checkRequireLogin)
|
|
|
|
sC.POST("/fork", handlers.Fork, logged)
|
|
|
|
sC.GET("/forks", handlers.Forks, checkRequireLogin)
|
|
|
|
sC.PUT("/checkbox", handlers.Checkbox, logged, writePermission)
|
2024-12-16 00:09:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
customFs := os.DirFS(filepath.Join(config.GetHomeDir(), "custom"))
|
2025-01-06 13:18:31 +00:00
|
|
|
r.GET("/assets/*", func(ctx *context.Context) error {
|
2024-12-29 10:40:23 +00:00
|
|
|
if _, err := public.Files.Open(path.Join("assets", ctx.Param("*"))); !s.dev && err == nil {
|
2024-12-16 00:09:32 +00:00
|
|
|
ctx.Response().Header().Set("Cache-Control", "public, max-age=31536000")
|
|
|
|
ctx.Response().Header().Set("Expires", time.Now().AddDate(1, 0, 0).Format(http.TimeFormat))
|
|
|
|
|
|
|
|
return echo.WrapHandler(http.FileServer(http.FS(public.Files)))(ctx)
|
|
|
|
}
|
|
|
|
|
|
|
|
// if the custom file is an .html template, render it
|
|
|
|
if strings.HasSuffix(ctx.Param("*"), ".html") {
|
2024-12-29 10:40:23 +00:00
|
|
|
if err := ctx.HTML_(ctx.Param("*")); err != nil {
|
|
|
|
return ctx.NotFound("Page not found")
|
2024-12-16 00:09:32 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return echo.WrapHandler(http.StripPrefix("/assets/", http.FileServer(http.FS(customFs))))(ctx)
|
|
|
|
})
|
|
|
|
|
|
|
|
// Git HTTP routes
|
|
|
|
if config.C.HttpGit {
|
2025-01-06 13:18:31 +00:00
|
|
|
r.Any("/:user/:gistname/*", handlers.GitHttp, gistSoftInit)
|
2024-12-16 00:09:32 +00:00
|
|
|
}
|
|
|
|
|
2024-12-29 10:40:23 +00:00
|
|
|
r.Any("/*", noRouteFound)
|
2024-12-16 00:09:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Router wraps echo.Group to provide custom Handler support
|
|
|
|
type Router struct {
|
|
|
|
*echo.Group
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewRouter(g *echo.Group) *Router {
|
|
|
|
return &Router{Group: g}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Router) SubGroup(prefix string, m ...Middleware) *Router {
|
|
|
|
echoMiddleware := make([]echo.MiddlewareFunc, len(m))
|
|
|
|
for i, mw := range m {
|
|
|
|
mw := mw // capture for closure
|
|
|
|
echoMiddleware[i] = func(next echo.HandlerFunc) echo.HandlerFunc {
|
2025-01-06 13:18:31 +00:00
|
|
|
return chain(func(c *context.Context) error {
|
2024-12-16 00:09:32 +00:00
|
|
|
return next(c)
|
|
|
|
}, mw).toEchoHandler()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NewRouter(r.Group.Group(prefix, echoMiddleware...))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Router) GET(path string, h Handler, m ...Middleware) {
|
2025-01-02 19:55:33 +00:00
|
|
|
r.Group.GET(path, chain(h, m...).toEchoHandler())
|
2024-12-16 00:09:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Router) POST(path string, h Handler, m ...Middleware) {
|
2025-01-02 19:55:33 +00:00
|
|
|
r.Group.POST(path, chain(h, m...).toEchoHandler())
|
2024-12-16 00:09:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Router) PUT(path string, h Handler, m ...Middleware) {
|
2025-01-02 19:55:33 +00:00
|
|
|
r.Group.PUT(path, chain(h, m...).toEchoHandler())
|
2024-12-16 00:09:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Router) DELETE(path string, h Handler, m ...Middleware) {
|
2025-01-02 19:55:33 +00:00
|
|
|
r.Group.DELETE(path, chain(h, m...).toEchoHandler())
|
2024-12-16 00:09:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Router) PATCH(path string, h Handler, m ...Middleware) {
|
2025-01-02 19:55:33 +00:00
|
|
|
r.Group.PATCH(path, chain(h, m...).toEchoHandler())
|
2024-12-16 00:09:32 +00:00
|
|
|
}
|
|
|
|
|
2024-12-29 10:40:23 +00:00
|
|
|
func (r *Router) Any(path string, h Handler, m ...Middleware) {
|
2025-01-02 19:55:33 +00:00
|
|
|
r.Group.Any(path, chain(h, m...).toEchoHandler())
|
2024-12-29 10:40:23 +00:00
|
|
|
}
|
|
|
|
|
2024-12-16 00:09:32 +00:00
|
|
|
func (r *Router) Use(middleware ...Middleware) {
|
|
|
|
for _, m := range middleware {
|
|
|
|
m := m // capture for closure
|
|
|
|
r.Group.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
|
2025-01-06 13:18:31 +00:00
|
|
|
return chain(func(c *context.Context) error {
|
2024-12-16 00:09:32 +00:00
|
|
|
return next(c)
|
|
|
|
}, m).toEchoHandler()
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|