code refactor

This commit is contained in:
MaysWind
2024-08-16 23:56:23 +08:00
parent e532f372b5
commit 560edf9fbf
27 changed files with 437 additions and 245 deletions
+2 -2
View File
@@ -153,7 +153,7 @@ func startWebServer(c *cli.Context) error {
router.StaticFile("/desktop/"+workboxFileNames[i], filepath.Join(config.StaticRootPath, workboxFileNames[i])) router.StaticFile("/desktop/"+workboxFileNames[i], filepath.Join(config.StaticRootPath, workboxFileNames[i]))
} }
if config.AvatarProvider == settings.InternalAvatarProvider { if config.AvatarProvider == core.USER_AVATAR_PROVIDER_INTERNAL {
avatarRoute := router.Group("/avatar") avatarRoute := router.Group("/avatar")
avatarRoute.Use(bindMiddleware(middlewares.JWTAuthorizationByQueryString)) avatarRoute.Use(bindMiddleware(middlewares.JWTAuthorizationByQueryString))
{ {
@@ -261,7 +261,7 @@ func startWebServer(c *cli.Context) error {
apiV1Route.GET("/users/profile/get.json", bindApi(api.Users.UserProfileHandler)) apiV1Route.GET("/users/profile/get.json", bindApi(api.Users.UserProfileHandler))
apiV1Route.POST("/users/profile/update.json", bindApiWithTokenUpdate(api.Users.UserUpdateProfileHandler, config)) apiV1Route.POST("/users/profile/update.json", bindApiWithTokenUpdate(api.Users.UserUpdateProfileHandler, config))
if config.AvatarProvider == settings.InternalAvatarProvider { if config.AvatarProvider == core.USER_AVATAR_PROVIDER_INTERNAL {
apiV1Route.POST("/users/avatar/update.json", bindApi(api.Users.UserUpdateAvatarHandler)) apiV1Route.POST("/users/avatar/update.json", bindApi(api.Users.UserUpdateAvatarHandler))
apiV1Route.POST("/users/avatar/remove.json", bindApi(api.Users.UserRemoveAvatarHandler)) apiV1Route.POST("/users/avatar/remove.json", bindApi(api.Users.UserRemoveAvatarHandler))
} }
+11 -3
View File
@@ -16,12 +16,20 @@ import (
// AccountsApi represents account api // AccountsApi represents account api
type AccountsApi struct { type AccountsApi struct {
ApiUsingConfig
ApiUsingDuplicateChecker
accounts *services.AccountService accounts *services.AccountService
} }
// Initialize an account api singleton instance // Initialize an account api singleton instance
var ( var (
Accounts = &AccountsApi{ Accounts = &AccountsApi{
ApiUsingConfig: ApiUsingConfig{
container: settings.Container,
},
ApiUsingDuplicateChecker: ApiUsingDuplicateChecker{
container: duplicatechecker.Container,
},
accounts: services.Accounts, accounts: services.Accounts,
} }
) )
@@ -211,8 +219,8 @@ func (a *AccountsApi) AccountCreateHandler(c *core.Context) (any, *errs.Error) {
mainAccount := a.createNewAccountModel(uid, &accountCreateReq, maxOrderId+1) mainAccount := a.createNewAccountModel(uid, &accountCreateReq, maxOrderId+1)
childrenAccounts := a.createSubAccountModels(uid, &accountCreateReq) childrenAccounts := a.createSubAccountModels(uid, &accountCreateReq)
if settings.Container.Current.EnableDuplicateSubmissionsCheck && accountCreateReq.ClientSessionId != "" { if a.CurrentConfig().EnableDuplicateSubmissionsCheck && accountCreateReq.ClientSessionId != "" {
found, remark := duplicatechecker.Container.GetSubmissionRemark(duplicatechecker.DUPLICATE_CHECKER_TYPE_NEW_ACCOUNT, uid, accountCreateReq.ClientSessionId) found, remark := a.GetSubmissionRemark(duplicatechecker.DUPLICATE_CHECKER_TYPE_NEW_ACCOUNT, uid, accountCreateReq.ClientSessionId)
if found { if found {
log.InfofWithRequestId(c, "[accounts.AccountCreateHandler] another account \"id:%s\" has been created for user \"uid:%d\"", remark, uid) log.InfofWithRequestId(c, "[accounts.AccountCreateHandler] another account \"id:%s\" has been created for user \"uid:%d\"", remark, uid)
@@ -256,7 +264,7 @@ func (a *AccountsApi) AccountCreateHandler(c *core.Context) (any, *errs.Error) {
log.InfofWithRequestId(c, "[accounts.AccountCreateHandler] user \"uid:%d\" has created a new account \"id:%d\" successfully", uid, mainAccount.AccountId) log.InfofWithRequestId(c, "[accounts.AccountCreateHandler] user \"uid:%d\" has created a new account \"id:%d\" successfully", uid, mainAccount.AccountId)
duplicatechecker.Container.SetSubmissionRemark(duplicatechecker.DUPLICATE_CHECKER_TYPE_NEW_ACCOUNT, uid, accountCreateReq.ClientSessionId, utils.Int64ToString(mainAccount.AccountId)) a.SetSubmissionRemark(duplicatechecker.DUPLICATE_CHECKER_TYPE_NEW_ACCOUNT, uid, accountCreateReq.ClientSessionId, utils.Int64ToString(mainAccount.AccountId))
accountInfoResp := mainAccount.ToAccountInfoResponse() accountInfoResp := mainAccount.ToAccountInfoResponse()
if len(childrenAccounts) > 0 { if len(childrenAccounts) > 0 {
+7 -2
View File
@@ -18,11 +18,16 @@ const amapRestApiUrl = "https://restapi.amap.com/"
// AmapApiProxy represents amap api proxy // AmapApiProxy represents amap api proxy
type AmapApiProxy struct { type AmapApiProxy struct {
ApiUsingConfig
} }
// Initialize a amap api proxy singleton instance // Initialize a amap api proxy singleton instance
var ( var (
AmapApis = &AmapApiProxy{} AmapApis = &AmapApiProxy{
ApiUsingConfig: ApiUsingConfig{
container: settings.Container,
},
}
) )
// AmapApiProxyHandler returns amap api response // AmapApiProxyHandler returns amap api response
@@ -38,7 +43,7 @@ func (p *AmapApiProxy) AmapApiProxyHandler(c *core.Context) (*httputil.ReversePr
} }
director := func(req *http.Request) { director := func(req *http.Request) {
targetRawUrl := fmt.Sprintf("%s?%s&jscode=%s", targetUrl, req.URL.RawQuery, settings.Container.Current.AmapApplicationSecret) targetRawUrl := fmt.Sprintf("%s?%s&jscode=%s", targetUrl, req.URL.RawQuery, p.CurrentConfig().AmapApplicationSecret)
targetUrl, _ := url.Parse(targetRawUrl) targetUrl, _ := url.Parse(targetRawUrl)
oldCookies := req.Cookies() oldCookies := req.Cookies()
+9 -5
View File
@@ -13,6 +13,7 @@ import (
// AuthorizationsApi represents authorization api // AuthorizationsApi represents authorization api
type AuthorizationsApi struct { type AuthorizationsApi struct {
ApiUsingConfig
users *services.UserService users *services.UserService
tokens *services.TokenService tokens *services.TokenService
twoFactorAuthorizations *services.TwoFactorAuthorizationService twoFactorAuthorizations *services.TwoFactorAuthorizationService
@@ -21,6 +22,9 @@ type AuthorizationsApi struct {
// Initialize a authorization api singleton instance // Initialize a authorization api singleton instance
var ( var (
Authorizations = &AuthorizationsApi{ Authorizations = &AuthorizationsApi{
ApiUsingConfig: ApiUsingConfig{
container: settings.Container,
},
users: services.Users, users: services.Users,
tokens: services.Tokens, tokens: services.Tokens,
twoFactorAuthorizations: services.TwoFactorAuthorizations, twoFactorAuthorizations: services.TwoFactorAuthorizations,
@@ -49,7 +53,7 @@ func (a *AuthorizationsApi) AuthorizeHandler(c *core.Context) (any, *errs.Error)
return nil, errs.ErrUserIsDisabled return nil, errs.ErrUserIsDisabled
} }
if settings.Container.Current.EnableUserForceVerifyEmail && !user.EmailVerified { if a.CurrentConfig().EnableUserForceVerifyEmail && !user.EmailVerified {
hasValidEmailVerifyToken, err := a.tokens.ExistsValidTokenByType(c, user.Uid, core.USER_TOKEN_TYPE_EMAIL_VERIFY) hasValidEmailVerifyToken, err := a.tokens.ExistsValidTokenByType(c, user.Uid, core.USER_TOKEN_TYPE_EMAIL_VERIFY)
if err != nil { if err != nil {
@@ -143,7 +147,7 @@ func (a *AuthorizationsApi) TwoFactorAuthorizeHandler(c *core.Context) (any, *er
return nil, errs.ErrUserIsDisabled return nil, errs.ErrUserIsDisabled
} }
if settings.Container.Current.EnableUserForceVerifyEmail && !user.EmailVerified { if a.CurrentConfig().EnableUserForceVerifyEmail && !user.EmailVerified {
log.WarnfWithRequestId(c, "[authorizations.TwoFactorAuthorizeHandler] user \"uid:%d\" has not verified email", user.Uid) log.WarnfWithRequestId(c, "[authorizations.TwoFactorAuthorizeHandler] user \"uid:%d\" has not verified email", user.Uid)
return nil, errs.ErrEmailIsNotVerified return nil, errs.ErrEmailIsNotVerified
} }
@@ -205,7 +209,7 @@ func (a *AuthorizationsApi) TwoFactorAuthorizeByRecoveryCodeHandler(c *core.Cont
return nil, errs.ErrUserIsDisabled return nil, errs.ErrUserIsDisabled
} }
if settings.Container.Current.EnableUserForceVerifyEmail && !user.EmailVerified { if a.CurrentConfig().EnableUserForceVerifyEmail && !user.EmailVerified {
log.WarnfWithRequestId(c, "[authorizations.TwoFactorAuthorizeByRecoveryCodeHandler] user \"uid:%d\" has not verified email", user.Uid) log.WarnfWithRequestId(c, "[authorizations.TwoFactorAuthorizeByRecoveryCodeHandler] user \"uid:%d\" has not verified email", user.Uid)
return nil, errs.ErrEmailIsNotVerified return nil, errs.ErrEmailIsNotVerified
} }
@@ -244,7 +248,7 @@ func (a *AuthorizationsApi) getAuthResponse(c *core.Context, token string, need2
return &models.AuthResponse{ return &models.AuthResponse{
Token: token, Token: token,
Need2FA: need2FA, Need2FA: need2FA,
User: user.ToUserBasicInfo(), User: a.GetUserBasicInfo(user),
NotificationContent: settings.Container.GetAfterLoginNotificationContent(user.Language, c.GetClientLocale()), NotificationContent: a.GetAfterLoginNotificationContent(user.Language, c.GetClientLocale()),
} }
} }
+94
View File
@@ -0,0 +1,94 @@
package api
import (
"github.com/mayswind/ezbookkeeping/pkg/duplicatechecker"
"github.com/mayswind/ezbookkeeping/pkg/models"
"github.com/mayswind/ezbookkeeping/pkg/settings"
)
// ApiUsingConfig represents an api that need to use config
type ApiUsingConfig struct {
container *settings.ConfigContainer
}
// CurrentConfig returns the current config
func (a *ApiUsingConfig) CurrentConfig() *settings.Config {
return a.container.Current
}
// GetUserBasicInfo returns the view-object of user basic info according to the user model
func (a *ApiUsingConfig) GetUserBasicInfo(user *models.User) *models.UserBasicInfo {
return user.ToUserBasicInfo(a.CurrentConfig().AvatarProvider, a.CurrentConfig().RootUrl)
}
// GetAfterRegisterNotificationContent returns the notification content displayed each time users register
func (a *ApiUsingConfig) GetAfterRegisterNotificationContent(userLanguage string, clientLanguage string) string {
language := userLanguage
if language == "" {
language = clientLanguage
}
if !a.container.Current.AfterRegisterNotification.Enabled {
return ""
}
if multiLanguageContent, exists := a.container.Current.AfterRegisterNotification.MultiLanguageContent[language]; exists {
return multiLanguageContent
}
return a.container.Current.AfterRegisterNotification.DefaultContent
}
// GetAfterLoginNotificationContent returns the notification content displayed each time users log in
func (a *ApiUsingConfig) GetAfterLoginNotificationContent(userLanguage string, clientLanguage string) string {
language := userLanguage
if language == "" {
language = clientLanguage
}
if !a.container.Current.AfterLoginNotification.Enabled {
return ""
}
if multiLanguageContent, exists := a.container.Current.AfterLoginNotification.MultiLanguageContent[language]; exists {
return multiLanguageContent
}
return a.container.Current.AfterLoginNotification.DefaultContent
}
// GetAfterOpenNotificationContent returns the notification content displayed each time users open the app
func (a *ApiUsingConfig) GetAfterOpenNotificationContent(userLanguage string, clientLanguage string) string {
language := userLanguage
if language == "" {
language = clientLanguage
}
if !a.container.Current.AfterOpenNotification.Enabled {
return ""
}
if multiLanguageContent, exists := a.container.Current.AfterOpenNotification.MultiLanguageContent[language]; exists {
return multiLanguageContent
}
return a.container.Current.AfterOpenNotification.DefaultContent
}
// ApiUsingDuplicateChecker represents an api that need to use duplicate checker
type ApiUsingDuplicateChecker struct {
container *duplicatechecker.DuplicateCheckerContainer
}
// GetSubmissionRemark returns whether the same submission has been processed and related remark by the current duplicate checker
func (a *ApiUsingDuplicateChecker) GetSubmissionRemark(checkerType duplicatechecker.DuplicateCheckerType, uid int64, identification string) (bool, string) {
return a.container.GetSubmissionRemark(checkerType, uid, identification)
}
// SetSubmissionRemark saves the identification and remark to in-memory cache by the current duplicate checker
func (a *ApiUsingDuplicateChecker) SetSubmissionRemark(checkerType duplicatechecker.DuplicateCheckerType, uid int64, identification string, remark string) {
a.container.SetSubmissionRemark(checkerType, uid, identification, remark)
}
+5 -1
View File
@@ -19,6 +19,7 @@ const pageCountForDataExport = 1000
// DataManagementsApi represents data management api // DataManagementsApi represents data management api
type DataManagementsApi struct { type DataManagementsApi struct {
ApiUsingConfig
ezBookKeepingCsvExporter *converters.EzBookKeepingCSVFileExporter ezBookKeepingCsvExporter *converters.EzBookKeepingCSVFileExporter
ezBookKeepingTsvExporter *converters.EzBookKeepingTSVFileExporter ezBookKeepingTsvExporter *converters.EzBookKeepingTSVFileExporter
tokens *services.TokenService tokens *services.TokenService
@@ -33,6 +34,9 @@ type DataManagementsApi struct {
// Initialize a data management api singleton instance // Initialize a data management api singleton instance
var ( var (
DataManagements = &DataManagementsApi{ DataManagements = &DataManagementsApi{
ApiUsingConfig: ApiUsingConfig{
container: settings.Container,
},
ezBookKeepingCsvExporter: &converters.EzBookKeepingCSVFileExporter{}, ezBookKeepingCsvExporter: &converters.EzBookKeepingCSVFileExporter{},
ezBookKeepingTsvExporter: &converters.EzBookKeepingTSVFileExporter{}, ezBookKeepingTsvExporter: &converters.EzBookKeepingTSVFileExporter{},
tokens: services.Tokens, tokens: services.Tokens,
@@ -162,7 +166,7 @@ func (a *DataManagementsApi) ClearDataHandler(c *core.Context) (any, *errs.Error
} }
func (a *DataManagementsApi) getExportedFileContent(c *core.Context, fileType string) ([]byte, string, *errs.Error) { func (a *DataManagementsApi) getExportedFileContent(c *core.Context, fileType string) ([]byte, string, *errs.Error) {
if !settings.Container.Current.EnableDataExport { if !a.CurrentConfig().EnableDataExport {
return nil, "", errs.ErrDataExportNotAllowed return nil, "", errs.ErrDataExportNotAllowed
} }
+11 -5
View File
@@ -18,11 +18,17 @@ import (
) )
// ExchangeRatesApi represents exchange rate api // ExchangeRatesApi represents exchange rate api
type ExchangeRatesApi struct{} type ExchangeRatesApi struct {
ApiUsingConfig
}
// Initialize a exchange rate api singleton instance // Initialize a exchange rate api singleton instance
var ( var (
ExchangeRates = &ExchangeRatesApi{} ExchangeRates = &ExchangeRatesApi{
ApiUsingConfig: ApiUsingConfig{
container: settings.Container,
},
}
) )
// LatestExchangeRateHandler returns latest exchange rate data // LatestExchangeRateHandler returns latest exchange rate data
@@ -36,9 +42,9 @@ func (a *ExchangeRatesApi) LatestExchangeRateHandler(c *core.Context) (any, *err
uid := c.GetCurrentUid() uid := c.GetCurrentUid()
transport := http.DefaultTransport.(*http.Transport).Clone() transport := http.DefaultTransport.(*http.Transport).Clone()
utils.SetProxyUrl(transport, settings.Container.Current.ExchangeRatesProxy) utils.SetProxyUrl(transport, a.CurrentConfig().ExchangeRatesProxy)
if settings.Container.Current.ExchangeRatesSkipTLSVerify { if a.CurrentConfig().ExchangeRatesSkipTLSVerify {
transport.TLSClientConfig = &tls.Config{ transport.TLSClientConfig = &tls.Config{
InsecureSkipVerify: true, InsecureSkipVerify: true,
} }
@@ -46,7 +52,7 @@ func (a *ExchangeRatesApi) LatestExchangeRateHandler(c *core.Context) (any, *err
client := &http.Client{ client := &http.Client{
Transport: transport, Transport: transport,
Timeout: time.Duration(settings.Container.Current.ExchangeRatesRequestTimeout) * time.Millisecond, Timeout: time.Duration(a.CurrentConfig().ExchangeRatesRequestTimeout) * time.Millisecond,
} }
urls := dataSource.GetRequestUrls() urls := dataSource.GetRequestUrls()
+7 -3
View File
@@ -13,6 +13,7 @@ import (
// ForgetPasswordsApi represents user forget password api // ForgetPasswordsApi represents user forget password api
type ForgetPasswordsApi struct { type ForgetPasswordsApi struct {
ApiUsingConfig
users *services.UserService users *services.UserService
tokens *services.TokenService tokens *services.TokenService
forgetPasswords *services.ForgetPasswordService forgetPasswords *services.ForgetPasswordService
@@ -21,6 +22,9 @@ type ForgetPasswordsApi struct {
// Initialize a user api singleton instance // Initialize a user api singleton instance
var ( var (
ForgetPasswords = &ForgetPasswordsApi{ ForgetPasswords = &ForgetPasswordsApi{
ApiUsingConfig: ApiUsingConfig{
container: settings.Container,
},
users: services.Users, users: services.Users,
tokens: services.Tokens, tokens: services.Tokens,
forgetPasswords: services.ForgetPasswords, forgetPasswords: services.ForgetPasswords,
@@ -52,12 +56,12 @@ func (a *ForgetPasswordsApi) UserForgetPasswordRequestHandler(c *core.Context) (
return nil, errs.ErrUserIsDisabled return nil, errs.ErrUserIsDisabled
} }
if settings.Container.Current.ForgetPasswordRequireVerifyEmail && !user.EmailVerified { if a.CurrentConfig().ForgetPasswordRequireVerifyEmail && !user.EmailVerified {
log.WarnfWithRequestId(c, "[forget_passwords.UserForgetPasswordRequestHandler] user \"uid:%d\" has not verified email", user.Uid) log.WarnfWithRequestId(c, "[forget_passwords.UserForgetPasswordRequestHandler] user \"uid:%d\" has not verified email", user.Uid)
return nil, errs.ErrEmailIsNotVerified return nil, errs.ErrEmailIsNotVerified
} }
if !settings.Container.Current.EnableSMTP { if !a.CurrentConfig().EnableSMTP {
return nil, errs.ErrSMTPServerNotEnabled return nil, errs.ErrSMTPServerNotEnabled
} }
@@ -105,7 +109,7 @@ func (a *ForgetPasswordsApi) UserResetPasswordHandler(c *core.Context) (any, *er
return nil, errs.ErrUserIsDisabled return nil, errs.ErrUserIsDisabled
} }
if settings.Container.Current.ForgetPasswordRequireVerifyEmail && !user.EmailVerified { if a.CurrentConfig().ForgetPasswordRequireVerifyEmail && !user.EmailVerified {
log.WarnfWithRequestId(c, "[forget_passwords.UserResetPasswordHandler] user \"uid:%d\" has not verified email", user.Uid) log.WarnfWithRequestId(c, "[forget_passwords.UserResetPasswordHandler] user \"uid:%d\" has not verified email", user.Uid)
return nil, errs.ErrEmailIsNotVerified return nil, errs.ErrEmailIsNotVerified
} }
+13 -8
View File
@@ -24,11 +24,16 @@ const tianDiTuMapAnnotationUrlFormat = "https://t0.tianditu.gov.cn/cva_w/wmts?SE
// MapImageProxy represents map image proxy // MapImageProxy represents map image proxy
type MapImageProxy struct { type MapImageProxy struct {
ApiUsingConfig
} }
// Initialize a map image proxy singleton instance // Initialize a map image proxy singleton instance
var ( var (
MapImages = &MapImageProxy{} MapImages = &MapImageProxy{
ApiUsingConfig: ApiUsingConfig{
container: settings.Container,
},
}
) )
// MapTileImageProxyHandler returns map tile image // MapTileImageProxyHandler returns map tile image
@@ -47,7 +52,7 @@ func (p *MapImageProxy) MapTileImageProxyHandler(c *core.Context) (*httputil.Rev
} else if mapProvider == settings.CartoDBMapProvider { } else if mapProvider == settings.CartoDBMapProvider {
return cartoDBMapTileImageUrlFormat, nil return cartoDBMapTileImageUrlFormat, nil
} else if mapProvider == settings.TomTomMapProvider { } else if mapProvider == settings.TomTomMapProvider {
targetUrl := tomtomMapTileImageUrlFormat + "?key=" + settings.Container.Current.TomTomMapAPIKey targetUrl := tomtomMapTileImageUrlFormat + "?key=" + p.CurrentConfig().TomTomMapAPIKey
language := c.Query("language") language := c.Query("language")
if language != "" { if language != "" {
@@ -56,9 +61,9 @@ func (p *MapImageProxy) MapTileImageProxyHandler(c *core.Context) (*httputil.Rev
return targetUrl, nil return targetUrl, nil
} else if mapProvider == settings.TianDiTuProvider { } else if mapProvider == settings.TianDiTuProvider {
return tianDiTuMapTileImageUrlFormat + "&tk=" + settings.Container.Current.TianDiTuAPIKey, nil return tianDiTuMapTileImageUrlFormat + "&tk=" + p.CurrentConfig().TianDiTuAPIKey, nil
} else if mapProvider == settings.CustomProvider { } else if mapProvider == settings.CustomProvider {
return settings.Container.Current.CustomMapTileServerTileLayerUrl, nil return p.CurrentConfig().CustomMapTileServerTileLayerUrl, nil
} }
return "", errs.ErrParameterInvalid return "", errs.ErrParameterInvalid
@@ -69,9 +74,9 @@ func (p *MapImageProxy) MapTileImageProxyHandler(c *core.Context) (*httputil.Rev
func (p *MapImageProxy) MapAnnotationImageProxyHandler(c *core.Context) (*httputil.ReverseProxy, *errs.Error) { func (p *MapImageProxy) MapAnnotationImageProxyHandler(c *core.Context) (*httputil.ReverseProxy, *errs.Error) {
return p.mapImageProxyHandler(c, func(c *core.Context, mapProvider string) (string, *errs.Error) { return p.mapImageProxyHandler(c, func(c *core.Context, mapProvider string) (string, *errs.Error) {
if mapProvider == settings.TianDiTuProvider { if mapProvider == settings.TianDiTuProvider {
return tianDiTuMapAnnotationUrlFormat + "&tk=" + settings.Container.Current.TianDiTuAPIKey, nil return tianDiTuMapAnnotationUrlFormat + "&tk=" + p.CurrentConfig().TianDiTuAPIKey, nil
} else if mapProvider == settings.CustomProvider { } else if mapProvider == settings.CustomProvider {
return settings.Container.Current.CustomMapTileServerAnnotationLayerUrl, nil return p.CurrentConfig().CustomMapTileServerAnnotationLayerUrl, nil
} }
return "", errs.ErrParameterInvalid return "", errs.ErrParameterInvalid
@@ -82,7 +87,7 @@ func (p *MapImageProxy) mapImageProxyHandler(c *core.Context, fn func(c *core.Co
mapProvider := strings.Replace(c.Query("provider"), "-", "_", -1) mapProvider := strings.Replace(c.Query("provider"), "-", "_", -1)
targetUrl := "" targetUrl := ""
if mapProvider != settings.Container.Current.MapProvider { if mapProvider != p.CurrentConfig().MapProvider {
return nil, errs.ErrMapProviderNotCurrent return nil, errs.ErrMapProviderNotCurrent
} }
@@ -105,7 +110,7 @@ func (p *MapImageProxy) mapImageProxyHandler(c *core.Context, fn func(c *core.Co
} }
transport := http.DefaultTransport.(*http.Transport).Clone() transport := http.DefaultTransport.(*http.Transport).Clone()
utils.SetProxyUrl(transport, settings.Container.Current.MapProxy) utils.SetProxyUrl(transport, p.CurrentConfig().MapProxy)
director := func(req *http.Request) { director := func(req *http.Request) {
imageRawUrl := targetUrl imageRawUrl := targetUrl
+7 -2
View File
@@ -19,16 +19,21 @@ const (
// QrCodesApi represents qrcode generator api // QrCodesApi represents qrcode generator api
type QrCodesApi struct { type QrCodesApi struct {
ApiUsingConfig
} }
// Initialize a qrcode generator api singleton instance // Initialize a qrcode generator api singleton instance
var ( var (
QrCodes = &QrCodesApi{} QrCodes = &QrCodesApi{
ApiUsingConfig: ApiUsingConfig{
container: settings.Container,
},
}
) )
// MobileUrlQrCodeHandler returns a mobile url qr code image // MobileUrlQrCodeHandler returns a mobile url qr code image
func (a *QrCodesApi) MobileUrlQrCodeHandler(c *core.Context) ([]byte, string, *errs.Error) { func (a *QrCodesApi) MobileUrlQrCodeHandler(c *core.Context) ([]byte, string, *errs.Error) {
fullUrl := settings.Container.Current.RootUrl + "mobile" fullUrl := a.CurrentConfig().RootUrl + "mobile"
data, err := a.generateUrlQrCode(c, fullUrl) data, err := a.generateUrlQrCode(c, fullUrl)
if err != nil { if err != nil {
+9 -5
View File
@@ -15,6 +15,7 @@ import (
// TokensApi represents token api // TokensApi represents token api
type TokensApi struct { type TokensApi struct {
ApiUsingConfig
tokens *services.TokenService tokens *services.TokenService
users *services.UserService users *services.UserService
} }
@@ -22,6 +23,9 @@ type TokensApi struct {
// Initialize a token api singleton instance // Initialize a token api singleton instance
var ( var (
Tokens = &TokensApi{ Tokens = &TokensApi{
ApiUsingConfig: ApiUsingConfig{
container: settings.Container,
},
tokens: services.Tokens, tokens: services.Tokens,
users: services.Users, users: services.Users,
} }
@@ -180,7 +184,7 @@ func (a *TokensApi) TokenRefreshHandler(c *core.Context) (any, *errs.Error) {
now := time.Now().Unix() now := time.Now().Unix()
oldTokenClaims := c.GetTokenClaims() oldTokenClaims := c.GetTokenClaims()
if now-oldTokenClaims.IssuedAt < int64(settings.Container.Current.TokenMinRefreshInterval) { if now-oldTokenClaims.IssuedAt < int64(a.CurrentConfig().TokenMinRefreshInterval) {
log.InfofWithRequestId(c, "[token.TokenRefreshHandler] token of user \"uid:%d\" does not need to be refreshed", uid) log.InfofWithRequestId(c, "[token.TokenRefreshHandler] token of user \"uid:%d\" does not need to be refreshed", uid)
userTokenId, err := utils.StringToInt64(oldTokenClaims.UserTokenId) userTokenId, err := utils.StringToInt64(oldTokenClaims.UserTokenId)
@@ -204,8 +208,8 @@ func (a *TokensApi) TokenRefreshHandler(c *core.Context) (any, *errs.Error) {
} }
refreshResp := &models.TokenRefreshResponse{ refreshResp := &models.TokenRefreshResponse{
User: user.ToUserBasicInfo(), User: a.GetUserBasicInfo(user),
NotificationContent: settings.Container.GetAfterOpenNotificationContent(user.Language, c.GetClientLocale()), NotificationContent: a.GetAfterOpenNotificationContent(user.Language, c.GetClientLocale()),
} }
return refreshResp, nil return refreshResp, nil
@@ -233,8 +237,8 @@ func (a *TokensApi) TokenRefreshHandler(c *core.Context) (any, *errs.Error) {
refreshResp := &models.TokenRefreshResponse{ refreshResp := &models.TokenRefreshResponse{
NewToken: token, NewToken: token,
OldTokenId: a.tokens.GenerateTokenId(oldTokenRecord), OldTokenId: a.tokens.GenerateTokenId(oldTokenRecord),
User: user.ToUserBasicInfo(), User: a.GetUserBasicInfo(user),
NotificationContent: settings.Container.GetAfterOpenNotificationContent(user.Language, c.GetClientLocale()), NotificationContent: a.GetAfterOpenNotificationContent(user.Language, c.GetClientLocale()),
} }
return refreshResp, nil return refreshResp, nil
+11 -3
View File
@@ -17,12 +17,20 @@ import (
// TransactionCategoriesApi represents transaction category api // TransactionCategoriesApi represents transaction category api
type TransactionCategoriesApi struct { type TransactionCategoriesApi struct {
ApiUsingConfig
ApiUsingDuplicateChecker
categories *services.TransactionCategoryService categories *services.TransactionCategoryService
} }
// Initialize a transaction category api singleton instance // Initialize a transaction category api singleton instance
var ( var (
TransactionCategories = &TransactionCategoriesApi{ TransactionCategories = &TransactionCategoriesApi{
ApiUsingConfig: ApiUsingConfig{
container: settings.Container,
},
ApiUsingDuplicateChecker: ApiUsingDuplicateChecker{
container: duplicatechecker.Container,
},
categories: services.TransactionCategories, categories: services.TransactionCategories,
} }
) )
@@ -122,8 +130,8 @@ func (a *TransactionCategoriesApi) CategoryCreateHandler(c *core.Context) (any,
category := a.createNewCategoryModel(uid, &categoryCreateReq, maxOrderId+1) category := a.createNewCategoryModel(uid, &categoryCreateReq, maxOrderId+1)
if settings.Container.Current.EnableDuplicateSubmissionsCheck && categoryCreateReq.ClientSessionId != "" { if a.CurrentConfig().EnableDuplicateSubmissionsCheck && categoryCreateReq.ClientSessionId != "" {
found, remark := duplicatechecker.Container.GetSubmissionRemark(duplicatechecker.DUPLICATE_CHECKER_TYPE_NEW_CATEGORY, uid, categoryCreateReq.ClientSessionId) found, remark := a.GetSubmissionRemark(duplicatechecker.DUPLICATE_CHECKER_TYPE_NEW_CATEGORY, uid, categoryCreateReq.ClientSessionId)
if found { if found {
log.InfofWithRequestId(c, "[transaction_categories.CategoryCreateHandler] another category \"id:%s\" has been created for user \"uid:%d\"", remark, uid) log.InfofWithRequestId(c, "[transaction_categories.CategoryCreateHandler] another category \"id:%s\" has been created for user \"uid:%d\"", remark, uid)
@@ -153,7 +161,7 @@ func (a *TransactionCategoriesApi) CategoryCreateHandler(c *core.Context) (any,
log.InfofWithRequestId(c, "[transaction_categories.CategoryCreateHandler] user \"uid:%d\" has created a new category \"id:%d\" successfully", uid, category.CategoryId) log.InfofWithRequestId(c, "[transaction_categories.CategoryCreateHandler] user \"uid:%d\" has created a new category \"id:%d\" successfully", uid, category.CategoryId)
duplicatechecker.Container.SetSubmissionRemark(duplicatechecker.DUPLICATE_CHECKER_TYPE_NEW_CATEGORY, uid, categoryCreateReq.ClientSessionId, utils.Int64ToString(category.CategoryId)) a.SetSubmissionRemark(duplicatechecker.DUPLICATE_CHECKER_TYPE_NEW_CATEGORY, uid, categoryCreateReq.ClientSessionId, utils.Int64ToString(category.CategoryId))
categoryResp := category.ToTransactionCategoryInfoResponse() categoryResp := category.ToTransactionCategoryInfoResponse()
return categoryResp, nil return categoryResp, nil
+11 -3
View File
@@ -16,12 +16,20 @@ import (
// TransactionTemplatesApi represents transaction template api // TransactionTemplatesApi represents transaction template api
type TransactionTemplatesApi struct { type TransactionTemplatesApi struct {
ApiUsingConfig
ApiUsingDuplicateChecker
templates *services.TransactionTemplateService templates *services.TransactionTemplateService
} }
// Initialize a transaction template api singleton instance // Initialize a transaction template api singleton instance
var ( var (
TransactionTemplates = &TransactionTemplatesApi{ TransactionTemplates = &TransactionTemplatesApi{
ApiUsingConfig: ApiUsingConfig{
container: settings.Container,
},
ApiUsingDuplicateChecker: ApiUsingDuplicateChecker{
container: duplicatechecker.Container,
},
templates: services.TransactionTemplates, templates: services.TransactionTemplates,
} }
) )
@@ -117,8 +125,8 @@ func (a *TransactionTemplatesApi) TemplateCreateHandler(c *core.Context) (any, *
serverUtcOffset := utils.GetServerTimezoneOffsetMinutes() serverUtcOffset := utils.GetServerTimezoneOffsetMinutes()
template := a.createNewTemplateModel(uid, &templateCreateReq, maxOrderId+1) template := a.createNewTemplateModel(uid, &templateCreateReq, maxOrderId+1)
if settings.Container.Current.EnableDuplicateSubmissionsCheck && templateCreateReq.ClientSessionId != "" { if a.CurrentConfig().EnableDuplicateSubmissionsCheck && templateCreateReq.ClientSessionId != "" {
found, remark := duplicatechecker.Container.GetSubmissionRemark(duplicatechecker.DUPLICATE_CHECKER_TYPE_NEW_TEMPLATE, uid, templateCreateReq.ClientSessionId) found, remark := a.GetSubmissionRemark(duplicatechecker.DUPLICATE_CHECKER_TYPE_NEW_TEMPLATE, uid, templateCreateReq.ClientSessionId)
if found { if found {
log.InfofWithRequestId(c, "[transaction_templates.TemplateCreateHandler] another template \"id:%s\" has been created for user \"uid:%d\"", remark, uid) log.InfofWithRequestId(c, "[transaction_templates.TemplateCreateHandler] another template \"id:%s\" has been created for user \"uid:%d\"", remark, uid)
@@ -148,7 +156,7 @@ func (a *TransactionTemplatesApi) TemplateCreateHandler(c *core.Context) (any, *
log.InfofWithRequestId(c, "[transaction_templates.TemplateCreateHandler] user \"uid:%d\" has created a new template \"id:%d\" successfully", uid, template.TemplateId) log.InfofWithRequestId(c, "[transaction_templates.TemplateCreateHandler] user \"uid:%d\" has created a new template \"id:%d\" successfully", uid, template.TemplateId)
duplicatechecker.Container.SetSubmissionRemark(duplicatechecker.DUPLICATE_CHECKER_TYPE_NEW_TEMPLATE, uid, templateCreateReq.ClientSessionId, utils.Int64ToString(template.TemplateId)) a.SetSubmissionRemark(duplicatechecker.DUPLICATE_CHECKER_TYPE_NEW_TEMPLATE, uid, templateCreateReq.ClientSessionId, utils.Int64ToString(template.TemplateId))
templateResp := template.ToTransactionTemplateInfoResponse(serverUtcOffset) templateResp := template.ToTransactionTemplateInfoResponse(serverUtcOffset)
return templateResp, nil return templateResp, nil
+11 -3
View File
@@ -18,6 +18,8 @@ import (
// TransactionsApi represents transaction api // TransactionsApi represents transaction api
type TransactionsApi struct { type TransactionsApi struct {
ApiUsingConfig
ApiUsingDuplicateChecker
transactions *services.TransactionService transactions *services.TransactionService
transactionCategories *services.TransactionCategoryService transactionCategories *services.TransactionCategoryService
transactionTags *services.TransactionTagService transactionTags *services.TransactionTagService
@@ -28,6 +30,12 @@ type TransactionsApi struct {
// Initialize a transaction api singleton instance // Initialize a transaction api singleton instance
var ( var (
Transactions = &TransactionsApi{ Transactions = &TransactionsApi{
ApiUsingConfig: ApiUsingConfig{
container: settings.Container,
},
ApiUsingDuplicateChecker: ApiUsingDuplicateChecker{
container: duplicatechecker.Container,
},
transactions: services.Transactions, transactions: services.Transactions,
transactionCategories: services.TransactionCategories, transactionCategories: services.TransactionCategories,
transactionTags: services.TransactionTags, transactionTags: services.TransactionTags,
@@ -663,8 +671,8 @@ func (a *TransactionsApi) TransactionCreateHandler(c *core.Context) (any, *errs.
return nil, errs.ErrCannotCreateTransactionWithThisTransactionTime return nil, errs.ErrCannotCreateTransactionWithThisTransactionTime
} }
if settings.Container.Current.EnableDuplicateSubmissionsCheck && transactionCreateReq.ClientSessionId != "" { if a.CurrentConfig().EnableDuplicateSubmissionsCheck && transactionCreateReq.ClientSessionId != "" {
found, remark := duplicatechecker.Container.GetSubmissionRemark(duplicatechecker.DUPLICATE_CHECKER_TYPE_NEW_TRANSACTION, uid, transactionCreateReq.ClientSessionId) found, remark := a.GetSubmissionRemark(duplicatechecker.DUPLICATE_CHECKER_TYPE_NEW_TRANSACTION, uid, transactionCreateReq.ClientSessionId)
if found { if found {
log.InfofWithRequestId(c, "[transactions.TransactionCreateHandler] another transaction \"id:%s\" has been created for user \"uid:%d\"", remark, uid) log.InfofWithRequestId(c, "[transactions.TransactionCreateHandler] another transaction \"id:%s\" has been created for user \"uid:%d\"", remark, uid)
@@ -694,7 +702,7 @@ func (a *TransactionsApi) TransactionCreateHandler(c *core.Context) (any, *errs.
log.InfofWithRequestId(c, "[transactions.TransactionCreateHandler] user \"uid:%d\" has created a new transaction \"id:%d\" successfully", uid, transaction.TransactionId) log.InfofWithRequestId(c, "[transactions.TransactionCreateHandler] user \"uid:%d\" has created a new transaction \"id:%d\" successfully", uid, transaction.TransactionId)
duplicatechecker.Container.SetSubmissionRemark(duplicatechecker.DUPLICATE_CHECKER_TYPE_NEW_TRANSACTION, uid, transactionCreateReq.ClientSessionId, utils.Int64ToString(transaction.TransactionId)) a.SetSubmissionRemark(duplicatechecker.DUPLICATE_CHECKER_TYPE_NEW_TRANSACTION, uid, transactionCreateReq.ClientSessionId, utils.Int64ToString(transaction.TransactionId))
transactionResp := transaction.ToTransactionInfoResponse(tagIds, transactionEditable) transactionResp := transaction.ToTransactionInfoResponse(tagIds, transactionEditable)
return transactionResp, nil return transactionResp, nil
+32 -93
View File
@@ -1,8 +1,6 @@
package api package api
import ( import (
"io"
"os"
"strings" "strings"
"time" "time"
@@ -15,13 +13,13 @@ import (
"github.com/mayswind/ezbookkeeping/pkg/models" "github.com/mayswind/ezbookkeeping/pkg/models"
"github.com/mayswind/ezbookkeeping/pkg/services" "github.com/mayswind/ezbookkeeping/pkg/services"
"github.com/mayswind/ezbookkeeping/pkg/settings" "github.com/mayswind/ezbookkeeping/pkg/settings"
"github.com/mayswind/ezbookkeeping/pkg/storage"
"github.com/mayswind/ezbookkeeping/pkg/utils" "github.com/mayswind/ezbookkeeping/pkg/utils"
"github.com/mayswind/ezbookkeeping/pkg/validators" "github.com/mayswind/ezbookkeeping/pkg/validators"
) )
// UsersApi represents user api // UsersApi represents user api
type UsersApi struct { type UsersApi struct {
ApiUsingConfig
users *services.UserService users *services.UserService
tokens *services.TokenService tokens *services.TokenService
accounts *services.AccountService accounts *services.AccountService
@@ -30,6 +28,9 @@ type UsersApi struct {
// Initialize a user api singleton instance // Initialize a user api singleton instance
var ( var (
Users = &UsersApi{ Users = &UsersApi{
ApiUsingConfig: ApiUsingConfig{
container: settings.Container,
},
users: services.Users, users: services.Users,
tokens: services.Tokens, tokens: services.Tokens,
accounts: services.Accounts, accounts: services.Accounts,
@@ -38,7 +39,7 @@ var (
// UserRegisterHandler saves a new user by request parameters // UserRegisterHandler saves a new user by request parameters
func (a *UsersApi) UserRegisterHandler(c *core.Context) (any, *errs.Error) { func (a *UsersApi) UserRegisterHandler(c *core.Context) (any, *errs.Error) {
if !settings.Container.Current.EnableUserRegister { if !a.CurrentConfig().EnableUserRegister {
return nil, errs.ErrUserRegistrationNotAllowed return nil, errs.ErrUserRegistrationNotAllowed
} }
@@ -92,14 +93,14 @@ func (a *UsersApi) UserRegisterHandler(c *core.Context) (any, *errs.Error) {
authResp := &models.RegisterResponse{ authResp := &models.RegisterResponse{
AuthResponse: models.AuthResponse{ AuthResponse: models.AuthResponse{
Need2FA: false, Need2FA: false,
User: user.ToUserBasicInfo(), User: a.GetUserBasicInfo(user),
NotificationContent: settings.Container.GetAfterRegisterNotificationContent(user.Language, c.GetClientLocale()), NotificationContent: a.GetAfterRegisterNotificationContent(user.Language, c.GetClientLocale()),
}, },
NeedVerifyEmail: settings.Container.Current.EnableUserVerifyEmail && settings.Container.Current.EnableUserForceVerifyEmail, NeedVerifyEmail: a.CurrentConfig().EnableUserVerifyEmail && a.CurrentConfig().EnableUserForceVerifyEmail,
PresetCategoriesSaved: presetCategoriesSaved, PresetCategoriesSaved: presetCategoriesSaved,
} }
if settings.Container.Current.EnableUserVerifyEmail && settings.Container.Current.EnableSMTP { if a.CurrentConfig().EnableUserVerifyEmail && a.CurrentConfig().EnableSMTP {
token, _, err := a.tokens.CreateEmailVerifyToken(c, user) token, _, err := a.tokens.CreateEmailVerifyToken(c, user)
if err != nil { if err != nil {
@@ -115,7 +116,7 @@ func (a *UsersApi) UserRegisterHandler(c *core.Context) (any, *errs.Error) {
} }
} }
if settings.Container.Current.EnableUserForceVerifyEmail { if a.CurrentConfig().EnableUserForceVerifyEmail {
return authResp, nil return authResp, nil
} }
@@ -187,8 +188,8 @@ func (a *UsersApi) UserEmailVerifyHandler(c *core.Context) (any, *errs.Error) {
} }
resp.NewToken = token resp.NewToken = token
resp.User = user.ToUserBasicInfo() resp.User = a.GetUserBasicInfo(user)
resp.NotificationContent = settings.Container.GetAfterLoginNotificationContent(user.Language, c.GetClientLocale()) resp.NotificationContent = a.GetAfterLoginNotificationContent(user.Language, c.GetClientLocale())
c.SetTextualToken(token) c.SetTextualToken(token)
c.SetTokenClaims(claims) c.SetTokenClaims(claims)
@@ -212,7 +213,7 @@ func (a *UsersApi) UserProfileHandler(c *core.Context) (any, *errs.Error) {
return nil, errs.ErrUserNotFound return nil, errs.ErrUserNotFound
} }
userResp := user.ToUserProfileResponse() userResp := a.getUserProfileResponse(user)
return userResp, nil return userResp, nil
} }
@@ -447,10 +448,10 @@ func (a *UsersApi) UserUpdateProfileHandler(c *core.Context) (any, *errs.Error)
log.InfofWithRequestId(c, "[users.UserUpdateProfileHandler] user \"uid:%d\" has updated successfully", user.Uid) log.InfofWithRequestId(c, "[users.UserUpdateProfileHandler] user \"uid:%d\" has updated successfully", user.Uid)
resp := &models.UserProfileUpdateResponse{ resp := &models.UserProfileUpdateResponse{
User: user.ToUserBasicInfo(), User: a.GetUserBasicInfo(user),
} }
if emailSetToUnverified && settings.Container.Current.EnableUserVerifyEmail && settings.Container.Current.EnableSMTP { if emailSetToUnverified && a.CurrentConfig().EnableUserVerifyEmail && a.CurrentConfig().EnableSMTP {
err = a.tokens.DeleteTokensByType(c, uid, core.USER_TOKEN_TYPE_EMAIL_VERIFY) err = a.tokens.DeleteTokensByType(c, uid, core.USER_TOKEN_TYPE_EMAIL_VERIFY)
if err != nil { if err != nil {
@@ -547,32 +548,15 @@ func (a *UsersApi) UserUpdateAvatarHandler(c *core.Context) (any, *errs.Error) {
return nil, errs.ErrOperationFailed return nil, errs.ErrOperationFailed
} }
defer avatarFile.Close() err = a.users.UpdateUserAvatar(c, user.Uid, avatarFile, fileExtension, user.CustomAvatarType)
err = storage.Container.SaveAvatar(user.Uid, avatarFile, fileExtension)
if err != nil { if err != nil {
log.ErrorfWithRequestId(c, "[users.UserUpdateAvatarHandler] failed to save avatar file for user \"uid:%d\", because %s", user.Uid, err.Error()) log.ErrorfWithRequestId(c, "[users.UserUpdateAvatarHandler] failed to update avatar for user \"uid:%d\", because %s", user.Uid, err.Error())
return nil, errs.ErrOperationFailed
}
err = a.users.UpdateUserAvatar(c, user.Uid, fileExtension)
if err != nil {
log.ErrorfWithRequestId(c, "[users.UserUpdateAvatarHandler] failed to update user \"uid:%d\", because %s", user.Uid, err.Error())
return nil, errs.Or(err, errs.ErrOperationFailed) return nil, errs.Or(err, errs.ErrOperationFailed)
} }
if fileExtension != user.CustomAvatarType && user.CustomAvatarType != "" {
err = storage.Container.DeleteAvatar(user.Uid, user.CustomAvatarType)
if err != nil {
log.WarnfWithRequestId(c, "[users.UserUpdateAvatarHandler] failed to delete old avatar with extension \"%s\" for user \"uid:%d\", because %s", user.CustomAvatarType, user.Uid, err.Error())
}
}
user.CustomAvatarType = fileExtension user.CustomAvatarType = fileExtension
userResp := user.ToUserProfileResponse() userResp := a.getUserProfileResponse(user)
return userResp, nil return userResp, nil
} }
@@ -593,39 +577,21 @@ func (a *UsersApi) UserRemoveAvatarHandler(c *core.Context) (any, *errs.Error) {
return nil, errs.ErrNothingWillBeUpdated return nil, errs.ErrNothingWillBeUpdated
} }
err = storage.Container.DeleteAvatar(user.Uid, user.CustomAvatarType) err = a.users.RemoveUserAvatar(c, user.Uid, user.CustomAvatarType)
if err != nil { if err != nil {
log.ErrorfWithRequestId(c, "[users.UserRemoveAvatarHandler] failed to delete avatar file for user \"uid:%d\", because %s", user.Uid, err.Error()) log.ErrorfWithRequestId(c, "[users.UserRemoveAvatarHandler] failed to remove avatar for user \"uid:%d\", because %s", user.Uid, err.Error())
exists, err := storage.Container.ExistsAvatar(user.Uid, user.CustomAvatarType)
if err != nil {
log.ErrorfWithRequestId(c, "[users.UserRemoveAvatarHandler] failed to check whether avatar file exist for user \"uid:%d\", because %s", user.Uid, err.Error())
return nil, errs.ErrOperationFailed
}
if exists {
log.ErrorfWithRequestId(c, "[users.UserRemoveAvatarHandler] failed to delete whether avatar file exist for user \"uid:%d\", the avatar file still exist", user.Uid)
return nil, errs.ErrOperationFailed
}
}
err = a.users.UpdateUserAvatar(c, user.Uid, "")
if err != nil {
log.ErrorfWithRequestId(c, "[users.UserRemoveAvatarHandler] failed to update user \"uid:%d\", because %s", user.Uid, err.Error())
return nil, errs.Or(err, errs.ErrOperationFailed) return nil, errs.Or(err, errs.ErrOperationFailed)
} }
user.CustomAvatarType = "" user.CustomAvatarType = ""
userResp := user.ToUserProfileResponse() userResp := a.getUserProfileResponse(user)
return userResp, nil return userResp, nil
} }
// UserSendVerifyEmailByUnloginUserHandler sends unlogin user verify email // UserSendVerifyEmailByUnloginUserHandler sends unlogin user verify email
func (a *UsersApi) UserSendVerifyEmailByUnloginUserHandler(c *core.Context) (any, *errs.Error) { func (a *UsersApi) UserSendVerifyEmailByUnloginUserHandler(c *core.Context) (any, *errs.Error) {
if !settings.Container.Current.EnableUserVerifyEmail { if !a.CurrentConfig().EnableUserVerifyEmail {
return nil, errs.ErrEmailValidationNotAllowed return nil, errs.ErrEmailValidationNotAllowed
} }
@@ -657,7 +623,7 @@ func (a *UsersApi) UserSendVerifyEmailByUnloginUserHandler(c *core.Context) (any
return nil, errs.ErrEmailIsVerified return nil, errs.ErrEmailIsVerified
} }
if !settings.Container.Current.EnableSMTP { if !a.CurrentConfig().EnableSMTP {
return nil, errs.ErrSMTPServerNotEnabled return nil, errs.ErrSMTPServerNotEnabled
} }
@@ -681,7 +647,7 @@ func (a *UsersApi) UserSendVerifyEmailByUnloginUserHandler(c *core.Context) (any
// UserSendVerifyEmailByLoginedUserHandler sends logined user verify email // UserSendVerifyEmailByLoginedUserHandler sends logined user verify email
func (a *UsersApi) UserSendVerifyEmailByLoginedUserHandler(c *core.Context) (any, *errs.Error) { func (a *UsersApi) UserSendVerifyEmailByLoginedUserHandler(c *core.Context) (any, *errs.Error) {
if !settings.Container.Current.EnableUserVerifyEmail { if !a.CurrentConfig().EnableUserVerifyEmail {
return nil, errs.ErrEmailValidationNotAllowed return nil, errs.ErrEmailValidationNotAllowed
} }
@@ -701,7 +667,7 @@ func (a *UsersApi) UserSendVerifyEmailByLoginedUserHandler(c *core.Context) (any
return nil, errs.ErrEmailIsVerified return nil, errs.ErrEmailIsVerified
} }
if !settings.Container.Current.EnableSMTP { if !a.CurrentConfig().EnableSMTP {
return nil, errs.ErrSMTPServerNotEnabled return nil, errs.ErrSMTPServerNotEnabled
} }
@@ -741,46 +707,19 @@ func (a *UsersApi) UserGetAvatarHandler(c *core.Context) ([]byte, string, *errs.
return nil, "", errs.ErrUserIdInvalid return nil, "", errs.ErrUserIdInvalid
} }
user, err := a.users.GetUserById(c, uid) avatarData, err := a.users.GetUserAvatar(c, uid, fileExtension)
if err != nil { if err != nil {
if !errs.IsCustomError(err) { if !errs.IsCustomError(err) {
log.ErrorfWithRequestId(c, "[users.UserGetAvatarHandler] failed to get user, because %s", err.Error()) log.ErrorfWithRequestId(c, "[users.UserGetAvatarHandler] failed to get user avatar, because %s", err.Error())
} }
return nil, "", errs.ErrUserNotFound return nil, "", errs.Or(err, errs.ErrOperationFailed)
}
if user.CustomAvatarType == "" {
log.WarnfWithRequestId(c, "[users.UserGetAvatarHandler] user does not have avatar for user \"uid:%d\"", user.Uid)
return nil, "", errs.ErrUserAvatarNoExists
}
if user.CustomAvatarType != fileExtension {
log.WarnfWithRequestId(c, "[users.UserGetAvatarHandler] user avatar extension is invalid \"%s\" for user \"uid:%d\"", fileExtension, user.Uid)
return nil, "", errs.ErrUserAvatarNoExists
}
avatarFile, err := storage.Container.ReadAvatar(user.Uid, fileExtension)
if os.IsNotExist(err) {
log.WarnfWithRequestId(c, "[users.UserGetAvatarHandler] user avatar file not exist for user \"uid:%d\", because %s", user.Uid, err.Error())
return nil, "", errs.ErrUserAvatarNoExists
}
if err != nil {
log.ErrorfWithRequestId(c, "[users.UserGetAvatarHandler] failed to get user avatar object for user \"uid:%d\", because %s", user.Uid, err.Error())
return nil, "", errs.ErrOperationFailed
}
defer avatarFile.Close()
avatarData, err := io.ReadAll(avatarFile)
if err != nil {
log.ErrorfWithRequestId(c, "[users.UserGetAvatarHandler] failed to read user avatar object data for user \"uid:%d\", because %s", user.Uid, err.Error())
return nil, "", errs.ErrOperationFailed
} }
return avatarData, contentType, nil return avatarData, contentType, nil
} }
func (a *UsersApi) getUserProfileResponse(user *models.User) *models.UserProfileResponse {
return user.ToUserProfileResponse(a.CurrentConfig().AvatarProvider, a.CurrentConfig().RootUrl)
}
+13
View File
@@ -0,0 +1,13 @@
package cli
import "github.com/mayswind/ezbookkeeping/pkg/settings"
// CliUsingConfig represents an cli that need to use config
type CliUsingConfig struct {
container *settings.ConfigContainer
}
// CurrentConfig returns the current config
func (l *CliUsingConfig) CurrentConfig() *settings.Config {
return l.container.Current
}
+6 -2
View File
@@ -19,6 +19,7 @@ const pageCountForDataExport = 1000
// UserDataCli represents user data cli // UserDataCli represents user data cli
type UserDataCli struct { type UserDataCli struct {
CliUsingConfig
ezBookKeepingCsvExporter *converters.EzBookKeepingCSVFileExporter ezBookKeepingCsvExporter *converters.EzBookKeepingCSVFileExporter
ezBookKeepingTsvExporter *converters.EzBookKeepingTSVFileExporter ezBookKeepingTsvExporter *converters.EzBookKeepingTSVFileExporter
accounts *services.AccountService accounts *services.AccountService
@@ -34,6 +35,9 @@ type UserDataCli struct {
// Initialize an user data cli singleton instance // Initialize an user data cli singleton instance
var ( var (
UserData = &UserDataCli{ UserData = &UserDataCli{
CliUsingConfig: CliUsingConfig{
container: settings.Container,
},
ezBookKeepingCsvExporter: &converters.EzBookKeepingCSVFileExporter{}, ezBookKeepingCsvExporter: &converters.EzBookKeepingCSVFileExporter{},
ezBookKeepingTsvExporter: &converters.EzBookKeepingTSVFileExporter{}, ezBookKeepingTsvExporter: &converters.EzBookKeepingTSVFileExporter{},
accounts: services.Accounts, accounts: services.Accounts,
@@ -180,7 +184,7 @@ func (l *UserDataCli) SendPasswordResetMail(c *cli.Context, username string) err
return err return err
} }
if settings.Container.Current.ForgetPasswordRequireVerifyEmail && !user.EmailVerified { if l.CurrentConfig().ForgetPasswordRequireVerifyEmail && !user.EmailVerified {
log.BootWarnf("[user_data.SendPasswordResetMail] user \"uid:%d\" has not verified email", user.Uid) log.BootWarnf("[user_data.SendPasswordResetMail] user \"uid:%d\" has not verified email", user.Uid)
return errs.ErrEmailIsNotVerified return errs.ErrEmailIsNotVerified
} }
@@ -238,7 +242,7 @@ func (l *UserDataCli) DisableUser(c *cli.Context, username string) error {
// ResendVerifyEmail resends an email with account activation link // ResendVerifyEmail resends an email with account activation link
func (l *UserDataCli) ResendVerifyEmail(c *cli.Context, username string) error { func (l *UserDataCli) ResendVerifyEmail(c *cli.Context, username string) error {
if !settings.Container.Current.EnableUserVerifyEmail { if !l.CurrentConfig().EnableUserVerifyEmail {
return errs.ErrEmailValidationNotAllowed return errs.ErrEmailValidationNotAllowed
} }
+10
View File
@@ -0,0 +1,10 @@
package core
// UserAvatarProviderType represents type of the user avatar provider
type UserAvatarProviderType string
// User avatar provider types
const (
USER_AVATAR_PROVIDER_INTERNAL UserAvatarProviderType = "internal"
USER_AVATAR_PROVIDER_GRAVATAR UserAvatarProviderType = "gravatar"
)
+2
View File
@@ -34,4 +34,6 @@ var (
ErrNoUserAvatar = NewNormalError(NormalSubcategoryUser, 25, http.StatusBadRequest, "no user avatar") ErrNoUserAvatar = NewNormalError(NormalSubcategoryUser, 25, http.StatusBadRequest, "no user avatar")
ErrUserAvatarIsEmpty = NewNormalError(NormalSubcategoryUser, 26, http.StatusBadRequest, "user avatar is empty") ErrUserAvatarIsEmpty = NewNormalError(NormalSubcategoryUser, 26, http.StatusBadRequest, "user avatar is empty")
ErrUserAvatarNoExists = NewNormalError(NormalSubcategoryUser, 27, http.StatusNotFound, "user avatar not exists") ErrUserAvatarNoExists = NewNormalError(NormalSubcategoryUser, 27, http.StatusNotFound, "user avatar not exists")
ErrUserAvatarNotSet = NewNormalError(NormalSubcategoryUser, 28, http.StatusNotFound, "user avatar not set")
ErrUserAvatarExtensionInvalid = NewNormalError(NormalSubcategoryUser, 29, http.StatusNotFound, "user avatar file extension invalid")
) )
+9 -16
View File
@@ -5,7 +5,6 @@ import (
"time" "time"
"github.com/mayswind/ezbookkeeping/pkg/core" "github.com/mayswind/ezbookkeeping/pkg/core"
"github.com/mayswind/ezbookkeeping/pkg/settings"
"github.com/mayswind/ezbookkeeping/pkg/utils" "github.com/mayswind/ezbookkeeping/pkg/utils"
) )
@@ -253,13 +252,13 @@ func (u *User) CanEditTransactionByTransactionTime(transactionTime int64, utcOff
} }
// ToUserBasicInfo returns a user basic view-object according to database model // ToUserBasicInfo returns a user basic view-object according to database model
func (u *User) ToUserBasicInfo() *UserBasicInfo { func (u *User) ToUserBasicInfo(avatarProvider core.UserAvatarProviderType, rootUrl string) *UserBasicInfo {
return &UserBasicInfo{ return &UserBasicInfo{
Username: u.Username, Username: u.Username,
Email: u.Email, Email: u.Email,
Nickname: u.Nickname, Nickname: u.Nickname,
AvatarUrl: u.getAvatarUrl(), AvatarUrl: u.getAvatarUrl(avatarProvider, rootUrl),
AvatarProvider: u.getAvatarProvider(), AvatarProvider: string(avatarProvider),
DefaultAccountId: u.DefaultAccountId, DefaultAccountId: u.DefaultAccountId,
TransactionEditScope: u.TransactionEditScope, TransactionEditScope: u.TransactionEditScope,
Language: u.Language, Language: u.Language,
@@ -280,23 +279,17 @@ func (u *User) ToUserBasicInfo() *UserBasicInfo {
} }
// ToUserProfileResponse returns a user profile view-object according to database model // ToUserProfileResponse returns a user profile view-object according to database model
func (u *User) ToUserProfileResponse() *UserProfileResponse { func (u *User) ToUserProfileResponse(avatarProvider core.UserAvatarProviderType, rootUrl string) *UserProfileResponse {
return &UserProfileResponse{ return &UserProfileResponse{
UserBasicInfo: u.ToUserBasicInfo(), UserBasicInfo: u.ToUserBasicInfo(avatarProvider, rootUrl),
LastLoginAt: u.LastLoginUnixTime, LastLoginAt: u.LastLoginUnixTime,
} }
} }
func (u *User) getAvatarProvider() string { func (u *User) getAvatarUrl(avatarProvider core.UserAvatarProviderType, rootUrl string) string {
return settings.Container.Current.AvatarProvider if avatarProvider == core.USER_AVATAR_PROVIDER_INTERNAL {
} return utils.GetInternalAvatarUrl(u.Uid, u.CustomAvatarType, rootUrl)
} else if avatarProvider == core.USER_AVATAR_PROVIDER_GRAVATAR {
func (u *User) getAvatarUrl() string {
avatarProvider := settings.Container.Current.AvatarProvider
if avatarProvider == settings.InternalAvatarProvider {
return utils.GetInternalAvatarUrl(u.Uid, u.CustomAvatarType, settings.Container.Current.RootUrl)
} else if avatarProvider == settings.GravatarProvider {
return utils.GetGravatarUrl(u.Email) return utils.GetGravatarUrl(u.Email)
} }
+32
View File
@@ -1,10 +1,13 @@
package services package services
import ( import (
"fmt"
"github.com/mayswind/ezbookkeeping/pkg/datastore" "github.com/mayswind/ezbookkeeping/pkg/datastore"
"github.com/mayswind/ezbookkeeping/pkg/errs" "github.com/mayswind/ezbookkeeping/pkg/errs"
"github.com/mayswind/ezbookkeeping/pkg/mail" "github.com/mayswind/ezbookkeeping/pkg/mail"
"github.com/mayswind/ezbookkeeping/pkg/settings" "github.com/mayswind/ezbookkeeping/pkg/settings"
"github.com/mayswind/ezbookkeeping/pkg/storage"
"github.com/mayswind/ezbookkeeping/pkg/uuid" "github.com/mayswind/ezbookkeeping/pkg/uuid"
) )
@@ -76,3 +79,32 @@ func (s *ServiceUsingUuid) GenerateUuid(uuidType uuid.UuidType) int64 {
func (s *ServiceUsingUuid) GenerateUuids(uuidType uuid.UuidType, count uint8) []int64 { func (s *ServiceUsingUuid) GenerateUuids(uuidType uuid.UuidType, count uint8) []int64 {
return s.container.GenerateUuids(uuidType, count) return s.container.GenerateUuids(uuidType, count)
} }
// ServiceUsingStorage represents a service that need to use storage
type ServiceUsingStorage struct {
container *storage.StorageContainer
}
// ExistsAvatar returns whether the user avatar exists from the current avatar object storage
func (s *ServiceUsingStorage) ExistsAvatar(uid int64, fileExtension string) (bool, error) {
return s.container.ExistsAvatar(s.getUserAvatarPath(uid, fileExtension))
}
// ReadAvatar returns the user avatar from the current avatar object storage
func (s *ServiceUsingStorage) ReadAvatar(uid int64, fileExtension string) (storage.ObjectInStorage, error) {
return s.container.ReadAvatar(s.getUserAvatarPath(uid, fileExtension))
}
// SaveAvatar returns whether save the user avatar into the current avatar object storage successfully
func (s *ServiceUsingStorage) SaveAvatar(uid int64, object storage.ObjectInStorage, fileExtension string) error {
return s.container.SaveAvatar(s.getUserAvatarPath(uid, fileExtension), object)
}
// DeleteAvatar returns whether delete the user avatar from the current avatar object storage successfully
func (s *ServiceUsingStorage) DeleteAvatar(uid int64, fileExtension string) error {
return s.container.DeleteAvatar(s.getUserAvatarPath(uid, fileExtension))
}
func (s *ServiceUsingStorage) getUserAvatarPath(uid int64, fileExtension string) string {
return fmt.Sprintf("%d.%s", uid, fileExtension)
}
+103 -3
View File
@@ -3,7 +3,10 @@ package services
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"io"
"mime/multipart"
"net/url" "net/url"
"os"
"time" "time"
"xorm.io/xorm" "xorm.io/xorm"
@@ -12,9 +15,11 @@ import (
"github.com/mayswind/ezbookkeeping/pkg/datastore" "github.com/mayswind/ezbookkeeping/pkg/datastore"
"github.com/mayswind/ezbookkeeping/pkg/errs" "github.com/mayswind/ezbookkeeping/pkg/errs"
"github.com/mayswind/ezbookkeeping/pkg/locales" "github.com/mayswind/ezbookkeeping/pkg/locales"
"github.com/mayswind/ezbookkeeping/pkg/log"
"github.com/mayswind/ezbookkeeping/pkg/mail" "github.com/mayswind/ezbookkeeping/pkg/mail"
"github.com/mayswind/ezbookkeeping/pkg/models" "github.com/mayswind/ezbookkeeping/pkg/models"
"github.com/mayswind/ezbookkeeping/pkg/settings" "github.com/mayswind/ezbookkeeping/pkg/settings"
"github.com/mayswind/ezbookkeeping/pkg/storage"
"github.com/mayswind/ezbookkeeping/pkg/templates" "github.com/mayswind/ezbookkeeping/pkg/templates"
"github.com/mayswind/ezbookkeeping/pkg/utils" "github.com/mayswind/ezbookkeeping/pkg/utils"
"github.com/mayswind/ezbookkeeping/pkg/uuid" "github.com/mayswind/ezbookkeeping/pkg/uuid"
@@ -28,6 +33,7 @@ type UserService struct {
ServiceUsingConfig ServiceUsingConfig
ServiceUsingMailer ServiceUsingMailer
ServiceUsingUuid ServiceUsingUuid
ServiceUsingStorage
} }
// Initialize a user service singleton instance // Initialize a user service singleton instance
@@ -45,6 +51,9 @@ var (
ServiceUsingUuid: ServiceUsingUuid{ ServiceUsingUuid: ServiceUsingUuid{
container: uuid.Container, container: uuid.Container,
}, },
ServiceUsingStorage: ServiceUsingStorage{
container: storage.Container,
},
} }
) )
@@ -126,6 +135,50 @@ func (s *UserService) GetUserByEmail(c *core.Context, email string) (*models.Use
return user, nil return user, nil
} }
// GetUserAvatar returns the user avatar image data and image file extension according to user uid
func (s *UserService) GetUserAvatar(c *core.Context, uid int64, fileExtension string) ([]byte, error) {
if uid <= 0 {
return nil, errs.ErrUserIdInvalid
}
user := &models.User{}
has, err := s.UserDB().NewSession(c).ID(uid).Cols("uid", "deleted", "custom_avatar_type").Where("deleted=?", false).Get(user)
if err != nil {
return nil, err
} else if !has {
return nil, errs.ErrUserNotFound
}
if user.CustomAvatarType == "" {
return nil, errs.ErrUserAvatarNotSet
}
if user.CustomAvatarType != fileExtension {
return nil, errs.ErrUserAvatarExtensionInvalid
}
avatarFile, err := s.ReadAvatar(user.Uid, user.CustomAvatarType)
if os.IsNotExist(err) {
return nil, errs.ErrUserAvatarNoExists
}
if err != nil {
return nil, err
}
defer avatarFile.Close()
avatarData, err := io.ReadAll(avatarFile)
if err != nil {
return nil, err
}
return avatarData, nil
}
// CreateUser saves a new user model to database // CreateUser saves a new user model to database
func (s *UserService) CreateUser(c *core.Context, user *models.User) error { func (s *UserService) CreateUser(c *core.Context, user *models.User) error {
exists, err := s.ExistsUsername(c, user.Username) exists, err := s.ExistsUsername(c, user.Username)
@@ -294,16 +347,63 @@ func (s *UserService) UpdateUser(c *core.Context, user *models.User, modifyUserL
return keyProfileUpdated, emailSetToUnverified, nil return keyProfileUpdated, emailSetToUnverified, nil
} }
// UpdateUserAvatar updated the custom avatar type of specified user // UpdateUserAvatar updates the custom avatar type of specified user
func (s *UserService) UpdateUserAvatar(c *core.Context, uid int64, customAvatarType string) error { func (s *UserService) UpdateUserAvatar(c *core.Context, uid int64, avatarFile multipart.File, fileExtension string, oldFileExtension string) error {
if uid <= 0 { if uid <= 0 {
return errs.ErrUserIdInvalid return errs.ErrUserIdInvalid
} }
defer avatarFile.Close()
err := s.SaveAvatar(uid, avatarFile, fileExtension)
if err != nil {
return err
}
now := time.Now().Unix() now := time.Now().Unix()
updateModel := &models.User{ updateModel := &models.User{
CustomAvatarType: customAvatarType, CustomAvatarType: fileExtension,
UpdatedUnixTime: now,
}
err = s.UserDB().DoTransaction(c, func(sess *xorm.Session) error {
_, err := sess.ID(uid).Cols("custom_avatar_type", "updated_unix_time").Where("deleted=?", false).Update(updateModel)
return err
})
if err != nil {
return err
}
if fileExtension != oldFileExtension && oldFileExtension != "" {
err = s.DeleteAvatar(uid, oldFileExtension)
if err != nil {
log.WarnfWithRequestId(c, "[users.UpdateUserAvatar] failed to delete old avatar with extension \"%s\" for user \"uid:%d\", because %s", oldFileExtension, uid, err.Error())
}
}
return nil
}
// RemoveUserAvatar removes the custom avatar type of specified user
func (s *UserService) RemoveUserAvatar(c *core.Context, uid int64, fileExtension string) error {
if uid <= 0 {
return errs.ErrUserIdInvalid
}
err := s.DeleteAvatar(uid, fileExtension)
if err != nil && !os.IsNotExist(err) {
return err
}
now := time.Now().Unix()
updateModel := &models.User{
CustomAvatarType: "",
UpdatedUnixTime: now, UpdatedUnixTime: now,
} }
+6 -11
View File
@@ -10,6 +10,7 @@ import (
"gopkg.in/ini.v1" "gopkg.in/ini.v1"
"github.com/mayswind/ezbookkeeping/pkg/core"
"github.com/mayswind/ezbookkeeping/pkg/errs" "github.com/mayswind/ezbookkeeping/pkg/errs"
"github.com/mayswind/ezbookkeeping/pkg/locales" "github.com/mayswind/ezbookkeeping/pkg/locales"
) )
@@ -74,12 +75,6 @@ const (
InMemoryDuplicateCheckerType string = "in_memory" InMemoryDuplicateCheckerType string = "in_memory"
) )
// User avatar provider types
const (
InternalAvatarProvider string = "internal"
GravatarProvider string = "gravatar"
)
// Map provider types // Map provider types
const ( const (
OpenStreetMapProvider string = "openstreetmap" OpenStreetMapProvider string = "openstreetmap"
@@ -276,7 +271,7 @@ type Config struct {
EnableUserForceVerifyEmail bool EnableUserForceVerifyEmail bool
EnableUserForgetPassword bool EnableUserForgetPassword bool
ForgetPasswordRequireVerifyEmail bool ForgetPasswordRequireVerifyEmail bool
AvatarProvider string AvatarProvider core.UserAvatarProviderType
// Data // Data
EnableDataExport bool EnableDataExport bool
@@ -744,10 +739,10 @@ func loadUserConfiguration(config *Config, configFile *ini.File, sectionName str
config.EnableUserForgetPassword = getConfigItemBoolValue(configFile, sectionName, "enable_forget_password", false) config.EnableUserForgetPassword = getConfigItemBoolValue(configFile, sectionName, "enable_forget_password", false)
config.ForgetPasswordRequireVerifyEmail = getConfigItemBoolValue(configFile, sectionName, "forget_password_require_email_verify", false) config.ForgetPasswordRequireVerifyEmail = getConfigItemBoolValue(configFile, sectionName, "forget_password_require_email_verify", false)
if getConfigItemStringValue(configFile, sectionName, "avatar_provider") == InternalAvatarProvider { if getConfigItemStringValue(configFile, sectionName, "avatar_provider") == string(core.USER_AVATAR_PROVIDER_INTERNAL) {
config.AvatarProvider = InternalAvatarProvider config.AvatarProvider = core.USER_AVATAR_PROVIDER_INTERNAL
} else if getConfigItemStringValue(configFile, sectionName, "avatar_provider") == GravatarProvider { } else if getConfigItemStringValue(configFile, sectionName, "avatar_provider") == string(core.USER_AVATAR_PROVIDER_GRAVATAR) {
config.AvatarProvider = GravatarProvider config.AvatarProvider = core.USER_AVATAR_PROVIDER_GRAVATAR
} else if getConfigItemStringValue(configFile, sectionName, "avatar_provider") == "" { } else if getConfigItemStringValue(configFile, sectionName, "avatar_provider") == "" {
config.AvatarProvider = "" config.AvatarProvider = ""
} else { } else {
-57
View File
@@ -16,60 +16,3 @@ var (
func SetCurrentConfig(config *Config) { func SetCurrentConfig(config *Config) {
Container.Current = config Container.Current = config
} }
// GetAfterRegisterNotificationContent returns the notification content displayed each time users register
func (c *ConfigContainer) GetAfterRegisterNotificationContent(userLanguage string, clientLanguage string) string {
language := userLanguage
if language == "" {
language = clientLanguage
}
if !c.Current.AfterRegisterNotification.Enabled {
return ""
}
if multiLanguageContent, exists := c.Current.AfterRegisterNotification.MultiLanguageContent[language]; exists {
return multiLanguageContent
}
return c.Current.AfterRegisterNotification.DefaultContent
}
// GetAfterLoginNotificationContent returns the notification content displayed each time users log in
func (c *ConfigContainer) GetAfterLoginNotificationContent(userLanguage string, clientLanguage string) string {
language := userLanguage
if language == "" {
language = clientLanguage
}
if !c.Current.AfterLoginNotification.Enabled {
return ""
}
if multiLanguageContent, exists := c.Current.AfterLoginNotification.MultiLanguageContent[language]; exists {
return multiLanguageContent
}
return c.Current.AfterLoginNotification.DefaultContent
}
// GetAfterOpenNotificationContent returns the notification content displayed each time users open the app
func (c *ConfigContainer) GetAfterOpenNotificationContent(userLanguage string, clientLanguage string) string {
language := userLanguage
if language == "" {
language = clientLanguage
}
if !c.Current.AfterOpenNotification.Enabled {
return ""
}
if multiLanguageContent, exists := c.Current.AfterOpenNotification.MultiLanguageContent[language]; exists {
return multiLanguageContent
}
return c.Current.AfterOpenNotification.DefaultContent
}
+12 -18
View File
@@ -1,8 +1,6 @@
package storage package storage
import ( import (
"fmt"
"github.com/mayswind/ezbookkeeping/pkg/errs" "github.com/mayswind/ezbookkeeping/pkg/errs"
"github.com/mayswind/ezbookkeeping/pkg/settings" "github.com/mayswind/ezbookkeeping/pkg/settings"
) )
@@ -36,26 +34,22 @@ func InitializeStorageContainer(config *settings.Config) error {
return errs.ErrInvalidStorageType return errs.ErrInvalidStorageType
} }
// ExistsAvatar returns whether the user avatar exists from the current object storage // ExistsAvatar returns whether the avatar file exists from the current avatar object storage
func (s *StorageContainer) ExistsAvatar(uid int64, fileExtension string) (bool, error) { func (s *StorageContainer) ExistsAvatar(path string) (bool, error) {
return s.AvatarCurrentStorage.Exists(s.getUserAvatarPath(uid, fileExtension)) return s.AvatarCurrentStorage.Exists(path)
} }
// ReadAvatar returns the user avatar from the current object storage // ReadAvatar returns the avatar file from the current avatar object storage
func (s *StorageContainer) ReadAvatar(uid int64, fileExtension string) (ObjectInStorage, error) { func (s *StorageContainer) ReadAvatar(path string) (ObjectInStorage, error) {
return s.AvatarCurrentStorage.Read(s.getUserAvatarPath(uid, fileExtension)) return s.AvatarCurrentStorage.Read(path)
} }
// SaveAvatar returns whether save the user avatar into the current object storage successfully // SaveAvatar returns whether save the avatar file into the current avatar object storage successfully
func (s *StorageContainer) SaveAvatar(uid int64, object ObjectInStorage, fileExtension string) error { func (s *StorageContainer) SaveAvatar(path string, object ObjectInStorage) error {
return s.AvatarCurrentStorage.Save(s.getUserAvatarPath(uid, fileExtension), object) return s.AvatarCurrentStorage.Save(path, object)
} }
// DeleteAvatar returns whether delete the user avatar from the current object storage successfully // DeleteAvatar returns whether delete the avatar file from the current avatar object storage successfully
func (s *StorageContainer) DeleteAvatar(uid int64, fileExtension string) error { func (s *StorageContainer) DeleteAvatar(path string) error {
return s.AvatarCurrentStorage.Delete(s.getUserAvatarPath(uid, fileExtension)) return s.AvatarCurrentStorage.Delete(path)
}
func (s *StorageContainer) getUserAvatarPath(uid int64, fileExtension string) string {
return fmt.Sprintf("%d.%s", uid, fileExtension)
} }
+2
View File
@@ -967,6 +967,8 @@
"no user avatar": "There is no user avatar file", "no user avatar": "There is no user avatar file",
"user avatar is empty": "User avatar file is empty", "user avatar is empty": "User avatar file is empty",
"user avatar not exists": "User avatar does not exist", "user avatar not exists": "User avatar does not exist",
"user avatar not set": "User avatar is not set",
"user avatar file extension invalid": "User avatar file extension is invalid",
"unauthorized access": "Unauthorized access", "unauthorized access": "Unauthorized access",
"current token is invalid": "Current token is invalid", "current token is invalid": "Current token is invalid",
"current token is expired": "Current token is expired", "current token is expired": "Current token is expired",
+2
View File
@@ -967,6 +967,8 @@
"no user avatar": "没有用户头像文件", "no user avatar": "没有用户头像文件",
"user avatar is empty": "用户头像文件为空", "user avatar is empty": "用户头像文件为空",
"user avatar not exists": "用户头像不存在", "user avatar not exists": "用户头像不存在",
"user avatar not set": "用户没有设置头像",
"user avatar file extension invalid": "用户头像文件扩展名无效",
"unauthorized access": "未授权的登录", "unauthorized access": "未授权的登录",
"current token is invalid": "当前认证令牌无效", "current token is invalid": "当前认证令牌无效",
"current token is expired": "当前认证令牌已过期", "current token is expired": "当前认证令牌已过期",