feat(settings): move tag metadata to user settings (#6017)
This commit is contained in:
@@ -204,6 +204,8 @@ message InstanceSetting {
|
||||
}
|
||||
|
||||
// Tag metadata configuration.
|
||||
// Active tag metadata is stored in per-user tag settings.
|
||||
// This message remains for backward compatibility with existing clients and migrations.
|
||||
message TagsSetting {
|
||||
// Map of tag name pattern to tag metadata.
|
||||
// Each key is treated as an anchored regular expression (^pattern$),
|
||||
|
||||
@@ -10,6 +10,7 @@ import "google/api/resource.proto";
|
||||
import "google/protobuf/empty.proto";
|
||||
import "google/protobuf/field_mask.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "google/type/color.proto";
|
||||
|
||||
option go_package = "gen/api/v1";
|
||||
|
||||
@@ -426,6 +427,7 @@ message UserSetting {
|
||||
oneof value {
|
||||
GeneralSetting general_setting = 2;
|
||||
WebhooksSetting webhooks_setting = 5;
|
||||
TagsSetting tags_setting = 6;
|
||||
}
|
||||
|
||||
// Enumeration of user setting keys.
|
||||
@@ -435,6 +437,8 @@ message UserSetting {
|
||||
GENERAL = 1;
|
||||
// WEBHOOKS is the key for user webhooks.
|
||||
WEBHOOKS = 4;
|
||||
// TAGS is the key for per-user tag metadata.
|
||||
TAGS = 5;
|
||||
}
|
||||
|
||||
// General user settings configuration.
|
||||
@@ -449,6 +453,22 @@ message UserSetting {
|
||||
string theme = 4 [(google.api.field_behavior) = OPTIONAL];
|
||||
}
|
||||
|
||||
// Tag metadata for user-specific display rules.
|
||||
message TagMetadata {
|
||||
// Optional background color for the tag label.
|
||||
// When unset, the default tag color is used.
|
||||
google.type.Color background_color = 1 [(google.api.field_behavior) = OPTIONAL];
|
||||
// Whether memos with this tag should have their content blurred.
|
||||
bool blur_content = 2 [(google.api.field_behavior) = OPTIONAL];
|
||||
}
|
||||
|
||||
// User-specific tag metadata.
|
||||
message TagsSetting {
|
||||
// Map of tag name pattern to tag metadata.
|
||||
// Each key is treated as an anchored regular expression (^pattern$).
|
||||
map<string, TagMetadata> tags = 1 [(google.api.field_behavior) = OPTIONAL];
|
||||
}
|
||||
|
||||
// User webhooks configuration.
|
||||
message WebhooksSetting {
|
||||
// List of user webhooks.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.6.1
|
||||
// - protoc-gen-go-grpc v1.6.2
|
||||
// - protoc (unknown)
|
||||
// source: api/v1/ai_service.proto
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.6.1
|
||||
// - protoc-gen-go-grpc v1.6.2
|
||||
// - protoc (unknown)
|
||||
// source: api/v1/attachment_service.proto
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.6.1
|
||||
// - protoc-gen-go-grpc v1.6.2
|
||||
// - protoc (unknown)
|
||||
// source: api/v1/auth_service.proto
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.6.1
|
||||
// - protoc-gen-go-grpc v1.6.2
|
||||
// - protoc (unknown)
|
||||
// source: api/v1/idp_service.proto
|
||||
|
||||
|
||||
@@ -1133,6 +1133,8 @@ func (x *InstanceSetting_TagMetadata) GetBlurContent() bool {
|
||||
}
|
||||
|
||||
// Tag metadata configuration.
|
||||
// Active tag metadata is stored in per-user tag settings.
|
||||
// This message remains for backward compatibility with existing clients and migrations.
|
||||
type InstanceSetting_TagsSetting struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// Map of tag name pattern to tag metadata.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.6.1
|
||||
// - protoc-gen-go-grpc v1.6.2
|
||||
// - protoc (unknown)
|
||||
// source: api/v1/instance_service.proto
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.6.1
|
||||
// - protoc-gen-go-grpc v1.6.2
|
||||
// - protoc (unknown)
|
||||
// source: api/v1/memo_service.proto
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.6.1
|
||||
// - protoc-gen-go-grpc v1.6.2
|
||||
// - protoc (unknown)
|
||||
// source: api/v1/shortcut_service.proto
|
||||
|
||||
|
||||
+254
-111
@@ -8,6 +8,7 @@ package apiv1
|
||||
|
||||
import (
|
||||
_ "google.golang.org/genproto/googleapis/api/annotations"
|
||||
color "google.golang.org/genproto/googleapis/type/color"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
emptypb "google.golang.org/protobuf/types/known/emptypb"
|
||||
@@ -86,6 +87,8 @@ const (
|
||||
UserSetting_GENERAL UserSetting_Key = 1
|
||||
// WEBHOOKS is the key for user webhooks.
|
||||
UserSetting_WEBHOOKS UserSetting_Key = 4
|
||||
// TAGS is the key for per-user tag metadata.
|
||||
UserSetting_TAGS UserSetting_Key = 5
|
||||
)
|
||||
|
||||
// Enum value maps for UserSetting_Key.
|
||||
@@ -94,11 +97,13 @@ var (
|
||||
0: "KEY_UNSPECIFIED",
|
||||
1: "GENERAL",
|
||||
4: "WEBHOOKS",
|
||||
5: "TAGS",
|
||||
}
|
||||
UserSetting_Key_value = map[string]int32{
|
||||
"KEY_UNSPECIFIED": 0,
|
||||
"GENERAL": 1,
|
||||
"WEBHOOKS": 4,
|
||||
"TAGS": 5,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -1103,6 +1108,7 @@ type UserSetting struct {
|
||||
//
|
||||
// *UserSetting_GeneralSetting_
|
||||
// *UserSetting_WebhooksSetting_
|
||||
// *UserSetting_TagsSetting_
|
||||
Value isUserSetting_Value `protobuf_oneof:"value"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
@@ -1170,6 +1176,15 @@ func (x *UserSetting) GetWebhooksSetting() *UserSetting_WebhooksSetting {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *UserSetting) GetTagsSetting() *UserSetting_TagsSetting {
|
||||
if x != nil {
|
||||
if x, ok := x.Value.(*UserSetting_TagsSetting_); ok {
|
||||
return x.TagsSetting
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type isUserSetting_Value interface {
|
||||
isUserSetting_Value()
|
||||
}
|
||||
@@ -1182,10 +1197,16 @@ type UserSetting_WebhooksSetting_ struct {
|
||||
WebhooksSetting *UserSetting_WebhooksSetting `protobuf:"bytes,5,opt,name=webhooks_setting,json=webhooksSetting,proto3,oneof"`
|
||||
}
|
||||
|
||||
type UserSetting_TagsSetting_ struct {
|
||||
TagsSetting *UserSetting_TagsSetting `protobuf:"bytes,6,opt,name=tags_setting,json=tagsSetting,proto3,oneof"`
|
||||
}
|
||||
|
||||
func (*UserSetting_GeneralSetting_) isUserSetting_Value() {}
|
||||
|
||||
func (*UserSetting_WebhooksSetting_) isUserSetting_Value() {}
|
||||
|
||||
func (*UserSetting_TagsSetting_) isUserSetting_Value() {}
|
||||
|
||||
type GetUserSettingRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// Required. The resource name of the user setting.
|
||||
@@ -2949,6 +2970,109 @@ func (x *UserSetting_GeneralSetting) GetTheme() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Tag metadata for user-specific display rules.
|
||||
type UserSetting_TagMetadata struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// Optional background color for the tag label.
|
||||
// When unset, the default tag color is used.
|
||||
BackgroundColor *color.Color `protobuf:"bytes,1,opt,name=background_color,json=backgroundColor,proto3" json:"background_color,omitempty"`
|
||||
// Whether memos with this tag should have their content blurred.
|
||||
BlurContent bool `protobuf:"varint,2,opt,name=blur_content,json=blurContent,proto3" json:"blur_content,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *UserSetting_TagMetadata) Reset() {
|
||||
*x = UserSetting_TagMetadata{}
|
||||
mi := &file_api_v1_user_service_proto_msgTypes[44]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *UserSetting_TagMetadata) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*UserSetting_TagMetadata) ProtoMessage() {}
|
||||
|
||||
func (x *UserSetting_TagMetadata) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_api_v1_user_service_proto_msgTypes[44]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use UserSetting_TagMetadata.ProtoReflect.Descriptor instead.
|
||||
func (*UserSetting_TagMetadata) Descriptor() ([]byte, []int) {
|
||||
return file_api_v1_user_service_proto_rawDescGZIP(), []int{13, 1}
|
||||
}
|
||||
|
||||
func (x *UserSetting_TagMetadata) GetBackgroundColor() *color.Color {
|
||||
if x != nil {
|
||||
return x.BackgroundColor
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *UserSetting_TagMetadata) GetBlurContent() bool {
|
||||
if x != nil {
|
||||
return x.BlurContent
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// User-specific tag metadata.
|
||||
type UserSetting_TagsSetting struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// Map of tag name pattern to tag metadata.
|
||||
// Each key is treated as an anchored regular expression (^pattern$).
|
||||
Tags map[string]*UserSetting_TagMetadata `protobuf:"bytes,1,rep,name=tags,proto3" json:"tags,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *UserSetting_TagsSetting) Reset() {
|
||||
*x = UserSetting_TagsSetting{}
|
||||
mi := &file_api_v1_user_service_proto_msgTypes[45]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *UserSetting_TagsSetting) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*UserSetting_TagsSetting) ProtoMessage() {}
|
||||
|
||||
func (x *UserSetting_TagsSetting) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_api_v1_user_service_proto_msgTypes[45]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use UserSetting_TagsSetting.ProtoReflect.Descriptor instead.
|
||||
func (*UserSetting_TagsSetting) Descriptor() ([]byte, []int) {
|
||||
return file_api_v1_user_service_proto_rawDescGZIP(), []int{13, 2}
|
||||
}
|
||||
|
||||
func (x *UserSetting_TagsSetting) GetTags() map[string]*UserSetting_TagMetadata {
|
||||
if x != nil {
|
||||
return x.Tags
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// User webhooks configuration.
|
||||
type UserSetting_WebhooksSetting struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
@@ -2960,7 +3084,7 @@ type UserSetting_WebhooksSetting struct {
|
||||
|
||||
func (x *UserSetting_WebhooksSetting) Reset() {
|
||||
*x = UserSetting_WebhooksSetting{}
|
||||
mi := &file_api_v1_user_service_proto_msgTypes[44]
|
||||
mi := &file_api_v1_user_service_proto_msgTypes[46]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -2972,7 +3096,7 @@ func (x *UserSetting_WebhooksSetting) String() string {
|
||||
func (*UserSetting_WebhooksSetting) ProtoMessage() {}
|
||||
|
||||
func (x *UserSetting_WebhooksSetting) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_api_v1_user_service_proto_msgTypes[44]
|
||||
mi := &file_api_v1_user_service_proto_msgTypes[46]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -2985,7 +3109,7 @@ func (x *UserSetting_WebhooksSetting) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use UserSetting_WebhooksSetting.ProtoReflect.Descriptor instead.
|
||||
func (*UserSetting_WebhooksSetting) Descriptor() ([]byte, []int) {
|
||||
return file_api_v1_user_service_proto_rawDescGZIP(), []int{13, 1}
|
||||
return file_api_v1_user_service_proto_rawDescGZIP(), []int{13, 3}
|
||||
}
|
||||
|
||||
func (x *UserSetting_WebhooksSetting) GetWebhooks() []*UserWebhook {
|
||||
@@ -3013,7 +3137,7 @@ type UserNotification_MemoCommentPayload struct {
|
||||
|
||||
func (x *UserNotification_MemoCommentPayload) Reset() {
|
||||
*x = UserNotification_MemoCommentPayload{}
|
||||
mi := &file_api_v1_user_service_proto_msgTypes[45]
|
||||
mi := &file_api_v1_user_service_proto_msgTypes[48]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -3025,7 +3149,7 @@ func (x *UserNotification_MemoCommentPayload) String() string {
|
||||
func (*UserNotification_MemoCommentPayload) ProtoMessage() {}
|
||||
|
||||
func (x *UserNotification_MemoCommentPayload) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_api_v1_user_service_proto_msgTypes[45]
|
||||
mi := &file_api_v1_user_service_proto_msgTypes[48]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -3087,7 +3211,7 @@ type UserNotification_MemoMentionPayload struct {
|
||||
|
||||
func (x *UserNotification_MemoMentionPayload) Reset() {
|
||||
*x = UserNotification_MemoMentionPayload{}
|
||||
mi := &file_api_v1_user_service_proto_msgTypes[46]
|
||||
mi := &file_api_v1_user_service_proto_msgTypes[49]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -3099,7 +3223,7 @@ func (x *UserNotification_MemoMentionPayload) String() string {
|
||||
func (*UserNotification_MemoMentionPayload) ProtoMessage() {}
|
||||
|
||||
func (x *UserNotification_MemoMentionPayload) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_api_v1_user_service_proto_msgTypes[46]
|
||||
mi := &file_api_v1_user_service_proto_msgTypes[49]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -3147,7 +3271,7 @@ var File_api_v1_user_service_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_api_v1_user_service_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"\x19api/v1/user_service.proto\x12\fmemos.api.v1\x1a\x13api/v1/common.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x17google/api/client.proto\x1a\x1fgoogle/api/field_behavior.proto\x1a\x19google/api/resource.proto\x1a\x1bgoogle/protobuf/empty.proto\x1a google/protobuf/field_mask.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\xc1\x04\n" +
|
||||
"\x19api/v1/user_service.proto\x12\fmemos.api.v1\x1a\x13api/v1/common.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x17google/api/client.proto\x1a\x1fgoogle/api/field_behavior.proto\x1a\x19google/api/resource.proto\x1a\x1bgoogle/protobuf/empty.proto\x1a google/protobuf/field_mask.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x17google/type/color.proto\"\xc1\x04\n" +
|
||||
"\x04User\x12\x17\n" +
|
||||
"\x04name\x18\x01 \x01(\tB\x03\xe0A\bR\x04name\x120\n" +
|
||||
"\x04role\x18\x02 \x01(\x0e2\x17.memos.api.v1.User.RoleB\x03\xe0A\x02R\x04role\x12\x1f\n" +
|
||||
@@ -3231,21 +3355,31 @@ const file_api_v1_user_service_proto_rawDesc = "" +
|
||||
"\x05state\x18\x01 \x01(\x0e2\x13.memos.api.v1.StateB\x03\xe0A\x01R\x05state\x12\x1b\n" +
|
||||
"\x06filter\x18\x02 \x01(\tB\x03\xe0A\x01R\x06filter\"I\n" +
|
||||
"\x18ListAllUserStatsResponse\x12-\n" +
|
||||
"\x05stats\x18\x01 \x03(\v2\x17.memos.api.v1.UserStatsR\x05stats\"\xb4\x04\n" +
|
||||
"\x05stats\x18\x01 \x03(\v2\x17.memos.api.v1.UserStatsR\x05stats\"\xbf\a\n" +
|
||||
"\vUserSetting\x12\x17\n" +
|
||||
"\x04name\x18\x01 \x01(\tB\x03\xe0A\bR\x04name\x12S\n" +
|
||||
"\x0fgeneral_setting\x18\x02 \x01(\v2(.memos.api.v1.UserSetting.GeneralSettingH\x00R\x0egeneralSetting\x12V\n" +
|
||||
"\x10webhooks_setting\x18\x05 \x01(\v2).memos.api.v1.UserSetting.WebhooksSettingH\x00R\x0fwebhooksSetting\x1av\n" +
|
||||
"\x10webhooks_setting\x18\x05 \x01(\v2).memos.api.v1.UserSetting.WebhooksSettingH\x00R\x0fwebhooksSetting\x12J\n" +
|
||||
"\ftags_setting\x18\x06 \x01(\v2%.memos.api.v1.UserSetting.TagsSettingH\x00R\vtagsSetting\x1av\n" +
|
||||
"\x0eGeneralSetting\x12\x1b\n" +
|
||||
"\x06locale\x18\x01 \x01(\tB\x03\xe0A\x01R\x06locale\x12,\n" +
|
||||
"\x0fmemo_visibility\x18\x03 \x01(\tB\x03\xe0A\x01R\x0ememoVisibility\x12\x19\n" +
|
||||
"\x05theme\x18\x04 \x01(\tB\x03\xe0A\x01R\x05theme\x1aH\n" +
|
||||
"\x05theme\x18\x04 \x01(\tB\x03\xe0A\x01R\x05theme\x1ay\n" +
|
||||
"\vTagMetadata\x12B\n" +
|
||||
"\x10background_color\x18\x01 \x01(\v2\x12.google.type.ColorB\x03\xe0A\x01R\x0fbackgroundColor\x12&\n" +
|
||||
"\fblur_content\x18\x02 \x01(\bB\x03\xe0A\x01R\vblurContent\x1a\xb7\x01\n" +
|
||||
"\vTagsSetting\x12H\n" +
|
||||
"\x04tags\x18\x01 \x03(\v2/.memos.api.v1.UserSetting.TagsSetting.TagsEntryB\x03\xe0A\x01R\x04tags\x1a^\n" +
|
||||
"\tTagsEntry\x12\x10\n" +
|
||||
"\x03key\x18\x01 \x01(\tR\x03key\x12;\n" +
|
||||
"\x05value\x18\x02 \x01(\v2%.memos.api.v1.UserSetting.TagMetadataR\x05value:\x028\x01\x1aH\n" +
|
||||
"\x0fWebhooksSetting\x125\n" +
|
||||
"\bwebhooks\x18\x01 \x03(\v2\x19.memos.api.v1.UserWebhookR\bwebhooks\"5\n" +
|
||||
"\bwebhooks\x18\x01 \x03(\v2\x19.memos.api.v1.UserWebhookR\bwebhooks\"?\n" +
|
||||
"\x03Key\x12\x13\n" +
|
||||
"\x0fKEY_UNSPECIFIED\x10\x00\x12\v\n" +
|
||||
"\aGENERAL\x10\x01\x12\f\n" +
|
||||
"\bWEBHOOKS\x10\x04:]\xeaAZ\n" +
|
||||
"\bWEBHOOKS\x10\x04\x12\b\n" +
|
||||
"\x04TAGS\x10\x05:]\xeaAZ\n" +
|
||||
"\x18memos.api.v1/UserSetting\x12#users/{username}/settings/{setting}*\fuserSettings2\vuserSettingB\a\n" +
|
||||
"\x05value\"M\n" +
|
||||
"\x15GetUserSettingRequest\x124\n" +
|
||||
@@ -3439,7 +3573,7 @@ func file_api_v1_user_service_proto_rawDescGZIP() []byte {
|
||||
}
|
||||
|
||||
var file_api_v1_user_service_proto_enumTypes = make([]protoimpl.EnumInfo, 4)
|
||||
var file_api_v1_user_service_proto_msgTypes = make([]protoimpl.MessageInfo, 47)
|
||||
var file_api_v1_user_service_proto_msgTypes = make([]protoimpl.MessageInfo, 50)
|
||||
var file_api_v1_user_service_proto_goTypes = []any{
|
||||
(User_Role)(0), // 0: memos.api.v1.User.Role
|
||||
(UserSetting_Key)(0), // 1: memos.api.v1.UserSetting.Key
|
||||
@@ -3489,113 +3623,121 @@ var file_api_v1_user_service_proto_goTypes = []any{
|
||||
(*UserStats_MemoTypeStats)(nil), // 45: memos.api.v1.UserStats.MemoTypeStats
|
||||
nil, // 46: memos.api.v1.UserStats.TagCountEntry
|
||||
(*UserSetting_GeneralSetting)(nil), // 47: memos.api.v1.UserSetting.GeneralSetting
|
||||
(*UserSetting_WebhooksSetting)(nil), // 48: memos.api.v1.UserSetting.WebhooksSetting
|
||||
(*UserNotification_MemoCommentPayload)(nil), // 49: memos.api.v1.UserNotification.MemoCommentPayload
|
||||
(*UserNotification_MemoMentionPayload)(nil), // 50: memos.api.v1.UserNotification.MemoMentionPayload
|
||||
(State)(0), // 51: memos.api.v1.State
|
||||
(*timestamppb.Timestamp)(nil), // 52: google.protobuf.Timestamp
|
||||
(*fieldmaskpb.FieldMask)(nil), // 53: google.protobuf.FieldMask
|
||||
(*emptypb.Empty)(nil), // 54: google.protobuf.Empty
|
||||
(*UserSetting_TagMetadata)(nil), // 48: memos.api.v1.UserSetting.TagMetadata
|
||||
(*UserSetting_TagsSetting)(nil), // 49: memos.api.v1.UserSetting.TagsSetting
|
||||
(*UserSetting_WebhooksSetting)(nil), // 50: memos.api.v1.UserSetting.WebhooksSetting
|
||||
nil, // 51: memos.api.v1.UserSetting.TagsSetting.TagsEntry
|
||||
(*UserNotification_MemoCommentPayload)(nil), // 52: memos.api.v1.UserNotification.MemoCommentPayload
|
||||
(*UserNotification_MemoMentionPayload)(nil), // 53: memos.api.v1.UserNotification.MemoMentionPayload
|
||||
(State)(0), // 54: memos.api.v1.State
|
||||
(*timestamppb.Timestamp)(nil), // 55: google.protobuf.Timestamp
|
||||
(*fieldmaskpb.FieldMask)(nil), // 56: google.protobuf.FieldMask
|
||||
(*color.Color)(nil), // 57: google.type.Color
|
||||
(*emptypb.Empty)(nil), // 58: google.protobuf.Empty
|
||||
}
|
||||
var file_api_v1_user_service_proto_depIdxs = []int32{
|
||||
0, // 0: memos.api.v1.User.role:type_name -> memos.api.v1.User.Role
|
||||
51, // 1: memos.api.v1.User.state:type_name -> memos.api.v1.State
|
||||
52, // 2: memos.api.v1.User.create_time:type_name -> google.protobuf.Timestamp
|
||||
52, // 3: memos.api.v1.User.update_time:type_name -> google.protobuf.Timestamp
|
||||
54, // 1: memos.api.v1.User.state:type_name -> memos.api.v1.State
|
||||
55, // 2: memos.api.v1.User.create_time:type_name -> google.protobuf.Timestamp
|
||||
55, // 3: memos.api.v1.User.update_time:type_name -> google.protobuf.Timestamp
|
||||
4, // 4: memos.api.v1.ListUsersResponse.users:type_name -> memos.api.v1.User
|
||||
4, // 5: memos.api.v1.BatchGetUsersResponse.users:type_name -> memos.api.v1.User
|
||||
53, // 6: memos.api.v1.GetUserRequest.read_mask:type_name -> google.protobuf.FieldMask
|
||||
56, // 6: memos.api.v1.GetUserRequest.read_mask:type_name -> google.protobuf.FieldMask
|
||||
4, // 7: memos.api.v1.CreateUserRequest.user:type_name -> memos.api.v1.User
|
||||
4, // 8: memos.api.v1.UpdateUserRequest.user:type_name -> memos.api.v1.User
|
||||
53, // 9: memos.api.v1.UpdateUserRequest.update_mask:type_name -> google.protobuf.FieldMask
|
||||
56, // 9: memos.api.v1.UpdateUserRequest.update_mask:type_name -> google.protobuf.FieldMask
|
||||
45, // 10: memos.api.v1.UserStats.memo_type_stats:type_name -> memos.api.v1.UserStats.MemoTypeStats
|
||||
46, // 11: memos.api.v1.UserStats.tag_count:type_name -> memos.api.v1.UserStats.TagCountEntry
|
||||
52, // 12: memos.api.v1.UserStats.memo_created_timestamps:type_name -> google.protobuf.Timestamp
|
||||
52, // 13: memos.api.v1.UserStats.memo_updated_timestamps:type_name -> google.protobuf.Timestamp
|
||||
51, // 14: memos.api.v1.ListAllUserStatsRequest.state:type_name -> memos.api.v1.State
|
||||
55, // 12: memos.api.v1.UserStats.memo_created_timestamps:type_name -> google.protobuf.Timestamp
|
||||
55, // 13: memos.api.v1.UserStats.memo_updated_timestamps:type_name -> google.protobuf.Timestamp
|
||||
54, // 14: memos.api.v1.ListAllUserStatsRequest.state:type_name -> memos.api.v1.State
|
||||
13, // 15: memos.api.v1.ListAllUserStatsResponse.stats:type_name -> memos.api.v1.UserStats
|
||||
47, // 16: memos.api.v1.UserSetting.general_setting:type_name -> memos.api.v1.UserSetting.GeneralSetting
|
||||
48, // 17: memos.api.v1.UserSetting.webhooks_setting:type_name -> memos.api.v1.UserSetting.WebhooksSetting
|
||||
17, // 18: memos.api.v1.UpdateUserSettingRequest.setting:type_name -> memos.api.v1.UserSetting
|
||||
53, // 19: memos.api.v1.UpdateUserSettingRequest.update_mask:type_name -> google.protobuf.FieldMask
|
||||
17, // 20: memos.api.v1.ListUserSettingsResponse.settings:type_name -> memos.api.v1.UserSetting
|
||||
22, // 21: memos.api.v1.ListLinkedIdentitiesResponse.linked_identities:type_name -> memos.api.v1.LinkedIdentity
|
||||
52, // 22: memos.api.v1.PersonalAccessToken.created_at:type_name -> google.protobuf.Timestamp
|
||||
52, // 23: memos.api.v1.PersonalAccessToken.expires_at:type_name -> google.protobuf.Timestamp
|
||||
52, // 24: memos.api.v1.PersonalAccessToken.last_used_at:type_name -> google.protobuf.Timestamp
|
||||
28, // 25: memos.api.v1.ListPersonalAccessTokensResponse.personal_access_tokens:type_name -> memos.api.v1.PersonalAccessToken
|
||||
28, // 26: memos.api.v1.CreatePersonalAccessTokenResponse.personal_access_token:type_name -> memos.api.v1.PersonalAccessToken
|
||||
52, // 27: memos.api.v1.UserWebhook.create_time:type_name -> google.protobuf.Timestamp
|
||||
52, // 28: memos.api.v1.UserWebhook.update_time:type_name -> google.protobuf.Timestamp
|
||||
34, // 29: memos.api.v1.ListUserWebhooksResponse.webhooks:type_name -> memos.api.v1.UserWebhook
|
||||
34, // 30: memos.api.v1.CreateUserWebhookRequest.webhook:type_name -> memos.api.v1.UserWebhook
|
||||
34, // 31: memos.api.v1.UpdateUserWebhookRequest.webhook:type_name -> memos.api.v1.UserWebhook
|
||||
53, // 32: memos.api.v1.UpdateUserWebhookRequest.update_mask:type_name -> google.protobuf.FieldMask
|
||||
4, // 33: memos.api.v1.UserNotification.sender_user:type_name -> memos.api.v1.User
|
||||
2, // 34: memos.api.v1.UserNotification.status:type_name -> memos.api.v1.UserNotification.Status
|
||||
52, // 35: memos.api.v1.UserNotification.create_time:type_name -> google.protobuf.Timestamp
|
||||
3, // 36: memos.api.v1.UserNotification.type:type_name -> memos.api.v1.UserNotification.Type
|
||||
49, // 37: memos.api.v1.UserNotification.memo_comment:type_name -> memos.api.v1.UserNotification.MemoCommentPayload
|
||||
50, // 38: memos.api.v1.UserNotification.memo_mention:type_name -> memos.api.v1.UserNotification.MemoMentionPayload
|
||||
40, // 39: memos.api.v1.ListUserNotificationsResponse.notifications:type_name -> memos.api.v1.UserNotification
|
||||
40, // 40: memos.api.v1.UpdateUserNotificationRequest.notification:type_name -> memos.api.v1.UserNotification
|
||||
53, // 41: memos.api.v1.UpdateUserNotificationRequest.update_mask:type_name -> google.protobuf.FieldMask
|
||||
34, // 42: memos.api.v1.UserSetting.WebhooksSetting.webhooks:type_name -> memos.api.v1.UserWebhook
|
||||
5, // 43: memos.api.v1.UserService.ListUsers:input_type -> memos.api.v1.ListUsersRequest
|
||||
7, // 44: memos.api.v1.UserService.BatchGetUsers:input_type -> memos.api.v1.BatchGetUsersRequest
|
||||
9, // 45: memos.api.v1.UserService.GetUser:input_type -> memos.api.v1.GetUserRequest
|
||||
10, // 46: memos.api.v1.UserService.CreateUser:input_type -> memos.api.v1.CreateUserRequest
|
||||
11, // 47: memos.api.v1.UserService.UpdateUser:input_type -> memos.api.v1.UpdateUserRequest
|
||||
12, // 48: memos.api.v1.UserService.DeleteUser:input_type -> memos.api.v1.DeleteUserRequest
|
||||
15, // 49: memos.api.v1.UserService.ListAllUserStats:input_type -> memos.api.v1.ListAllUserStatsRequest
|
||||
14, // 50: memos.api.v1.UserService.GetUserStats:input_type -> memos.api.v1.GetUserStatsRequest
|
||||
18, // 51: memos.api.v1.UserService.GetUserSetting:input_type -> memos.api.v1.GetUserSettingRequest
|
||||
19, // 52: memos.api.v1.UserService.UpdateUserSetting:input_type -> memos.api.v1.UpdateUserSettingRequest
|
||||
20, // 53: memos.api.v1.UserService.ListUserSettings:input_type -> memos.api.v1.ListUserSettingsRequest
|
||||
23, // 54: memos.api.v1.UserService.ListLinkedIdentities:input_type -> memos.api.v1.ListLinkedIdentitiesRequest
|
||||
25, // 55: memos.api.v1.UserService.CreateLinkedIdentity:input_type -> memos.api.v1.CreateLinkedIdentityRequest
|
||||
26, // 56: memos.api.v1.UserService.GetLinkedIdentity:input_type -> memos.api.v1.GetLinkedIdentityRequest
|
||||
27, // 57: memos.api.v1.UserService.DeleteLinkedIdentity:input_type -> memos.api.v1.DeleteLinkedIdentityRequest
|
||||
29, // 58: memos.api.v1.UserService.ListPersonalAccessTokens:input_type -> memos.api.v1.ListPersonalAccessTokensRequest
|
||||
31, // 59: memos.api.v1.UserService.CreatePersonalAccessToken:input_type -> memos.api.v1.CreatePersonalAccessTokenRequest
|
||||
33, // 60: memos.api.v1.UserService.DeletePersonalAccessToken:input_type -> memos.api.v1.DeletePersonalAccessTokenRequest
|
||||
35, // 61: memos.api.v1.UserService.ListUserWebhooks:input_type -> memos.api.v1.ListUserWebhooksRequest
|
||||
37, // 62: memos.api.v1.UserService.CreateUserWebhook:input_type -> memos.api.v1.CreateUserWebhookRequest
|
||||
38, // 63: memos.api.v1.UserService.UpdateUserWebhook:input_type -> memos.api.v1.UpdateUserWebhookRequest
|
||||
39, // 64: memos.api.v1.UserService.DeleteUserWebhook:input_type -> memos.api.v1.DeleteUserWebhookRequest
|
||||
41, // 65: memos.api.v1.UserService.ListUserNotifications:input_type -> memos.api.v1.ListUserNotificationsRequest
|
||||
43, // 66: memos.api.v1.UserService.UpdateUserNotification:input_type -> memos.api.v1.UpdateUserNotificationRequest
|
||||
44, // 67: memos.api.v1.UserService.DeleteUserNotification:input_type -> memos.api.v1.DeleteUserNotificationRequest
|
||||
6, // 68: memos.api.v1.UserService.ListUsers:output_type -> memos.api.v1.ListUsersResponse
|
||||
8, // 69: memos.api.v1.UserService.BatchGetUsers:output_type -> memos.api.v1.BatchGetUsersResponse
|
||||
4, // 70: memos.api.v1.UserService.GetUser:output_type -> memos.api.v1.User
|
||||
4, // 71: memos.api.v1.UserService.CreateUser:output_type -> memos.api.v1.User
|
||||
4, // 72: memos.api.v1.UserService.UpdateUser:output_type -> memos.api.v1.User
|
||||
54, // 73: memos.api.v1.UserService.DeleteUser:output_type -> google.protobuf.Empty
|
||||
16, // 74: memos.api.v1.UserService.ListAllUserStats:output_type -> memos.api.v1.ListAllUserStatsResponse
|
||||
13, // 75: memos.api.v1.UserService.GetUserStats:output_type -> memos.api.v1.UserStats
|
||||
17, // 76: memos.api.v1.UserService.GetUserSetting:output_type -> memos.api.v1.UserSetting
|
||||
17, // 77: memos.api.v1.UserService.UpdateUserSetting:output_type -> memos.api.v1.UserSetting
|
||||
21, // 78: memos.api.v1.UserService.ListUserSettings:output_type -> memos.api.v1.ListUserSettingsResponse
|
||||
24, // 79: memos.api.v1.UserService.ListLinkedIdentities:output_type -> memos.api.v1.ListLinkedIdentitiesResponse
|
||||
22, // 80: memos.api.v1.UserService.CreateLinkedIdentity:output_type -> memos.api.v1.LinkedIdentity
|
||||
22, // 81: memos.api.v1.UserService.GetLinkedIdentity:output_type -> memos.api.v1.LinkedIdentity
|
||||
54, // 82: memos.api.v1.UserService.DeleteLinkedIdentity:output_type -> google.protobuf.Empty
|
||||
30, // 83: memos.api.v1.UserService.ListPersonalAccessTokens:output_type -> memos.api.v1.ListPersonalAccessTokensResponse
|
||||
32, // 84: memos.api.v1.UserService.CreatePersonalAccessToken:output_type -> memos.api.v1.CreatePersonalAccessTokenResponse
|
||||
54, // 85: memos.api.v1.UserService.DeletePersonalAccessToken:output_type -> google.protobuf.Empty
|
||||
36, // 86: memos.api.v1.UserService.ListUserWebhooks:output_type -> memos.api.v1.ListUserWebhooksResponse
|
||||
34, // 87: memos.api.v1.UserService.CreateUserWebhook:output_type -> memos.api.v1.UserWebhook
|
||||
34, // 88: memos.api.v1.UserService.UpdateUserWebhook:output_type -> memos.api.v1.UserWebhook
|
||||
54, // 89: memos.api.v1.UserService.DeleteUserWebhook:output_type -> google.protobuf.Empty
|
||||
42, // 90: memos.api.v1.UserService.ListUserNotifications:output_type -> memos.api.v1.ListUserNotificationsResponse
|
||||
40, // 91: memos.api.v1.UserService.UpdateUserNotification:output_type -> memos.api.v1.UserNotification
|
||||
54, // 92: memos.api.v1.UserService.DeleteUserNotification:output_type -> google.protobuf.Empty
|
||||
68, // [68:93] is the sub-list for method output_type
|
||||
43, // [43:68] is the sub-list for method input_type
|
||||
43, // [43:43] is the sub-list for extension type_name
|
||||
43, // [43:43] is the sub-list for extension extendee
|
||||
0, // [0:43] is the sub-list for field type_name
|
||||
50, // 17: memos.api.v1.UserSetting.webhooks_setting:type_name -> memos.api.v1.UserSetting.WebhooksSetting
|
||||
49, // 18: memos.api.v1.UserSetting.tags_setting:type_name -> memos.api.v1.UserSetting.TagsSetting
|
||||
17, // 19: memos.api.v1.UpdateUserSettingRequest.setting:type_name -> memos.api.v1.UserSetting
|
||||
56, // 20: memos.api.v1.UpdateUserSettingRequest.update_mask:type_name -> google.protobuf.FieldMask
|
||||
17, // 21: memos.api.v1.ListUserSettingsResponse.settings:type_name -> memos.api.v1.UserSetting
|
||||
22, // 22: memos.api.v1.ListLinkedIdentitiesResponse.linked_identities:type_name -> memos.api.v1.LinkedIdentity
|
||||
55, // 23: memos.api.v1.PersonalAccessToken.created_at:type_name -> google.protobuf.Timestamp
|
||||
55, // 24: memos.api.v1.PersonalAccessToken.expires_at:type_name -> google.protobuf.Timestamp
|
||||
55, // 25: memos.api.v1.PersonalAccessToken.last_used_at:type_name -> google.protobuf.Timestamp
|
||||
28, // 26: memos.api.v1.ListPersonalAccessTokensResponse.personal_access_tokens:type_name -> memos.api.v1.PersonalAccessToken
|
||||
28, // 27: memos.api.v1.CreatePersonalAccessTokenResponse.personal_access_token:type_name -> memos.api.v1.PersonalAccessToken
|
||||
55, // 28: memos.api.v1.UserWebhook.create_time:type_name -> google.protobuf.Timestamp
|
||||
55, // 29: memos.api.v1.UserWebhook.update_time:type_name -> google.protobuf.Timestamp
|
||||
34, // 30: memos.api.v1.ListUserWebhooksResponse.webhooks:type_name -> memos.api.v1.UserWebhook
|
||||
34, // 31: memos.api.v1.CreateUserWebhookRequest.webhook:type_name -> memos.api.v1.UserWebhook
|
||||
34, // 32: memos.api.v1.UpdateUserWebhookRequest.webhook:type_name -> memos.api.v1.UserWebhook
|
||||
56, // 33: memos.api.v1.UpdateUserWebhookRequest.update_mask:type_name -> google.protobuf.FieldMask
|
||||
4, // 34: memos.api.v1.UserNotification.sender_user:type_name -> memos.api.v1.User
|
||||
2, // 35: memos.api.v1.UserNotification.status:type_name -> memos.api.v1.UserNotification.Status
|
||||
55, // 36: memos.api.v1.UserNotification.create_time:type_name -> google.protobuf.Timestamp
|
||||
3, // 37: memos.api.v1.UserNotification.type:type_name -> memos.api.v1.UserNotification.Type
|
||||
52, // 38: memos.api.v1.UserNotification.memo_comment:type_name -> memos.api.v1.UserNotification.MemoCommentPayload
|
||||
53, // 39: memos.api.v1.UserNotification.memo_mention:type_name -> memos.api.v1.UserNotification.MemoMentionPayload
|
||||
40, // 40: memos.api.v1.ListUserNotificationsResponse.notifications:type_name -> memos.api.v1.UserNotification
|
||||
40, // 41: memos.api.v1.UpdateUserNotificationRequest.notification:type_name -> memos.api.v1.UserNotification
|
||||
56, // 42: memos.api.v1.UpdateUserNotificationRequest.update_mask:type_name -> google.protobuf.FieldMask
|
||||
57, // 43: memos.api.v1.UserSetting.TagMetadata.background_color:type_name -> google.type.Color
|
||||
51, // 44: memos.api.v1.UserSetting.TagsSetting.tags:type_name -> memos.api.v1.UserSetting.TagsSetting.TagsEntry
|
||||
34, // 45: memos.api.v1.UserSetting.WebhooksSetting.webhooks:type_name -> memos.api.v1.UserWebhook
|
||||
48, // 46: memos.api.v1.UserSetting.TagsSetting.TagsEntry.value:type_name -> memos.api.v1.UserSetting.TagMetadata
|
||||
5, // 47: memos.api.v1.UserService.ListUsers:input_type -> memos.api.v1.ListUsersRequest
|
||||
7, // 48: memos.api.v1.UserService.BatchGetUsers:input_type -> memos.api.v1.BatchGetUsersRequest
|
||||
9, // 49: memos.api.v1.UserService.GetUser:input_type -> memos.api.v1.GetUserRequest
|
||||
10, // 50: memos.api.v1.UserService.CreateUser:input_type -> memos.api.v1.CreateUserRequest
|
||||
11, // 51: memos.api.v1.UserService.UpdateUser:input_type -> memos.api.v1.UpdateUserRequest
|
||||
12, // 52: memos.api.v1.UserService.DeleteUser:input_type -> memos.api.v1.DeleteUserRequest
|
||||
15, // 53: memos.api.v1.UserService.ListAllUserStats:input_type -> memos.api.v1.ListAllUserStatsRequest
|
||||
14, // 54: memos.api.v1.UserService.GetUserStats:input_type -> memos.api.v1.GetUserStatsRequest
|
||||
18, // 55: memos.api.v1.UserService.GetUserSetting:input_type -> memos.api.v1.GetUserSettingRequest
|
||||
19, // 56: memos.api.v1.UserService.UpdateUserSetting:input_type -> memos.api.v1.UpdateUserSettingRequest
|
||||
20, // 57: memos.api.v1.UserService.ListUserSettings:input_type -> memos.api.v1.ListUserSettingsRequest
|
||||
23, // 58: memos.api.v1.UserService.ListLinkedIdentities:input_type -> memos.api.v1.ListLinkedIdentitiesRequest
|
||||
25, // 59: memos.api.v1.UserService.CreateLinkedIdentity:input_type -> memos.api.v1.CreateLinkedIdentityRequest
|
||||
26, // 60: memos.api.v1.UserService.GetLinkedIdentity:input_type -> memos.api.v1.GetLinkedIdentityRequest
|
||||
27, // 61: memos.api.v1.UserService.DeleteLinkedIdentity:input_type -> memos.api.v1.DeleteLinkedIdentityRequest
|
||||
29, // 62: memos.api.v1.UserService.ListPersonalAccessTokens:input_type -> memos.api.v1.ListPersonalAccessTokensRequest
|
||||
31, // 63: memos.api.v1.UserService.CreatePersonalAccessToken:input_type -> memos.api.v1.CreatePersonalAccessTokenRequest
|
||||
33, // 64: memos.api.v1.UserService.DeletePersonalAccessToken:input_type -> memos.api.v1.DeletePersonalAccessTokenRequest
|
||||
35, // 65: memos.api.v1.UserService.ListUserWebhooks:input_type -> memos.api.v1.ListUserWebhooksRequest
|
||||
37, // 66: memos.api.v1.UserService.CreateUserWebhook:input_type -> memos.api.v1.CreateUserWebhookRequest
|
||||
38, // 67: memos.api.v1.UserService.UpdateUserWebhook:input_type -> memos.api.v1.UpdateUserWebhookRequest
|
||||
39, // 68: memos.api.v1.UserService.DeleteUserWebhook:input_type -> memos.api.v1.DeleteUserWebhookRequest
|
||||
41, // 69: memos.api.v1.UserService.ListUserNotifications:input_type -> memos.api.v1.ListUserNotificationsRequest
|
||||
43, // 70: memos.api.v1.UserService.UpdateUserNotification:input_type -> memos.api.v1.UpdateUserNotificationRequest
|
||||
44, // 71: memos.api.v1.UserService.DeleteUserNotification:input_type -> memos.api.v1.DeleteUserNotificationRequest
|
||||
6, // 72: memos.api.v1.UserService.ListUsers:output_type -> memos.api.v1.ListUsersResponse
|
||||
8, // 73: memos.api.v1.UserService.BatchGetUsers:output_type -> memos.api.v1.BatchGetUsersResponse
|
||||
4, // 74: memos.api.v1.UserService.GetUser:output_type -> memos.api.v1.User
|
||||
4, // 75: memos.api.v1.UserService.CreateUser:output_type -> memos.api.v1.User
|
||||
4, // 76: memos.api.v1.UserService.UpdateUser:output_type -> memos.api.v1.User
|
||||
58, // 77: memos.api.v1.UserService.DeleteUser:output_type -> google.protobuf.Empty
|
||||
16, // 78: memos.api.v1.UserService.ListAllUserStats:output_type -> memos.api.v1.ListAllUserStatsResponse
|
||||
13, // 79: memos.api.v1.UserService.GetUserStats:output_type -> memos.api.v1.UserStats
|
||||
17, // 80: memos.api.v1.UserService.GetUserSetting:output_type -> memos.api.v1.UserSetting
|
||||
17, // 81: memos.api.v1.UserService.UpdateUserSetting:output_type -> memos.api.v1.UserSetting
|
||||
21, // 82: memos.api.v1.UserService.ListUserSettings:output_type -> memos.api.v1.ListUserSettingsResponse
|
||||
24, // 83: memos.api.v1.UserService.ListLinkedIdentities:output_type -> memos.api.v1.ListLinkedIdentitiesResponse
|
||||
22, // 84: memos.api.v1.UserService.CreateLinkedIdentity:output_type -> memos.api.v1.LinkedIdentity
|
||||
22, // 85: memos.api.v1.UserService.GetLinkedIdentity:output_type -> memos.api.v1.LinkedIdentity
|
||||
58, // 86: memos.api.v1.UserService.DeleteLinkedIdentity:output_type -> google.protobuf.Empty
|
||||
30, // 87: memos.api.v1.UserService.ListPersonalAccessTokens:output_type -> memos.api.v1.ListPersonalAccessTokensResponse
|
||||
32, // 88: memos.api.v1.UserService.CreatePersonalAccessToken:output_type -> memos.api.v1.CreatePersonalAccessTokenResponse
|
||||
58, // 89: memos.api.v1.UserService.DeletePersonalAccessToken:output_type -> google.protobuf.Empty
|
||||
36, // 90: memos.api.v1.UserService.ListUserWebhooks:output_type -> memos.api.v1.ListUserWebhooksResponse
|
||||
34, // 91: memos.api.v1.UserService.CreateUserWebhook:output_type -> memos.api.v1.UserWebhook
|
||||
34, // 92: memos.api.v1.UserService.UpdateUserWebhook:output_type -> memos.api.v1.UserWebhook
|
||||
58, // 93: memos.api.v1.UserService.DeleteUserWebhook:output_type -> google.protobuf.Empty
|
||||
42, // 94: memos.api.v1.UserService.ListUserNotifications:output_type -> memos.api.v1.ListUserNotificationsResponse
|
||||
40, // 95: memos.api.v1.UserService.UpdateUserNotification:output_type -> memos.api.v1.UserNotification
|
||||
58, // 96: memos.api.v1.UserService.DeleteUserNotification:output_type -> google.protobuf.Empty
|
||||
72, // [72:97] is the sub-list for method output_type
|
||||
47, // [47:72] is the sub-list for method input_type
|
||||
47, // [47:47] is the sub-list for extension type_name
|
||||
47, // [47:47] is the sub-list for extension extendee
|
||||
0, // [0:47] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_api_v1_user_service_proto_init() }
|
||||
@@ -3607,6 +3749,7 @@ func file_api_v1_user_service_proto_init() {
|
||||
file_api_v1_user_service_proto_msgTypes[13].OneofWrappers = []any{
|
||||
(*UserSetting_GeneralSetting_)(nil),
|
||||
(*UserSetting_WebhooksSetting_)(nil),
|
||||
(*UserSetting_TagsSetting_)(nil),
|
||||
}
|
||||
file_api_v1_user_service_proto_msgTypes[36].OneofWrappers = []any{
|
||||
(*UserNotification_MemoComment)(nil),
|
||||
@@ -3618,7 +3761,7 @@ func file_api_v1_user_service_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_api_v1_user_service_proto_rawDesc), len(file_api_v1_user_service_proto_rawDesc)),
|
||||
NumEnums: 4,
|
||||
NumMessages: 47,
|
||||
NumMessages: 50,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.6.1
|
||||
// - protoc-gen-go-grpc v1.6.2
|
||||
// - protoc (unknown)
|
||||
// source: api/v1/user_service.proto
|
||||
|
||||
|
||||
+30
-1
@@ -2878,7 +2878,10 @@ components:
|
||||
Each key is treated as an anchored regular expression (^pattern$),
|
||||
so a single entry like "project/.*" matches all tags under that prefix.
|
||||
Exact tag names are also valid (they are trivially valid regex patterns).
|
||||
description: Tag metadata configuration.
|
||||
description: |-
|
||||
Tag metadata configuration.
|
||||
Active tag metadata is stored in per-user tag settings.
|
||||
This message remains for backward compatibility with existing clients and migrations.
|
||||
InstanceSetting_TranscriptionConfig:
|
||||
type: object
|
||||
properties:
|
||||
@@ -3835,6 +3838,8 @@ components:
|
||||
$ref: '#/components/schemas/UserSetting_GeneralSetting'
|
||||
webhooksSetting:
|
||||
$ref: '#/components/schemas/UserSetting_WebhooksSetting'
|
||||
tagsSetting:
|
||||
$ref: '#/components/schemas/UserSetting_TagsSetting'
|
||||
description: User settings message
|
||||
UserSetting_GeneralSetting:
|
||||
type: object
|
||||
@@ -3852,6 +3857,30 @@ components:
|
||||
This references a CSS file in the web/public/themes/ directory.
|
||||
If not set, the default theme will be used.
|
||||
description: General user settings configuration.
|
||||
UserSetting_TagMetadata:
|
||||
type: object
|
||||
properties:
|
||||
backgroundColor:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/Color'
|
||||
description: |-
|
||||
Optional background color for the tag label.
|
||||
When unset, the default tag color is used.
|
||||
blurContent:
|
||||
type: boolean
|
||||
description: Whether memos with this tag should have their content blurred.
|
||||
description: Tag metadata for user-specific display rules.
|
||||
UserSetting_TagsSetting:
|
||||
type: object
|
||||
properties:
|
||||
tags:
|
||||
type: object
|
||||
additionalProperties:
|
||||
$ref: '#/components/schemas/UserSetting_TagMetadata'
|
||||
description: |-
|
||||
Map of tag name pattern to tag metadata.
|
||||
Each key is treated as an anchored regular expression (^pattern$).
|
||||
description: User-specific tag metadata.
|
||||
UserSetting_WebhooksSetting:
|
||||
type: object
|
||||
properties:
|
||||
|
||||
@@ -867,6 +867,8 @@ func (x *InstanceTagMetadata) GetBlurContent() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Active tag metadata is stored in per-user tag settings.
|
||||
// This message remains for backward compatibility with existing clients and migrations.
|
||||
type InstanceTagsSetting struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// Map of tag name pattern to tag metadata.
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
color "google.golang.org/genproto/googleapis/type/color"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
|
||||
@@ -36,6 +37,8 @@ const (
|
||||
UserSetting_REFRESH_TOKENS UserSetting_Key = 6
|
||||
// Personal access tokens for the user.
|
||||
UserSetting_PERSONAL_ACCESS_TOKENS UserSetting_Key = 7
|
||||
// Per-user tag metadata.
|
||||
UserSetting_TAGS UserSetting_Key = 8
|
||||
)
|
||||
|
||||
// Enum value maps for UserSetting_Key.
|
||||
@@ -47,6 +50,7 @@ var (
|
||||
5: "WEBHOOKS",
|
||||
6: "REFRESH_TOKENS",
|
||||
7: "PERSONAL_ACCESS_TOKENS",
|
||||
8: "TAGS",
|
||||
}
|
||||
UserSetting_Key_value = map[string]int32{
|
||||
"KEY_UNSPECIFIED": 0,
|
||||
@@ -55,6 +59,7 @@ var (
|
||||
"WEBHOOKS": 5,
|
||||
"REFRESH_TOKENS": 6,
|
||||
"PERSONAL_ACCESS_TOKENS": 7,
|
||||
"TAGS": 8,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -96,6 +101,7 @@ type UserSetting struct {
|
||||
// *UserSetting_Webhooks
|
||||
// *UserSetting_RefreshTokens
|
||||
// *UserSetting_PersonalAccessTokens
|
||||
// *UserSetting_Tags
|
||||
Value isUserSetting_Value `protobuf_oneof:"value"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
@@ -197,6 +203,15 @@ func (x *UserSetting) GetPersonalAccessTokens() *PersonalAccessTokensUserSetting
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *UserSetting) GetTags() *TagsUserSetting {
|
||||
if x != nil {
|
||||
if x, ok := x.Value.(*UserSetting_Tags); ok {
|
||||
return x.Tags
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type isUserSetting_Value interface {
|
||||
isUserSetting_Value()
|
||||
}
|
||||
@@ -221,6 +236,10 @@ type UserSetting_PersonalAccessTokens struct {
|
||||
PersonalAccessTokens *PersonalAccessTokensUserSetting `protobuf:"bytes,9,opt,name=personal_access_tokens,json=personalAccessTokens,proto3,oneof"`
|
||||
}
|
||||
|
||||
type UserSetting_Tags struct {
|
||||
Tags *TagsUserSetting `protobuf:"bytes,10,opt,name=tags,proto3,oneof"`
|
||||
}
|
||||
|
||||
func (*UserSetting_General) isUserSetting_Value() {}
|
||||
|
||||
func (*UserSetting_Shortcuts) isUserSetting_Value() {}
|
||||
@@ -231,6 +250,8 @@ func (*UserSetting_RefreshTokens) isUserSetting_Value() {}
|
||||
|
||||
func (*UserSetting_PersonalAccessTokens) isUserSetting_Value() {}
|
||||
|
||||
func (*UserSetting_Tags) isUserSetting_Value() {}
|
||||
|
||||
type GeneralUserSetting struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// The user's locale.
|
||||
@@ -295,6 +316,109 @@ func (x *GeneralUserSetting) GetTheme() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
type UserTagMetadata struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// Optional background color for the tag label.
|
||||
// When unset, the default tag color is used.
|
||||
BackgroundColor *color.Color `protobuf:"bytes,1,opt,name=background_color,json=backgroundColor,proto3" json:"background_color,omitempty"`
|
||||
// Whether memos with this tag should have their content blurred.
|
||||
BlurContent bool `protobuf:"varint,2,opt,name=blur_content,json=blurContent,proto3" json:"blur_content,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *UserTagMetadata) Reset() {
|
||||
*x = UserTagMetadata{}
|
||||
mi := &file_store_user_setting_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *UserTagMetadata) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*UserTagMetadata) ProtoMessage() {}
|
||||
|
||||
func (x *UserTagMetadata) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_store_user_setting_proto_msgTypes[2]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use UserTagMetadata.ProtoReflect.Descriptor instead.
|
||||
func (*UserTagMetadata) Descriptor() ([]byte, []int) {
|
||||
return file_store_user_setting_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *UserTagMetadata) GetBackgroundColor() *color.Color {
|
||||
if x != nil {
|
||||
return x.BackgroundColor
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *UserTagMetadata) GetBlurContent() bool {
|
||||
if x != nil {
|
||||
return x.BlurContent
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type TagsUserSetting struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// Map of tag name pattern to tag metadata.
|
||||
// Each key is treated as an anchored regular expression (^pattern$),
|
||||
// so a single entry like "project/.*" matches all tags under that prefix.
|
||||
// Exact tag names are also valid.
|
||||
Tags map[string]*UserTagMetadata `protobuf:"bytes,1,rep,name=tags,proto3" json:"tags,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *TagsUserSetting) Reset() {
|
||||
*x = TagsUserSetting{}
|
||||
mi := &file_store_user_setting_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *TagsUserSetting) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*TagsUserSetting) ProtoMessage() {}
|
||||
|
||||
func (x *TagsUserSetting) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_store_user_setting_proto_msgTypes[3]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use TagsUserSetting.ProtoReflect.Descriptor instead.
|
||||
func (*TagsUserSetting) Descriptor() ([]byte, []int) {
|
||||
return file_store_user_setting_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *TagsUserSetting) GetTags() map[string]*UserTagMetadata {
|
||||
if x != nil {
|
||||
return x.Tags
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type RefreshTokensUserSetting struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
RefreshTokens []*RefreshTokensUserSetting_RefreshToken `protobuf:"bytes,1,rep,name=refresh_tokens,json=refreshTokens,proto3" json:"refresh_tokens,omitempty"`
|
||||
@@ -304,7 +428,7 @@ type RefreshTokensUserSetting struct {
|
||||
|
||||
func (x *RefreshTokensUserSetting) Reset() {
|
||||
*x = RefreshTokensUserSetting{}
|
||||
mi := &file_store_user_setting_proto_msgTypes[2]
|
||||
mi := &file_store_user_setting_proto_msgTypes[4]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -316,7 +440,7 @@ func (x *RefreshTokensUserSetting) String() string {
|
||||
func (*RefreshTokensUserSetting) ProtoMessage() {}
|
||||
|
||||
func (x *RefreshTokensUserSetting) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_store_user_setting_proto_msgTypes[2]
|
||||
mi := &file_store_user_setting_proto_msgTypes[4]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -329,7 +453,7 @@ func (x *RefreshTokensUserSetting) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use RefreshTokensUserSetting.ProtoReflect.Descriptor instead.
|
||||
func (*RefreshTokensUserSetting) Descriptor() ([]byte, []int) {
|
||||
return file_store_user_setting_proto_rawDescGZIP(), []int{2}
|
||||
return file_store_user_setting_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
func (x *RefreshTokensUserSetting) GetRefreshTokens() []*RefreshTokensUserSetting_RefreshToken {
|
||||
@@ -348,7 +472,7 @@ type PersonalAccessTokensUserSetting struct {
|
||||
|
||||
func (x *PersonalAccessTokensUserSetting) Reset() {
|
||||
*x = PersonalAccessTokensUserSetting{}
|
||||
mi := &file_store_user_setting_proto_msgTypes[3]
|
||||
mi := &file_store_user_setting_proto_msgTypes[5]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -360,7 +484,7 @@ func (x *PersonalAccessTokensUserSetting) String() string {
|
||||
func (*PersonalAccessTokensUserSetting) ProtoMessage() {}
|
||||
|
||||
func (x *PersonalAccessTokensUserSetting) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_store_user_setting_proto_msgTypes[3]
|
||||
mi := &file_store_user_setting_proto_msgTypes[5]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -373,7 +497,7 @@ func (x *PersonalAccessTokensUserSetting) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use PersonalAccessTokensUserSetting.ProtoReflect.Descriptor instead.
|
||||
func (*PersonalAccessTokensUserSetting) Descriptor() ([]byte, []int) {
|
||||
return file_store_user_setting_proto_rawDescGZIP(), []int{3}
|
||||
return file_store_user_setting_proto_rawDescGZIP(), []int{5}
|
||||
}
|
||||
|
||||
func (x *PersonalAccessTokensUserSetting) GetTokens() []*PersonalAccessTokensUserSetting_PersonalAccessToken {
|
||||
@@ -392,7 +516,7 @@ type ShortcutsUserSetting struct {
|
||||
|
||||
func (x *ShortcutsUserSetting) Reset() {
|
||||
*x = ShortcutsUserSetting{}
|
||||
mi := &file_store_user_setting_proto_msgTypes[4]
|
||||
mi := &file_store_user_setting_proto_msgTypes[6]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -404,7 +528,7 @@ func (x *ShortcutsUserSetting) String() string {
|
||||
func (*ShortcutsUserSetting) ProtoMessage() {}
|
||||
|
||||
func (x *ShortcutsUserSetting) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_store_user_setting_proto_msgTypes[4]
|
||||
mi := &file_store_user_setting_proto_msgTypes[6]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -417,7 +541,7 @@ func (x *ShortcutsUserSetting) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use ShortcutsUserSetting.ProtoReflect.Descriptor instead.
|
||||
func (*ShortcutsUserSetting) Descriptor() ([]byte, []int) {
|
||||
return file_store_user_setting_proto_rawDescGZIP(), []int{4}
|
||||
return file_store_user_setting_proto_rawDescGZIP(), []int{6}
|
||||
}
|
||||
|
||||
func (x *ShortcutsUserSetting) GetShortcuts() []*ShortcutsUserSetting_Shortcut {
|
||||
@@ -436,7 +560,7 @@ type WebhooksUserSetting struct {
|
||||
|
||||
func (x *WebhooksUserSetting) Reset() {
|
||||
*x = WebhooksUserSetting{}
|
||||
mi := &file_store_user_setting_proto_msgTypes[5]
|
||||
mi := &file_store_user_setting_proto_msgTypes[7]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -448,7 +572,7 @@ func (x *WebhooksUserSetting) String() string {
|
||||
func (*WebhooksUserSetting) ProtoMessage() {}
|
||||
|
||||
func (x *WebhooksUserSetting) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_store_user_setting_proto_msgTypes[5]
|
||||
mi := &file_store_user_setting_proto_msgTypes[7]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -461,7 +585,7 @@ func (x *WebhooksUserSetting) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use WebhooksUserSetting.ProtoReflect.Descriptor instead.
|
||||
func (*WebhooksUserSetting) Descriptor() ([]byte, []int) {
|
||||
return file_store_user_setting_proto_rawDescGZIP(), []int{5}
|
||||
return file_store_user_setting_proto_rawDescGZIP(), []int{7}
|
||||
}
|
||||
|
||||
func (x *WebhooksUserSetting) GetWebhooks() []*WebhooksUserSetting_Webhook {
|
||||
@@ -489,7 +613,7 @@ type RefreshTokensUserSetting_RefreshToken struct {
|
||||
|
||||
func (x *RefreshTokensUserSetting_RefreshToken) Reset() {
|
||||
*x = RefreshTokensUserSetting_RefreshToken{}
|
||||
mi := &file_store_user_setting_proto_msgTypes[6]
|
||||
mi := &file_store_user_setting_proto_msgTypes[9]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -501,7 +625,7 @@ func (x *RefreshTokensUserSetting_RefreshToken) String() string {
|
||||
func (*RefreshTokensUserSetting_RefreshToken) ProtoMessage() {}
|
||||
|
||||
func (x *RefreshTokensUserSetting_RefreshToken) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_store_user_setting_proto_msgTypes[6]
|
||||
mi := &file_store_user_setting_proto_msgTypes[9]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -514,7 +638,7 @@ func (x *RefreshTokensUserSetting_RefreshToken) ProtoReflect() protoreflect.Mess
|
||||
|
||||
// Deprecated: Use RefreshTokensUserSetting_RefreshToken.ProtoReflect.Descriptor instead.
|
||||
func (*RefreshTokensUserSetting_RefreshToken) Descriptor() ([]byte, []int) {
|
||||
return file_store_user_setting_proto_rawDescGZIP(), []int{2, 0}
|
||||
return file_store_user_setting_proto_rawDescGZIP(), []int{4, 0}
|
||||
}
|
||||
|
||||
func (x *RefreshTokensUserSetting_RefreshToken) GetTokenId() string {
|
||||
@@ -570,7 +694,7 @@ type RefreshTokensUserSetting_ClientInfo struct {
|
||||
|
||||
func (x *RefreshTokensUserSetting_ClientInfo) Reset() {
|
||||
*x = RefreshTokensUserSetting_ClientInfo{}
|
||||
mi := &file_store_user_setting_proto_msgTypes[7]
|
||||
mi := &file_store_user_setting_proto_msgTypes[10]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -582,7 +706,7 @@ func (x *RefreshTokensUserSetting_ClientInfo) String() string {
|
||||
func (*RefreshTokensUserSetting_ClientInfo) ProtoMessage() {}
|
||||
|
||||
func (x *RefreshTokensUserSetting_ClientInfo) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_store_user_setting_proto_msgTypes[7]
|
||||
mi := &file_store_user_setting_proto_msgTypes[10]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -595,7 +719,7 @@ func (x *RefreshTokensUserSetting_ClientInfo) ProtoReflect() protoreflect.Messag
|
||||
|
||||
// Deprecated: Use RefreshTokensUserSetting_ClientInfo.ProtoReflect.Descriptor instead.
|
||||
func (*RefreshTokensUserSetting_ClientInfo) Descriptor() ([]byte, []int) {
|
||||
return file_store_user_setting_proto_rawDescGZIP(), []int{2, 1}
|
||||
return file_store_user_setting_proto_rawDescGZIP(), []int{4, 1}
|
||||
}
|
||||
|
||||
func (x *RefreshTokensUserSetting_ClientInfo) GetUserAgent() string {
|
||||
@@ -653,7 +777,7 @@ type PersonalAccessTokensUserSetting_PersonalAccessToken struct {
|
||||
|
||||
func (x *PersonalAccessTokensUserSetting_PersonalAccessToken) Reset() {
|
||||
*x = PersonalAccessTokensUserSetting_PersonalAccessToken{}
|
||||
mi := &file_store_user_setting_proto_msgTypes[8]
|
||||
mi := &file_store_user_setting_proto_msgTypes[11]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -665,7 +789,7 @@ func (x *PersonalAccessTokensUserSetting_PersonalAccessToken) String() string {
|
||||
func (*PersonalAccessTokensUserSetting_PersonalAccessToken) ProtoMessage() {}
|
||||
|
||||
func (x *PersonalAccessTokensUserSetting_PersonalAccessToken) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_store_user_setting_proto_msgTypes[8]
|
||||
mi := &file_store_user_setting_proto_msgTypes[11]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -678,7 +802,7 @@ func (x *PersonalAccessTokensUserSetting_PersonalAccessToken) ProtoReflect() pro
|
||||
|
||||
// Deprecated: Use PersonalAccessTokensUserSetting_PersonalAccessToken.ProtoReflect.Descriptor instead.
|
||||
func (*PersonalAccessTokensUserSetting_PersonalAccessToken) Descriptor() ([]byte, []int) {
|
||||
return file_store_user_setting_proto_rawDescGZIP(), []int{3, 0}
|
||||
return file_store_user_setting_proto_rawDescGZIP(), []int{5, 0}
|
||||
}
|
||||
|
||||
func (x *PersonalAccessTokensUserSetting_PersonalAccessToken) GetTokenId() string {
|
||||
@@ -734,7 +858,7 @@ type ShortcutsUserSetting_Shortcut struct {
|
||||
|
||||
func (x *ShortcutsUserSetting_Shortcut) Reset() {
|
||||
*x = ShortcutsUserSetting_Shortcut{}
|
||||
mi := &file_store_user_setting_proto_msgTypes[9]
|
||||
mi := &file_store_user_setting_proto_msgTypes[12]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -746,7 +870,7 @@ func (x *ShortcutsUserSetting_Shortcut) String() string {
|
||||
func (*ShortcutsUserSetting_Shortcut) ProtoMessage() {}
|
||||
|
||||
func (x *ShortcutsUserSetting_Shortcut) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_store_user_setting_proto_msgTypes[9]
|
||||
mi := &file_store_user_setting_proto_msgTypes[12]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -759,7 +883,7 @@ func (x *ShortcutsUserSetting_Shortcut) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use ShortcutsUserSetting_Shortcut.ProtoReflect.Descriptor instead.
|
||||
func (*ShortcutsUserSetting_Shortcut) Descriptor() ([]byte, []int) {
|
||||
return file_store_user_setting_proto_rawDescGZIP(), []int{4, 0}
|
||||
return file_store_user_setting_proto_rawDescGZIP(), []int{6, 0}
|
||||
}
|
||||
|
||||
func (x *ShortcutsUserSetting_Shortcut) GetId() string {
|
||||
@@ -797,7 +921,7 @@ type WebhooksUserSetting_Webhook struct {
|
||||
|
||||
func (x *WebhooksUserSetting_Webhook) Reset() {
|
||||
*x = WebhooksUserSetting_Webhook{}
|
||||
mi := &file_store_user_setting_proto_msgTypes[10]
|
||||
mi := &file_store_user_setting_proto_msgTypes[13]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -809,7 +933,7 @@ func (x *WebhooksUserSetting_Webhook) String() string {
|
||||
func (*WebhooksUserSetting_Webhook) ProtoMessage() {}
|
||||
|
||||
func (x *WebhooksUserSetting_Webhook) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_store_user_setting_proto_msgTypes[10]
|
||||
mi := &file_store_user_setting_proto_msgTypes[13]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -822,7 +946,7 @@ func (x *WebhooksUserSetting_Webhook) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use WebhooksUserSetting_Webhook.ProtoReflect.Descriptor instead.
|
||||
func (*WebhooksUserSetting_Webhook) Descriptor() ([]byte, []int) {
|
||||
return file_store_user_setting_proto_rawDescGZIP(), []int{5, 0}
|
||||
return file_store_user_setting_proto_rawDescGZIP(), []int{7, 0}
|
||||
}
|
||||
|
||||
func (x *WebhooksUserSetting_Webhook) GetId() string {
|
||||
@@ -850,7 +974,7 @@ var File_store_user_setting_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_store_user_setting_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"\x18store/user_setting.proto\x12\vmemos.store\x1a\x1fgoogle/protobuf/timestamp.proto\"\xcb\x04\n" +
|
||||
"\x18store/user_setting.proto\x12\vmemos.store\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x17google/type/color.proto\"\x89\x05\n" +
|
||||
"\vUserSetting\x12\x17\n" +
|
||||
"\auser_id\x18\x01 \x01(\x05R\x06userId\x12.\n" +
|
||||
"\x03key\x18\x02 \x01(\x0e2\x1c.memos.store.UserSetting.KeyR\x03key\x12;\n" +
|
||||
@@ -858,19 +982,30 @@ const file_store_user_setting_proto_rawDesc = "" +
|
||||
"\tshortcuts\x18\x06 \x01(\v2!.memos.store.ShortcutsUserSettingH\x00R\tshortcuts\x12>\n" +
|
||||
"\bwebhooks\x18\a \x01(\v2 .memos.store.WebhooksUserSettingH\x00R\bwebhooks\x12N\n" +
|
||||
"\x0erefresh_tokens\x18\b \x01(\v2%.memos.store.RefreshTokensUserSettingH\x00R\rrefreshTokens\x12d\n" +
|
||||
"\x16personal_access_tokens\x18\t \x01(\v2,.memos.store.PersonalAccessTokensUserSettingH\x00R\x14personalAccessTokens\"t\n" +
|
||||
"\x16personal_access_tokens\x18\t \x01(\v2,.memos.store.PersonalAccessTokensUserSettingH\x00R\x14personalAccessTokens\x122\n" +
|
||||
"\x04tags\x18\n" +
|
||||
" \x01(\v2\x1c.memos.store.TagsUserSettingH\x00R\x04tags\"~\n" +
|
||||
"\x03Key\x12\x13\n" +
|
||||
"\x0fKEY_UNSPECIFIED\x10\x00\x12\v\n" +
|
||||
"\aGENERAL\x10\x01\x12\r\n" +
|
||||
"\tSHORTCUTS\x10\x04\x12\f\n" +
|
||||
"\bWEBHOOKS\x10\x05\x12\x12\n" +
|
||||
"\x0eREFRESH_TOKENS\x10\x06\x12\x1a\n" +
|
||||
"\x16PERSONAL_ACCESS_TOKENS\x10\aB\a\n" +
|
||||
"\x16PERSONAL_ACCESS_TOKENS\x10\a\x12\b\n" +
|
||||
"\x04TAGS\x10\bB\a\n" +
|
||||
"\x05value\"k\n" +
|
||||
"\x12GeneralUserSetting\x12\x16\n" +
|
||||
"\x06locale\x18\x01 \x01(\tR\x06locale\x12'\n" +
|
||||
"\x0fmemo_visibility\x18\x02 \x01(\tR\x0ememoVisibility\x12\x14\n" +
|
||||
"\x05theme\x18\x03 \x01(\tR\x05theme\"\xa4\x04\n" +
|
||||
"\x05theme\x18\x03 \x01(\tR\x05theme\"s\n" +
|
||||
"\x0fUserTagMetadata\x12=\n" +
|
||||
"\x10background_color\x18\x01 \x01(\v2\x12.google.type.ColorR\x0fbackgroundColor\x12!\n" +
|
||||
"\fblur_content\x18\x02 \x01(\bR\vblurContent\"\xa4\x01\n" +
|
||||
"\x0fTagsUserSetting\x12:\n" +
|
||||
"\x04tags\x18\x01 \x03(\v2&.memos.store.TagsUserSetting.TagsEntryR\x04tags\x1aU\n" +
|
||||
"\tTagsEntry\x12\x10\n" +
|
||||
"\x03key\x18\x01 \x01(\tR\x03key\x122\n" +
|
||||
"\x05value\x18\x02 \x01(\v2\x1c.memos.store.UserTagMetadataR\x05value:\x028\x01\"\xa4\x04\n" +
|
||||
"\x18RefreshTokensUserSetting\x12Y\n" +
|
||||
"\x0erefresh_tokens\x18\x01 \x03(\v22.memos.store.RefreshTokensUserSetting.RefreshTokenR\rrefreshTokens\x1a\x94\x02\n" +
|
||||
"\fRefreshToken\x12\x19\n" +
|
||||
@@ -932,44 +1067,52 @@ func file_store_user_setting_proto_rawDescGZIP() []byte {
|
||||
}
|
||||
|
||||
var file_store_user_setting_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
||||
var file_store_user_setting_proto_msgTypes = make([]protoimpl.MessageInfo, 11)
|
||||
var file_store_user_setting_proto_msgTypes = make([]protoimpl.MessageInfo, 14)
|
||||
var file_store_user_setting_proto_goTypes = []any{
|
||||
(UserSetting_Key)(0), // 0: memos.store.UserSetting.Key
|
||||
(*UserSetting)(nil), // 1: memos.store.UserSetting
|
||||
(*GeneralUserSetting)(nil), // 2: memos.store.GeneralUserSetting
|
||||
(*RefreshTokensUserSetting)(nil), // 3: memos.store.RefreshTokensUserSetting
|
||||
(*PersonalAccessTokensUserSetting)(nil), // 4: memos.store.PersonalAccessTokensUserSetting
|
||||
(*ShortcutsUserSetting)(nil), // 5: memos.store.ShortcutsUserSetting
|
||||
(*WebhooksUserSetting)(nil), // 6: memos.store.WebhooksUserSetting
|
||||
(*RefreshTokensUserSetting_RefreshToken)(nil), // 7: memos.store.RefreshTokensUserSetting.RefreshToken
|
||||
(*RefreshTokensUserSetting_ClientInfo)(nil), // 8: memos.store.RefreshTokensUserSetting.ClientInfo
|
||||
(*PersonalAccessTokensUserSetting_PersonalAccessToken)(nil), // 9: memos.store.PersonalAccessTokensUserSetting.PersonalAccessToken
|
||||
(*ShortcutsUserSetting_Shortcut)(nil), // 10: memos.store.ShortcutsUserSetting.Shortcut
|
||||
(*WebhooksUserSetting_Webhook)(nil), // 11: memos.store.WebhooksUserSetting.Webhook
|
||||
(*timestamppb.Timestamp)(nil), // 12: google.protobuf.Timestamp
|
||||
(UserSetting_Key)(0), // 0: memos.store.UserSetting.Key
|
||||
(*UserSetting)(nil), // 1: memos.store.UserSetting
|
||||
(*GeneralUserSetting)(nil), // 2: memos.store.GeneralUserSetting
|
||||
(*UserTagMetadata)(nil), // 3: memos.store.UserTagMetadata
|
||||
(*TagsUserSetting)(nil), // 4: memos.store.TagsUserSetting
|
||||
(*RefreshTokensUserSetting)(nil), // 5: memos.store.RefreshTokensUserSetting
|
||||
(*PersonalAccessTokensUserSetting)(nil), // 6: memos.store.PersonalAccessTokensUserSetting
|
||||
(*ShortcutsUserSetting)(nil), // 7: memos.store.ShortcutsUserSetting
|
||||
(*WebhooksUserSetting)(nil), // 8: memos.store.WebhooksUserSetting
|
||||
nil, // 9: memos.store.TagsUserSetting.TagsEntry
|
||||
(*RefreshTokensUserSetting_RefreshToken)(nil), // 10: memos.store.RefreshTokensUserSetting.RefreshToken
|
||||
(*RefreshTokensUserSetting_ClientInfo)(nil), // 11: memos.store.RefreshTokensUserSetting.ClientInfo
|
||||
(*PersonalAccessTokensUserSetting_PersonalAccessToken)(nil), // 12: memos.store.PersonalAccessTokensUserSetting.PersonalAccessToken
|
||||
(*ShortcutsUserSetting_Shortcut)(nil), // 13: memos.store.ShortcutsUserSetting.Shortcut
|
||||
(*WebhooksUserSetting_Webhook)(nil), // 14: memos.store.WebhooksUserSetting.Webhook
|
||||
(*color.Color)(nil), // 15: google.type.Color
|
||||
(*timestamppb.Timestamp)(nil), // 16: google.protobuf.Timestamp
|
||||
}
|
||||
var file_store_user_setting_proto_depIdxs = []int32{
|
||||
0, // 0: memos.store.UserSetting.key:type_name -> memos.store.UserSetting.Key
|
||||
2, // 1: memos.store.UserSetting.general:type_name -> memos.store.GeneralUserSetting
|
||||
5, // 2: memos.store.UserSetting.shortcuts:type_name -> memos.store.ShortcutsUserSetting
|
||||
6, // 3: memos.store.UserSetting.webhooks:type_name -> memos.store.WebhooksUserSetting
|
||||
3, // 4: memos.store.UserSetting.refresh_tokens:type_name -> memos.store.RefreshTokensUserSetting
|
||||
4, // 5: memos.store.UserSetting.personal_access_tokens:type_name -> memos.store.PersonalAccessTokensUserSetting
|
||||
7, // 6: memos.store.RefreshTokensUserSetting.refresh_tokens:type_name -> memos.store.RefreshTokensUserSetting.RefreshToken
|
||||
9, // 7: memos.store.PersonalAccessTokensUserSetting.tokens:type_name -> memos.store.PersonalAccessTokensUserSetting.PersonalAccessToken
|
||||
10, // 8: memos.store.ShortcutsUserSetting.shortcuts:type_name -> memos.store.ShortcutsUserSetting.Shortcut
|
||||
11, // 9: memos.store.WebhooksUserSetting.webhooks:type_name -> memos.store.WebhooksUserSetting.Webhook
|
||||
12, // 10: memos.store.RefreshTokensUserSetting.RefreshToken.expires_at:type_name -> google.protobuf.Timestamp
|
||||
12, // 11: memos.store.RefreshTokensUserSetting.RefreshToken.created_at:type_name -> google.protobuf.Timestamp
|
||||
8, // 12: memos.store.RefreshTokensUserSetting.RefreshToken.client_info:type_name -> memos.store.RefreshTokensUserSetting.ClientInfo
|
||||
12, // 13: memos.store.PersonalAccessTokensUserSetting.PersonalAccessToken.expires_at:type_name -> google.protobuf.Timestamp
|
||||
12, // 14: memos.store.PersonalAccessTokensUserSetting.PersonalAccessToken.created_at:type_name -> google.protobuf.Timestamp
|
||||
12, // 15: memos.store.PersonalAccessTokensUserSetting.PersonalAccessToken.last_used_at:type_name -> google.protobuf.Timestamp
|
||||
16, // [16:16] is the sub-list for method output_type
|
||||
16, // [16:16] is the sub-list for method input_type
|
||||
16, // [16:16] is the sub-list for extension type_name
|
||||
16, // [16:16] is the sub-list for extension extendee
|
||||
0, // [0:16] is the sub-list for field type_name
|
||||
7, // 2: memos.store.UserSetting.shortcuts:type_name -> memos.store.ShortcutsUserSetting
|
||||
8, // 3: memos.store.UserSetting.webhooks:type_name -> memos.store.WebhooksUserSetting
|
||||
5, // 4: memos.store.UserSetting.refresh_tokens:type_name -> memos.store.RefreshTokensUserSetting
|
||||
6, // 5: memos.store.UserSetting.personal_access_tokens:type_name -> memos.store.PersonalAccessTokensUserSetting
|
||||
4, // 6: memos.store.UserSetting.tags:type_name -> memos.store.TagsUserSetting
|
||||
15, // 7: memos.store.UserTagMetadata.background_color:type_name -> google.type.Color
|
||||
9, // 8: memos.store.TagsUserSetting.tags:type_name -> memos.store.TagsUserSetting.TagsEntry
|
||||
10, // 9: memos.store.RefreshTokensUserSetting.refresh_tokens:type_name -> memos.store.RefreshTokensUserSetting.RefreshToken
|
||||
12, // 10: memos.store.PersonalAccessTokensUserSetting.tokens:type_name -> memos.store.PersonalAccessTokensUserSetting.PersonalAccessToken
|
||||
13, // 11: memos.store.ShortcutsUserSetting.shortcuts:type_name -> memos.store.ShortcutsUserSetting.Shortcut
|
||||
14, // 12: memos.store.WebhooksUserSetting.webhooks:type_name -> memos.store.WebhooksUserSetting.Webhook
|
||||
3, // 13: memos.store.TagsUserSetting.TagsEntry.value:type_name -> memos.store.UserTagMetadata
|
||||
16, // 14: memos.store.RefreshTokensUserSetting.RefreshToken.expires_at:type_name -> google.protobuf.Timestamp
|
||||
16, // 15: memos.store.RefreshTokensUserSetting.RefreshToken.created_at:type_name -> google.protobuf.Timestamp
|
||||
11, // 16: memos.store.RefreshTokensUserSetting.RefreshToken.client_info:type_name -> memos.store.RefreshTokensUserSetting.ClientInfo
|
||||
16, // 17: memos.store.PersonalAccessTokensUserSetting.PersonalAccessToken.expires_at:type_name -> google.protobuf.Timestamp
|
||||
16, // 18: memos.store.PersonalAccessTokensUserSetting.PersonalAccessToken.created_at:type_name -> google.protobuf.Timestamp
|
||||
16, // 19: memos.store.PersonalAccessTokensUserSetting.PersonalAccessToken.last_used_at:type_name -> google.protobuf.Timestamp
|
||||
20, // [20:20] is the sub-list for method output_type
|
||||
20, // [20:20] is the sub-list for method input_type
|
||||
20, // [20:20] is the sub-list for extension type_name
|
||||
20, // [20:20] is the sub-list for extension extendee
|
||||
0, // [0:20] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_store_user_setting_proto_init() }
|
||||
@@ -983,6 +1126,7 @@ func file_store_user_setting_proto_init() {
|
||||
(*UserSetting_Webhooks)(nil),
|
||||
(*UserSetting_RefreshTokens)(nil),
|
||||
(*UserSetting_PersonalAccessTokens)(nil),
|
||||
(*UserSetting_Tags)(nil),
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
@@ -990,7 +1134,7 @@ func file_store_user_setting_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_store_user_setting_proto_rawDesc), len(file_store_user_setting_proto_rawDesc)),
|
||||
NumEnums: 1,
|
||||
NumMessages: 11,
|
||||
NumMessages: 14,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
|
||||
@@ -121,6 +121,8 @@ message InstanceTagMetadata {
|
||||
bool blur_content = 2;
|
||||
}
|
||||
|
||||
// Active tag metadata is stored in per-user tag settings.
|
||||
// This message remains for backward compatibility with existing clients and migrations.
|
||||
message InstanceTagsSetting {
|
||||
// Map of tag name pattern to tag metadata.
|
||||
// Each key is treated as an anchored regular expression (^pattern$),
|
||||
|
||||
@@ -3,6 +3,7 @@ syntax = "proto3";
|
||||
package memos.store;
|
||||
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "google/type/color.proto";
|
||||
|
||||
option go_package = "gen/store";
|
||||
|
||||
@@ -19,6 +20,8 @@ message UserSetting {
|
||||
REFRESH_TOKENS = 6;
|
||||
// Personal access tokens for the user.
|
||||
PERSONAL_ACCESS_TOKENS = 7;
|
||||
// Per-user tag metadata.
|
||||
TAGS = 8;
|
||||
}
|
||||
|
||||
int32 user_id = 1;
|
||||
@@ -30,6 +33,7 @@ message UserSetting {
|
||||
WebhooksUserSetting webhooks = 7;
|
||||
RefreshTokensUserSetting refresh_tokens = 8;
|
||||
PersonalAccessTokensUserSetting personal_access_tokens = 9;
|
||||
TagsUserSetting tags = 10;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +47,22 @@ message GeneralUserSetting {
|
||||
string theme = 3;
|
||||
}
|
||||
|
||||
message UserTagMetadata {
|
||||
// Optional background color for the tag label.
|
||||
// When unset, the default tag color is used.
|
||||
google.type.Color background_color = 1;
|
||||
// Whether memos with this tag should have their content blurred.
|
||||
bool blur_content = 2;
|
||||
}
|
||||
|
||||
message TagsUserSetting {
|
||||
// Map of tag name pattern to tag metadata.
|
||||
// Each key is treated as an anchored regular expression (^pattern$),
|
||||
// so a single entry like "project/.*" matches all tags under that prefix.
|
||||
// Exact tag names are also valid.
|
||||
map<string, UserTagMetadata> tags = 1;
|
||||
}
|
||||
|
||||
message RefreshTokensUserSetting {
|
||||
message RefreshToken {
|
||||
// Unique identifier (matches 'tid' claim in JWT)
|
||||
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
colorpb "google.golang.org/genproto/googleapis/type/color"
|
||||
"google.golang.org/protobuf/types/known/fieldmaskpb"
|
||||
|
||||
apiv1 "github.com/usememos/memos/proto/gen/api/v1"
|
||||
storepb "github.com/usememos/memos/proto/gen/store"
|
||||
@@ -56,3 +58,149 @@ func TestListUserSettingsOmitsInternalStoreSettings(t *testing.T) {
|
||||
require.Equal(t, "users/locale-user/settings/GENERAL", resp.Settings[0].Name)
|
||||
require.Equal(t, "ja", resp.Settings[0].GetGeneralSetting().Locale)
|
||||
}
|
||||
|
||||
func TestUserTagSettings(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
ts := NewTestService(t)
|
||||
defer ts.Cleanup()
|
||||
|
||||
user, err := ts.CreateRegularUser(ctx, "tag-user")
|
||||
require.NoError(t, err)
|
||||
userCtx := ts.CreateUserContext(ctx, user.ID)
|
||||
|
||||
tagsSetting := &apiv1.UserSetting{
|
||||
Name: "users/tag-user/settings/TAGS",
|
||||
Value: &apiv1.UserSetting_TagsSetting_{
|
||||
TagsSetting: &apiv1.UserSetting_TagsSetting{
|
||||
Tags: map[string]*apiv1.UserSetting_TagMetadata{
|
||||
"bug": {
|
||||
BackgroundColor: &colorpb.Color{
|
||||
Red: 0.9,
|
||||
Green: 0.1,
|
||||
Blue: 0.1,
|
||||
},
|
||||
},
|
||||
"private/.*": {
|
||||
BlurContent: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
updated, err := ts.Service.UpdateUserSetting(userCtx, &apiv1.UpdateUserSettingRequest{
|
||||
Setting: tagsSetting,
|
||||
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"tags"}},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "users/tag-user/settings/TAGS", updated.Name)
|
||||
require.Contains(t, updated.GetTagsSetting().GetTags(), "bug")
|
||||
require.True(t, updated.GetTagsSetting().GetTags()["private/.*"].BlurContent)
|
||||
|
||||
got, err := ts.Service.GetUserSetting(userCtx, &apiv1.GetUserSettingRequest{Name: "users/tag-user/settings/TAGS"})
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, got.GetTagsSetting().GetTags(), "bug")
|
||||
|
||||
resp, err := ts.Service.ListUserSettings(userCtx, &apiv1.ListUserSettingsRequest{
|
||||
Parent: apiv1server.BuildUserName(user.Username),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
var foundTags bool
|
||||
for _, setting := range resp.Settings {
|
||||
if setting.GetTagsSetting() != nil {
|
||||
foundTags = true
|
||||
require.Equal(t, "users/tag-user/settings/TAGS", setting.Name)
|
||||
}
|
||||
}
|
||||
require.True(t, foundTags)
|
||||
}
|
||||
|
||||
func TestUserTagSettingsRejectInvalidInput(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
ts := NewTestService(t)
|
||||
defer ts.Cleanup()
|
||||
|
||||
user, err := ts.CreateRegularUser(ctx, "invalid-tag-user")
|
||||
require.NoError(t, err)
|
||||
userCtx := ts.CreateUserContext(ctx, user.ID)
|
||||
|
||||
_, err = ts.Service.UpdateUserSetting(userCtx, &apiv1.UpdateUserSettingRequest{
|
||||
Setting: &apiv1.UserSetting{
|
||||
Name: "users/invalid-tag-user/settings/TAGS",
|
||||
Value: &apiv1.UserSetting_TagsSetting_{
|
||||
TagsSetting: &apiv1.UserSetting_TagsSetting{
|
||||
Tags: map[string]*apiv1.UserSetting_TagMetadata{
|
||||
"(": {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"tags"}},
|
||||
})
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "invalid")
|
||||
|
||||
_, err = ts.Service.UpdateUserSetting(userCtx, &apiv1.UpdateUserSettingRequest{
|
||||
Setting: &apiv1.UserSetting{
|
||||
Name: "users/invalid-tag-user/settings/TAGS",
|
||||
Value: &apiv1.UserSetting_TagsSetting_{
|
||||
TagsSetting: &apiv1.UserSetting_TagsSetting{
|
||||
Tags: map[string]*apiv1.UserSetting_TagMetadata{
|
||||
"bug": {
|
||||
BackgroundColor: &colorpb.Color{Red: 1.2},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"tags"}},
|
||||
})
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "invalid")
|
||||
|
||||
_, err = ts.Service.UpdateUserSetting(userCtx, &apiv1.UpdateUserSettingRequest{
|
||||
Setting: &apiv1.UserSetting{
|
||||
Name: "users/invalid-tag-user/settings/TAGS",
|
||||
Value: &apiv1.UserSetting_TagsSetting_{
|
||||
TagsSetting: &apiv1.UserSetting_TagsSetting{
|
||||
Tags: map[string]*apiv1.UserSetting_TagMetadata{
|
||||
"bug": {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"locale"}},
|
||||
})
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "unsupported update mask path")
|
||||
}
|
||||
|
||||
func TestUserTagSettingsPermissionDeniedForOtherUser(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
ts := NewTestService(t)
|
||||
defer ts.Cleanup()
|
||||
|
||||
_, err := ts.CreateRegularUser(ctx, "tag-owner")
|
||||
require.NoError(t, err)
|
||||
other, err := ts.CreateRegularUser(ctx, "tag-other")
|
||||
require.NoError(t, err)
|
||||
otherCtx := ts.CreateUserContext(ctx, other.ID)
|
||||
|
||||
_, err = ts.Service.GetUserSetting(otherCtx, &apiv1.GetUserSettingRequest{Name: "users/tag-owner/settings/TAGS"})
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "permission denied")
|
||||
|
||||
_, err = ts.Service.UpdateUserSetting(otherCtx, &apiv1.UpdateUserSettingRequest{
|
||||
Setting: &apiv1.UserSetting{
|
||||
Name: "users/tag-owner/settings/TAGS",
|
||||
Value: &apiv1.UserSetting_TagsSetting_{
|
||||
TagsSetting: &apiv1.UserSetting_TagsSetting{
|
||||
Tags: map[string]*apiv1.UserSetting_TagMetadata{"bug": {}},
|
||||
},
|
||||
},
|
||||
},
|
||||
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"tags"}},
|
||||
})
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "permission denied")
|
||||
}
|
||||
|
||||
@@ -37,6 +37,29 @@ func validatePassword(password string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateUserTagsSetting(setting *v1pb.UserSetting_TagsSetting) error {
|
||||
if setting == nil {
|
||||
return errors.New("tags setting is required")
|
||||
}
|
||||
for tag, metadata := range setting.Tags {
|
||||
if strings.TrimSpace(tag) == "" {
|
||||
return errors.New("tag key cannot be empty")
|
||||
}
|
||||
if _, err := regexp.Compile(tag); err != nil {
|
||||
return errors.Wrapf(err, "tag key %q is not a valid regex pattern", tag)
|
||||
}
|
||||
if metadata == nil {
|
||||
return errors.Errorf("tag metadata is required for %q", tag)
|
||||
}
|
||||
if metadata.GetBackgroundColor() != nil {
|
||||
if err := validateInstanceColor(metadata.GetBackgroundColor()); err != nil {
|
||||
return errors.Wrapf(err, "background_color for %q", tag)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *APIV1Service) ListUsers(ctx context.Context, request *v1pb.ListUsersRequest) (*v1pb.ListUsersResponse, error) {
|
||||
currentUser, err := s.fetchCurrentUser(ctx)
|
||||
if err != nil {
|
||||
@@ -626,6 +649,33 @@ func (s *APIV1Service) UpdateUserSetting(ctx context.Context, request *v1pb.Upda
|
||||
GeneralSetting: updatedGeneral,
|
||||
},
|
||||
}
|
||||
case storepb.UserSetting_TAGS:
|
||||
var shouldUpdateTags bool
|
||||
for _, field := range request.UpdateMask.Paths {
|
||||
switch field {
|
||||
case "tags":
|
||||
shouldUpdateTags = true
|
||||
default:
|
||||
return nil, status.Errorf(codes.InvalidArgument, "unsupported update mask path for tags setting: %s", field)
|
||||
}
|
||||
}
|
||||
if !shouldUpdateTags {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "update mask must include tags")
|
||||
}
|
||||
|
||||
incomingTags := request.Setting.GetTagsSetting()
|
||||
if incomingTags == nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "tags setting is required")
|
||||
}
|
||||
if err := validateUserTagsSetting(incomingTags); err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "invalid user tags setting: %v", err)
|
||||
}
|
||||
updatedSetting = &v1pb.UserSetting{
|
||||
Name: request.Setting.Name,
|
||||
Value: &v1pb.UserSetting_TagsSetting_{
|
||||
TagsSetting: incomingTags,
|
||||
},
|
||||
}
|
||||
default:
|
||||
return nil, status.Errorf(codes.InvalidArgument, "setting type %s should not be updated via UpdateUserSetting", storeKey.String())
|
||||
}
|
||||
@@ -1286,6 +1336,8 @@ func convertSettingKeyToStore(key string) (storepb.UserSetting_Key, error) {
|
||||
return storepb.UserSetting_GENERAL, nil
|
||||
case v1pb.UserSetting_Key_name[int32(v1pb.UserSetting_WEBHOOKS)]:
|
||||
return storepb.UserSetting_WEBHOOKS, nil
|
||||
case v1pb.UserSetting_Key_name[int32(v1pb.UserSetting_TAGS)]:
|
||||
return storepb.UserSetting_TAGS, nil
|
||||
default:
|
||||
return storepb.UserSetting_KEY_UNSPECIFIED, errors.Errorf("unknown setting key: %s", key)
|
||||
}
|
||||
@@ -1300,11 +1352,49 @@ func convertSettingKeyFromStore(key storepb.UserSetting_Key) string {
|
||||
return "SHORTCUTS" // Not defined in API proto
|
||||
case storepb.UserSetting_WEBHOOKS:
|
||||
return v1pb.UserSetting_Key_name[int32(v1pb.UserSetting_WEBHOOKS)]
|
||||
case storepb.UserSetting_TAGS:
|
||||
return v1pb.UserSetting_Key_name[int32(v1pb.UserSetting_TAGS)]
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
func convertUserTagsSettingFromStore(setting *storepb.TagsUserSetting) *v1pb.UserSetting_TagsSetting {
|
||||
if setting == nil {
|
||||
return &v1pb.UserSetting_TagsSetting{Tags: map[string]*v1pb.UserSetting_TagMetadata{}}
|
||||
}
|
||||
tags := make(map[string]*v1pb.UserSetting_TagMetadata, len(setting.Tags))
|
||||
for tag, metadata := range setting.Tags {
|
||||
if metadata == nil {
|
||||
tags[tag] = &v1pb.UserSetting_TagMetadata{}
|
||||
continue
|
||||
}
|
||||
tags[tag] = &v1pb.UserSetting_TagMetadata{
|
||||
BackgroundColor: metadata.GetBackgroundColor(),
|
||||
BlurContent: metadata.GetBlurContent(),
|
||||
}
|
||||
}
|
||||
return &v1pb.UserSetting_TagsSetting{Tags: tags}
|
||||
}
|
||||
|
||||
func convertUserTagsSettingToStore(setting *v1pb.UserSetting_TagsSetting) *storepb.TagsUserSetting {
|
||||
if setting == nil {
|
||||
return &storepb.TagsUserSetting{Tags: map[string]*storepb.UserTagMetadata{}}
|
||||
}
|
||||
tags := make(map[string]*storepb.UserTagMetadata, len(setting.Tags))
|
||||
for tag, metadata := range setting.Tags {
|
||||
if metadata == nil {
|
||||
tags[tag] = &storepb.UserTagMetadata{}
|
||||
continue
|
||||
}
|
||||
tags[tag] = &storepb.UserTagMetadata{
|
||||
BackgroundColor: metadata.GetBackgroundColor(),
|
||||
BlurContent: metadata.GetBlurContent(),
|
||||
}
|
||||
}
|
||||
return &storepb.TagsUserSetting{Tags: tags}
|
||||
}
|
||||
|
||||
// convertUserSettingFromStore converts store UserSetting to API UserSetting.
|
||||
func convertUserSettingFromStore(storeSetting *storepb.UserSetting, user *store.User, key storepb.UserSetting_Key) *v1pb.UserSetting {
|
||||
if storeSetting == nil {
|
||||
@@ -1325,6 +1415,10 @@ func convertUserSettingFromStore(storeSetting *storepb.UserSetting, user *store.
|
||||
Webhooks: []*v1pb.UserWebhook{},
|
||||
},
|
||||
}
|
||||
case storepb.UserSetting_TAGS:
|
||||
setting.Value = &v1pb.UserSetting_TagsSetting_{
|
||||
TagsSetting: &v1pb.UserSetting_TagsSetting{Tags: map[string]*v1pb.UserSetting_TagMetadata{}},
|
||||
}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
@@ -1370,6 +1464,10 @@ func convertUserSettingFromStore(storeSetting *storepb.UserSetting, user *store.
|
||||
Webhooks: apiWebhooks,
|
||||
},
|
||||
}
|
||||
case storepb.UserSetting_TAGS:
|
||||
setting.Value = &v1pb.UserSetting_TagsSetting_{
|
||||
TagsSetting: convertUserTagsSettingFromStore(storeSetting.GetTags()),
|
||||
}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
@@ -1416,6 +1514,14 @@ func convertUserSettingToStore(apiSetting *v1pb.UserSetting, userID int32, key s
|
||||
} else {
|
||||
return nil, errors.Errorf("webhooks setting is required")
|
||||
}
|
||||
case storepb.UserSetting_TAGS:
|
||||
if tags := apiSetting.GetTagsSetting(); tags != nil {
|
||||
storeSetting.Value = &storepb.UserSetting_Tags{
|
||||
Tags: convertUserTagsSettingToStore(tags),
|
||||
}
|
||||
} else {
|
||||
return nil, errors.Errorf("tags setting is required")
|
||||
}
|
||||
default:
|
||||
return nil, errors.Errorf("unsupported setting key: %v", key)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
INSERT IGNORE INTO `user_setting` (`user_id`, `key`, `value`)
|
||||
SELECT
|
||||
`user`.`id`,
|
||||
'TAGS',
|
||||
`system_setting`.`value`
|
||||
FROM `user`
|
||||
JOIN `system_setting` ON `system_setting`.`name` = 'TAGS'
|
||||
WHERE `system_setting`.`value` IS NOT NULL
|
||||
AND `system_setting`.`value` != ''
|
||||
AND `system_setting`.`value` != '{}';
|
||||
@@ -0,0 +1,11 @@
|
||||
INSERT INTO user_setting (user_id, key, value)
|
||||
SELECT
|
||||
"user".id,
|
||||
'TAGS',
|
||||
system_setting.value
|
||||
FROM "user"
|
||||
JOIN system_setting ON system_setting.name = 'TAGS'
|
||||
WHERE system_setting.value IS NOT NULL
|
||||
AND system_setting.value != ''
|
||||
AND system_setting.value != '{}'
|
||||
ON CONFLICT (user_id, key) DO NOTHING;
|
||||
@@ -0,0 +1,10 @@
|
||||
INSERT OR IGNORE INTO user_setting (user_id, key, value)
|
||||
SELECT
|
||||
user.id,
|
||||
'TAGS',
|
||||
system_setting.value
|
||||
FROM user
|
||||
JOIN system_setting ON system_setting.name = 'TAGS'
|
||||
WHERE system_setting.value IS NOT NULL
|
||||
AND system_setting.value != ''
|
||||
AND system_setting.value != '{}';
|
||||
@@ -2,12 +2,16 @@ package test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
colorpb "google.golang.org/genproto/googleapis/type/color"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
|
||||
storepb "github.com/usememos/memos/proto/gen/store"
|
||||
"github.com/usememos/memos/store"
|
||||
)
|
||||
|
||||
@@ -110,6 +114,100 @@ func TestMigrationMultipleReRuns(t *testing.T) {
|
||||
require.Equal(t, initialVersion, finalVersion, "version should remain unchanged after multiple re-runs")
|
||||
}
|
||||
|
||||
// TestMigrationCopiesInstanceTagsToUserSettings verifies instance tag metadata is copied into user settings.
|
||||
func TestMigrationCopiesInstanceTagsToUserSettings(t *testing.T) {
|
||||
if getDriverFromEnv() != "sqlite" {
|
||||
t.Skip("skipping focused migration fixture for non-sqlite driver")
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
dsn := fmt.Sprintf("%s/memos_tag_migration.db", t.TempDir())
|
||||
|
||||
db, err := sql.Open("sqlite", dsn)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = db.ExecContext(ctx, `
|
||||
CREATE TABLE system_setting (
|
||||
name TEXT NOT NULL,
|
||||
value TEXT NOT NULL,
|
||||
description TEXT NOT NULL DEFAULT '',
|
||||
UNIQUE(name)
|
||||
);
|
||||
CREATE TABLE user (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
username TEXT NOT NULL UNIQUE,
|
||||
role TEXT NOT NULL DEFAULT 'USER'
|
||||
);
|
||||
CREATE TABLE user_setting (
|
||||
user_id INTEGER NOT NULL,
|
||||
key TEXT NOT NULL,
|
||||
value TEXT NOT NULL,
|
||||
UNIQUE(user_id, key)
|
||||
);
|
||||
CREATE TABLE memo (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT
|
||||
);
|
||||
`)
|
||||
require.NoError(t, err)
|
||||
|
||||
basicSettingBytes, err := protojson.Marshal(&storepb.InstanceBasicSetting{SchemaVersion: "0.29.1"})
|
||||
require.NoError(t, err)
|
||||
tagsSettingBytes, err := protojson.Marshal(&storepb.InstanceTagsSetting{
|
||||
Tags: map[string]*storepb.InstanceTagMetadata{
|
||||
"bug": {
|
||||
BackgroundColor: &colorpb.Color{Red: 0.9, Green: 0.1, Blue: 0.1},
|
||||
},
|
||||
"private/.*": {
|
||||
BlurContent: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
existingUserTagsBytes, err := protojson.Marshal(&storepb.TagsUserSetting{
|
||||
Tags: map[string]*storepb.UserTagMetadata{
|
||||
"existing": {
|
||||
BlurContent: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = db.ExecContext(ctx, "INSERT INTO system_setting (name, value) VALUES ('BASIC', ?), ('TAGS', ?)", string(basicSettingBytes), string(tagsSettingBytes))
|
||||
require.NoError(t, err)
|
||||
_, err = db.ExecContext(ctx, "INSERT INTO user (id, username, role) VALUES (1, 'tag-owner', 'USER'), (2, 'keeps-existing', 'USER')")
|
||||
require.NoError(t, err)
|
||||
_, err = db.ExecContext(ctx, "INSERT INTO user_setting (user_id, key, value) VALUES (2, 'TAGS', ?)", string(existingUserTagsBytes))
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, db.Close())
|
||||
|
||||
ts := NewTestingStoreWithDSN(ctx, t, "sqlite", dsn)
|
||||
require.NoError(t, ts.Migrate(ctx))
|
||||
defer ts.Close()
|
||||
|
||||
copiedUserID := int32(1)
|
||||
copied, err := ts.GetUserSetting(ctx, &store.FindUserSetting{
|
||||
UserID: &copiedUserID,
|
||||
Key: storepb.UserSetting_TAGS,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, copied.GetTags().GetTags(), "bug")
|
||||
bugMetadata := copied.GetTags().GetTags()["bug"]
|
||||
require.NotNil(t, bugMetadata.GetBackgroundColor())
|
||||
require.InDelta(t, 0.9, bugMetadata.GetBackgroundColor().GetRed(), 1e-6)
|
||||
require.InDelta(t, 0.1, bugMetadata.GetBackgroundColor().GetGreen(), 1e-6)
|
||||
require.InDelta(t, 0.1, bugMetadata.GetBackgroundColor().GetBlue(), 1e-6)
|
||||
require.True(t, copied.GetTags().GetTags()["private/.*"].GetBlurContent())
|
||||
|
||||
existingUserID := int32(2)
|
||||
existing, err := ts.GetUserSetting(ctx, &store.FindUserSetting{
|
||||
UserID: &existingUserID,
|
||||
Key: storepb.UserSetting_TAGS,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, existing.GetTags().GetTags(), "existing")
|
||||
require.NotContains(t, existing.GetTags().GetTags(), "bug")
|
||||
}
|
||||
|
||||
// TestMigrationFromStableVersion verifies that upgrading from a stable Memos version
|
||||
// to the current version works correctly. This is the critical upgrade path test.
|
||||
//
|
||||
|
||||
@@ -435,6 +435,12 @@ func convertUserSettingFromRaw(raw *UserSetting) (*storepb.UserSetting, error) {
|
||||
return nil, err
|
||||
}
|
||||
userSetting.Value = &storepb.UserSetting_General{General: generalUserSetting}
|
||||
case storepb.UserSetting_TAGS:
|
||||
tagsUserSetting := &storepb.TagsUserSetting{}
|
||||
if err := protojsonUnmarshaler.Unmarshal([]byte(raw.Value), tagsUserSetting); err != nil {
|
||||
return nil, errors.Wrap(err, "unmarshal tags user setting")
|
||||
}
|
||||
userSetting.Value = &storepb.UserSetting_Tags{Tags: tagsUserSetting}
|
||||
case storepb.UserSetting_REFRESH_TOKENS:
|
||||
refreshTokensUserSetting := &storepb.RefreshTokensUserSetting{}
|
||||
if err := protojsonUnmarshaler.Unmarshal([]byte(raw.Value), refreshTokensUserSetting); err != nil {
|
||||
@@ -480,6 +486,13 @@ func convertUserSettingToRaw(userSetting *storepb.UserSetting) (*UserSetting, er
|
||||
return nil, err
|
||||
}
|
||||
raw.Value = string(value)
|
||||
case storepb.UserSetting_TAGS:
|
||||
tagsUserSetting := userSetting.GetTags()
|
||||
value, err := protojson.Marshal(tagsUserSetting)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "marshal tags user setting")
|
||||
}
|
||||
raw.Value = string(value)
|
||||
case storepb.UserSetting_REFRESH_TOKENS:
|
||||
refreshTokensUserSetting := userSetting.GetRefreshTokens()
|
||||
value, err := protojson.Marshal(refreshTokensUserSetting)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Element } from "hast";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import { useInstance } from "@/contexts/InstanceContext";
|
||||
import { useAuth } from "@/contexts/AuthContext";
|
||||
import { type MemoFilter, stringifyFilters, useMemoFilterContext } from "@/contexts/MemoFilterContext";
|
||||
import useNavigateTo from "@/hooks/useNavigateTo";
|
||||
import { colorToHex } from "@/lib/color";
|
||||
@@ -20,14 +20,15 @@ export const Tag: React.FC<TagProps> = ({ "data-tag": dataTag, children, classNa
|
||||
const location = useLocation();
|
||||
const navigateTo = useNavigateTo();
|
||||
const { getFiltersByFactor, removeFilter, addFilter } = useMemoFilterContext();
|
||||
const { tagsSetting } = useInstance();
|
||||
const { userTagsSetting } = useAuth();
|
||||
|
||||
const tag = dataTag || "";
|
||||
|
||||
// Custom color from admin tag metadata. Dynamic hex values must use inline styles
|
||||
// Custom color from user tag metadata. Dynamic hex values must use inline styles
|
||||
// because Tailwind can't scan dynamically constructed class names.
|
||||
// Text uses a darkened variant (40% color + black) for contrast on light backgrounds.
|
||||
const bgHex = colorToHex(findTagMetadata(tag, tagsSetting)?.backgroundColor);
|
||||
const metadata = userTagsSetting ? findTagMetadata(tag, userTagsSetting) : undefined;
|
||||
const bgHex = colorToHex(metadata?.backgroundColor);
|
||||
const tagStyle: React.CSSProperties | undefined = bgHex
|
||||
? {
|
||||
borderColor: bgHex,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import { useInstance } from "@/contexts/InstanceContext";
|
||||
import { useAuth } from "@/contexts/AuthContext";
|
||||
import useCurrentUser from "@/hooks/useCurrentUser";
|
||||
import { useUser } from "@/hooks/useUserQueries";
|
||||
import { findTagMetadata } from "@/lib/tag";
|
||||
@@ -23,15 +23,15 @@ const MemoView: React.FC<MemoViewProps> = (props: MemoViewProps) => {
|
||||
const [cardWidth, setCardWidth] = useState(0);
|
||||
|
||||
const currentUser = useCurrentUser();
|
||||
const { tagsSetting } = useInstance();
|
||||
const { userTagsSetting } = useAuth();
|
||||
const creator = useUser(memoData.creator).data;
|
||||
const isArchived = memoData.state === State.ARCHIVED;
|
||||
const readonly = memoData.creator !== currentUser?.name && !isSuperUser(currentUser);
|
||||
const parentPage = parentPageProp || "/";
|
||||
|
||||
// Blur content when any tag has blur_content enabled in the instance tag settings.
|
||||
// Blur content when any tag has blur_content enabled in the current user's tag settings.
|
||||
const [showBlurredContent, setShowBlurredContent] = useState(false);
|
||||
const blurred = memoData.tags?.some((tag) => findTagMetadata(tag, tagsSetting)?.blurContent) ?? false;
|
||||
const blurred = memoData.tags?.some((tag) => userTagsSetting && findTagMetadata(tag, userTagsSetting)?.blurContent) ?? false;
|
||||
const toggleBlurVisibility = useCallback(() => setShowBlurredContent((prev) => !prev), []);
|
||||
|
||||
const { previewState, openPreview, setPreviewOpen } = useImagePreview();
|
||||
|
||||
@@ -7,23 +7,23 @@ import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { useInstance } from "@/contexts/InstanceContext";
|
||||
import { useTagCounts } from "@/hooks/useUserQueries";
|
||||
import { useAuth } from "@/contexts/AuthContext";
|
||||
import { buildUserSettingName } from "@/helpers/resource-names";
|
||||
import { useTagCounts, useUpdateUserSetting } from "@/hooks/useUserQueries";
|
||||
import { colorToHex } from "@/lib/color";
|
||||
import { isValidTagPattern } from "@/lib/tag";
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
InstanceSetting_Key,
|
||||
InstanceSetting_TagMetadataSchema,
|
||||
InstanceSetting_TagsSettingSchema,
|
||||
InstanceSettingSchema,
|
||||
} from "@/types/proto/api/v1/instance_service_pb";
|
||||
UserSetting_Key,
|
||||
UserSetting_TagMetadataSchema,
|
||||
UserSetting_TagsSettingSchema,
|
||||
UserSettingSchema,
|
||||
} from "@/types/proto/api/v1/user_service_pb";
|
||||
import { ColorSchema } from "@/types/proto/google/type/color_pb";
|
||||
import { useTranslate } from "@/utils/i18n";
|
||||
import SettingGroup from "./SettingGroup";
|
||||
import { SettingList, SettingPanel } from "./SettingList";
|
||||
import SettingSection from "./SettingSection";
|
||||
import useInstanceSettingUpdater, { buildInstanceSettingName } from "./useInstanceSettingUpdater";
|
||||
|
||||
const DEFAULT_TAG_COLOR = "#ffffff";
|
||||
|
||||
@@ -50,9 +50,10 @@ const toLocalTagMeta = (meta: {
|
||||
|
||||
const TagsSection = () => {
|
||||
const t = useTranslate();
|
||||
const saveInstanceSetting = useInstanceSettingUpdater();
|
||||
const { tagsSetting: originalSetting } = useInstance();
|
||||
const { data: tagCounts = {} } = useTagCounts(false);
|
||||
const { currentUser, userTagsSetting, refetchSettings } = useAuth();
|
||||
const { mutateAsync: updateUserSetting } = useUpdateUserSetting();
|
||||
const { data: tagCounts = {} } = useTagCounts(true);
|
||||
const originalSetting = useMemo(() => userTagsSetting ?? create(UserSetting_TagsSettingSchema, {}), [userTagsSetting]);
|
||||
|
||||
// Local state: map of tagName → { color, blur } for editing.
|
||||
const [localTags, setLocalTags] = useState<Record<string, LocalTagMeta>>(() =>
|
||||
@@ -130,24 +131,28 @@ const TagsSection = () => {
|
||||
const tags = Object.fromEntries(
|
||||
Object.entries(localTags).map(([name, meta]) => [
|
||||
name,
|
||||
create(InstanceSetting_TagMetadataSchema, {
|
||||
create(UserSetting_TagMetadataSchema, {
|
||||
blurContent: meta.blur,
|
||||
...(meta.color ? { backgroundColor: hexToColor(meta.color) } : {}),
|
||||
}),
|
||||
]),
|
||||
);
|
||||
|
||||
await saveInstanceSetting({
|
||||
key: InstanceSetting_Key.TAGS,
|
||||
setting: create(InstanceSettingSchema, {
|
||||
name: buildInstanceSettingName(InstanceSetting_Key.TAGS),
|
||||
if (!currentUser) {
|
||||
return;
|
||||
}
|
||||
|
||||
await updateUserSetting({
|
||||
setting: create(UserSettingSchema, {
|
||||
name: buildUserSettingName(currentUser.name, UserSetting_Key.TAGS),
|
||||
value: {
|
||||
case: "tagsSetting",
|
||||
value: create(InstanceSetting_TagsSettingSchema, { tags }),
|
||||
value: create(UserSetting_TagsSettingSchema, { tags }),
|
||||
},
|
||||
}),
|
||||
errorContext: "Update tags setting",
|
||||
updateMask: ["tags"],
|
||||
});
|
||||
await refetchSettings();
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -273,7 +278,7 @@ const TagsSection = () => {
|
||||
</SettingGroup>
|
||||
|
||||
<div className="w-full flex justify-end">
|
||||
<Button disabled={!hasChanges} onClick={handleSave}>
|
||||
<Button disabled={!hasChanges || !currentUser} onClick={handleSave}>
|
||||
{t("common.save")}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -98,11 +98,10 @@ export const SETTINGS_SECTIONS: SettingSectionDefinition[] = [
|
||||
},
|
||||
{
|
||||
key: "tags",
|
||||
scope: "admin",
|
||||
scope: "basic",
|
||||
labelKey: "setting.tags.label",
|
||||
icon: TagsIcon,
|
||||
component: TagsSection,
|
||||
preloadSettingKeys: [InstanceSetting_Key.TAGS],
|
||||
},
|
||||
{
|
||||
key: "storage",
|
||||
|
||||
@@ -4,12 +4,18 @@ import { clearAccessToken, getAccessToken } from "@/auth-state";
|
||||
import { authServiceClient, refreshAccessToken, shortcutServiceClient, userServiceClient } from "@/connect";
|
||||
import { userKeys } from "@/hooks/useUserQueries";
|
||||
import type { Shortcut } from "@/types/proto/api/v1/shortcut_service_pb";
|
||||
import type { User, UserSetting_GeneralSetting, UserSetting_WebhooksSetting } from "@/types/proto/api/v1/user_service_pb";
|
||||
import type {
|
||||
User,
|
||||
UserSetting_GeneralSetting,
|
||||
UserSetting_TagsSetting,
|
||||
UserSetting_WebhooksSetting,
|
||||
} from "@/types/proto/api/v1/user_service_pb";
|
||||
|
||||
interface AuthState {
|
||||
currentUser: User | undefined;
|
||||
userGeneralSetting: UserSetting_GeneralSetting | undefined;
|
||||
userWebhooksSetting: UserSetting_WebhooksSetting | undefined;
|
||||
userTagsSetting: UserSetting_TagsSetting | undefined;
|
||||
shortcuts: Shortcut[];
|
||||
isInitialized: boolean;
|
||||
isLoading: boolean;
|
||||
@@ -30,6 +36,7 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
||||
currentUser: undefined,
|
||||
userGeneralSetting: undefined,
|
||||
userWebhooksSetting: undefined,
|
||||
userTagsSetting: undefined,
|
||||
shortcuts: [],
|
||||
isInitialized: false,
|
||||
isLoading: true,
|
||||
@@ -43,10 +50,12 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
||||
|
||||
const generalSetting = settings.find((s) => s.value.case === "generalSetting");
|
||||
const webhooksSetting = settings.find((s) => s.value.case === "webhooksSetting");
|
||||
const tagsSetting = settings.find((s) => s.value.case === "tagsSetting");
|
||||
|
||||
return {
|
||||
userGeneralSetting: generalSetting?.value.case === "generalSetting" ? generalSetting.value.value : undefined,
|
||||
userWebhooksSetting: webhooksSetting?.value.case === "webhooksSetting" ? webhooksSetting.value.value : undefined,
|
||||
userTagsSetting: tagsSetting?.value.case === "tagsSetting" ? tagsSetting.value.value : undefined,
|
||||
shortcuts,
|
||||
};
|
||||
}, []);
|
||||
@@ -73,6 +82,7 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
||||
currentUser: undefined,
|
||||
userGeneralSetting: undefined,
|
||||
userWebhooksSetting: undefined,
|
||||
userTagsSetting: undefined,
|
||||
shortcuts: [],
|
||||
isInitialized: true,
|
||||
isLoading: false,
|
||||
@@ -89,6 +99,7 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
||||
currentUser: undefined,
|
||||
userGeneralSetting: undefined,
|
||||
userWebhooksSetting: undefined,
|
||||
userTagsSetting: undefined,
|
||||
shortcuts: [],
|
||||
isInitialized: true,
|
||||
isLoading: false,
|
||||
@@ -115,6 +126,7 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
||||
currentUser: undefined,
|
||||
userGeneralSetting: undefined,
|
||||
userWebhooksSetting: undefined,
|
||||
userTagsSetting: undefined,
|
||||
shortcuts: [],
|
||||
isInitialized: true,
|
||||
isLoading: false,
|
||||
@@ -133,6 +145,7 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
||||
currentUser: undefined,
|
||||
userGeneralSetting: undefined,
|
||||
userWebhooksSetting: undefined,
|
||||
userTagsSetting: undefined,
|
||||
shortcuts: [],
|
||||
isInitialized: true,
|
||||
isLoading: false,
|
||||
|
||||
@@ -16,8 +16,6 @@ import {
|
||||
InstanceSetting_NotificationSettingSchema,
|
||||
InstanceSetting_StorageSetting,
|
||||
InstanceSetting_StorageSettingSchema,
|
||||
InstanceSetting_TagsSetting,
|
||||
InstanceSetting_TagsSettingSchema,
|
||||
} from "@/types/proto/api/v1/instance_service_pb";
|
||||
|
||||
const instanceSettingNamePrefix = "instance/settings/";
|
||||
@@ -42,7 +40,6 @@ interface InstanceContextValue extends InstanceState {
|
||||
generalSetting: InstanceSetting_GeneralSetting;
|
||||
memoRelatedSetting: InstanceSetting_MemoRelatedSetting;
|
||||
storageSetting: InstanceSetting_StorageSetting;
|
||||
tagsSetting: InstanceSetting_TagsSetting;
|
||||
notificationSetting: InstanceSetting_NotificationSetting;
|
||||
aiSetting: InstanceSetting_AISetting;
|
||||
initialize: () => Promise<void>;
|
||||
@@ -89,14 +86,6 @@ export function InstanceProvider({ children }: { children: ReactNode }) {
|
||||
return create(InstanceSetting_StorageSettingSchema, {});
|
||||
}, [state.settings]);
|
||||
|
||||
const tagsSetting = useMemo((): InstanceSetting_TagsSetting => {
|
||||
const setting = state.settings.find((s) => s.name === `${instanceSettingNamePrefix}TAGS`);
|
||||
if (setting?.value.case === "tagsSetting") {
|
||||
return setting.value.value;
|
||||
}
|
||||
return create(InstanceSetting_TagsSettingSchema, {});
|
||||
}, [state.settings]);
|
||||
|
||||
const notificationSetting = useMemo((): InstanceSetting_NotificationSetting => {
|
||||
const setting = state.settings.find((s) => s.name === `${instanceSettingNamePrefix}NOTIFICATION`);
|
||||
if (setting?.value.case === "notificationSetting") {
|
||||
@@ -119,11 +108,7 @@ export function InstanceProvider({ children }: { children: ReactNode }) {
|
||||
const profile = await instanceServiceClient.getInstanceProfile({});
|
||||
|
||||
const settingsResponse = await instanceServiceClient.batchGetInstanceSettings({
|
||||
names: [
|
||||
buildInstanceSettingName(InstanceSetting_Key.GENERAL),
|
||||
buildInstanceSettingName(InstanceSetting_Key.MEMO_RELATED),
|
||||
buildInstanceSettingName(InstanceSetting_Key.TAGS),
|
||||
],
|
||||
names: [buildInstanceSettingName(InstanceSetting_Key.GENERAL), buildInstanceSettingName(InstanceSetting_Key.MEMO_RELATED)],
|
||||
});
|
||||
for (const setting of settingsResponse.settings) {
|
||||
fetchedSettingsRef.current.add(setting.name);
|
||||
@@ -204,7 +189,6 @@ export function InstanceProvider({ children }: { children: ReactNode }) {
|
||||
generalSetting,
|
||||
memoRelatedSetting,
|
||||
storageSetting,
|
||||
tagsSetting,
|
||||
notificationSetting,
|
||||
aiSetting,
|
||||
initialize,
|
||||
@@ -217,7 +201,6 @@ export function InstanceProvider({ children }: { children: ReactNode }) {
|
||||
generalSetting,
|
||||
memoRelatedSetting,
|
||||
storageSetting,
|
||||
tagsSetting,
|
||||
notificationSetting,
|
||||
aiSetting,
|
||||
initialize,
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
import type { InstanceSetting_TagMetadata, InstanceSetting_TagsSetting } from "@/types/proto/api/v1/instance_service_pb";
|
||||
import type { UserSetting_TagMetadata, UserSetting_TagsSetting } from "@/types/proto/api/v1/user_service_pb";
|
||||
|
||||
// Cache compiled regexes to avoid re-compiling on every tag render.
|
||||
const compiledPatternCache = new Map<string, RegExp | null>();
|
||||
@@ -25,7 +25,7 @@ const getCompiledPattern = (pattern: string): RegExp | null => {
|
||||
* 1. Exact key match (O(1) fast path, backward-compatible).
|
||||
* 2. Iterate all keys and test as anchored regex — first match wins.
|
||||
*/
|
||||
export const findTagMetadata = (tag: string, tagsSetting: InstanceSetting_TagsSetting): InstanceSetting_TagMetadata | undefined => {
|
||||
export const findTagMetadata = (tag: string, tagsSetting: UserSetting_TagsSetting): UserSetting_TagMetadata | undefined => {
|
||||
// Fast path: exact match.
|
||||
if (tagsSetting.tags[tag]) {
|
||||
return tagsSetting.tags[tag];
|
||||
|
||||
@@ -444,6 +444,8 @@ export const InstanceSetting_TagMetadataSchema: GenMessage<InstanceSetting_TagMe
|
||||
|
||||
/**
|
||||
* Tag metadata configuration.
|
||||
* Active tag metadata is stored in per-user tag settings.
|
||||
* This message remains for backward compatibility with existing clients and migrations.
|
||||
*
|
||||
* @generated from message memos.api.v1.InstanceSetting.TagsSetting
|
||||
*/
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user