From 05a93667eb540fcba09b8f1603f67834165e9b67 Mon Sep 17 00:00:00 2001 From: MaysWind Date: Mon, 5 Aug 2024 01:25:26 +0800 Subject: [PATCH] display notification every time users open the app or login --- conf/ezbookkeeping.ini | 15 ++++++++++ pkg/api/authorizations.go | 7 +++-- pkg/api/tokens.go | 10 ++++--- pkg/api/users.go | 7 +++-- pkg/models/auth_response.go | 7 +++-- pkg/models/token_record.go | 7 +++-- pkg/models/user.go | 5 ++-- pkg/settings/setting.go | 46 +++++++++++++++++++++++++++++++ pkg/settings/setting_container.go | 26 +++++++++++++++++ 9 files changed, 113 insertions(+), 17 deletions(-) diff --git a/conf/ezbookkeeping.ini b/conf/ezbookkeeping.ini index 5071a742..805588bb 100644 --- a/conf/ezbookkeeping.ini +++ b/conf/ezbookkeeping.ini @@ -187,6 +187,21 @@ avatar_provider = internal # Set to true to allow users to export their data enable_export = true +[notification] +# Set to true to display custom notification in home page every time users login +enable_notification_after_login = false + +# The notification content displayed each time users log in, it supports multi-language configuration +# Add an underscore and a language tag after the setting key to configure the notification content in that language, the same below +# For example, after_login_notification_content_zh_hans means the notification content in Simplified Chinese +after_login_notification_content = + +# Set to true to display custom notification in home page every time users open the app +enable_notification_after_open = false + +# The notification content displayed each time users open the app, it supports multi-language configuration +after_open_notification_content = + [map] # Map provider, supports the following types: # "openstreetmap": https://www.openstreetmap.org diff --git a/pkg/api/authorizations.go b/pkg/api/authorizations.go index 80ed5824..cd73253e 100644 --- a/pkg/api/authorizations.go +++ b/pkg/api/authorizations.go @@ -242,8 +242,9 @@ func (a *AuthorizationsApi) TwoFactorAuthorizeByRecoveryCodeHandler(c *core.Cont func (a *AuthorizationsApi) getAuthResponse(token string, need2FA bool, user *models.User) *models.AuthResponse { return &models.AuthResponse{ - Token: token, - Need2FA: need2FA, - User: user.ToUserBasicInfo(), + Token: token, + Need2FA: need2FA, + User: user.ToUserBasicInfo(), + NotificationContent: settings.Container.GetAfterLoginNotificationContent(user.Language), } } diff --git a/pkg/api/tokens.go b/pkg/api/tokens.go index 888d2c4f..e679d09a 100644 --- a/pkg/api/tokens.go +++ b/pkg/api/tokens.go @@ -204,7 +204,8 @@ func (a *TokensApi) TokenRefreshHandler(c *core.Context) (any, *errs.Error) { } refreshResp := &models.TokenRefreshResponse{ - User: user.ToUserBasicInfo(), + User: user.ToUserBasicInfo(), + NotificationContent: settings.Container.GetAfterOpenNotificationContent(user.Language), } return refreshResp, nil @@ -230,9 +231,10 @@ func (a *TokensApi) TokenRefreshHandler(c *core.Context) (any, *errs.Error) { log.InfofWithRequestId(c, "[token.TokenRefreshHandler] user \"uid:%d\" token refreshed, new token will be expired at %d", user.Uid, claims.ExpiresAt) refreshResp := &models.TokenRefreshResponse{ - NewToken: token, - OldTokenId: a.tokens.GenerateTokenId(oldTokenRecord), - User: user.ToUserBasicInfo(), + NewToken: token, + OldTokenId: a.tokens.GenerateTokenId(oldTokenRecord), + User: user.ToUserBasicInfo(), + NotificationContent: settings.Container.GetAfterOpenNotificationContent(user.Language), } return refreshResp, nil diff --git a/pkg/api/users.go b/pkg/api/users.go index f04d43e7..78ae9617 100644 --- a/pkg/api/users.go +++ b/pkg/api/users.go @@ -91,8 +91,9 @@ func (a *UsersApi) UserRegisterHandler(c *core.Context) (any, *errs.Error) { authResp := &models.RegisterResponse{ AuthResponse: models.AuthResponse{ - Need2FA: false, - User: user.ToUserBasicInfo(), + Need2FA: false, + User: user.ToUserBasicInfo(), + NotificationContent: settings.Container.GetAfterLoginNotificationContent(user.Language), }, NeedVerifyEmail: settings.Container.Current.EnableUserVerifyEmail && settings.Container.Current.EnableUserForceVerifyEmail, PresetCategoriesSaved: presetCategoriesSaved, @@ -187,6 +188,8 @@ func (a *UsersApi) UserEmailVerifyHandler(c *core.Context) (any, *errs.Error) { resp.NewToken = token resp.User = user.ToUserBasicInfo() + resp.NotificationContent = settings.Container.GetAfterLoginNotificationContent(user.Language) + c.SetTextualToken(token) c.SetTokenClaims(claims) diff --git a/pkg/models/auth_response.go b/pkg/models/auth_response.go index ec959d91..df901ad1 100644 --- a/pkg/models/auth_response.go +++ b/pkg/models/auth_response.go @@ -2,9 +2,10 @@ package models // AuthResponse returns a view-object of user authorization type AuthResponse struct { - Token string `json:"token"` - Need2FA bool `json:"need2FA"` - User *UserBasicInfo `json:"user"` + Token string `json:"token"` + Need2FA bool `json:"need2FA"` + User *UserBasicInfo `json:"user"` + NotificationContent string `json:"notificationContent,omitempty"` } // RegisterResponse returns a view-object of user register response diff --git a/pkg/models/token_record.go b/pkg/models/token_record.go index 68d9230b..17d5050b 100644 --- a/pkg/models/token_record.go +++ b/pkg/models/token_record.go @@ -24,9 +24,10 @@ type TokenRevokeRequest struct { // TokenRefreshResponse represents all parameters of token refreshing request type TokenRefreshResponse struct { - NewToken string `json:"newToken,omitempty"` - OldTokenId string `json:"oldTokenId,omitempty"` - User *UserBasicInfo `json:"user"` + NewToken string `json:"newToken,omitempty"` + OldTokenId string `json:"oldTokenId,omitempty"` + User *UserBasicInfo `json:"user"` + NotificationContent string `json:"notificationContent,omitempty"` } // TokenInfoResponse represents a view-object of token diff --git a/pkg/models/user.go b/pkg/models/user.go index 4576a574..6371a4bb 100644 --- a/pkg/models/user.go +++ b/pkg/models/user.go @@ -164,8 +164,9 @@ type UserVerifyEmailRequest struct { // UserVerifyEmailResponse represents all response parameters after user have verified email type UserVerifyEmailResponse struct { - NewToken string `json:"newToken,omitempty"` - User *UserBasicInfo `json:"user"` + NewToken string `json:"newToken,omitempty"` + User *UserBasicInfo `json:"user"` + NotificationContent string `json:"notificationContent,omitempty"` } // UserResendVerifyEmailRequest represents all parameters of user resend verify email request diff --git a/pkg/settings/setting.go b/pkg/settings/setting.go index eabaf535..b07d7252 100644 --- a/pkg/settings/setting.go +++ b/pkg/settings/setting.go @@ -11,6 +11,7 @@ import ( "gopkg.in/ini.v1" "github.com/mayswind/ezbookkeeping/pkg/errs" + "github.com/mayswind/ezbookkeeping/pkg/locales" ) const ( @@ -178,6 +179,13 @@ type MinIOConfig struct { RootPath string } +// NotificationConfig represents a notification setting config +type NotificationConfig struct { + Enabled bool + DefaultContent string + MultiLanguageContent map[string]string +} + // Config represents the global setting config type Config struct { // Global @@ -263,6 +271,10 @@ type Config struct { // Data EnableDataExport bool + // Notification + AfterLoginNotification NotificationConfig + AfterOpenNotification NotificationConfig + // Map MapProvider string EnableMapDataFetchProxy bool @@ -371,6 +383,12 @@ func LoadConfiguration(configFilePath string) (*Config, error) { return nil, err } + err = loadNotificationConfiguration(config, cfgFile, "notification") + + if err != nil { + return nil, err + } + err = loadMapConfiguration(config, cfgFile, "map") if err != nil { @@ -700,6 +718,13 @@ func loadDataConfiguration(config *Config, configFile *ini.File, sectionName str return nil } +func loadNotificationConfiguration(config *Config, configFile *ini.File, sectionName string) error { + config.AfterLoginNotification = getNotificationConfiguration(configFile, sectionName, "enable_notification_after_login", "after_login_notification_content") + config.AfterOpenNotification = getNotificationConfiguration(configFile, sectionName, "enable_notification_after_open", "after_open_notification_content") + + return nil +} + func loadMapConfiguration(config *Config, configFile *ini.File, sectionName string) error { mapProvider := getConfigItemStringValue(configFile, sectionName, "map_provider") @@ -820,6 +845,27 @@ func getFinalPath(workingPath, p string) (string, error) { return p, err } +func getNotificationConfiguration(configFile *ini.File, sectionName string, enableKey string, contentKey string) NotificationConfig { + config := NotificationConfig{ + Enabled: getConfigItemBoolValue(configFile, sectionName, enableKey, false), + DefaultContent: getConfigItemStringValue(configFile, sectionName, contentKey, ""), + MultiLanguageContent: make(map[string]string), + } + + for languageTag := range locales.AllLanguages { + multiLanguageContentKey := strings.ToLower(languageTag) + multiLanguageContentKey = strings.Replace(multiLanguageContentKey, "-", "_", -1) + multiLanguageContentKey = contentKey + "_" + multiLanguageContentKey + content := getConfigItemStringValue(configFile, sectionName, multiLanguageContentKey, "") + + if content != "" { + config.MultiLanguageContent[languageTag] = content + } + } + + return config +} + func getConfigItemIsSet(configFile *ini.File, sectionName string, itemName string) bool { environmentKey := getEnvironmentKey(sectionName, itemName) environmentValue := os.Getenv(environmentKey) diff --git a/pkg/settings/setting_container.go b/pkg/settings/setting_container.go index c9d161d6..b19a38ec 100644 --- a/pkg/settings/setting_container.go +++ b/pkg/settings/setting_container.go @@ -16,3 +16,29 @@ var ( func SetCurrentConfig(config *Config) { Container.Current = config } + +// GetAfterLoginNotificationContent returns the notification content displayed each time users log in +func (c *ConfigContainer) GetAfterLoginNotificationContent(language string) string { + 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(language string) string { + if !c.Current.AfterOpenNotification.Enabled { + return "" + } + + if multiLanguageContent, exists := c.Current.AfterOpenNotification.MultiLanguageContent[language]; exists { + return multiLanguageContent + } + + return c.Current.AfterOpenNotification.DefaultContent +}