package v1 import ( "context" "fmt" "github.com/pkg/errors" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" v1pb "github.com/usememos/memos/proto/gen/api/v1" storepb "github.com/usememos/memos/proto/gen/store" "github.com/usememos/memos/store" ) // GetInstanceProfile returns the instance profile. func (s *APIV1Service) GetInstanceProfile(ctx context.Context, _ *v1pb.GetInstanceProfileRequest) (*v1pb.InstanceProfile, error) { instanceProfile := &v1pb.InstanceProfile{ Version: s.Profile.Version, Mode: s.Profile.Mode, InstanceUrl: s.Profile.InstanceURL, } owner, err := s.GetInstanceOwner(ctx) if err != nil { return nil, status.Errorf(codes.Internal, "failed to get instance owner: %v", err) } if owner != nil { instanceProfile.Owner = owner.Name } return instanceProfile, nil } func (s *APIV1Service) GetInstanceSetting(ctx context.Context, request *v1pb.GetInstanceSettingRequest) (*v1pb.InstanceSetting, error) { instanceSettingKeyString, err := ExtractInstanceSettingKeyFromName(request.Name) if err != nil { return nil, status.Errorf(codes.InvalidArgument, "invalid instance setting name: %v", err) } instanceSettingKey := storepb.InstanceSettingKey(storepb.InstanceSettingKey_value[instanceSettingKeyString]) // Get instance setting from store with default value. switch instanceSettingKey { case storepb.InstanceSettingKey_BASIC: _, err = s.Store.GetInstanceBasicSetting(ctx) case storepb.InstanceSettingKey_GENERAL: _, err = s.Store.GetInstanceGeneralSetting(ctx) case storepb.InstanceSettingKey_MEMO_RELATED: _, err = s.Store.GetInstanceMemoRelatedSetting(ctx) case storepb.InstanceSettingKey_STORAGE: _, err = s.Store.GetInstanceStorageSetting(ctx) default: return nil, status.Errorf(codes.InvalidArgument, "unsupported instance setting key: %v", instanceSettingKey) } if err != nil { return nil, status.Errorf(codes.Internal, "failed to get instance setting: %v", err) } instanceSetting, err := s.Store.GetInstanceSetting(ctx, &store.FindInstanceSetting{ Name: instanceSettingKey.String(), }) if err != nil { return nil, status.Errorf(codes.Internal, "failed to get instance setting: %v", err) } if instanceSetting == nil { return nil, status.Errorf(codes.NotFound, "instance setting not found") } // For storage setting, only host can get it. if instanceSetting.Key == storepb.InstanceSettingKey_STORAGE { user, err := s.GetCurrentUser(ctx) if err != nil { return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err) } if user == nil || user.Role != store.RoleHost { return nil, status.Errorf(codes.PermissionDenied, "permission denied") } } return convertInstanceSettingFromStore(instanceSetting), nil } func (s *APIV1Service) UpdateInstanceSetting(ctx context.Context, request *v1pb.UpdateInstanceSettingRequest) (*v1pb.InstanceSetting, error) { user, err := s.GetCurrentUser(ctx) if err != nil { return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err) } if user == nil { return nil, status.Errorf(codes.Unauthenticated, "user not authenticated") } if user.Role != store.RoleHost { return nil, status.Errorf(codes.PermissionDenied, "permission denied") } // TODO: Apply update_mask if specified _ = request.UpdateMask updateSetting := convertInstanceSettingToStore(request.Setting) instanceSetting, err := s.Store.UpsertInstanceSetting(ctx, updateSetting) if err != nil { return nil, status.Errorf(codes.Internal, "failed to upsert instance setting: %v", err) } return convertInstanceSettingFromStore(instanceSetting), nil } func convertInstanceSettingFromStore(setting *storepb.InstanceSetting) *v1pb.InstanceSetting { instanceSetting := &v1pb.InstanceSetting{ Name: fmt.Sprintf("instance/settings/%s", setting.Key.String()), } switch setting.Value.(type) { case *storepb.InstanceSetting_GeneralSetting: instanceSetting.Value = &v1pb.InstanceSetting_GeneralSetting_{ GeneralSetting: convertInstanceGeneralSettingFromStore(setting.GetGeneralSetting()), } case *storepb.InstanceSetting_StorageSetting: instanceSetting.Value = &v1pb.InstanceSetting_StorageSetting_{ StorageSetting: convertInstanceStorageSettingFromStore(setting.GetStorageSetting()), } case *storepb.InstanceSetting_MemoRelatedSetting: instanceSetting.Value = &v1pb.InstanceSetting_MemoRelatedSetting_{ MemoRelatedSetting: convertInstanceMemoRelatedSettingFromStore(setting.GetMemoRelatedSetting()), } } return instanceSetting } func convertInstanceSettingToStore(setting *v1pb.InstanceSetting) *storepb.InstanceSetting { settingKeyString, _ := ExtractInstanceSettingKeyFromName(setting.Name) instanceSetting := &storepb.InstanceSetting{ Key: storepb.InstanceSettingKey(storepb.InstanceSettingKey_value[settingKeyString]), Value: &storepb.InstanceSetting_GeneralSetting{ GeneralSetting: convertInstanceGeneralSettingToStore(setting.GetGeneralSetting()), }, } switch instanceSetting.Key { case storepb.InstanceSettingKey_GENERAL: instanceSetting.Value = &storepb.InstanceSetting_GeneralSetting{ GeneralSetting: convertInstanceGeneralSettingToStore(setting.GetGeneralSetting()), } case storepb.InstanceSettingKey_STORAGE: instanceSetting.Value = &storepb.InstanceSetting_StorageSetting{ StorageSetting: convertInstanceStorageSettingToStore(setting.GetStorageSetting()), } case storepb.InstanceSettingKey_MEMO_RELATED: instanceSetting.Value = &storepb.InstanceSetting_MemoRelatedSetting{ MemoRelatedSetting: convertInstanceMemoRelatedSettingToStore(setting.GetMemoRelatedSetting()), } default: // Keep the default GeneralSetting value } return instanceSetting } func convertInstanceGeneralSettingFromStore(setting *storepb.InstanceGeneralSetting) *v1pb.InstanceSetting_GeneralSetting { if setting == nil { return nil } // Backfill theme if empty theme := setting.Theme if theme == "" { theme = "default" } generalSetting := &v1pb.InstanceSetting_GeneralSetting{ Theme: theme, DisallowUserRegistration: setting.DisallowUserRegistration, DisallowPasswordAuth: setting.DisallowPasswordAuth, AdditionalScript: setting.AdditionalScript, AdditionalStyle: setting.AdditionalStyle, WeekStartDayOffset: setting.WeekStartDayOffset, DisallowChangeUsername: setting.DisallowChangeUsername, DisallowChangeNickname: setting.DisallowChangeNickname, } if setting.CustomProfile != nil { generalSetting.CustomProfile = &v1pb.InstanceSetting_GeneralSetting_CustomProfile{ Title: setting.CustomProfile.Title, Description: setting.CustomProfile.Description, LogoUrl: setting.CustomProfile.LogoUrl, Locale: setting.CustomProfile.Locale, } } return generalSetting } func convertInstanceGeneralSettingToStore(setting *v1pb.InstanceSetting_GeneralSetting) *storepb.InstanceGeneralSetting { if setting == nil { return nil } generalSetting := &storepb.InstanceGeneralSetting{ Theme: setting.Theme, DisallowUserRegistration: setting.DisallowUserRegistration, DisallowPasswordAuth: setting.DisallowPasswordAuth, AdditionalScript: setting.AdditionalScript, AdditionalStyle: setting.AdditionalStyle, WeekStartDayOffset: setting.WeekStartDayOffset, DisallowChangeUsername: setting.DisallowChangeUsername, DisallowChangeNickname: setting.DisallowChangeNickname, } if setting.CustomProfile != nil { generalSetting.CustomProfile = &storepb.InstanceCustomProfile{ Title: setting.CustomProfile.Title, Description: setting.CustomProfile.Description, LogoUrl: setting.CustomProfile.LogoUrl, Locale: setting.CustomProfile.Locale, } } return generalSetting } func convertInstanceStorageSettingFromStore(settingpb *storepb.InstanceStorageSetting) *v1pb.InstanceSetting_StorageSetting { if settingpb == nil { return nil } setting := &v1pb.InstanceSetting_StorageSetting{ StorageType: v1pb.InstanceSetting_StorageSetting_StorageType(settingpb.StorageType), FilepathTemplate: settingpb.FilepathTemplate, UploadSizeLimitMb: settingpb.UploadSizeLimitMb, } if settingpb.S3Config != nil { setting.S3Config = &v1pb.InstanceSetting_StorageSetting_S3Config{ AccessKeyId: settingpb.S3Config.AccessKeyId, AccessKeySecret: settingpb.S3Config.AccessKeySecret, Endpoint: settingpb.S3Config.Endpoint, Region: settingpb.S3Config.Region, Bucket: settingpb.S3Config.Bucket, UsePathStyle: settingpb.S3Config.UsePathStyle, } } return setting } func convertInstanceStorageSettingToStore(setting *v1pb.InstanceSetting_StorageSetting) *storepb.InstanceStorageSetting { if setting == nil { return nil } settingpb := &storepb.InstanceStorageSetting{ StorageType: storepb.InstanceStorageSetting_StorageType(setting.StorageType), FilepathTemplate: setting.FilepathTemplate, UploadSizeLimitMb: setting.UploadSizeLimitMb, } if setting.S3Config != nil { settingpb.S3Config = &storepb.StorageS3Config{ AccessKeyId: setting.S3Config.AccessKeyId, AccessKeySecret: setting.S3Config.AccessKeySecret, Endpoint: setting.S3Config.Endpoint, Region: setting.S3Config.Region, Bucket: setting.S3Config.Bucket, UsePathStyle: setting.S3Config.UsePathStyle, } } return settingpb } func convertInstanceMemoRelatedSettingFromStore(setting *storepb.InstanceMemoRelatedSetting) *v1pb.InstanceSetting_MemoRelatedSetting { if setting == nil { return nil } return &v1pb.InstanceSetting_MemoRelatedSetting{ DisallowPublicVisibility: setting.DisallowPublicVisibility, DisplayWithUpdateTime: setting.DisplayWithUpdateTime, ContentLengthLimit: setting.ContentLengthLimit, EnableDoubleClickEdit: setting.EnableDoubleClickEdit, EnableLinkPreview: setting.EnableLinkPreview, Reactions: setting.Reactions, EnableBlurNsfwContent: setting.EnableBlurNsfwContent, NsfwTags: setting.NsfwTags, } } func convertInstanceMemoRelatedSettingToStore(setting *v1pb.InstanceSetting_MemoRelatedSetting) *storepb.InstanceMemoRelatedSetting { if setting == nil { return nil } return &storepb.InstanceMemoRelatedSetting{ DisallowPublicVisibility: setting.DisallowPublicVisibility, DisplayWithUpdateTime: setting.DisplayWithUpdateTime, ContentLengthLimit: setting.ContentLengthLimit, EnableDoubleClickEdit: setting.EnableDoubleClickEdit, EnableLinkPreview: setting.EnableLinkPreview, Reactions: setting.Reactions, EnableBlurNsfwContent: setting.EnableBlurNsfwContent, NsfwTags: setting.NsfwTags, } } var ownerCache *v1pb.User func (s *APIV1Service) GetInstanceOwner(ctx context.Context) (*v1pb.User, error) { if ownerCache != nil { return ownerCache, nil } hostUserType := store.RoleHost user, err := s.Store.GetUser(ctx, &store.FindUser{ Role: &hostUserType, }) if err != nil { return nil, errors.Wrapf(err, "failed to find owner") } if user == nil { return nil, nil } ownerCache = convertUserFromStore(user) return ownerCache, nil }