chore: update workspace setting service

This commit is contained in:
Steven
2024-02-20 23:02:01 +08:00
parent e6d0c00cf6
commit 8e11826db1
41 changed files with 2213 additions and 3678 deletions

View File

@@ -185,14 +185,21 @@ func (s *APIV1Service) UploadResource(c echo.Context) error {
return echo.NewHTTPError(http.StatusUnauthorized, "Missing user in session")
}
// This is the backend default max upload size limit.
maxUploadSetting := s.Store.GetWorkspaceSettingWithDefaultValue(ctx, SystemSettingMaxUploadSizeMiBName.String(), "32")
maxUploadSetting, err := s.Store.GetWorkspaceSetting(ctx, &store.FindWorkspaceSetting{Name: SystemSettingMaxUploadSizeMiBName.String()})
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to get max upload size").SetInternal(err)
}
var settingMaxUploadSizeBytes int
if settingMaxUploadSizeMiB, err := strconv.Atoi(maxUploadSetting); err == nil {
settingMaxUploadSizeBytes = settingMaxUploadSizeMiB * MebiByte
if maxUploadSetting != nil {
if settingMaxUploadSizeMiB, err := strconv.Atoi(maxUploadSetting.Value); err == nil {
settingMaxUploadSizeBytes = settingMaxUploadSizeMiB * MebiByte
} else {
log.Warn("Failed to parse max upload size", zap.Error(err))
settingMaxUploadSizeBytes = 0
}
} else {
log.Warn("Failed to parse max upload size", zap.Error(err))
settingMaxUploadSizeBytes = 0
// Default to 32 MiB.
settingMaxUploadSizeBytes = 32 * MebiByte
}
file, err := c.FormFile("file")

View File

@@ -4,6 +4,7 @@ import "strings"
var authenticationAllowlistMethods = map[string]bool{
"/memos.api.v2.WorkspaceService/GetWorkspaceProfile": true,
"/memos.api.v2.WorkspaceService/GetWorkspaceSetting": true,
"/memos.api.v2.AuthService/GetAuthStatus": true,
"/memos.api.v2.AuthService/SignIn": true,
"/memos.api.v2.AuthService/SignInWithSSO": true,

View File

@@ -12,6 +12,7 @@ tags:
- name: TagService
- name: WebhookService
- name: WorkspaceService
- name: WorkspaceSettingService
consumes:
- application/json
produces:
@@ -1115,27 +1116,65 @@ paths:
$ref: '#/definitions/googlerpcStatus'
tags:
- WorkspaceService
patch:
summary: UpdateWorkspaceProfile updates the workspace profile.
operationId: WorkspaceService_UpdateWorkspaceProfile
/api/v2/workspace/{name}:
get:
summary: GetWorkspaceSetting returns the setting by name.
operationId: WorkspaceSettingService_GetWorkspaceSetting
responses:
"200":
description: A successful response.
schema:
$ref: '#/definitions/v2UpdateWorkspaceProfileResponse'
$ref: '#/definitions/v2GetWorkspaceSettingResponse'
default:
description: An unexpected error response.
schema:
$ref: '#/definitions/googlerpcStatus'
parameters:
- name: workspaceProfile
description: System info is the updated data.
- name: name
description: |-
The resource name of the workspace setting.
Format: settings/{setting}
in: path
required: true
type: string
pattern: settings/[^/]+
tags:
- WorkspaceSettingService
/api/v2/workspace/{setting.name}:
patch:
summary: SetWorkspaceSetting updates the setting.
operationId: WorkspaceSettingService_SetWorkspaceSetting
responses:
"200":
description: A successful response.
schema:
$ref: '#/definitions/v2SetWorkspaceSettingResponse'
default:
description: An unexpected error response.
schema:
$ref: '#/definitions/googlerpcStatus'
parameters:
- name: setting.name
description: |-
name is the name of the setting.
Format: settings/{setting}
in: path
required: true
type: string
pattern: settings/[^/]+
- name: setting
description: setting is the setting to update.
in: body
required: true
schema:
$ref: '#/definitions/v2WorkspaceProfile'
type: object
properties:
generalSetting:
$ref: '#/definitions/apiv2WorkspaceGeneralSetting'
description: general_setting is the general setting of workspace.
title: setting is the setting to update.
tags:
- WorkspaceService
- WorkspaceSettingService
/api/v2/{inbox.name}:
patch:
summary: UpdateInbox updates an inbox.
@@ -1611,6 +1650,35 @@ definitions:
type: string
url:
type: string
apiv2WorkspaceGeneralSetting:
type: object
properties:
instanceUrl:
type: string
description: instance_url is the instance URL.
disallowSignup:
type: boolean
description: disallow_signup is the flag to disallow signup.
disallowPasswordLogin:
type: boolean
description: disallow_password_login is the flag to disallow password login.
additionalScript:
type: string
description: additional_script is the additional script.
additionalStyle:
type: string
description: additional_style is the additional style.
apiv2WorkspaceSetting:
type: object
properties:
name:
type: string
title: |-
name is the name of the setting.
Format: settings/{setting}
generalSetting:
$ref: '#/definitions/apiv2WorkspaceGeneralSetting'
description: general_setting is the general setting of workspace.
googlerpcStatus:
type: object
properties:
@@ -1784,6 +1852,11 @@ definitions:
properties:
workspaceProfile:
$ref: '#/definitions/v2WorkspaceProfile'
v2GetWorkspaceSettingResponse:
type: object
properties:
setting:
$ref: '#/definitions/apiv2WorkspaceSetting'
v2Inbox:
type: object
properties:
@@ -2024,6 +2097,11 @@ definitions:
type: object
v2SetMemoResourcesResponse:
type: object
v2SetWorkspaceSettingResponse:
type: object
properties:
setting:
$ref: '#/definitions/apiv2WorkspaceSetting'
v2SignInResponse:
type: object
properties:
@@ -2081,11 +2159,6 @@ definitions:
properties:
webhook:
$ref: '#/definitions/apiv2Webhook'
v2UpdateWorkspaceProfileResponse:
type: object
properties:
workspaceProfile:
$ref: '#/definitions/v2WorkspaceProfile'
v2UpsertMemoReactionResponse:
type: object
properties:
@@ -2158,13 +2231,19 @@ definitions:
properties:
version:
type: string
title: version is the current version of instance
mode:
type: string
description: mode is the instance mode (e.g. "prod", "dev" or "demo").
allowRegistration:
type: boolean
description: allow_registration is whether the registration is allowed.
disablePasswordLogin:
type: boolean
description: allow_password_login is whether the password login is allowed.
additionalScript:
type: string
description: additional_script is the additional script.
additionalStyle:
type: string
description: additional_style is the additional style.

View File

@@ -255,7 +255,7 @@ func (s *APIV2Service) buildAccessTokenCookie(ctx context.Context, accessToken s
if err != nil {
return "", errors.Wrap(err, "failed to get workspace setting")
}
if workspaceGeneralSetting.InstanceUrl != "" && strings.HasPrefix(workspaceGeneralSetting.InstanceUrl, "https://") {
if strings.HasPrefix(workspaceGeneralSetting.InstanceUrl, "https://") {
attrs = append(attrs, "SameSite=None")
attrs = append(attrs, "Secure")
} else {

View File

@@ -248,7 +248,6 @@ func (s *APIV2Service) UpdateMemo(ctx context.Context, request *apiv2pb.UpdateMe
update.Visibility = &visibility
} else if path == "row_status" {
rowStatus := convertRowStatusToStore(request.Memo.RowStatus)
println("rowStatus", rowStatus)
update.RowStatus = &rowStatus
} else if path == "created_ts" {
createdTs := request.Memo.CreateTime.AsTime().Unix()

View File

@@ -10,8 +10,9 @@ import (
)
const (
UserNamePrefix = "users/"
InboxNamePrefix = "inboxes/"
WorkspaceSettingNamePrefix = "settings/"
UserNamePrefix = "users/"
InboxNamePrefix = "inboxes/"
)
// GetNameParentTokens returns the tokens from a resource name.
@@ -34,6 +35,14 @@ func GetNameParentTokens(name string, tokenPrefixes ...string) ([]string, error)
return tokens, nil
}
func ExtractWorkspaceSettingKeyFromName(name string) (string, error) {
tokens, err := GetNameParentTokens(name, WorkspaceSettingNamePrefix)
if err != nil {
return "", err
}
return tokens[0], nil
}
// ExtractUsernameFromName returns the username from a resource name.
func ExtractUsernameFromName(name string) (string, error) {
tokens, err := GetNameParentTokens(name, UserNamePrefix)

View File

@@ -22,6 +22,7 @@ import (
type APIV2Service struct {
apiv2pb.UnimplementedWorkspaceServiceServer
apiv2pb.UnimplementedWorkspaceSettingServiceServer
apiv2pb.UnimplementedAuthServiceServer
apiv2pb.UnimplementedUserServiceServer
apiv2pb.UnimplementedMemoServiceServer
@@ -56,6 +57,7 @@ func NewAPIV2Service(secret string, profile *profile.Profile, store *store.Store
}
apiv2pb.RegisterWorkspaceServiceServer(grpcServer, apiv2Service)
apiv2pb.RegisterWorkspaceSettingServiceServer(grpcServer, apiv2Service)
apiv2pb.RegisterAuthServiceServer(grpcServer, apiv2Service)
apiv2pb.RegisterUserServiceServer(grpcServer, apiv2Service)
apiv2pb.RegisterMemoServiceServer(grpcServer, apiv2Service)
@@ -90,6 +92,9 @@ func (s *APIV2Service) RegisterGateway(ctx context.Context, e *echo.Echo) error
if err := apiv2pb.RegisterWorkspaceServiceHandler(context.Background(), gwMux, conn); err != nil {
return err
}
if err := apiv2pb.RegisterWorkspaceSettingServiceHandler(context.Background(), gwMux, conn); err != nil {
return err
}
if err := apiv2pb.RegisterAuthServiceHandler(context.Background(), gwMux, conn); err != nil {
return err
}

View File

@@ -2,109 +2,16 @@ package v2
import (
"context"
"strconv"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/proto"
"github.com/pkg/errors"
apiv2pb "github.com/usememos/memos/proto/gen/api/v2"
storepb "github.com/usememos/memos/proto/gen/store"
"github.com/usememos/memos/store"
)
func (s *APIV2Service) GetWorkspaceProfile(_ context.Context, _ *apiv2pb.GetWorkspaceProfileRequest) (*apiv2pb.GetWorkspaceProfileResponse, error) {
func (s *APIV2Service) GetWorkspaceProfile(ctx context.Context, _ *apiv2pb.GetWorkspaceProfileRequest) (*apiv2pb.GetWorkspaceProfileResponse, error) {
workspaceProfile := &apiv2pb.WorkspaceProfile{
Version: s.Profile.Version,
Mode: s.Profile.Mode,
}
response := &apiv2pb.GetWorkspaceProfileResponse{
return &apiv2pb.GetWorkspaceProfileResponse{
WorkspaceProfile: workspaceProfile,
}
return response, nil
}
func (s *APIV2Service) UpdateWorkspaceProfile(ctx context.Context, request *apiv2pb.UpdateWorkspaceProfileRequest) (*apiv2pb.UpdateWorkspaceProfileResponse, error) {
user, err := getCurrentUser(ctx, s.Store)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
}
if user.Role != store.RoleHost {
return nil, status.Errorf(codes.PermissionDenied, "permission denied")
}
if request.UpdateMask == nil || len(request.UpdateMask.Paths) == 0 {
return nil, status.Errorf(codes.InvalidArgument, "update mask is required")
}
// Update system settings.
for _, field := range request.UpdateMask.Paths {
if field == "allow_registration" {
_, err := s.Store.UpsertWorkspaceSetting(ctx, &store.WorkspaceSetting{
Name: "allow-signup",
Value: strconv.FormatBool(request.WorkspaceProfile.AllowRegistration),
})
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to update allow_registration system setting: %v", err)
}
} else if field == "disable_password_login" {
if s.Profile.Mode == "demo" {
return nil, status.Errorf(codes.PermissionDenied, "disabling password login is not allowed in demo mode")
}
_, err := s.Store.UpsertWorkspaceSetting(ctx, &store.WorkspaceSetting{
Name: "disable-password-login",
Value: strconv.FormatBool(request.WorkspaceProfile.DisablePasswordLogin),
})
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to update disable_password_login system setting: %v", err)
}
} else if field == "additional_script" {
if s.Profile.Mode == "demo" {
return nil, status.Errorf(codes.PermissionDenied, "additional script is not allowed in demo mode")
}
_, err := s.Store.UpsertWorkspaceSetting(ctx, &store.WorkspaceSetting{
Name: "additional-script",
Value: request.WorkspaceProfile.AdditionalScript,
})
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to update additional_script system setting: %v", err)
}
} else if field == "additional_style" {
if s.Profile.Mode == "demo" {
return nil, status.Errorf(codes.PermissionDenied, "additional style is not allowed in demo mode")
}
_, err := s.Store.UpsertWorkspaceSetting(ctx, &store.WorkspaceSetting{
Name: "additional-style",
Value: request.WorkspaceProfile.AdditionalStyle,
})
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to update additional_style system setting: %v", err)
}
}
}
workspaceProfileMessage, err := s.GetWorkspaceProfile(ctx, &apiv2pb.GetWorkspaceProfileRequest{})
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get system info: %v", err)
}
return &apiv2pb.UpdateWorkspaceProfileResponse{
WorkspaceProfile: workspaceProfileMessage.WorkspaceProfile,
}, nil
}
func (s *APIV2Service) GetWorkspaceGeneralSetting(ctx context.Context) (*storepb.WorkspaceGeneralSetting, error) {
workspaceSetting, err := s.Store.GetWorkspaceSetting(ctx, &store.FindWorkspaceSetting{
Name: storepb.WorkspaceSettingKey_WORKSPACE_SETTING_GENERAL.String(),
})
if err != nil {
return nil, errors.Wrap(err, "failed to get workspace setting")
}
workspaceGeneralSetting := &storepb.WorkspaceGeneralSetting{}
if workspaceSetting != nil {
if err := proto.Unmarshal([]byte(workspaceSetting.Value), workspaceGeneralSetting); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal workspace setting")
}
}
return workspaceGeneralSetting, nil
}

View File

@@ -0,0 +1,113 @@
package v2
import (
"context"
"fmt"
"github.com/pkg/errors"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/proto"
apiv2pb "github.com/usememos/memos/proto/gen/api/v2"
storepb "github.com/usememos/memos/proto/gen/store"
"github.com/usememos/memos/store"
)
func (s *APIV2Service) GetWorkspaceSetting(ctx context.Context, request *apiv2pb.GetWorkspaceSettingRequest) (*apiv2pb.GetWorkspaceSettingResponse, error) {
settingKeyString, err := ExtractWorkspaceSettingKeyFromName(request.Name)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "invalid workspace setting name: %v", err)
}
settingKey := storepb.WorkspaceSettingKey(storepb.WorkspaceSettingKey_value[settingKeyString])
workspaceSetting, err := s.Store.GetWorkspaceSettingV1(ctx, &store.FindWorkspaceSettingV1{
Key: settingKey,
})
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get workspace setting: %v", err)
}
if workspaceSetting == nil {
return nil, status.Errorf(codes.NotFound, "workspace setting not found")
}
return &apiv2pb.GetWorkspaceSettingResponse{
Setting: convertWorkspaceSettingFromStore(workspaceSetting),
}, nil
}
func (s *APIV2Service) SetWorkspaceSetting(ctx context.Context, request *apiv2pb.SetWorkspaceSettingRequest) (*apiv2pb.SetWorkspaceSettingResponse, error) {
user, err := getCurrentUser(ctx, s.Store)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
}
if user.Role != store.RoleHost {
return nil, status.Errorf(codes.PermissionDenied, "permission denied")
}
if _, err := s.Store.UpsertWorkspaceSettingV1(ctx, convertWorkspaceSettingToStore(request.Setting)); err != nil {
return nil, status.Errorf(codes.Internal, "failed to upsert workspace setting: %v", err)
}
return &apiv2pb.SetWorkspaceSettingResponse{}, nil
}
func (s *APIV2Service) GetWorkspaceGeneralSetting(ctx context.Context) (*storepb.WorkspaceGeneralSetting, error) {
workspaceSetting, err := s.Store.GetWorkspaceSetting(ctx, &store.FindWorkspaceSetting{
Name: storepb.WorkspaceSettingKey_WORKSPACE_SETTING_GENERAL.String(),
})
if err != nil {
return nil, errors.Wrap(err, "failed to get workspace setting")
}
workspaceGeneralSetting := &storepb.WorkspaceGeneralSetting{}
if workspaceSetting != nil {
if err := proto.Unmarshal([]byte(workspaceSetting.Value), workspaceGeneralSetting); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal workspace setting")
}
}
return workspaceGeneralSetting, nil
}
func convertWorkspaceSettingFromStore(setting *storepb.WorkspaceSetting) *apiv2pb.WorkspaceSetting {
return &apiv2pb.WorkspaceSetting{
Name: fmt.Sprintf("%s%s", WorkspaceSettingNamePrefix, setting.Key.String()),
Value: &apiv2pb.WorkspaceSetting_GeneralSetting{
GeneralSetting: convertWorkspaceGeneralSettingFromStore(setting.GetGeneral()),
},
}
}
func convertWorkspaceSettingToStore(setting *apiv2pb.WorkspaceSetting) *storepb.WorkspaceSetting {
settingKeyString, _ := ExtractWorkspaceSettingKeyFromName(setting.Name)
return &storepb.WorkspaceSetting{
Key: storepb.WorkspaceSettingKey(storepb.WorkspaceSettingKey_value[settingKeyString]),
Value: &storepb.WorkspaceSetting_General{
General: convertWorkspaceGeneralSettingToStore(setting.GetGeneralSetting()),
},
}
}
func convertWorkspaceGeneralSettingFromStore(setting *storepb.WorkspaceGeneralSetting) *apiv2pb.WorkspaceGeneralSetting {
if setting == nil {
return nil
}
return &apiv2pb.WorkspaceGeneralSetting{
InstanceUrl: setting.InstanceUrl,
DisallowSignup: setting.DisallowSignup,
DisallowPasswordLogin: setting.DisallowPasswordLogin,
AdditionalScript: setting.AdditionalScript,
AdditionalStyle: setting.AdditionalStyle,
}
}
func convertWorkspaceGeneralSettingToStore(setting *apiv2pb.WorkspaceGeneralSetting) *storepb.WorkspaceGeneralSetting {
if setting == nil {
return nil
}
return &storepb.WorkspaceGeneralSetting{
InstanceUrl: setting.InstanceUrl,
DisallowSignup: setting.DisallowSignup,
DisallowPasswordLogin: setting.DisallowPasswordLogin,
AdditionalScript: setting.AdditionalScript,
AdditionalStyle: setting.AdditionalStyle,
}
}