server.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. package server
  2. import (
  3. "fmt"
  4. "time"
  5. "github.com/usememos/memos/api"
  6. "github.com/usememos/memos/common"
  7. "github.com/usememos/memos/server/profile"
  8. "github.com/usememos/memos/store"
  9. "github.com/gorilla/securecookie"
  10. "github.com/gorilla/sessions"
  11. "github.com/labstack/echo-contrib/session"
  12. "github.com/labstack/echo/v4"
  13. "github.com/labstack/echo/v4/middleware"
  14. )
  15. type Server struct {
  16. e *echo.Echo
  17. Collector *MetricCollector
  18. Profile *profile.Profile
  19. Store *store.Store
  20. }
  21. func NewServer(profile *profile.Profile) *Server {
  22. e := echo.New()
  23. e.Debug = true
  24. e.HideBanner = true
  25. e.HidePort = true
  26. s := &Server{
  27. e: e,
  28. Profile: profile,
  29. }
  30. e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
  31. Format: `{"time":"${time_rfc3339}",` +
  32. `"method":"${method}","uri":"${uri}",` +
  33. `"status":${status},"error":"${error}"}` + "\n",
  34. }))
  35. e.Use(middleware.CSRFWithConfig(middleware.CSRFConfig{
  36. Skipper: s.OpenAPISkipper,
  37. TokenLookup: "cookie:_csrf",
  38. }))
  39. e.Use(middleware.CORS())
  40. e.Use(middleware.Secure())
  41. e.Use(middleware.TimeoutWithConfig(middleware.TimeoutConfig{
  42. Skipper: middleware.DefaultSkipper,
  43. ErrorMessage: "Request timeout",
  44. Timeout: 30 * time.Second,
  45. }))
  46. embedFrontend(e)
  47. // In dev mode, set the const secret key to make signin session persistence.
  48. secret := []byte("usememos")
  49. if profile.Mode == "prod" {
  50. secret = securecookie.GenerateRandomKey(16)
  51. }
  52. e.Use(session.Middleware(sessions.NewCookieStore(secret)))
  53. rootGroup := e.Group("")
  54. s.registerRSSRoutes(rootGroup)
  55. webhookGroup := e.Group("/h")
  56. s.registerResourcePublicRoutes(webhookGroup)
  57. publicGroup := e.Group("/o")
  58. s.registerResourcePublicRoutes(publicGroup)
  59. s.registerGetterPublicRoutes(publicGroup)
  60. apiGroup := e.Group("/api")
  61. apiGroup.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
  62. return aclMiddleware(s, next)
  63. })
  64. s.registerSystemRoutes(apiGroup)
  65. s.registerAuthRoutes(apiGroup)
  66. s.registerUserRoutes(apiGroup)
  67. s.registerMemoRoutes(apiGroup)
  68. s.registerShortcutRoutes(apiGroup)
  69. s.registerResourceRoutes(apiGroup)
  70. s.registerTagRoutes(apiGroup)
  71. return s
  72. }
  73. func (server *Server) Run() error {
  74. return server.e.Start(fmt.Sprintf(":%d", server.Profile.Port))
  75. }
  76. func (server *Server) OpenAPISkipper(c echo.Context) bool {
  77. ctx := c.Request().Context()
  78. path := c.Path()
  79. // Skip auth.
  80. if common.HasPrefixes(path, "/api/auth") {
  81. return true
  82. }
  83. // If there is openId in query string and related user is found, then skip auth.
  84. openID := c.QueryParam("openId")
  85. if openID != "" {
  86. userFind := &api.UserFind{
  87. OpenID: &openID,
  88. }
  89. user, err := server.Store.FindUser(ctx, userFind)
  90. if err != nil && common.ErrorCode(err) != common.NotFound {
  91. return false
  92. }
  93. if user != nil {
  94. // Stores userID into context.
  95. c.Set(getUserIDContextKey(), user.ID)
  96. return true
  97. }
  98. }
  99. return false
  100. }