mirror of
https://github.com/usememos/memos.git
synced 2025-06-05 22:09:59 +02:00
chore: migrate user setting to api v1 package (#1855)
* chore: migrate to api v1 package * chore: update
This commit is contained in:
137
api/v1/activity.go
Normal file
137
api/v1/activity.go
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
package v1
|
||||||
|
|
||||||
|
import "github.com/usememos/memos/server/profile"
|
||||||
|
|
||||||
|
// ActivityType is the type for an activity.
|
||||||
|
type ActivityType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// User related.
|
||||||
|
|
||||||
|
// ActivityUserCreate is the type for creating users.
|
||||||
|
ActivityUserCreate ActivityType = "user.create"
|
||||||
|
// ActivityUserUpdate is the type for updating users.
|
||||||
|
ActivityUserUpdate ActivityType = "user.update"
|
||||||
|
// ActivityUserDelete is the type for deleting users.
|
||||||
|
ActivityUserDelete ActivityType = "user.delete"
|
||||||
|
// ActivityUserAuthSignIn is the type for user signin.
|
||||||
|
ActivityUserAuthSignIn ActivityType = "user.auth.signin"
|
||||||
|
// ActivityUserAuthSignUp is the type for user signup.
|
||||||
|
ActivityUserAuthSignUp ActivityType = "user.auth.signup"
|
||||||
|
// ActivityUserSettingUpdate is the type for updating user settings.
|
||||||
|
ActivityUserSettingUpdate ActivityType = "user.setting.update"
|
||||||
|
|
||||||
|
// Memo related.
|
||||||
|
|
||||||
|
// ActivityMemoCreate is the type for creating memos.
|
||||||
|
ActivityMemoCreate ActivityType = "memo.create"
|
||||||
|
// ActivityMemoUpdate is the type for updating memos.
|
||||||
|
ActivityMemoUpdate ActivityType = "memo.update"
|
||||||
|
// ActivityMemoDelete is the type for deleting memos.
|
||||||
|
ActivityMemoDelete ActivityType = "memo.delete"
|
||||||
|
|
||||||
|
// Shortcut related.
|
||||||
|
|
||||||
|
// ActivityShortcutCreate is the type for creating shortcuts.
|
||||||
|
ActivityShortcutCreate ActivityType = "shortcut.create"
|
||||||
|
// ActivityShortcutUpdate is the type for updating shortcuts.
|
||||||
|
ActivityShortcutUpdate ActivityType = "shortcut.update"
|
||||||
|
// ActivityShortcutDelete is the type for deleting shortcuts.
|
||||||
|
ActivityShortcutDelete ActivityType = "shortcut.delete"
|
||||||
|
|
||||||
|
// Resource related.
|
||||||
|
|
||||||
|
// ActivityResourceCreate is the type for creating resources.
|
||||||
|
ActivityResourceCreate ActivityType = "resource.create"
|
||||||
|
// ActivityResourceDelete is the type for deleting resources.
|
||||||
|
ActivityResourceDelete ActivityType = "resource.delete"
|
||||||
|
|
||||||
|
// Tag related.
|
||||||
|
|
||||||
|
// ActivityTagCreate is the type for creating tags.
|
||||||
|
ActivityTagCreate ActivityType = "tag.create"
|
||||||
|
// ActivityTagDelete is the type for deleting tags.
|
||||||
|
ActivityTagDelete ActivityType = "tag.delete"
|
||||||
|
|
||||||
|
// Server related.
|
||||||
|
|
||||||
|
// ActivityServerStart is the type for starting server.
|
||||||
|
ActivityServerStart ActivityType = "server.start"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ActivityLevel is the level of activities.
|
||||||
|
type ActivityLevel string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ActivityInfo is the INFO level of activities.
|
||||||
|
ActivityInfo ActivityLevel = "INFO"
|
||||||
|
// ActivityWarn is the WARN level of activities.
|
||||||
|
ActivityWarn ActivityLevel = "WARN"
|
||||||
|
// ActivityError is the ERROR level of activities.
|
||||||
|
ActivityError ActivityLevel = "ERROR"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ActivityUserCreatePayload struct {
|
||||||
|
UserID int `json:"userId"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
Role Role `json:"role"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ActivityUserAuthSignInPayload struct {
|
||||||
|
UserID int `json:"userId"`
|
||||||
|
IP string `json:"ip"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ActivityUserAuthSignUpPayload struct {
|
||||||
|
Username string `json:"username"`
|
||||||
|
IP string `json:"ip"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ActivityMemoCreatePayload struct {
|
||||||
|
Content string `json:"content"`
|
||||||
|
Visibility string `json:"visibility"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ActivityShortcutCreatePayload struct {
|
||||||
|
Title string `json:"title"`
|
||||||
|
Payload string `json:"payload"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ActivityResourceCreatePayload struct {
|
||||||
|
Filename string `json:"filename"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Size int64 `json:"size"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ActivityTagCreatePayload struct {
|
||||||
|
TagName string `json:"tagName"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ActivityServerStartPayload struct {
|
||||||
|
ServerID string `json:"serverId"`
|
||||||
|
Profile *profile.Profile `json:"profile"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Activity struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
|
||||||
|
// Standard fields
|
||||||
|
CreatorID int `json:"creatorId"`
|
||||||
|
CreatedTs int64 `json:"createdTs"`
|
||||||
|
|
||||||
|
// Domain specific fields
|
||||||
|
Type ActivityType `json:"type"`
|
||||||
|
Level ActivityLevel `json:"level"`
|
||||||
|
Payload string `json:"payload"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ActivityCreate is the API message for creating an activity.
|
||||||
|
type ActivityCreate struct {
|
||||||
|
// Standard fields
|
||||||
|
CreatorID int
|
||||||
|
|
||||||
|
// Domain specific fields
|
||||||
|
Type ActivityType `json:"type"`
|
||||||
|
Level ActivityLevel
|
||||||
|
Payload string `json:"payload"`
|
||||||
|
}
|
@ -8,7 +8,6 @@ import (
|
|||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"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"
|
||||||
@ -41,16 +40,15 @@ func (s *APIV1Service) registerAuthRoutes(g *echo.Group, secret string) {
|
|||||||
return echo.NewHTTPError(http.StatusBadRequest, "Malformatted signin request").SetInternal(err)
|
return echo.NewHTTPError(http.StatusBadRequest, "Malformatted signin request").SetInternal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
userFind := &api.UserFind{
|
user, err := s.Store.GetUser(ctx, &store.FindUserMessage{
|
||||||
Username: &signin.Username,
|
Username: &signin.Username,
|
||||||
}
|
})
|
||||||
user, err := s.Store.FindUser(ctx, userFind)
|
|
||||||
if err != nil && common.ErrorCode(err) != common.NotFound {
|
if err != nil && common.ErrorCode(err) != common.NotFound {
|
||||||
return echo.NewHTTPError(http.StatusInternalServerError, "Incorrect login credentials, please try again")
|
return echo.NewHTTPError(http.StatusInternalServerError, "Incorrect login credentials, please try again")
|
||||||
}
|
}
|
||||||
if user == nil {
|
if user == nil {
|
||||||
return echo.NewHTTPError(http.StatusUnauthorized, "Incorrect login credentials, please try again")
|
return echo.NewHTTPError(http.StatusUnauthorized, "Incorrect login credentials, please try again")
|
||||||
} else if user.RowStatus == api.Archived {
|
} else if user.RowStatus == store.Archived {
|
||||||
return echo.NewHTTPError(http.StatusForbidden, fmt.Sprintf("User has been archived with username %s", signin.Username))
|
return echo.NewHTTPError(http.StatusForbidden, fmt.Sprintf("User has been archived with username %s", signin.Username))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,20 +108,19 @@ func (s *APIV1Service) registerAuthRoutes(g *echo.Group, secret string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := s.Store.FindUser(ctx, &api.UserFind{
|
user, err := s.Store.GetUser(ctx, &store.FindUserMessage{
|
||||||
Username: &userInfo.Identifier,
|
Username: &userInfo.Identifier,
|
||||||
})
|
})
|
||||||
if err != nil && common.ErrorCode(err) != common.NotFound {
|
if err != nil && common.ErrorCode(err) != common.NotFound {
|
||||||
return echo.NewHTTPError(http.StatusInternalServerError, "Incorrect login credentials, please try again")
|
return echo.NewHTTPError(http.StatusInternalServerError, "Incorrect login credentials, please try again")
|
||||||
}
|
}
|
||||||
if user == nil {
|
if user == nil {
|
||||||
userCreate := &api.UserCreate{
|
userCreate := &store.UserMessage{
|
||||||
Username: userInfo.Identifier,
|
Username: userInfo.Identifier,
|
||||||
// The new signup user should be normal user by default.
|
// The new signup user should be normal user by default.
|
||||||
Role: api.NormalUser,
|
Role: store.NormalUser,
|
||||||
Nickname: userInfo.DisplayName,
|
Nickname: userInfo.DisplayName,
|
||||||
Email: userInfo.Email,
|
Email: userInfo.Email,
|
||||||
Password: userInfo.Email,
|
|
||||||
OpenID: common.GenUUID(),
|
OpenID: common.GenUUID(),
|
||||||
}
|
}
|
||||||
password, err := common.RandomString(20)
|
password, err := common.RandomString(20)
|
||||||
@ -135,12 +132,12 @@ func (s *APIV1Service) registerAuthRoutes(g *echo.Group, secret string) {
|
|||||||
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to generate password hash").SetInternal(err)
|
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to generate password hash").SetInternal(err)
|
||||||
}
|
}
|
||||||
userCreate.PasswordHash = string(passwordHash)
|
userCreate.PasswordHash = string(passwordHash)
|
||||||
user, err = s.Store.CreateUser(ctx, userCreate)
|
user, err = s.Store.CreateUserV1(ctx, userCreate)
|
||||||
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 user.RowStatus == api.Archived {
|
if user.RowStatus == store.Archived {
|
||||||
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))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,27 +157,27 @@ func (s *APIV1Service) registerAuthRoutes(g *echo.Group, secret string) {
|
|||||||
return echo.NewHTTPError(http.StatusBadRequest, "Malformatted signup request").SetInternal(err)
|
return echo.NewHTTPError(http.StatusBadRequest, "Malformatted signup request").SetInternal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
userCreate := &api.UserCreate{
|
hostUserType := store.Host
|
||||||
Username: signup.Username,
|
existedHostUsers, err := s.Store.ListUsers(ctx, &store.FindUserMessage{
|
||||||
// The new signup user should be normal user by default.
|
|
||||||
Role: api.NormalUser,
|
|
||||||
Nickname: signup.Username,
|
|
||||||
Password: signup.Password,
|
|
||||||
OpenID: common.GenUUID(),
|
|
||||||
}
|
|
||||||
hostUserType := api.Host
|
|
||||||
existedHostUsers, err := s.Store.FindUserList(ctx, &api.UserFind{
|
|
||||||
Role: &hostUserType,
|
Role: &hostUserType,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return echo.NewHTTPError(http.StatusBadRequest, "Failed to find users").SetInternal(err)
|
return echo.NewHTTPError(http.StatusBadRequest, "Failed to find users").SetInternal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
userCreate := &store.UserMessage{
|
||||||
|
Username: signup.Username,
|
||||||
|
// The new signup user should be normal user by default.
|
||||||
|
Role: store.NormalUser,
|
||||||
|
Nickname: signup.Username,
|
||||||
|
OpenID: common.GenUUID(),
|
||||||
|
}
|
||||||
if len(existedHostUsers) == 0 {
|
if len(existedHostUsers) == 0 {
|
||||||
// Change the default role to host if there is no host user.
|
// Change the default role to host if there is no host user.
|
||||||
userCreate.Role = api.Host
|
userCreate.Role = store.Host
|
||||||
} else {
|
} else {
|
||||||
allowSignUpSetting, err := s.Store.FindSystemSetting(ctx, &api.SystemSettingFind{
|
allowSignUpSetting, err := s.Store.GetSystemSetting(ctx, &store.FindSystemSettingMessage{
|
||||||
Name: api.SystemSettingAllowSignUpName,
|
Name: SystemSettingAllowSignUpName.String(),
|
||||||
})
|
})
|
||||||
if err != nil && common.ErrorCode(err) != common.NotFound {
|
if err != nil && common.ErrorCode(err) != common.NotFound {
|
||||||
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find system setting").SetInternal(err)
|
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find system setting").SetInternal(err)
|
||||||
@ -198,17 +195,13 @@ func (s *APIV1Service) registerAuthRoutes(g *echo.Group, secret string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := userCreate.Validate(); err != nil {
|
|
||||||
return echo.NewHTTPError(http.StatusBadRequest, "Invalid user create format").SetInternal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
passwordHash, err := bcrypt.GenerateFromPassword([]byte(signup.Password), bcrypt.DefaultCost)
|
passwordHash, err := bcrypt.GenerateFromPassword([]byte(signup.Password), bcrypt.DefaultCost)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to generate password hash").SetInternal(err)
|
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to generate password hash").SetInternal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
userCreate.PasswordHash = string(passwordHash)
|
userCreate.PasswordHash = string(passwordHash)
|
||||||
user, err := s.Store.CreateUser(ctx, userCreate)
|
user, err := s.Store.CreateUserV1(ctx, userCreate)
|
||||||
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)
|
||||||
}
|
}
|
||||||
@ -228,9 +221,9 @@ func (s *APIV1Service) registerAuthRoutes(g *echo.Group, secret string) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *APIV1Service) createAuthSignInActivity(c echo.Context, user *api.User) error {
|
func (s *APIV1Service) createAuthSignInActivity(c echo.Context, user *store.UserMessage) error {
|
||||||
ctx := c.Request().Context()
|
ctx := c.Request().Context()
|
||||||
payload := api.ActivityUserAuthSignInPayload{
|
payload := ActivityUserAuthSignInPayload{
|
||||||
UserID: user.ID,
|
UserID: user.ID,
|
||||||
IP: echo.ExtractIPFromRealIPHeader()(c.Request()),
|
IP: echo.ExtractIPFromRealIPHeader()(c.Request()),
|
||||||
}
|
}
|
||||||
@ -238,10 +231,10 @@ func (s *APIV1Service) createAuthSignInActivity(c echo.Context, user *api.User)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "failed to marshal activity payload")
|
return errors.Wrap(err, "failed to marshal activity payload")
|
||||||
}
|
}
|
||||||
activity, err := s.Store.CreateActivity(ctx, &api.ActivityCreate{
|
activity, err := s.Store.CreateActivityV1(ctx, &store.ActivityMessage{
|
||||||
CreatorID: user.ID,
|
CreatorID: user.ID,
|
||||||
Type: api.ActivityUserAuthSignIn,
|
Type: string(ActivityUserAuthSignIn),
|
||||||
Level: api.ActivityInfo,
|
Level: string(ActivityInfo),
|
||||||
Payload: string(payloadBytes),
|
Payload: string(payloadBytes),
|
||||||
})
|
})
|
||||||
if err != nil || activity == nil {
|
if err != nil || activity == nil {
|
||||||
@ -250,9 +243,9 @@ func (s *APIV1Service) createAuthSignInActivity(c echo.Context, user *api.User)
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *APIV1Service) createAuthSignUpActivity(c echo.Context, user *api.User) error {
|
func (s *APIV1Service) createAuthSignUpActivity(c echo.Context, user *store.UserMessage) error {
|
||||||
ctx := c.Request().Context()
|
ctx := c.Request().Context()
|
||||||
payload := api.ActivityUserAuthSignUpPayload{
|
payload := ActivityUserAuthSignUpPayload{
|
||||||
Username: user.Username,
|
Username: user.Username,
|
||||||
IP: echo.ExtractIPFromRealIPHeader()(c.Request()),
|
IP: echo.ExtractIPFromRealIPHeader()(c.Request()),
|
||||||
}
|
}
|
||||||
@ -260,10 +253,10 @@ func (s *APIV1Service) createAuthSignUpActivity(c echo.Context, user *api.User)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "failed to marshal activity payload")
|
return errors.Wrap(err, "failed to marshal activity payload")
|
||||||
}
|
}
|
||||||
activity, err := s.Store.CreateActivity(ctx, &api.ActivityCreate{
|
activity, err := s.Store.CreateActivityV1(ctx, &store.ActivityMessage{
|
||||||
CreatorID: user.ID,
|
CreatorID: user.ID,
|
||||||
Type: api.ActivityUserAuthSignUp,
|
Type: string(ActivityUserAuthSignUp),
|
||||||
Level: api.ActivityInfo,
|
Level: string(ActivityInfo),
|
||||||
Payload: string(payloadBytes),
|
Payload: string(payloadBytes),
|
||||||
})
|
})
|
||||||
if err != nil || activity == nil {
|
if err != nil || activity == nil {
|
||||||
|
25
api/v1/memo.go
Normal file
25
api/v1/memo.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package v1
|
||||||
|
|
||||||
|
// Visibility is the type of a visibility.
|
||||||
|
type Visibility string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Public is the PUBLIC visibility.
|
||||||
|
Public Visibility = "PUBLIC"
|
||||||
|
// Protected is the PROTECTED visibility.
|
||||||
|
Protected Visibility = "PROTECTED"
|
||||||
|
// Private is the PRIVATE visibility.
|
||||||
|
Private Visibility = "PRIVATE"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (v Visibility) String() string {
|
||||||
|
switch v {
|
||||||
|
case Public:
|
||||||
|
return "PUBLIC"
|
||||||
|
case Protected:
|
||||||
|
return "PROTECTED"
|
||||||
|
case Private:
|
||||||
|
return "PRIVATE"
|
||||||
|
}
|
||||||
|
return "PRIVATE"
|
||||||
|
}
|
194
api/v1/system_setting.go
Normal file
194
api/v1/system_setting.go
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SystemSettingName string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// SystemSettingServerIDName is the name of server id.
|
||||||
|
SystemSettingServerIDName SystemSettingName = "server-id"
|
||||||
|
// SystemSettingSecretSessionName is the name of secret session.
|
||||||
|
SystemSettingSecretSessionName SystemSettingName = "secret-session"
|
||||||
|
// SystemSettingAllowSignUpName is the name of allow signup setting.
|
||||||
|
SystemSettingAllowSignUpName SystemSettingName = "allow-signup"
|
||||||
|
// SystemSettingDisablePublicMemosName is the name of disable public memos setting.
|
||||||
|
SystemSettingDisablePublicMemosName SystemSettingName = "disable-public-memos"
|
||||||
|
// SystemSettingMaxUploadSizeMiBName is the name of max upload size setting.
|
||||||
|
SystemSettingMaxUploadSizeMiBName SystemSettingName = "max-upload-size-mib"
|
||||||
|
// SystemSettingAdditionalStyleName is the name of additional style.
|
||||||
|
SystemSettingAdditionalStyleName SystemSettingName = "additional-style"
|
||||||
|
// SystemSettingAdditionalScriptName is the name of additional script.
|
||||||
|
SystemSettingAdditionalScriptName SystemSettingName = "additional-script"
|
||||||
|
// SystemSettingCustomizedProfileName is the name of customized server profile.
|
||||||
|
SystemSettingCustomizedProfileName SystemSettingName = "customized-profile"
|
||||||
|
// SystemSettingStorageServiceIDName is the name of storage service ID.
|
||||||
|
SystemSettingStorageServiceIDName SystemSettingName = "storage-service-id"
|
||||||
|
// SystemSettingLocalStoragePathName is the name of local storage path.
|
||||||
|
SystemSettingLocalStoragePathName SystemSettingName = "local-storage-path"
|
||||||
|
// SystemSettingOpenAIConfigName is the name of OpenAI config.
|
||||||
|
SystemSettingOpenAIConfigName SystemSettingName = "openai-config"
|
||||||
|
// SystemSettingTelegramBotToken is the name of Telegram Bot Token.
|
||||||
|
SystemSettingTelegramBotTokenName SystemSettingName = "telegram-bot-token"
|
||||||
|
SystemSettingMemoDisplayWithUpdatedTsName SystemSettingName = "memo-display-with-updated-ts"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CustomizedProfile is the struct definition for SystemSettingCustomizedProfileName system setting item.
|
||||||
|
type CustomizedProfile struct {
|
||||||
|
// Name is the server name, default is `memos`
|
||||||
|
Name string `json:"name"`
|
||||||
|
// LogoURL is the url of logo image.
|
||||||
|
LogoURL string `json:"logoUrl"`
|
||||||
|
// Description is the server description.
|
||||||
|
Description string `json:"description"`
|
||||||
|
// Locale is the server default locale.
|
||||||
|
Locale string `json:"locale"`
|
||||||
|
// Appearance is the server default appearance.
|
||||||
|
Appearance string `json:"appearance"`
|
||||||
|
// ExternalURL is the external url of server. e.g. https://usermemos.com
|
||||||
|
ExternalURL string `json:"externalUrl"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type OpenAIConfig struct {
|
||||||
|
Key string `json:"key"`
|
||||||
|
Host string `json:"host"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (key SystemSettingName) String() string {
|
||||||
|
switch key {
|
||||||
|
case SystemSettingServerIDName:
|
||||||
|
return "server-id"
|
||||||
|
case SystemSettingSecretSessionName:
|
||||||
|
return "secret-session"
|
||||||
|
case SystemSettingAllowSignUpName:
|
||||||
|
return "allow-signup"
|
||||||
|
case SystemSettingDisablePublicMemosName:
|
||||||
|
return "disable-public-memos"
|
||||||
|
case SystemSettingMaxUploadSizeMiBName:
|
||||||
|
return "max-upload-size-mib"
|
||||||
|
case SystemSettingAdditionalStyleName:
|
||||||
|
return "additional-style"
|
||||||
|
case SystemSettingAdditionalScriptName:
|
||||||
|
return "additional-script"
|
||||||
|
case SystemSettingCustomizedProfileName:
|
||||||
|
return "customized-profile"
|
||||||
|
case SystemSettingStorageServiceIDName:
|
||||||
|
return "storage-service-id"
|
||||||
|
case SystemSettingLocalStoragePathName:
|
||||||
|
return "local-storage-path"
|
||||||
|
case SystemSettingOpenAIConfigName:
|
||||||
|
return "openai-config"
|
||||||
|
case SystemSettingTelegramBotTokenName:
|
||||||
|
return "telegram-bot-token"
|
||||||
|
case SystemSettingMemoDisplayWithUpdatedTsName:
|
||||||
|
return "memo-display-with-updated-ts"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type SystemSetting struct {
|
||||||
|
Name SystemSettingName `json:"name"`
|
||||||
|
// Value is a JSON string with basic value.
|
||||||
|
Value string `json:"value"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SystemSettingUpsert struct {
|
||||||
|
Name SystemSettingName `json:"name"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
}
|
||||||
|
|
||||||
|
const systemSettingUnmarshalError = `failed to unmarshal value from system setting "%v"`
|
||||||
|
|
||||||
|
func (upsert SystemSettingUpsert) Validate() error {
|
||||||
|
switch settingName := upsert.Name; settingName {
|
||||||
|
case SystemSettingServerIDName:
|
||||||
|
return fmt.Errorf("updating %v is not allowed", settingName)
|
||||||
|
case SystemSettingAllowSignUpName:
|
||||||
|
var value bool
|
||||||
|
if err := json.Unmarshal([]byte(upsert.Value), &value); err != nil {
|
||||||
|
return fmt.Errorf(systemSettingUnmarshalError, settingName)
|
||||||
|
}
|
||||||
|
case SystemSettingDisablePublicMemosName:
|
||||||
|
var value bool
|
||||||
|
if err := json.Unmarshal([]byte(upsert.Value), &value); err != nil {
|
||||||
|
return fmt.Errorf(systemSettingUnmarshalError, settingName)
|
||||||
|
}
|
||||||
|
case SystemSettingMaxUploadSizeMiBName:
|
||||||
|
var value int
|
||||||
|
if err := json.Unmarshal([]byte(upsert.Value), &value); err != nil {
|
||||||
|
return fmt.Errorf(systemSettingUnmarshalError, settingName)
|
||||||
|
}
|
||||||
|
case SystemSettingAdditionalStyleName:
|
||||||
|
var value string
|
||||||
|
if err := json.Unmarshal([]byte(upsert.Value), &value); err != nil {
|
||||||
|
return fmt.Errorf(systemSettingUnmarshalError, settingName)
|
||||||
|
}
|
||||||
|
case SystemSettingAdditionalScriptName:
|
||||||
|
var value string
|
||||||
|
if err := json.Unmarshal([]byte(upsert.Value), &value); err != nil {
|
||||||
|
return fmt.Errorf(systemSettingUnmarshalError, settingName)
|
||||||
|
}
|
||||||
|
case SystemSettingCustomizedProfileName:
|
||||||
|
customizedProfile := CustomizedProfile{
|
||||||
|
Name: "memos",
|
||||||
|
LogoURL: "",
|
||||||
|
Description: "",
|
||||||
|
Locale: "en",
|
||||||
|
Appearance: "system",
|
||||||
|
ExternalURL: "",
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal([]byte(upsert.Value), &customizedProfile); err != nil {
|
||||||
|
return fmt.Errorf(systemSettingUnmarshalError, settingName)
|
||||||
|
}
|
||||||
|
case SystemSettingStorageServiceIDName:
|
||||||
|
// Note: 0 is the default value(database) for storage service ID.
|
||||||
|
value := 0
|
||||||
|
if err := json.Unmarshal([]byte(upsert.Value), &value); err != nil {
|
||||||
|
return fmt.Errorf(systemSettingUnmarshalError, settingName)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
case SystemSettingLocalStoragePathName:
|
||||||
|
value := ""
|
||||||
|
if err := json.Unmarshal([]byte(upsert.Value), &value); err != nil {
|
||||||
|
return fmt.Errorf(systemSettingUnmarshalError, settingName)
|
||||||
|
}
|
||||||
|
case SystemSettingOpenAIConfigName:
|
||||||
|
value := OpenAIConfig{}
|
||||||
|
if err := json.Unmarshal([]byte(upsert.Value), &value); err != nil {
|
||||||
|
return fmt.Errorf(systemSettingUnmarshalError, settingName)
|
||||||
|
}
|
||||||
|
case SystemSettingTelegramBotTokenName:
|
||||||
|
if upsert.Value == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// Bot Token with Reverse Proxy shoule like `http.../bot<token>`
|
||||||
|
if strings.HasPrefix(upsert.Value, "http") {
|
||||||
|
slashIndex := strings.LastIndexAny(upsert.Value, "/")
|
||||||
|
if strings.HasPrefix(upsert.Value[slashIndex:], "/bot") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("token start with `http` must end with `/bot<token>`")
|
||||||
|
}
|
||||||
|
fragments := strings.Split(upsert.Value, ":")
|
||||||
|
if len(fragments) != 2 {
|
||||||
|
return fmt.Errorf(systemSettingUnmarshalError, settingName)
|
||||||
|
}
|
||||||
|
case SystemSettingMemoDisplayWithUpdatedTsName:
|
||||||
|
var value bool
|
||||||
|
if err := json.Unmarshal([]byte(upsert.Value), &value); err != nil {
|
||||||
|
return fmt.Errorf(systemSettingUnmarshalError, settingName)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("invalid system setting name")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type SystemSettingFind struct {
|
||||||
|
Name SystemSettingName `json:"name"`
|
||||||
|
}
|
134
api/v1/user_setting.go
Normal file
134
api/v1/user_setting.go
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserSettingKey string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// UserSettingLocaleKey is the key type for user locale.
|
||||||
|
UserSettingLocaleKey UserSettingKey = "locale"
|
||||||
|
// UserSettingAppearanceKey is the key type for user appearance.
|
||||||
|
UserSettingAppearanceKey UserSettingKey = "appearance"
|
||||||
|
// UserSettingMemoVisibilityKey is the key type for user preference memo default visibility.
|
||||||
|
UserSettingMemoVisibilityKey UserSettingKey = "memo-visibility"
|
||||||
|
// UserSettingTelegramUserID is the key type for telegram UserID of memos user.
|
||||||
|
UserSettingTelegramUserIDKey UserSettingKey = "telegram-user-id"
|
||||||
|
)
|
||||||
|
|
||||||
|
// String returns the string format of UserSettingKey type.
|
||||||
|
func (key UserSettingKey) String() string {
|
||||||
|
switch key {
|
||||||
|
case UserSettingLocaleKey:
|
||||||
|
return "locale"
|
||||||
|
case UserSettingAppearanceKey:
|
||||||
|
return "appearance"
|
||||||
|
case UserSettingMemoVisibilityKey:
|
||||||
|
return "memo-visibility"
|
||||||
|
case UserSettingTelegramUserIDKey:
|
||||||
|
return "telegram-user-id"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
UserSettingLocaleValue = []string{
|
||||||
|
"de",
|
||||||
|
"en",
|
||||||
|
"es",
|
||||||
|
"fr",
|
||||||
|
"hr",
|
||||||
|
"it",
|
||||||
|
"ja",
|
||||||
|
"ko",
|
||||||
|
"nl",
|
||||||
|
"pl",
|
||||||
|
"pt-BR",
|
||||||
|
"ru",
|
||||||
|
"sl",
|
||||||
|
"sv",
|
||||||
|
"tr",
|
||||||
|
"uk",
|
||||||
|
"vi",
|
||||||
|
"zh-Hans",
|
||||||
|
"zh-Hant",
|
||||||
|
}
|
||||||
|
UserSettingAppearanceValue = []string{"system", "light", "dark"}
|
||||||
|
UserSettingMemoVisibilityValue = []Visibility{Private, Protected, Public}
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserSetting struct {
|
||||||
|
UserID int
|
||||||
|
Key UserSettingKey `json:"key"`
|
||||||
|
// Value is a JSON string with basic value
|
||||||
|
Value string `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserSettingUpsert struct {
|
||||||
|
UserID int `json:"-"`
|
||||||
|
Key UserSettingKey `json:"key"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (upsert UserSettingUpsert) Validate() error {
|
||||||
|
if upsert.Key == UserSettingLocaleKey {
|
||||||
|
localeValue := "en"
|
||||||
|
err := json.Unmarshal([]byte(upsert.Value), &localeValue)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to unmarshal user setting locale value")
|
||||||
|
}
|
||||||
|
if !slices.Contains(UserSettingLocaleValue, localeValue) {
|
||||||
|
return fmt.Errorf("invalid user setting locale value")
|
||||||
|
}
|
||||||
|
} else if upsert.Key == UserSettingAppearanceKey {
|
||||||
|
appearanceValue := "system"
|
||||||
|
err := json.Unmarshal([]byte(upsert.Value), &appearanceValue)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to unmarshal user setting appearance value")
|
||||||
|
}
|
||||||
|
if !slices.Contains(UserSettingAppearanceValue, appearanceValue) {
|
||||||
|
return fmt.Errorf("invalid user setting appearance value")
|
||||||
|
}
|
||||||
|
} else if upsert.Key == UserSettingMemoVisibilityKey {
|
||||||
|
memoVisibilityValue := Private
|
||||||
|
err := json.Unmarshal([]byte(upsert.Value), &memoVisibilityValue)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to unmarshal user setting memo visibility value")
|
||||||
|
}
|
||||||
|
if !slices.Contains(UserSettingMemoVisibilityValue, memoVisibilityValue) {
|
||||||
|
return fmt.Errorf("invalid user setting memo visibility value")
|
||||||
|
}
|
||||||
|
} else if upsert.Key == UserSettingTelegramUserIDKey {
|
||||||
|
var s string
|
||||||
|
err := json.Unmarshal([]byte(upsert.Value), &s)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid user setting telegram user id value")
|
||||||
|
}
|
||||||
|
|
||||||
|
if s == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if _, err := strconv.Atoi(s); err != nil {
|
||||||
|
return fmt.Errorf("invalid user setting telegram user id value")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("invalid user setting key")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserSettingFind struct {
|
||||||
|
UserID *int
|
||||||
|
|
||||||
|
Key UserSettingKey `json:"key"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserSettingDelete struct {
|
||||||
|
UserID int
|
||||||
|
}
|
@ -8,7 +8,7 @@ import (
|
|||||||
"github.com/golang-jwt/jwt/v4"
|
"github.com/golang-jwt/jwt/v4"
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/usememos/memos/api"
|
"github.com/usememos/memos/store"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -64,7 +64,7 @@ func GenerateRefreshToken(userName string, userID int, secret string) (string, e
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GenerateTokensAndSetCookies generates jwt token and saves it to the http-only cookie.
|
// GenerateTokensAndSetCookies generates jwt token and saves it to the http-only cookie.
|
||||||
func GenerateTokensAndSetCookies(c echo.Context, user *api.User, secret string) error {
|
func GenerateTokensAndSetCookies(c echo.Context, user *store.UserMessage, secret string) error {
|
||||||
accessToken, err := GenerateAccessToken(user.Username, user.ID, secret)
|
accessToken, err := GenerateAccessToken(user.Username, user.ID, secret)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "failed to generate access token")
|
return errors.Wrap(err, "failed to generate access token")
|
||||||
|
@ -10,9 +10,9 @@ import (
|
|||||||
"github.com/golang-jwt/jwt/v4"
|
"github.com/golang-jwt/jwt/v4"
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/usememos/memos/api"
|
|
||||||
"github.com/usememos/memos/common"
|
"github.com/usememos/memos/common"
|
||||||
"github.com/usememos/memos/server/auth"
|
"github.com/usememos/memos/server/auth"
|
||||||
|
"github.com/usememos/memos/store"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -136,7 +136,7 @@ func JWTMiddleware(server *Server, next echo.HandlerFunc, secret string) echo.Ha
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Even if there is no error, we still need to make sure the user still exists.
|
// Even if there is no error, we still need to make sure the user still exists.
|
||||||
user, err := server.Store.FindUser(ctx, &api.UserFind{
|
user, err := server.Store.GetUser(ctx, &store.FindUserMessage{
|
||||||
ID: &userID,
|
ID: &userID,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
|
|
||||||
"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/common"
|
"github.com/usememos/memos/common"
|
||||||
"github.com/usememos/memos/store"
|
"github.com/usememos/memos/store"
|
||||||
|
|
||||||
@ -37,9 +38,9 @@ func (s *Server) registerMemoRoutes(g *echo.Group) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if createMemoRequest.Visibility == "" {
|
if createMemoRequest.Visibility == "" {
|
||||||
userMemoVisibilitySetting, err := s.Store.FindUserSetting(ctx, &api.UserSettingFind{
|
userMemoVisibilitySetting, err := s.Store.GetUserSetting(ctx, &store.FindUserSettingMessage{
|
||||||
UserID: &userID,
|
UserID: &userID,
|
||||||
Key: api.UserSettingMemoVisibilityKey,
|
Key: apiv1.UserSettingMemoVisibilityKey.String(),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find user setting").SetInternal(err)
|
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find user setting").SetInternal(err)
|
||||||
|
@ -7,7 +7,9 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"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/common"
|
"github.com/usememos/memos/common"
|
||||||
"github.com/usememos/memos/plugin/telegram"
|
"github.com/usememos/memos/plugin/telegram"
|
||||||
"github.com/usememos/memos/store"
|
"github.com/usememos/memos/store"
|
||||||
@ -37,14 +39,13 @@ func (t *telegramHandler) MessageHandle(ctx context.Context, bot *telegram.Bot,
|
|||||||
}
|
}
|
||||||
|
|
||||||
var creatorID int
|
var creatorID int
|
||||||
userSettingList, err := t.store.FindUserSettingList(ctx, &api.UserSettingFind{
|
userSettingMessageList, err := t.store.ListUserSettings(ctx, &store.FindUserSettingMessage{
|
||||||
Key: api.UserSettingTelegramUserIDKey,
|
Key: apiv1.UserSettingTelegramUserIDKey.String(),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_, err := bot.EditMessage(ctx, message.Chat.ID, reply.MessageID, fmt.Sprintf("Fail to find memo user: %s", err), nil)
|
return errors.Wrap(err, "Failed to find userSettingList")
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
for _, userSetting := range userSettingList {
|
for _, userSetting := range userSettingMessageList {
|
||||||
var value string
|
var value string
|
||||||
if err := json.Unmarshal([]byte(userSetting.Value), &value); err != nil {
|
if err := json.Unmarshal([]byte(userSetting.Value), &value); err != nil {
|
||||||
continue
|
continue
|
||||||
|
@ -9,7 +9,9 @@ import (
|
|||||||
|
|
||||||
"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/common"
|
"github.com/usememos/memos/common"
|
||||||
|
"github.com/usememos/memos/store"
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
@ -83,7 +85,7 @@ func (s *Server) registerUserRoutes(g *echo.Group) {
|
|||||||
return echo.NewHTTPError(http.StatusUnauthorized, "Missing auth session")
|
return echo.NewHTTPError(http.StatusUnauthorized, "Missing auth session")
|
||||||
}
|
}
|
||||||
|
|
||||||
userSettingUpsert := &api.UserSettingUpsert{}
|
userSettingUpsert := &apiv1.UserSettingUpsert{}
|
||||||
if err := json.NewDecoder(c.Request().Body).Decode(userSettingUpsert); err != nil {
|
if err := json.NewDecoder(c.Request().Body).Decode(userSettingUpsert); err != nil {
|
||||||
return echo.NewHTTPError(http.StatusBadRequest, "Malformatted post user setting upsert request").SetInternal(err)
|
return echo.NewHTTPError(http.StatusBadRequest, "Malformatted post user setting upsert request").SetInternal(err)
|
||||||
}
|
}
|
||||||
@ -92,10 +94,15 @@ func (s *Server) registerUserRoutes(g *echo.Group) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
userSettingUpsert.UserID = userID
|
userSettingUpsert.UserID = userID
|
||||||
userSetting, err := s.Store.UpsertUserSetting(ctx, userSettingUpsert)
|
userSettingMessage, err := s.Store.UpsertUserSettingV1(ctx, &store.UserSettingMessage{
|
||||||
|
UserID: userID,
|
||||||
|
Key: userSettingUpsert.Key.String(),
|
||||||
|
Value: userSettingUpsert.Value,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to upsert user setting").SetInternal(err)
|
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to upsert user setting").SetInternal(err)
|
||||||
}
|
}
|
||||||
|
userSetting := convertUserSettingFromStore(userSettingMessage)
|
||||||
return c.JSON(http.StatusOK, composeResponse(userSetting))
|
return c.JSON(http.StatusOK, composeResponse(userSetting))
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -115,12 +122,21 @@ func (s *Server) registerUserRoutes(g *echo.Group) {
|
|||||||
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find user").SetInternal(err)
|
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find user").SetInternal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
userSettingList, err := s.Store.FindUserSettingList(ctx, &api.UserSettingFind{
|
userSettingMessageList, err := s.Store.ListUserSettings(ctx, &store.FindUserSettingMessage{
|
||||||
UserID: &userID,
|
UserID: &userID,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find userSettingList").SetInternal(err)
|
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find userSettingList").SetInternal(err)
|
||||||
}
|
}
|
||||||
|
userSettingList := []*api.UserSetting{}
|
||||||
|
for _, userSettingMessage := range userSettingMessageList {
|
||||||
|
userSettingV1 := convertUserSettingFromStore(userSettingMessage)
|
||||||
|
userSettingList = append(userSettingList, &api.UserSetting{
|
||||||
|
UserID: userSettingV1.UserID,
|
||||||
|
Key: api.UserSettingKey(userSettingV1.Key),
|
||||||
|
Value: userSettingV1.Value,
|
||||||
|
})
|
||||||
|
}
|
||||||
user.UserSettingList = userSettingList
|
user.UserSettingList = userSettingList
|
||||||
return c.JSON(http.StatusOK, composeResponse(user))
|
return c.JSON(http.StatusOK, composeResponse(user))
|
||||||
})
|
})
|
||||||
@ -202,12 +218,21 @@ func (s *Server) registerUserRoutes(g *echo.Group) {
|
|||||||
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to patch user").SetInternal(err)
|
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to patch user").SetInternal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
userSettingList, err := s.Store.FindUserSettingList(ctx, &api.UserSettingFind{
|
userSettingMessageList, err := s.Store.ListUserSettings(ctx, &store.FindUserSettingMessage{
|
||||||
UserID: &userID,
|
UserID: &userID,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find userSettingList").SetInternal(err)
|
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find userSettingList").SetInternal(err)
|
||||||
}
|
}
|
||||||
|
userSettingList := []*api.UserSetting{}
|
||||||
|
for _, userSettingMessage := range userSettingMessageList {
|
||||||
|
userSettingV1 := convertUserSettingFromStore(userSettingMessage)
|
||||||
|
userSettingList = append(userSettingList, &api.UserSetting{
|
||||||
|
UserID: userSettingV1.UserID,
|
||||||
|
Key: api.UserSettingKey(userSettingV1.Key),
|
||||||
|
Value: userSettingV1.Value,
|
||||||
|
})
|
||||||
|
}
|
||||||
user.UserSettingList = userSettingList
|
user.UserSettingList = userSettingList
|
||||||
return c.JSON(http.StatusOK, composeResponse(user))
|
return c.JSON(http.StatusOK, composeResponse(user))
|
||||||
})
|
})
|
||||||
@ -271,3 +296,11 @@ func (s *Server) createUserCreateActivity(c echo.Context, user *api.User) error
|
|||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func convertUserSettingFromStore(userSetting *store.UserSettingMessage) *apiv1.UserSetting {
|
||||||
|
return &apiv1.UserSetting{
|
||||||
|
UserID: userSetting.UserID,
|
||||||
|
Key: apiv1.UserSettingKey(userSetting.Key),
|
||||||
|
Value: userSetting.Value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -7,6 +7,51 @@ import (
|
|||||||
"github.com/usememos/memos/api"
|
"github.com/usememos/memos/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type ActivityMessage struct {
|
||||||
|
ID int
|
||||||
|
|
||||||
|
// Standard fields
|
||||||
|
CreatorID int
|
||||||
|
CreatedTs int64
|
||||||
|
|
||||||
|
// Domain specific fields
|
||||||
|
Type string
|
||||||
|
Level string
|
||||||
|
Payload string
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateActivityV1 creates an instance of Activity.
|
||||||
|
func (s *Store) CreateActivityV1(ctx context.Context, create *ActivityMessage) (*ActivityMessage, error) {
|
||||||
|
tx, err := s.db.BeginTx(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, FormatError(err)
|
||||||
|
}
|
||||||
|
defer tx.Rollback()
|
||||||
|
|
||||||
|
query := `
|
||||||
|
INSERT INTO activity (
|
||||||
|
creator_id,
|
||||||
|
type,
|
||||||
|
level,
|
||||||
|
payload
|
||||||
|
)
|
||||||
|
VALUES (?, ?, ?, ?)
|
||||||
|
RETURNING id, created_ts
|
||||||
|
`
|
||||||
|
if err := tx.QueryRowContext(ctx, query, create.CreatorID, create.Type, create.Level, create.Payload).Scan(
|
||||||
|
&create.ID,
|
||||||
|
&create.CreatedTs,
|
||||||
|
); err != nil {
|
||||||
|
return nil, FormatError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tx.Commit(); err != nil {
|
||||||
|
return nil, FormatError(err)
|
||||||
|
}
|
||||||
|
activityMessage := create
|
||||||
|
return activityMessage, nil
|
||||||
|
}
|
||||||
|
|
||||||
// activityRaw is the store model for an Activity.
|
// activityRaw is the store model for an Activity.
|
||||||
// Fields have exactly the same meanings as Activity.
|
// Fields have exactly the same meanings as Activity.
|
||||||
type activityRaw struct {
|
type activityRaw struct {
|
||||||
@ -38,10 +83,6 @@ func (raw *activityRaw) toActivity() *api.Activity {
|
|||||||
|
|
||||||
// CreateActivity creates an instance of Activity.
|
// CreateActivity creates an instance of Activity.
|
||||||
func (s *Store) CreateActivity(ctx context.Context, create *api.ActivityCreate) (*api.Activity, error) {
|
func (s *Store) CreateActivity(ctx context.Context, create *api.ActivityCreate) (*api.Activity, error) {
|
||||||
if s.Profile.Mode == "prod" {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
tx, err := s.db.BeginTx(ctx, nil)
|
tx, err := s.db.BeginTx(ctx, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, FormatError(err)
|
return nil, FormatError(err)
|
||||||
|
@ -2,14 +2,8 @@ package store
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/usememos/memos/api"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func getUserSettingCacheKey(userSetting userSettingRaw) string {
|
func getUserSettingCacheKeyV1(userID int, key string) string {
|
||||||
return fmt.Sprintf("%d-%s", userSetting.UserID, userSetting.Key.String())
|
return fmt.Sprintf("%d-%s", userID, key)
|
||||||
}
|
|
||||||
|
|
||||||
func getUserSettingFindCacheKey(userSettingFind *api.UserSettingFind) string {
|
|
||||||
return fmt.Sprintf("%d-%s", userSettingFind.UserID, userSettingFind.Key.String())
|
|
||||||
}
|
}
|
||||||
|
@ -291,7 +291,7 @@ func listIdentityProviders(ctx context.Context, tx *sql.Tx, find *FindIdentityPr
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := rows.Err(); err != nil {
|
if err := rows.Err(); err != nil {
|
||||||
return nil, FormatError(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return identityProviderMessages, nil
|
return identityProviderMessages, nil
|
||||||
|
@ -14,9 +14,9 @@ type Store struct {
|
|||||||
db *sql.DB
|
db *sql.DB
|
||||||
systemSettingCache sync.Map // map[string]*systemSettingRaw
|
systemSettingCache sync.Map // map[string]*systemSettingRaw
|
||||||
userCache sync.Map // map[int]*userRaw
|
userCache sync.Map // map[int]*userRaw
|
||||||
userSettingCache sync.Map // map[string]*userSettingRaw
|
userSettingCache sync.Map // map[string]*UserSettingMessage
|
||||||
shortcutCache sync.Map // map[int]*shortcutRaw
|
shortcutCache sync.Map // map[int]*shortcutRaw
|
||||||
idpCache sync.Map // map[int]*identityProviderMessage
|
idpCache sync.Map // map[int]*IdentityProviderMessage
|
||||||
resourceCache sync.Map // map[int]*resourceRaw
|
resourceCache sync.Map // map[int]*resourceRaw
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,101 @@ import (
|
|||||||
"github.com/usememos/memos/common"
|
"github.com/usememos/memos/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type SystemSettingMessage struct {
|
||||||
|
Name string
|
||||||
|
Value string
|
||||||
|
Description string
|
||||||
|
}
|
||||||
|
|
||||||
|
type FindSystemSettingMessage struct {
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) ListSystemSettings(ctx context.Context, find *FindSystemSettingMessage) ([]*SystemSettingMessage, error) {
|
||||||
|
tx, err := s.db.BeginTx(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, FormatError(err)
|
||||||
|
}
|
||||||
|
defer tx.Rollback()
|
||||||
|
|
||||||
|
list, err := listSystemSettings(ctx, tx, find)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, systemSettingMessage := range list {
|
||||||
|
s.systemSettingCache.Store(systemSettingMessage.Name, systemSettingMessage)
|
||||||
|
}
|
||||||
|
return list, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) GetSystemSetting(ctx context.Context, find *FindSystemSettingMessage) (*SystemSettingMessage, error) {
|
||||||
|
if find.Name != "" {
|
||||||
|
if cache, ok := s.systemSettingCache.Load(find.Name); ok {
|
||||||
|
return cache.(*SystemSettingMessage), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tx, err := s.db.BeginTx(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, FormatError(err)
|
||||||
|
}
|
||||||
|
defer tx.Rollback()
|
||||||
|
|
||||||
|
list, err := listSystemSettings(ctx, tx, find)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(list) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
systemSettingMessage := list[0]
|
||||||
|
s.systemSettingCache.Store(systemSettingMessage.Name, systemSettingMessage)
|
||||||
|
return systemSettingMessage, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func listSystemSettings(ctx context.Context, tx *sql.Tx, find *FindSystemSettingMessage) ([]*SystemSettingMessage, error) {
|
||||||
|
where, args := []string{"1 = 1"}, []any{}
|
||||||
|
if find.Name != "" {
|
||||||
|
where, args = append(where, "name = ?"), append(args, find.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
query := `
|
||||||
|
SELECT
|
||||||
|
name,
|
||||||
|
value,
|
||||||
|
description
|
||||||
|
FROM system_setting
|
||||||
|
WHERE ` + strings.Join(where, " AND ")
|
||||||
|
|
||||||
|
rows, err := tx.QueryContext(ctx, query, args...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, FormatError(err)
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
list := []*SystemSettingMessage{}
|
||||||
|
for rows.Next() {
|
||||||
|
systemSettingMessage := &SystemSettingMessage{}
|
||||||
|
if err := rows.Scan(
|
||||||
|
&systemSettingMessage.Name,
|
||||||
|
&systemSettingMessage.Value,
|
||||||
|
&systemSettingMessage.Description,
|
||||||
|
); err != nil {
|
||||||
|
return nil, FormatError(err)
|
||||||
|
}
|
||||||
|
list = append(list, systemSettingMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return list, nil
|
||||||
|
}
|
||||||
|
|
||||||
type systemSettingRaw struct {
|
type systemSettingRaw struct {
|
||||||
Name api.SystemSettingName
|
Name api.SystemSettingName
|
||||||
Value string
|
Value string
|
||||||
|
@ -10,23 +10,6 @@ import (
|
|||||||
"github.com/usememos/memos/common"
|
"github.com/usememos/memos/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Store) SeedDataForNewUser(ctx context.Context, user *api.User) error {
|
|
||||||
// Create a memo for the user.
|
|
||||||
_, err := s.CreateMemo(ctx, &MemoMessage{
|
|
||||||
CreatorID: user.ID,
|
|
||||||
Content: "#inbox Welcome to Memos!",
|
|
||||||
Visibility: Private,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = s.UpsertTag(ctx, &api.TagUpsert{
|
|
||||||
CreatorID: user.ID,
|
|
||||||
Name: "inbox",
|
|
||||||
})
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Role is the type of a role.
|
// Role is the type of a role.
|
||||||
type Role string
|
type Role string
|
||||||
|
|
||||||
@ -289,10 +272,6 @@ func (s *Store) CreateUser(ctx context.Context, create *api.UserCreate) (*api.Us
|
|||||||
s.userCache.Store(userRaw.ID, userRaw)
|
s.userCache.Store(userRaw.ID, userRaw)
|
||||||
user := userRaw.toUser()
|
user := userRaw.toUser()
|
||||||
|
|
||||||
if err := s.SeedDataForNewUser(ctx, user); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return user, nil
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,33 +4,35 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/usememos/memos/api"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type userSettingRaw struct {
|
type UserSettingMessage struct {
|
||||||
UserID int
|
UserID int
|
||||||
Key api.UserSettingKey
|
Key string
|
||||||
Value string
|
Value string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (raw *userSettingRaw) toUserSetting() *api.UserSetting {
|
type FindUserSettingMessage struct {
|
||||||
return &api.UserSetting{
|
UserID *int
|
||||||
UserID: raw.UserID,
|
Key string
|
||||||
Key: raw.Key,
|
|
||||||
Value: raw.Value,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) UpsertUserSetting(ctx context.Context, upsert *api.UserSettingUpsert) (*api.UserSetting, error) {
|
func (s *Store) UpsertUserSettingV1(ctx context.Context, upsert *UserSettingMessage) (*UserSettingMessage, error) {
|
||||||
tx, err := s.db.BeginTx(ctx, nil)
|
tx, err := s.db.BeginTx(ctx, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, FormatError(err)
|
return nil, FormatError(err)
|
||||||
}
|
}
|
||||||
defer tx.Rollback()
|
defer tx.Rollback()
|
||||||
|
|
||||||
userSettingRaw, err := upsertUserSetting(ctx, tx, upsert)
|
query := `
|
||||||
if err != nil {
|
INSERT INTO user_setting (
|
||||||
|
user_id, key, value
|
||||||
|
)
|
||||||
|
VALUES (?, ?, ?)
|
||||||
|
ON CONFLICT(user_id, key) DO UPDATE
|
||||||
|
SET value = EXCLUDED.value
|
||||||
|
`
|
||||||
|
if _, err := tx.ExecContext(ctx, query, upsert.UserID, upsert.Key, upsert.Value); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,39 +40,34 @@ func (s *Store) UpsertUserSetting(ctx context.Context, upsert *api.UserSettingUp
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
s.userSettingCache.Store(getUserSettingCacheKey(*userSettingRaw), userSettingRaw)
|
userSettingMessage := upsert
|
||||||
userSetting := userSettingRaw.toUserSetting()
|
s.userSettingCache.Store(getUserSettingCacheKeyV1(userSettingMessage.UserID, userSettingMessage.Key), userSettingMessage)
|
||||||
|
return userSettingMessage, nil
|
||||||
return userSetting, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) FindUserSettingList(ctx context.Context, find *api.UserSettingFind) ([]*api.UserSetting, error) {
|
func (s *Store) ListUserSettings(ctx context.Context, find *FindUserSettingMessage) ([]*UserSettingMessage, error) {
|
||||||
tx, err := s.db.BeginTx(ctx, nil)
|
tx, err := s.db.BeginTx(ctx, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, FormatError(err)
|
return nil, FormatError(err)
|
||||||
}
|
}
|
||||||
defer tx.Rollback()
|
defer tx.Rollback()
|
||||||
|
|
||||||
userSettingRawList, err := findUserSettingList(ctx, tx, find)
|
userSettingList, err := listUserSettings(ctx, tx, find)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
list := []*api.UserSetting{}
|
for _, userSetting := range userSettingList {
|
||||||
for _, raw := range userSettingRawList {
|
s.userSettingCache.Store(getUserSettingCacheKeyV1(userSetting.UserID, userSetting.Key), userSetting)
|
||||||
s.userSettingCache.Store(getUserSettingCacheKey(*raw), raw)
|
|
||||||
list = append(list, raw.toUserSetting())
|
|
||||||
}
|
}
|
||||||
|
return userSettingList, nil
|
||||||
return list, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) FindUserSetting(ctx context.Context, find *api.UserSettingFind) (*api.UserSetting, error) {
|
func (s *Store) GetUserSetting(ctx context.Context, find *FindUserSettingMessage) (*UserSettingMessage, error) {
|
||||||
if userSetting, ok := s.userSettingCache.Load(getUserSettingFindCacheKey(find)); ok {
|
if find.UserID != nil {
|
||||||
if userSetting == nil {
|
if cache, ok := s.userSettingCache.Load(getUserSettingCacheKeyV1(*find.UserID, find.Key)); ok {
|
||||||
return nil, nil
|
return cache.(*UserSettingMessage), nil
|
||||||
}
|
}
|
||||||
return userSetting.(*userSettingRaw).toUserSetting(), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tx, err := s.db.BeginTx(ctx, nil)
|
tx, err := s.db.BeginTx(ctx, nil)
|
||||||
@ -79,51 +76,25 @@ func (s *Store) FindUserSetting(ctx context.Context, find *api.UserSettingFind)
|
|||||||
}
|
}
|
||||||
defer tx.Rollback()
|
defer tx.Rollback()
|
||||||
|
|
||||||
list, err := findUserSettingList(ctx, tx, find)
|
list, err := listUserSettings(ctx, tx, find)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(list) == 0 {
|
if len(list) == 0 {
|
||||||
s.userSettingCache.Store(getUserSettingFindCacheKey(find), nil)
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
userSettingMessage := list[0]
|
||||||
userSettingRaw := list[0]
|
s.userSettingCache.Store(getUserSettingCacheKeyV1(userSettingMessage.UserID, userSettingMessage.Key), userSettingMessage)
|
||||||
s.userSettingCache.Store(getUserSettingCacheKey(*userSettingRaw), userSettingRaw)
|
return userSettingMessage, nil
|
||||||
return userSettingRaw.toUserSetting(), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func upsertUserSetting(ctx context.Context, tx *sql.Tx, upsert *api.UserSettingUpsert) (*userSettingRaw, error) {
|
func listUserSettings(ctx context.Context, tx *sql.Tx, find *FindUserSettingMessage) ([]*UserSettingMessage, error) {
|
||||||
query := `
|
|
||||||
INSERT INTO user_setting (
|
|
||||||
user_id, key, value
|
|
||||||
)
|
|
||||||
VALUES (?, ?, ?)
|
|
||||||
ON CONFLICT(user_id, key) DO UPDATE
|
|
||||||
SET
|
|
||||||
value = EXCLUDED.value
|
|
||||||
RETURNING user_id, key, value
|
|
||||||
`
|
|
||||||
var userSettingRaw userSettingRaw
|
|
||||||
if err := tx.QueryRowContext(ctx, query, upsert.UserID, upsert.Key, upsert.Value).Scan(
|
|
||||||
&userSettingRaw.UserID,
|
|
||||||
&userSettingRaw.Key,
|
|
||||||
&userSettingRaw.Value,
|
|
||||||
); err != nil {
|
|
||||||
return nil, FormatError(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &userSettingRaw, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func findUserSettingList(ctx context.Context, tx *sql.Tx, find *api.UserSettingFind) ([]*userSettingRaw, error) {
|
|
||||||
where, args := []string{"1 = 1"}, []any{}
|
where, args := []string{"1 = 1"}, []any{}
|
||||||
|
|
||||||
if v := find.Key.String(); v != "" {
|
if v := find.Key; v != "" {
|
||||||
where, args = append(where, "key = ?"), append(args, v)
|
where, args = append(where, "key = ?"), append(args, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
if v := find.UserID; v != nil {
|
if v := find.UserID; v != nil {
|
||||||
where, args = append(where, "user_id = ?"), append(args, *find.UserID)
|
where, args = append(where, "user_id = ?"), append(args, *find.UserID)
|
||||||
}
|
}
|
||||||
@ -141,25 +112,24 @@ func findUserSettingList(ctx context.Context, tx *sql.Tx, find *api.UserSettingF
|
|||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
userSettingRawList := make([]*userSettingRaw, 0)
|
userSettingMessageList := make([]*UserSettingMessage, 0)
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var userSettingRaw userSettingRaw
|
var userSettingMessage UserSettingMessage
|
||||||
if err := rows.Scan(
|
if err := rows.Scan(
|
||||||
&userSettingRaw.UserID,
|
&userSettingMessage.UserID,
|
||||||
&userSettingRaw.Key,
|
&userSettingMessage.Key,
|
||||||
&userSettingRaw.Value,
|
&userSettingMessage.Value,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, FormatError(err)
|
return nil, FormatError(err)
|
||||||
}
|
}
|
||||||
|
userSettingMessageList = append(userSettingMessageList, &userSettingMessage)
|
||||||
userSettingRawList = append(userSettingRawList, &userSettingRaw)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := rows.Err(); err != nil {
|
if err := rows.Err(); err != nil {
|
||||||
return nil, FormatError(err)
|
return nil, FormatError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return userSettingRawList, nil
|
return userSettingMessageList, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func vacuumUserSetting(ctx context.Context, tx *sql.Tx) error {
|
func vacuumUserSetting(ctx context.Context, tx *sql.Tx) error {
|
||||||
|
@ -26,9 +26,6 @@ func TestMemoRelationServer(t *testing.T) {
|
|||||||
user, err := s.postAuthSignup(signup)
|
user, err := s.postAuthSignup(signup)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, signup.Username, user.Username)
|
require.Equal(t, signup.Username, user.Username)
|
||||||
memoList, err := s.getMemoList()
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Len(t, memoList, 1)
|
|
||||||
memo, err := s.postMemoCreate(&api.CreateMemoRequest{
|
memo, err := s.postMemoCreate(&api.CreateMemoRequest{
|
||||||
Content: "test memo",
|
Content: "test memo",
|
||||||
})
|
})
|
||||||
@ -45,9 +42,9 @@ func TestMemoRelationServer(t *testing.T) {
|
|||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, "test memo2", memo2.Content)
|
require.Equal(t, "test memo2", memo2.Content)
|
||||||
memoList, err = s.getMemoList()
|
memoList, err := s.getMemoList()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, memoList, 3)
|
require.Len(t, memoList, 2)
|
||||||
require.Len(t, memo2.RelationList, 1)
|
require.Len(t, memo2.RelationList, 1)
|
||||||
err = s.deleteMemoRelation(memo2.ID, memo.ID, api.MemoRelationReference)
|
err = s.deleteMemoRelation(memo2.ID, memo.ID, api.MemoRelationReference)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -26,17 +26,14 @@ func TestMemoServer(t *testing.T) {
|
|||||||
user, err := s.postAuthSignup(signup)
|
user, err := s.postAuthSignup(signup)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, signup.Username, user.Username)
|
require.Equal(t, signup.Username, user.Username)
|
||||||
memoList, err := s.getMemoList()
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Len(t, memoList, 1)
|
|
||||||
memo, err := s.postMemoCreate(&api.CreateMemoRequest{
|
memo, err := s.postMemoCreate(&api.CreateMemoRequest{
|
||||||
Content: "test memo",
|
Content: "test memo",
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, "test memo", memo.Content)
|
require.Equal(t, "test memo", memo.Content)
|
||||||
memoList, err = s.getMemoList()
|
memoList, err := s.getMemoList()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, memoList, 2)
|
require.Len(t, memoList, 1)
|
||||||
updatedContent := "updated memo"
|
updatedContent := "updated memo"
|
||||||
memo, err = s.patchMemo(&api.PatchMemoRequest{
|
memo, err = s.patchMemo(&api.PatchMemoRequest{
|
||||||
ID: memo.ID,
|
ID: memo.ID,
|
||||||
@ -62,7 +59,7 @@ func TestMemoServer(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
memoList, err = s.getMemoList()
|
memoList, err = s.getMemoList()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, memoList, 1)
|
require.Len(t, memoList, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TestingServer) getMemo(memoID int) (*api.MemoResponse, error) {
|
func (s *TestingServer) getMemo(memoID int) (*api.MemoResponse, error) {
|
||||||
|
@ -36,8 +36,8 @@ func TestMemoStore(t *testing.T) {
|
|||||||
CreatorID: &user.ID,
|
CreatorID: &user.ID,
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, 2, len(memoList))
|
require.Equal(t, 1, len(memoList))
|
||||||
require.Equal(t, memo, memoList[1])
|
require.Equal(t, memo, memoList[0])
|
||||||
err = ts.DeleteMemo(ctx, &store.DeleteMemoMessage{
|
err = ts.DeleteMemo(ctx, &store.DeleteMemoMessage{
|
||||||
ID: memo.ID,
|
ID: memo.ID,
|
||||||
})
|
})
|
||||||
|
Reference in New Issue
Block a user