refactor: migrate auth routes to v1 package (#1841)

* feat: add api v1 packages

* chore: migrate auth to v1

* chore: update test
This commit is contained in:
boojack 2023-06-17 21:25:46 +08:00 committed by GitHub
parent f1d85eeaec
commit 4ed9a3a0ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 180 additions and 131 deletions

View File

@ -1,17 +0,0 @@
package api
type SignIn struct {
Username string `json:"username"`
Password string `json:"password"`
}
type SSOSignIn struct {
IdentityProviderID int `json:"identityProviderId"`
Code string `json:"code"`
RedirectURI string `json:"redirectUri"`
}
type SignUp struct {
Username string `json:"username"`
Password string `json:"password"`
}

View File

@ -1,4 +1,4 @@
package server package v1
import ( import (
"encoding/json" "encoding/json"
@ -6,21 +6,37 @@ import (
"net/http" "net/http"
"regexp" "regexp"
"github.com/labstack/echo/v4"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/usememos/memos/api" "github.com/usememos/memos/api"
"github.com/usememos/memos/common" "github.com/usememos/memos/common"
"github.com/usememos/memos/plugin/idp" "github.com/usememos/memos/plugin/idp"
"github.com/usememos/memos/plugin/idp/oauth2" "github.com/usememos/memos/plugin/idp/oauth2"
"github.com/usememos/memos/server/auth"
"github.com/usememos/memos/store" "github.com/usememos/memos/store"
"github.com/labstack/echo/v4"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
) )
func (s *Server) registerAuthRoutes(g *echo.Group, secret string) { type SignIn struct {
Username string `json:"username"`
Password string `json:"password"`
}
type SSOSignIn struct {
IdentityProviderID int `json:"identityProviderId"`
Code string `json:"code"`
RedirectURI string `json:"redirectUri"`
}
type SignUp struct {
Username string `json:"username"`
Password string `json:"password"`
}
func (s *APIV1Service) registerAuthRoutes(g *echo.Group, secret string) {
g.POST("/auth/signin", func(c echo.Context) error { g.POST("/auth/signin", func(c echo.Context) error {
ctx := c.Request().Context() ctx := c.Request().Context()
signin := &api.SignIn{} signin := &SignIn{}
if err := json.NewDecoder(c.Request().Body).Decode(signin); err != nil { if err := json.NewDecoder(c.Request().Body).Decode(signin); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "Malformatted signin request").SetInternal(err) return echo.NewHTTPError(http.StatusBadRequest, "Malformatted signin request").SetInternal(err)
} }
@ -44,18 +60,18 @@ func (s *Server) registerAuthRoutes(g *echo.Group, secret string) {
return echo.NewHTTPError(http.StatusUnauthorized, "Incorrect login credentials, please try again") return echo.NewHTTPError(http.StatusUnauthorized, "Incorrect login credentials, please try again")
} }
if err := GenerateTokensAndSetCookies(c, user, secret); err != nil { if err := auth.GenerateTokensAndSetCookies(c, user, secret); err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to generate tokens").SetInternal(err) return echo.NewHTTPError(http.StatusInternalServerError, "Failed to generate tokens").SetInternal(err)
} }
if err := s.createUserAuthSignInActivity(c, user); err != nil { if err := s.createAuthSignInActivity(c, user); err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to create activity").SetInternal(err) return echo.NewHTTPError(http.StatusInternalServerError, "Failed to create activity").SetInternal(err)
} }
return c.JSON(http.StatusOK, composeResponse(user)) return c.JSON(http.StatusOK, user)
}) })
g.POST("/auth/signin/sso", func(c echo.Context) error { g.POST("/auth/signin/sso", func(c echo.Context) error {
ctx := c.Request().Context() ctx := c.Request().Context()
signin := &api.SSOSignIn{} signin := &SSOSignIn{}
if err := json.NewDecoder(c.Request().Body).Decode(signin); err != nil { if err := json.NewDecoder(c.Request().Body).Decode(signin); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "Malformatted signin request").SetInternal(err) return echo.NewHTTPError(http.StatusBadRequest, "Malformatted signin request").SetInternal(err)
} }
@ -128,18 +144,18 @@ func (s *Server) registerAuthRoutes(g *echo.Group, secret string) {
return echo.NewHTTPError(http.StatusForbidden, fmt.Sprintf("User has been archived with username %s", userInfo.Identifier)) return echo.NewHTTPError(http.StatusForbidden, fmt.Sprintf("User has been archived with username %s", userInfo.Identifier))
} }
if err := GenerateTokensAndSetCookies(c, user, secret); err != nil { if err := auth.GenerateTokensAndSetCookies(c, user, secret); err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to generate tokens").SetInternal(err) return echo.NewHTTPError(http.StatusInternalServerError, "Failed to generate tokens").SetInternal(err)
} }
if err := s.createUserAuthSignInActivity(c, user); err != nil { if err := s.createAuthSignInActivity(c, user); err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to create activity").SetInternal(err) return echo.NewHTTPError(http.StatusInternalServerError, "Failed to create activity").SetInternal(err)
} }
return c.JSON(http.StatusOK, composeResponse(user)) return c.JSON(http.StatusOK, user)
}) })
g.POST("/auth/signup", func(c echo.Context) error { g.POST("/auth/signup", func(c echo.Context) error {
ctx := c.Request().Context() ctx := c.Request().Context()
signup := &api.SignUp{} signup := &SignUp{}
if err := json.NewDecoder(c.Request().Body).Decode(signup); err != nil { if err := json.NewDecoder(c.Request().Body).Decode(signup); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "Malformatted signup request").SetInternal(err) return echo.NewHTTPError(http.StatusBadRequest, "Malformatted signup request").SetInternal(err)
} }
@ -196,23 +212,23 @@ func (s *Server) registerAuthRoutes(g *echo.Group, secret string) {
if err != nil { if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to create user").SetInternal(err) return echo.NewHTTPError(http.StatusInternalServerError, "Failed to create user").SetInternal(err)
} }
if err := GenerateTokensAndSetCookies(c, user, secret); err != nil { if err := auth.GenerateTokensAndSetCookies(c, user, secret); err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to generate tokens").SetInternal(err) return echo.NewHTTPError(http.StatusInternalServerError, "Failed to generate tokens").SetInternal(err)
} }
if err := s.createUserAuthSignUpActivity(c, user); err != nil { if err := s.createAuthSignUpActivity(c, user); err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to create activity").SetInternal(err) return echo.NewHTTPError(http.StatusInternalServerError, "Failed to create activity").SetInternal(err)
} }
return c.JSON(http.StatusOK, composeResponse(user)) return c.JSON(http.StatusOK, user)
}) })
g.POST("/auth/signout", func(c echo.Context) error { g.POST("/auth/signout", func(c echo.Context) error {
RemoveTokensAndCookies(c) auth.RemoveTokensAndCookies(c)
return c.JSON(http.StatusOK, true) return c.JSON(http.StatusOK, true)
}) })
} }
func (s *Server) createUserAuthSignInActivity(c echo.Context, user *api.User) error { func (s *APIV1Service) createAuthSignInActivity(c echo.Context, user *api.User) error {
ctx := c.Request().Context() ctx := c.Request().Context()
payload := api.ActivityUserAuthSignInPayload{ payload := api.ActivityUserAuthSignInPayload{
UserID: user.ID, UserID: user.ID,
@ -234,7 +250,7 @@ func (s *Server) createUserAuthSignInActivity(c echo.Context, user *api.User) er
return err return err
} }
func (s *Server) createUserAuthSignUpActivity(c echo.Context, user *api.User) error { func (s *APIV1Service) createAuthSignUpActivity(c echo.Context, user *api.User) error {
ctx := c.Request().Context() ctx := c.Request().Context()
payload := api.ActivityUserAuthSignUpPayload{ payload := api.ActivityUserAuthSignUpPayload{
Username: user.Username, Username: user.Username,

9
api/v1/test.go Normal file
View File

@ -0,0 +1,9 @@
package v1
import "github.com/labstack/echo/v4"
func (*APIV1Service) registerTestRoutes(g *echo.Group) {
g.GET("/test", func(c echo.Context) error {
return c.String(200, "Hello World")
})
}

27
api/v1/v1.go Normal file
View File

@ -0,0 +1,27 @@
package v1
import (
"github.com/labstack/echo/v4"
"github.com/usememos/memos/server/profile"
"github.com/usememos/memos/store"
)
type APIV1Service struct {
Secret string
Profile *profile.Profile
Store *store.Store
}
func NewAPIV1Service(secret string, profile *profile.Profile, store *store.Store) *APIV1Service {
return &APIV1Service{
Secret: secret,
Profile: profile,
Store: store,
}
}
func (s *APIV1Service) Register(e *echo.Echo) {
apiV1Group := e.Group("/api/v1")
s.registerTestRoutes(apiV1Group)
s.registerAuthRoutes(apiV1Group, s.Secret)
}

View File

@ -41,7 +41,15 @@ var (
Short: `An open-source, self-hosted memo hub with knowledge management and social networking.`, Short: `An open-source, self-hosted memo hub with knowledge management and social networking.`,
Run: func(_cmd *cobra.Command, _args []string) { Run: func(_cmd *cobra.Command, _args []string) {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
s, err := server.NewServer(ctx, profile) db := db.NewDB(profile)
if err := db.Open(ctx); err != nil {
cancel()
fmt.Printf("failed to open db, error: %+v\n", err)
return
}
store := store.New(db.DBInstance, profile)
s, err := server.NewServer(ctx, profile, store)
if err != nil { if err != nil {
cancel() cancel()
fmt.Printf("failed to create server, error: %+v\n", err) fmt.Printf("failed to create server, error: %+v\n", err)

View File

@ -1,10 +1,14 @@
package auth package auth
import ( import (
"net/http"
"strconv" "strconv"
"time" "time"
"github.com/golang-jwt/jwt/v4" "github.com/golang-jwt/jwt/v4"
"github.com/labstack/echo/v4"
"github.com/pkg/errors"
"github.com/usememos/memos/api"
) )
const ( const (
@ -59,6 +63,48 @@ func GenerateRefreshToken(userName string, userID int, secret string) (string, e
return generateToken(userName, userID, RefreshTokenAudienceName, expirationTime, []byte(secret)) return generateToken(userName, userID, RefreshTokenAudienceName, expirationTime, []byte(secret))
} }
// GenerateTokensAndSetCookies generates jwt token and saves it to the http-only cookie.
func GenerateTokensAndSetCookies(c echo.Context, user *api.User, secret string) error {
accessToken, err := GenerateAccessToken(user.Username, user.ID, secret)
if err != nil {
return errors.Wrap(err, "failed to generate access token")
}
cookieExp := time.Now().Add(CookieExpDuration)
setTokenCookie(c, AccessTokenCookieName, accessToken, cookieExp)
// We generate here a new refresh token and saving it to the cookie.
refreshToken, err := GenerateRefreshToken(user.Username, user.ID, secret)
if err != nil {
return errors.Wrap(err, "failed to generate refresh token")
}
setTokenCookie(c, RefreshTokenCookieName, refreshToken, cookieExp)
return nil
}
// RemoveTokensAndCookies removes the jwt token and refresh token from the cookies.
func RemoveTokensAndCookies(c echo.Context) {
// We set the expiration time to the past, so that the cookie will be removed.
cookieExp := time.Now().Add(-1 * time.Hour)
setTokenCookie(c, AccessTokenCookieName, "", cookieExp)
setTokenCookie(c, RefreshTokenCookieName, "", cookieExp)
}
// setTokenCookie sets the token to the cookie.
func setTokenCookie(c echo.Context, name, token string, expiration time.Time) {
cookie := new(http.Cookie)
cookie.Name = name
cookie.Value = token
cookie.Expires = expiration
cookie.Path = "/"
// Http-only helps mitigate the risk of client side script accessing the protected cookie.
cookie.HttpOnly = true
cookie.SameSite = http.SameSiteStrictMode
c.SetCookie(cookie)
}
// generateToken generates a jwt token.
func generateToken(username string, userID int, aud string, expirationTime time.Time, secret []byte) (string, error) { func generateToken(username string, userID int, aud string, expirationTime time.Time, secret []byte) (string, error) {
// Create the JWT claims, which includes the username and expiry time. // Create the JWT claims, which includes the username and expiry time.
claims := &claimsMessage{ claims := &claimsMessage{

View File

@ -27,12 +27,12 @@ func defaultAPIRequestSkipper(c echo.Context) bool {
return common.HasPrefixes(path, "/api") return common.HasPrefixes(path, "/api")
} }
func (server *Server) defaultAuthSkipper(c echo.Context) bool { func (s *Server) defaultAuthSkipper(c echo.Context) bool {
ctx := c.Request().Context() ctx := c.Request().Context()
path := c.Path() path := c.Path()
// Skip auth. // Skip auth.
if common.HasPrefixes(path, "/api/auth") { if common.HasPrefixes(path, "/api/v1/auth") {
return true return true
} }
@ -42,7 +42,7 @@ func (server *Server) defaultAuthSkipper(c echo.Context) bool {
userFind := &api.UserFind{ userFind := &api.UserFind{
OpenID: &openID, OpenID: &openID,
} }
user, err := server.Store.FindUser(ctx, userFind) user, err := s.Store.FindUser(ctx, userFind)
if err != nil && common.ErrorCode(err) != common.NotFound { if err != nil && common.ErrorCode(err) != common.NotFound {
return false return false
} }

View File

@ -33,47 +33,6 @@ func getUserIDContextKey() string {
return userIDContextKey return userIDContextKey
} }
// GenerateTokensAndSetCookies generates jwt token and saves it to the http-only cookie.
func GenerateTokensAndSetCookies(c echo.Context, user *api.User, secret string) error {
accessToken, err := auth.GenerateAccessToken(user.Username, user.ID, secret)
if err != nil {
return errors.Wrap(err, "failed to generate access token")
}
cookieExp := time.Now().Add(auth.CookieExpDuration)
setTokenCookie(c, auth.AccessTokenCookieName, accessToken, cookieExp)
// We generate here a new refresh token and saving it to the cookie.
refreshToken, err := auth.GenerateRefreshToken(user.Username, user.ID, secret)
if err != nil {
return errors.Wrap(err, "failed to generate refresh token")
}
setTokenCookie(c, auth.RefreshTokenCookieName, refreshToken, cookieExp)
return nil
}
// RemoveTokensAndCookies removes the jwt token and refresh token from the cookies.
func RemoveTokensAndCookies(c echo.Context) {
// We set the expiration time to the past, so that the cookie will be removed.
cookieExp := time.Now().Add(-1 * time.Hour)
setTokenCookie(c, auth.AccessTokenCookieName, "", cookieExp)
setTokenCookie(c, auth.RefreshTokenCookieName, "", cookieExp)
}
// Here we are creating a new cookie, which will store the valid JWT token.
func setTokenCookie(c echo.Context, name, token string, expiration time.Time) {
cookie := new(http.Cookie)
cookie.Name = name
cookie.Value = token
cookie.Expires = expiration
cookie.Path = "/"
// Http-only helps mitigate the risk of client side script accessing the protected cookie.
cookie.HttpOnly = true
cookie.SameSite = http.SameSiteStrictMode
c.SetCookie(cookie)
}
func extractTokenFromHeader(c echo.Context) (string, error) { func extractTokenFromHeader(c echo.Context) (string, error) {
authHeader := c.Request().Header.Get("Authorization") authHeader := c.Request().Header.Get("Authorization")
if authHeader == "" { if authHeader == "" {
@ -101,6 +60,15 @@ func findAccessToken(c echo.Context) string {
return accessToken return accessToken
} }
func audienceContains(audience jwt.ClaimStrings, token string) bool {
for _, v := range audience {
if v == token {
return true
}
}
return false
}
// JWTMiddleware validates the access token. // JWTMiddleware validates the access token.
// If the access token is about to expire or has expired and the request has a valid refresh token, it // If the access token is about to expire or has expired and the request has a valid refresh token, it
// will try to generate new access token and refresh token. // will try to generate new access token and refresh token.
@ -226,7 +194,7 @@ func JWTMiddleware(server *Server, next echo.HandlerFunc, secret string) echo.Ha
// If we have a valid refresh token, we will generate new access token and refresh token // If we have a valid refresh token, we will generate new access token and refresh token
if refreshToken != nil && refreshToken.Valid { if refreshToken != nil && refreshToken.Valid {
if err := GenerateTokensAndSetCookies(c, user, secret); err != nil { if err := auth.GenerateTokensAndSetCookies(c, user, secret); err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("Server error to refresh expired token. User Id %d", userID)).SetInternal(err) return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("Server error to refresh expired token. User Id %d", userID)).SetInternal(err)
} }
} }
@ -246,12 +214,3 @@ func JWTMiddleware(server *Server, next echo.HandlerFunc, secret string) echo.Ha
return next(c) return next(c)
} }
} }
func audienceContains(audience jwt.ClaimStrings, token string) bool {
for _, v := range audience {
if v == token {
return true
}
}
return false
}

View File

@ -2,17 +2,16 @@ package server
import ( import (
"context" "context"
"database/sql"
"encoding/json" "encoding/json"
"fmt" "fmt"
"time" "time"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/usememos/memos/api" "github.com/usememos/memos/api"
apiV1 "github.com/usememos/memos/api/v1"
"github.com/usememos/memos/plugin/telegram" "github.com/usememos/memos/plugin/telegram"
"github.com/usememos/memos/server/profile" "github.com/usememos/memos/server/profile"
"github.com/usememos/memos/store" "github.com/usememos/memos/store"
"github.com/usememos/memos/store/db"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware" "github.com/labstack/echo/v4/middleware"
@ -20,35 +19,28 @@ import (
type Server struct { type Server struct {
e *echo.Echo e *echo.Echo
db *sql.DB
ID string ID string
Secret string
Profile *profile.Profile Profile *profile.Profile
Store *store.Store Store *store.Store
telegramBot *telegram.Bot telegramBot *telegram.Bot
} }
func NewServer(ctx context.Context, profile *profile.Profile) (*Server, error) { func NewServer(ctx context.Context, profile *profile.Profile, store *store.Store) (*Server, error) {
e := echo.New() e := echo.New()
e.Debug = true e.Debug = true
e.HideBanner = true e.HideBanner = true
e.HidePort = true e.HidePort = true
db := db.NewDB(profile)
if err := db.Open(ctx); err != nil {
return nil, errors.Wrap(err, "cannot open db")
}
s := &Server{ s := &Server{
e: e, e: e,
db: db.DBInstance, Store: store,
Profile: profile, Profile: profile,
} }
storeInstance := store.New(db.DBInstance, profile)
s.Store = storeInstance
telegramBotHandler := newTelegramHandler(storeInstance) telegramBotHandler := newTelegramHandler(store)
s.telegramBot = telegram.NewBotWithHandler(telegramBotHandler) s.telegramBot = telegram.NewBotWithHandler(telegramBotHandler)
e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{ e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
@ -89,23 +81,23 @@ func NewServer(ctx context.Context, profile *profile.Profile) (*Server, error) {
return nil, err return nil, err
} }
} }
s.Secret = secret
rootGroup := e.Group("") rootGroup := e.Group("")
s.registerRSSRoutes(rootGroup) s.registerRSSRoutes(rootGroup)
publicGroup := e.Group("/o") publicGroup := e.Group("/o")
publicGroup.Use(func(next echo.HandlerFunc) echo.HandlerFunc { publicGroup.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
return JWTMiddleware(s, next, secret) return JWTMiddleware(s, next, s.Secret)
}) })
registerGetterPublicRoutes(publicGroup) registerGetterPublicRoutes(publicGroup)
s.registerResourcePublicRoutes(publicGroup) s.registerResourcePublicRoutes(publicGroup)
apiGroup := e.Group("/api") apiGroup := e.Group("/api")
apiGroup.Use(func(next echo.HandlerFunc) echo.HandlerFunc { apiGroup.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
return JWTMiddleware(s, next, secret) return JWTMiddleware(s, next, s.Secret)
}) })
s.registerSystemRoutes(apiGroup) s.registerSystemRoutes(apiGroup)
s.registerAuthRoutes(apiGroup, secret)
s.registerUserRoutes(apiGroup) s.registerUserRoutes(apiGroup)
s.registerMemoRoutes(apiGroup) s.registerMemoRoutes(apiGroup)
s.registerMemoResourceRoutes(apiGroup) s.registerMemoResourceRoutes(apiGroup)
@ -117,6 +109,9 @@ func NewServer(ctx context.Context, profile *profile.Profile) (*Server, error) {
s.registerOpenAIRoutes(apiGroup) s.registerOpenAIRoutes(apiGroup)
s.registerMemoRelationRoutes(apiGroup) s.registerMemoRelationRoutes(apiGroup)
apiV1Service := apiV1.NewAPIV1Service(s.Secret, profile, store)
apiV1Service.Register(e)
return s, nil return s, nil
} }
@ -140,7 +135,7 @@ func (s *Server) Shutdown(ctx context.Context) {
} }
// Close database connection // Close database connection
if err := s.db.Close(); err != nil { if err := s.Store.GetDB().Close(); err != nil {
fmt.Printf("failed to close database, error: %v\n", err) fmt.Printf("failed to close database, error: %v\n", err)
} }

View File

@ -28,6 +28,10 @@ func New(db *sql.DB, profile *profile.Profile) *Store {
} }
} }
func (s *Store) GetDB() *sql.DB {
return s.db
}
func (s *Store) Vacuum(ctx context.Context) error { func (s *Store) Vacuum(ctx context.Context) error {
tx, err := s.db.BeginTx(ctx, nil) tx, err := s.db.BeginTx(ctx, nil)
if err != nil { if err != nil {

View File

@ -9,6 +9,7 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/usememos/memos/api" "github.com/usememos/memos/api"
apiv1 "github.com/usememos/memos/api/v1"
) )
func TestAuthServer(t *testing.T) { func TestAuthServer(t *testing.T) {
@ -17,7 +18,7 @@ func TestAuthServer(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
defer s.Shutdown(ctx) defer s.Shutdown(ctx)
signup := &api.SignUp{ signup := &apiv1.SignUp{
Username: "testuser", Username: "testuser",
Password: "testpassword", Password: "testpassword",
} }
@ -26,15 +27,15 @@ func TestAuthServer(t *testing.T) {
require.Equal(t, signup.Username, user.Username) require.Equal(t, signup.Username, user.Username)
} }
func (s *TestingServer) postAuthSignup(signup *api.SignUp) (*api.User, error) { func (s *TestingServer) postAuthSignup(signup *apiv1.SignUp) (*api.User, error) {
rawData, err := json.Marshal(&signup) rawData, err := json.Marshal(&signup)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to marshal signup") return nil, errors.Wrap(err, "failed to marshal signup")
} }
reader := bytes.NewReader(rawData) reader := bytes.NewReader(rawData)
body, err := s.post("/api/auth/signup", reader, nil) body, err := s.post("/api/v1/auth/signup", reader, nil)
if err != nil { if err != nil {
return nil, err return nil, errors.Wrap(err, "fail to post request")
} }
buf := &bytes.Buffer{} buf := &bytes.Buffer{}
@ -43,12 +44,9 @@ func (s *TestingServer) postAuthSignup(signup *api.SignUp) (*api.User, error) {
return nil, errors.Wrap(err, "fail to read response body") return nil, errors.Wrap(err, "fail to read response body")
} }
type AuthSignupResponse struct { user := &api.User{}
Data *api.User `json:"data"` if err = json.Unmarshal(buf.Bytes(), user); err != nil {
}
res := new(AuthSignupResponse)
if err = json.Unmarshal(buf.Bytes(), res); err != nil {
return nil, errors.Wrap(err, "fail to unmarshal post signup response") return nil, errors.Wrap(err, "fail to unmarshal post signup response")
} }
return res.Data, nil return user, nil
} }

View File

@ -10,6 +10,7 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/usememos/memos/api" "github.com/usememos/memos/api"
apiv1 "github.com/usememos/memos/api/v1"
) )
func TestMemoRelationServer(t *testing.T) { func TestMemoRelationServer(t *testing.T) {
@ -18,7 +19,7 @@ func TestMemoRelationServer(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
defer s.Shutdown(ctx) defer s.Shutdown(ctx)
signup := &api.SignUp{ signup := &apiv1.SignUp{
Username: "testuser", Username: "testuser",
Password: "testpassword", Password: "testpassword",
} }

View File

@ -10,6 +10,7 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/usememos/memos/api" "github.com/usememos/memos/api"
apiv1 "github.com/usememos/memos/api/v1"
) )
func TestMemoServer(t *testing.T) { func TestMemoServer(t *testing.T) {
@ -18,7 +19,7 @@ func TestMemoServer(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
defer s.Shutdown(ctx) defer s.Shutdown(ctx)
signup := &api.SignUp{ signup := &apiv1.SignUp{
Username: "testuser", Username: "testuser",
Password: "testpassword", Password: "testpassword",
} }

View File

@ -13,6 +13,7 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/usememos/memos/server" "github.com/usememos/memos/server"
"github.com/usememos/memos/server/profile" "github.com/usememos/memos/server/profile"
"github.com/usememos/memos/store"
"github.com/usememos/memos/store/db" "github.com/usememos/memos/store/db"
"github.com/usememos/memos/test" "github.com/usememos/memos/test"
@ -34,19 +35,19 @@ func NewTestingServer(ctx context.Context, t *testing.T) (*TestingServer, error)
return nil, errors.Wrap(err, "failed to open db") return nil, errors.Wrap(err, "failed to open db")
} }
server, err := server.NewServer(ctx, profile) store := store.New(db.DBInstance, profile)
server, err := server.NewServer(ctx, profile, store)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to create server") return nil, errors.Wrap(err, "failed to create server")
} }
errChan := make(chan error, 1)
s := &TestingServer{ s := &TestingServer{
server: server, server: server,
client: &http.Client{}, client: &http.Client{},
profile: profile, profile: profile,
cookie: "", cookie: "",
} }
errChan := make(chan error, 1)
go func() { go func() {
if err := s.server.Start(ctx); err != nil { if err := s.server.Start(ctx); err != nil {
@ -126,7 +127,7 @@ func (s *TestingServer) request(method, uri string, body io.Reader, params, head
} }
if method == "POST" { if method == "POST" {
if strings.Contains(uri, "/api/auth/login") || strings.Contains(uri, "/api/auth/signup") { if strings.Contains(uri, "/api/v1/auth/login") || strings.Contains(uri, "/api/v1/auth/signup") {
cookie := "" cookie := ""
h := resp.Header.Get("Set-Cookie") h := resp.Header.Get("Set-Cookie")
parts := strings.Split(h, "; ") parts := strings.Split(h, "; ")
@ -140,7 +141,7 @@ func (s *TestingServer) request(method, uri string, body io.Reader, params, head
return nil, errors.Errorf("unable to find access token in the login response headers") return nil, errors.Errorf("unable to find access token in the login response headers")
} }
s.cookie = cookie s.cookie = cookie
} else if strings.Contains(uri, "/api/auth/logout") { } else if strings.Contains(uri, "/api/v1/auth/logout") {
s.cookie = "" s.cookie = ""
} }
} }

View File

@ -9,6 +9,7 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/usememos/memos/api" "github.com/usememos/memos/api"
apiv1 "github.com/usememos/memos/api/v1"
) )
func TestSystemServer(t *testing.T) { func TestSystemServer(t *testing.T) {
@ -21,7 +22,7 @@ func TestSystemServer(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, (*api.User)(nil), status.Host) require.Equal(t, (*api.User)(nil), status.Host)
signup := &api.SignUp{ signup := &apiv1.SignUp{
Username: "testuser", Username: "testuser",
Password: "testpassword", Password: "testpassword",
} }

View File

@ -23,14 +23,14 @@ export function vacuumDatabase() {
} }
export function signin(username: string, password: string) { export function signin(username: string, password: string) {
return axios.post<ResponseObject<User>>("/api/auth/signin", { return axios.post("/api/v1/auth/signin", {
username, username,
password, password,
}); });
} }
export function signinWithSSO(identityProviderId: IdentityProviderId, code: string, redirectUri: string) { export function signinWithSSO(identityProviderId: IdentityProviderId, code: string, redirectUri: string) {
return axios.post<ResponseObject<User>>("/api/auth/signin/sso", { return axios.post("/api/v1/auth/signin/sso", {
identityProviderId, identityProviderId,
code, code,
redirectUri, redirectUri,
@ -38,14 +38,14 @@ export function signinWithSSO(identityProviderId: IdentityProviderId, code: stri
} }
export function signup(username: string, password: string) { export function signup(username: string, password: string) {
return axios.post<ResponseObject<User>>("/api/auth/signup", { return axios.post("/api/v1/auth/signup", {
username, username,
password, password,
}); });
} }
export function signout() { export function signout() {
return axios.post("/api/auth/signout"); return axios.post("/api/v1/auth/signout");
} }
export function createUser(userCreate: UserCreate) { export function createUser(userCreate: UserCreate) {