mirror of
https://github.com/go-shiori/shiori.git
synced 2025-10-04 02:36:47 +08:00
* create needed field in sqlite database * update account model * update Account struct for save Account options * update sqlite database return account settings * save configure in sqlite as text and return that * read configure from user account and defualt configure for shiori * add api/ui for update settings in database user can save settings in database (in sqlite database) * check configures be in json format before save in database * support MariaDB * fix wrong comment * support PostgreSQL * revert unneeded change in new logic * change configures to config * change SaveAccount to SaveAccountSettings * add migrate database scripts * change default in migration scrtipts * update model * read config field as json from database * fix parse value config value & update config update * update default value for new user * update settings variable name to reflect database value in UI * fix typo * not panic if user not exist and update worng comment * visitor user can update there settings now * remove unneeded loading dialog * fix typo * update function for pg and mysql * remove IsJson * move scan method to model * simplify jsonify * simplify assignees value to account.Config * missing part of function * fix some typo and unneeded field in struct * add down migrate script for all database * change createEbook to CreateEbook * use json instead of text in mysql and postgres * implement * remove unneeded part * remove unneeded jsonify in code * return SelectContext and GetContext * remove defualt config in reques for new user it will be set in backend * New API * remove legacy API * remove validateSessionWithoutOwnerStatus * remove Jsonify function don't need that anymore * add unit test for database * update migrate script name * change put to patch * return PUT * fix Patch problem and now use PATCH instead of PUT * remove unneeded retuen * more cleaner code for request new settings * fix bug to handle string in Scan method thanks to fmartingr * fix Authorization & use GetAccount & remove username from request * shiori-settings remove and it read from shiori-account * add swagger documentation * API unit test * fix typo * remove unneeded coment Co-authored-by: Felipe Martin <812088+fmartingr@users.noreply.github.com> * better Documentation Co-authored-by: Felipe Martin <812088+fmartingr@users.noreply.github.com> * shiori-toke remove on logout * fix typo * add unit test check update config in database * update swag documentation * fix swag formaing error --------- Co-authored-by: Felipe Martin <812088+fmartingr@users.noreply.github.com>
198 lines
5.4 KiB
Go
198 lines
5.4 KiB
Go
package api_v1
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/go-shiori/shiori/internal/config"
|
|
"github.com/go-shiori/shiori/internal/http/context"
|
|
"github.com/go-shiori/shiori/internal/http/response"
|
|
"github.com/go-shiori/shiori/internal/model"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
type AuthAPIRoutes struct {
|
|
logger *logrus.Logger
|
|
deps *config.Dependencies
|
|
legacyLoginHandler model.LegacyLoginHandler
|
|
}
|
|
|
|
func (r *AuthAPIRoutes) Setup(group *gin.RouterGroup) model.Routes {
|
|
group.GET("/me", r.meHandler)
|
|
group.POST("/login", r.loginHandler)
|
|
group.POST("/refresh", r.refreshHandler)
|
|
group.PATCH("/account", r.settingsHandler)
|
|
return r
|
|
}
|
|
|
|
type loginRequestPayload struct {
|
|
Username string `json:"username" validate:"required"`
|
|
Password string `json:"password" validate:"required"`
|
|
RememberMe bool `json:"remember_me"`
|
|
}
|
|
|
|
func (p *loginRequestPayload) IsValid() error {
|
|
if p.Username == "" {
|
|
return fmt.Errorf("username should not be empty")
|
|
}
|
|
if p.Password == "" {
|
|
return fmt.Errorf("password should not be empty")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type loginResponseMessage struct {
|
|
Token string `json:"token"`
|
|
SessionID string `json:"session"` // Deprecated, used only for legacy APIs
|
|
Expiration int64 `json:"expires"` // Deprecated, used only for legacy APIs
|
|
}
|
|
|
|
type settingRequestPayload struct {
|
|
Config model.UserConfig `json:"config"`
|
|
}
|
|
|
|
// loginHandler godoc
|
|
//
|
|
// @Summary Login to an account using username and password
|
|
// @Tags Auth
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param payload body loginRequestPayload false "Login data"
|
|
// @Success 200 {object} loginResponseMessage "Login successful"
|
|
// @Failure 400 {object} nil "Invalid login data"
|
|
// @Router /api/v1/auth/login [post]
|
|
func (r *AuthAPIRoutes) loginHandler(c *gin.Context) {
|
|
var payload loginRequestPayload
|
|
if err := c.ShouldBindJSON(&payload); err != nil {
|
|
response.SendInternalServerError(c)
|
|
return
|
|
}
|
|
|
|
if err := payload.IsValid(); err != nil {
|
|
response.SendError(c, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
|
|
account, err := r.deps.Domains.Auth.GetAccountFromCredentials(c, payload.Username, payload.Password)
|
|
if err != nil {
|
|
response.SendError(c, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
|
|
expiration := time.Now().Add(time.Hour)
|
|
if payload.RememberMe {
|
|
expiration = time.Now().Add(time.Hour * 24 * 30)
|
|
}
|
|
|
|
token, err := r.deps.Domains.Auth.CreateTokenForAccount(account, expiration)
|
|
if err != nil {
|
|
response.SendInternalServerError(c)
|
|
return
|
|
}
|
|
|
|
sessionID, err := r.legacyLoginHandler(*account, time.Hour*24*30)
|
|
if err != nil {
|
|
r.logger.WithError(err).Error("failed execute legacy login handler")
|
|
response.SendInternalServerError(c)
|
|
return
|
|
}
|
|
|
|
responseMessage := loginResponseMessage{
|
|
Token: token,
|
|
SessionID: sessionID,
|
|
Expiration: expiration.Unix(),
|
|
}
|
|
|
|
response.Send(c, http.StatusOK, responseMessage)
|
|
}
|
|
|
|
// refreshHandler godoc
|
|
//
|
|
// @Summary Refresh a token for an account
|
|
// @Tags Auth
|
|
// @securityDefinitions.apikey ApiKeyAuth
|
|
// @Produce json
|
|
// @Success 200 {object} loginResponseMessage "Refresh successful"
|
|
// @Failure 403 {object} nil "Token not provided/invalid"
|
|
// @Router /api/v1/auth/refresh [post]
|
|
func (r *AuthAPIRoutes) refreshHandler(c *gin.Context) {
|
|
ctx := context.NewContextFromGin(c)
|
|
if !ctx.UserIsLogged() {
|
|
response.SendError(c, http.StatusForbidden, nil)
|
|
return
|
|
}
|
|
|
|
expiration := time.Now().Add(time.Hour * 72)
|
|
account, _ := c.Get(model.ContextAccountKey)
|
|
token, err := r.deps.Domains.Auth.CreateTokenForAccount(account.(*model.Account), expiration)
|
|
if err != nil {
|
|
response.SendInternalServerError(c)
|
|
return
|
|
}
|
|
|
|
responseMessage := loginResponseMessage{
|
|
Token: token,
|
|
}
|
|
|
|
response.Send(c, http.StatusAccepted, responseMessage)
|
|
}
|
|
|
|
// meHandler godoc
|
|
//
|
|
// @Summary Get information for the current logged in user
|
|
// @Tags Auth
|
|
// @securityDefinitions.apikey ApiKeyAuth
|
|
// @Produce json
|
|
// @Success 200 {object} model.Account
|
|
// @Failure 403 {object} nil "Token not provided/invalid"
|
|
// @Router /api/v1/auth/me [get]
|
|
func (r *AuthAPIRoutes) meHandler(c *gin.Context) {
|
|
ctx := context.NewContextFromGin(c)
|
|
if !ctx.UserIsLogged() {
|
|
response.SendError(c, http.StatusForbidden, nil)
|
|
return
|
|
}
|
|
|
|
response.Send(c, http.StatusOK, ctx.GetAccount())
|
|
}
|
|
|
|
// settingsHandler godoc
|
|
//
|
|
// @Summary Perform actions on the currently logged-in user.
|
|
// @Tags Auth
|
|
// @securityDefinitions.apikey ApiKeyAuth
|
|
// @Param payload body settingRequestPayload false "Config data"
|
|
// @Produce json
|
|
// @Success 200 {object} model.Account
|
|
// @Failure 403 {object} nil "Token not provided/invalid"
|
|
// @Router /api/v1/auth/account [patch]
|
|
func (r *AuthAPIRoutes) settingsHandler(c *gin.Context) {
|
|
ctx := context.NewContextFromGin(c)
|
|
if !ctx.UserIsLogged() {
|
|
response.SendError(c, http.StatusForbidden, nil)
|
|
}
|
|
var payload settingRequestPayload
|
|
if err := c.ShouldBindJSON(&payload); err != nil {
|
|
response.SendInternalServerError(c)
|
|
}
|
|
|
|
account := ctx.GetAccount()
|
|
account.Config = payload.Config
|
|
|
|
err := r.deps.Database.SaveAccountSettings(c, *account)
|
|
if err != nil {
|
|
response.SendInternalServerError(c)
|
|
}
|
|
|
|
response.Send(c, http.StatusOK, ctx.GetAccount())
|
|
}
|
|
|
|
func NewAuthAPIRoutes(logger *logrus.Logger, deps *config.Dependencies, loginHandler model.LegacyLoginHandler) *AuthAPIRoutes {
|
|
return &AuthAPIRoutes{
|
|
logger: logger,
|
|
deps: deps,
|
|
legacyLoginHandler: loginHandler,
|
|
}
|
|
}
|