diff --git a/docs/getting-started/subscription.md b/docs/getting-started/subscription.md
index 3af42c4..fb7905d 100644
--- a/docs/getting-started/subscription.md
+++ b/docs/getting-started/subscription.md
@@ -6,7 +6,7 @@ Slash is open source links shortener and sharing platform. Our source code is av
### Free
-The Free plan is designed for personal use not for commercial use. It allows you to create unlimited shortcuts and invite up to 5 members.
+The Free plan is designed for personal use not for commercial use. It allows you to create up to 100 shortcuts and invite up to 5 members.
### Pro
diff --git a/frontend/web/src/pages/SubscriptionSetting.tsx b/frontend/web/src/pages/SubscriptionSetting.tsx
index 1024048..a1fc2a9 100644
--- a/frontend/web/src/pages/SubscriptionSetting.tsx
+++ b/frontend/web/src/pages/SubscriptionSetting.tsx
@@ -111,11 +111,7 @@ const SubscriptionSetting: React.FC = () => {
-
- Unlimited shortcuts
-
- -
-
- Basic analytics
+ Full API access
-
@@ -123,7 +119,15 @@ const SubscriptionSetting: React.FC = () => {
-
- Full API access
+ Basic analytics
+
+ -
+
+ Up to 100 shortcuts
+
+ -
+
+ Up to 5 collections
-
@@ -149,7 +153,11 @@ const SubscriptionSetting: React.FC = () => {
-
- Custom branding
+ Unlimited shortcuts
+
+ -
+
+ Unlimited collections
-
@@ -180,13 +188,17 @@ const SubscriptionSetting: React.FC = () => {
Everything in Pro, and
+ -
+
+ Custom branding
+
-
Single Sign-On(SSO)
-
-
- Dedicated support
+
+ Email support
-
diff --git a/proto/api/v1/subscription_service.proto b/proto/api/v1/subscription_service.proto
index 00de2f3..97e3745 100644
--- a/proto/api/v1/subscription_service.proto
+++ b/proto/api/v1/subscription_service.proto
@@ -33,9 +33,13 @@ message Subscription {
google.protobuf.Timestamp expires_time = 3 [(google.api.field_behavior) = OUTPUT_ONLY];
- int32 seats = 4 [(google.api.field_behavior) = OUTPUT_ONLY];
+ repeated string features = 4 [(google.api.field_behavior) = OUTPUT_ONLY];
- repeated string features = 5 [(google.api.field_behavior) = OUTPUT_ONLY];
+ int32 seats = 5 [(google.api.field_behavior) = OUTPUT_ONLY];
+
+ int32 shortcuts_limit = 6 [(google.api.field_behavior) = OUTPUT_ONLY];
+
+ int32 collections_limit = 7 [(google.api.field_behavior) = OUTPUT_ONLY];
}
enum PlanType {
diff --git a/proto/gen/api/v1/README.md b/proto/gen/api/v1/README.md
index 2804e4a..f67261a 100644
--- a/proto/gen/api/v1/README.md
+++ b/proto/gen/api/v1/README.md
@@ -1138,8 +1138,10 @@
| plan | [PlanType](#slash-api-v1-PlanType) | | |
| started_time | [google.protobuf.Timestamp](#google-protobuf-Timestamp) | | |
| expires_time | [google.protobuf.Timestamp](#google-protobuf-Timestamp) | | |
-| seats | [int32](#int32) | | |
| features | [string](#string) | repeated | |
+| seats | [int32](#int32) | | |
+| shortcuts_limit | [int32](#int32) | | |
+| collections_limit | [int32](#int32) | | |
diff --git a/proto/gen/api/v1/subscription_service.pb.go b/proto/gen/api/v1/subscription_service.pb.go
index 7a99a06..4c84adb 100644
--- a/proto/gen/api/v1/subscription_service.pb.go
+++ b/proto/gen/api/v1/subscription_service.pb.go
@@ -79,11 +79,13 @@ type Subscription struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
- Plan PlanType `protobuf:"varint,1,opt,name=plan,proto3,enum=slash.api.v1.PlanType" json:"plan,omitempty"`
- StartedTime *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=started_time,json=startedTime,proto3" json:"started_time,omitempty"`
- ExpiresTime *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=expires_time,json=expiresTime,proto3" json:"expires_time,omitempty"`
- Seats int32 `protobuf:"varint,4,opt,name=seats,proto3" json:"seats,omitempty"`
- Features []string `protobuf:"bytes,5,rep,name=features,proto3" json:"features,omitempty"`
+ Plan PlanType `protobuf:"varint,1,opt,name=plan,proto3,enum=slash.api.v1.PlanType" json:"plan,omitempty"`
+ StartedTime *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=started_time,json=startedTime,proto3" json:"started_time,omitempty"`
+ ExpiresTime *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=expires_time,json=expiresTime,proto3" json:"expires_time,omitempty"`
+ Features []string `protobuf:"bytes,4,rep,name=features,proto3" json:"features,omitempty"`
+ Seats int32 `protobuf:"varint,5,opt,name=seats,proto3" json:"seats,omitempty"`
+ ShortcutsLimit int32 `protobuf:"varint,6,opt,name=shortcuts_limit,json=shortcutsLimit,proto3" json:"shortcuts_limit,omitempty"`
+ CollectionsLimit int32 `protobuf:"varint,7,opt,name=collections_limit,json=collectionsLimit,proto3" json:"collections_limit,omitempty"`
}
func (x *Subscription) Reset() {
@@ -139,6 +141,13 @@ func (x *Subscription) GetExpiresTime() *timestamppb.Timestamp {
return nil
}
+func (x *Subscription) GetFeatures() []string {
+ if x != nil {
+ return x.Features
+ }
+ return nil
+}
+
func (x *Subscription) GetSeats() int32 {
if x != nil {
return x.Seats
@@ -146,11 +155,18 @@ func (x *Subscription) GetSeats() int32 {
return 0
}
-func (x *Subscription) GetFeatures() []string {
+func (x *Subscription) GetShortcutsLimit() int32 {
if x != nil {
- return x.Features
+ return x.ShortcutsLimit
}
- return nil
+ return 0
+}
+
+func (x *Subscription) GetCollectionsLimit() int32 {
+ if x != nil {
+ return x.CollectionsLimit
+ }
+ return 0
}
type GetSubscriptionRequest struct {
@@ -288,7 +304,7 @@ var file_api_v1_subscription_service_proto_rawDesc = []byte{
0x64, 0x5f, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74,
- 0x6f, 0x22, 0x88, 0x02, 0x0a, 0x0c, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69,
+ 0x6f, 0x22, 0xea, 0x02, 0x0a, 0x0c, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69,
0x6f, 0x6e, 0x12, 0x30, 0x0a, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e,
0x32, 0x16, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e,
0x50, 0x6c, 0x61, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x42, 0x04, 0xe2, 0x41, 0x01, 0x03, 0x52, 0x04,
@@ -300,59 +316,65 @@ var file_api_v1_subscription_service_proto_rawDesc = []byte{
0x69, 0x72, 0x65, 0x73, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x42, 0x04, 0xe2, 0x41, 0x01,
- 0x03, 0x52, 0x0b, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1a,
- 0x0a, 0x05, 0x73, 0x65, 0x61, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x42, 0x04, 0xe2,
- 0x41, 0x01, 0x03, 0x52, 0x05, 0x73, 0x65, 0x61, 0x74, 0x73, 0x12, 0x20, 0x0a, 0x08, 0x66, 0x65,
- 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x42, 0x04, 0xe2, 0x41,
- 0x01, 0x03, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x22, 0x18, 0x0a, 0x16,
- 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52,
- 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x42, 0x0a, 0x19, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65,
- 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75,
- 0x65, 0x73, 0x74, 0x12, 0x25, 0x0a, 0x0b, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x6b,
- 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x04, 0xe2, 0x41, 0x01, 0x02, 0x52, 0x0a,
- 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x22, 0x1b, 0x0a, 0x19, 0x44, 0x65,
- 0x6c, 0x65, 0x74, 0x65, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e,
- 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2a, 0x48, 0x0a, 0x08, 0x50, 0x6c, 0x61, 0x6e, 0x54,
- 0x79, 0x70, 0x65, 0x12, 0x19, 0x0a, 0x15, 0x50, 0x4c, 0x41, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45,
- 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x08,
- 0x0a, 0x04, 0x46, 0x52, 0x45, 0x45, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x50, 0x52, 0x4f, 0x10,
- 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x45, 0x4e, 0x54, 0x45, 0x52, 0x50, 0x52, 0x49, 0x53, 0x45, 0x10,
- 0x03, 0x32, 0xf1, 0x02, 0x0a, 0x13, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69,
- 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x6d, 0x0a, 0x0f, 0x47, 0x65, 0x74,
- 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x2e, 0x73,
- 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53,
- 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65,
- 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76,
- 0x31, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x18,
- 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x75, 0x62, 0x73,
- 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x76, 0x0a, 0x12, 0x55, 0x70, 0x64, 0x61,
- 0x74, 0x65, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x27,
- 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70,
+ 0x03, 0x52, 0x0b, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x20,
+ 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09,
+ 0x42, 0x04, 0xe2, 0x41, 0x01, 0x03, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73,
+ 0x12, 0x1a, 0x0a, 0x05, 0x73, 0x65, 0x61, 0x74, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x42,
+ 0x04, 0xe2, 0x41, 0x01, 0x03, 0x52, 0x05, 0x73, 0x65, 0x61, 0x74, 0x73, 0x12, 0x2d, 0x0a, 0x0f,
+ 0x73, 0x68, 0x6f, 0x72, 0x74, 0x63, 0x75, 0x74, 0x73, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18,
+ 0x06, 0x20, 0x01, 0x28, 0x05, 0x42, 0x04, 0xe2, 0x41, 0x01, 0x03, 0x52, 0x0e, 0x73, 0x68, 0x6f,
+ 0x72, 0x74, 0x63, 0x75, 0x74, 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x31, 0x0a, 0x11, 0x63,
+ 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74,
+ 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x42, 0x04, 0xe2, 0x41, 0x01, 0x03, 0x52, 0x10, 0x63, 0x6f,
+ 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x22, 0x18,
+ 0x0a, 0x16, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f,
+ 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x42, 0x0a, 0x19, 0x55, 0x70, 0x64, 0x61,
+ 0x74, 0x65, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65,
+ 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x25, 0x0a, 0x0b, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65,
+ 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x04, 0xe2, 0x41, 0x01, 0x02,
+ 0x52, 0x0a, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x22, 0x1b, 0x0a, 0x19,
+ 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69,
+ 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2a, 0x48, 0x0a, 0x08, 0x50, 0x6c, 0x61,
+ 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x19, 0x0a, 0x15, 0x50, 0x4c, 0x41, 0x4e, 0x5f, 0x54, 0x59,
+ 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00,
+ 0x12, 0x08, 0x0a, 0x04, 0x46, 0x52, 0x45, 0x45, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x50, 0x52,
+ 0x4f, 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x45, 0x4e, 0x54, 0x45, 0x52, 0x50, 0x52, 0x49, 0x53,
+ 0x45, 0x10, 0x03, 0x32, 0xf1, 0x02, 0x0a, 0x13, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70,
+ 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x6d, 0x0a, 0x0f, 0x47,
+ 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x24,
+ 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65,
+ 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71,
+ 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x69,
+ 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e,
+ 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x75,
+ 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x76, 0x0a, 0x12, 0x55, 0x70,
0x64, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e,
- 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e,
- 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
- 0x69, 0x6f, 0x6e, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x3a, 0x01, 0x2a, 0x32, 0x10,
- 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e,
- 0x12, 0x73, 0x0a, 0x12, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72,
- 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x61,
- 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x75, 0x62, 0x73,
- 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
- 0x1a, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53,
- 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x18, 0x82, 0xd3, 0xe4,
- 0x93, 0x02, 0x12, 0x2a, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69,
- 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0xb6, 0x01, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x73, 0x6c,
- 0x61, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x42, 0x18, 0x53, 0x75, 0x62, 0x73,
- 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x50,
- 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x36, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
- 0x6f, 0x6d, 0x2f, 0x79, 0x6f, 0x75, 0x72, 0x73, 0x65, 0x6c, 0x66, 0x68, 0x6f, 0x73, 0x74, 0x65,
- 0x64, 0x2f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x65,
- 0x6e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x3b, 0x61, 0x70, 0x69, 0x76, 0x31, 0xa2, 0x02,
- 0x03, 0x53, 0x41, 0x58, 0xaa, 0x02, 0x0c, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x41, 0x70, 0x69,
- 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0c, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x5c, 0x41, 0x70, 0x69, 0x5c,
- 0x56, 0x31, 0xe2, 0x02, 0x18, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x5c, 0x41, 0x70, 0x69, 0x5c, 0x56,
- 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0e,
- 0x53, 0x6c, 0x61, 0x73, 0x68, 0x3a, 0x3a, 0x41, 0x70, 0x69, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06,
- 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+ 0x12, 0x27, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e,
+ 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69,
+ 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x73, 0x6c, 0x61, 0x73,
+ 0x68, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69,
+ 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x3a, 0x01, 0x2a,
+ 0x32, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69,
+ 0x6f, 0x6e, 0x12, 0x73, 0x0a, 0x12, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x75, 0x62, 0x73,
+ 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68,
+ 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x75,
+ 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
+ 0x74, 0x1a, 0x1a, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31,
+ 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x18, 0x82,
+ 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x2a, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x75, 0x62, 0x73, 0x63,
+ 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0xb6, 0x01, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e,
+ 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x42, 0x18, 0x53, 0x75,
+ 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
+ 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x36, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x79, 0x6f, 0x75, 0x72, 0x73, 0x65, 0x6c, 0x66, 0x68, 0x6f, 0x73,
+ 0x74, 0x65, 0x64, 0x2f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f,
+ 0x67, 0x65, 0x6e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x3b, 0x61, 0x70, 0x69, 0x76, 0x31,
+ 0xa2, 0x02, 0x03, 0x53, 0x41, 0x58, 0xaa, 0x02, 0x0c, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x41,
+ 0x70, 0x69, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0c, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x5c, 0x41, 0x70,
+ 0x69, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x18, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x5c, 0x41, 0x70, 0x69,
+ 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea,
+ 0x02, 0x0e, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x3a, 0x3a, 0x41, 0x70, 0x69, 0x3a, 0x3a, 0x56, 0x31,
+ 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
diff --git a/proto/gen/apidocs.swagger.yaml b/proto/gen/apidocs.swagger.yaml
index 9703a66..a7686a9 100644
--- a/proto/gen/apidocs.swagger.yaml
+++ b/proto/gen/apidocs.swagger.yaml
@@ -1135,15 +1135,23 @@ definitions:
type: string
format: date-time
readOnly: true
- seats:
- type: integer
- format: int32
- readOnly: true
features:
type: array
items:
type: string
readOnly: true
+ seats:
+ type: integer
+ format: int32
+ readOnly: true
+ shortcutsLimit:
+ type: integer
+ format: int32
+ readOnly: true
+ collectionsLimit:
+ type: integer
+ format: int32
+ readOnly: true
v1UpdateCollectionResponse:
type: object
properties:
diff --git a/server/route/api/v1/collection_service.go b/server/route/api/v1/collection_service.go
index acc1ab4..59eea6d 100644
--- a/server/route/api/v1/collection_service.go
+++ b/server/route/api/v1/collection_service.go
@@ -108,15 +108,14 @@ func (s *APIV1Service) CreateCollection(ctx context.Context, request *v1pb.Creat
return nil, status.Errorf(codes.InvalidArgument, "name and title are required")
}
- if !s.LicenseService.IsFeatureEnabled(license.FeatureTypeUnlimitedAccounts) {
- collections, err := s.Store.ListCollections(ctx, &store.FindCollection{
- VisibilityList: []store.Visibility{store.VisibilityWorkspace, store.VisibilityPublic},
- })
+ if !s.LicenseService.IsFeatureEnabled(license.FeatureTypeUnlimitedCollections) {
+ collections, err := s.Store.ListCollections(ctx, &store.FindCollection{})
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get collection list, err: %v", err)
}
- if len(collections) >= 5 {
- return nil, status.Errorf(codes.PermissionDenied, "Maximum number of collections reached")
+ collectionsLimit := int(s.LicenseService.GetSubscription().CollectionsLimit)
+ if len(collections) >= collectionsLimit {
+ return nil, status.Errorf(codes.PermissionDenied, "Maximum number of collections %d reached", collectionsLimit)
}
}
diff --git a/server/route/api/v1/shortcut_service.go b/server/route/api/v1/shortcut_service.go
index 578f79d..242e1f9 100644
--- a/server/route/api/v1/shortcut_service.go
+++ b/server/route/api/v1/shortcut_service.go
@@ -132,6 +132,17 @@ func (s *APIV1Service) CreateShortcut(ctx context.Context, request *v1pb.CreateS
return nil, status.Errorf(codes.InvalidArgument, "name and link are required")
}
+ if !s.LicenseService.IsFeatureEnabled(license.FeatureTypeUnlimitedShortcuts) {
+ shortcuts, err := s.Store.ListShortcuts(ctx, &store.FindShortcut{})
+ if err != nil {
+ return nil, status.Errorf(codes.Internal, "failed to get shortcut list, err: %v", err)
+ }
+ shortcutsLimit := int(s.LicenseService.GetSubscription().ShortcutsLimit)
+ if len(shortcuts) >= shortcutsLimit {
+ return nil, status.Errorf(codes.PermissionDenied, "Maximum number of shortcuts %d reached", shortcutsLimit)
+ }
+ }
+
user, err := getCurrentUser(ctx, s.Store)
if err != nil {
return nil, status.Errorf(codes.Unauthenticated, "failed to get current user: %v", err)
diff --git a/server/service/license/feature_matrix.go b/server/service/license/feature_matrix.go
index 2e7ea78..41e335e 100644
--- a/server/service/license/feature_matrix.go
+++ b/server/service/license/feature_matrix.go
@@ -18,6 +18,8 @@ const (
// FeatureTypeUnlimitedAccounts allows the user to create unlimited accounts.
FeatureTypeUnlimitedAccounts FeatureType = "ysh.slash.unlimited-accounts"
+ // FeatureTypeUnlimitedShortcuts allows the user to create unlimited shortcuts.
+ FeatureTypeUnlimitedShortcuts FeatureType = "ysh.slash.unlimited-shortcuts"
// FeatureTypeUnlimitedAccounts allows the user to create unlimited collections.
FeatureTypeUnlimitedCollections FeatureType = "ysh.slash.unlimited-collections"
@@ -36,6 +38,7 @@ var FeatureMatrix = map[FeatureType][3]bool{
FeatureTypeSSO: {false, false, false},
FeatureTypeAdvancedAnalytics: {false, false, false},
FeatureTypeUnlimitedAccounts: {false, true, false},
+ FeatureTypeUnlimitedShortcuts: {false, true, true},
FeatureTypeUnlimitedCollections: {false, true, true},
FeatureTypeCustomeBranding: {false, true, true},
}
@@ -58,6 +61,8 @@ func validateFeatureString(feature string) (FeatureType, bool) {
return FeatureTypeAdvancedAnalytics, true
case "ysh.slash.unlimited-accounts":
return FeatureTypeUnlimitedAccounts, true
+ case "ysh.slash.unlimited-shortcuts":
+ return FeatureTypeUnlimitedShortcuts, true
case "ysh.slash.unlimited-collections":
return FeatureTypeUnlimitedCollections, true
case "ysh.slash.custom-branding":
diff --git a/server/service/license/license.go b/server/service/license/license.go
index 5d33123..92a1571 100644
--- a/server/service/license/license.go
+++ b/server/service/license/license.go
@@ -30,12 +30,9 @@ type LicenseService struct {
// NewLicenseService creates a new LicenseService.
func NewLicenseService(profile *profile.Profile, store *store.Store) *LicenseService {
return &LicenseService{
- Profile: profile,
- Store: store,
- cachedSubscription: &v1pb.Subscription{
- Plan: v1pb.PlanType_FREE,
- Seats: 5,
- },
+ Profile: profile,
+ Store: store,
+ cachedSubscription: getSubscriptionForFreePlan(),
}
}
@@ -47,10 +44,7 @@ func (s *LicenseService) LoadSubscription(ctx context.Context) (*v1pb.Subscripti
return nil, errors.Wrap(err, "failed to get workspace setting")
}
- subscription := &v1pb.Subscription{
- Plan: v1pb.PlanType_FREE,
- Seats: 5,
- }
+ subscription := getSubscriptionForFreePlan()
licenseKey := ""
if workspaceSettingGeneral != nil {
licenseKey = workspaceSettingGeneral.GetGeneral().LicenseKey
@@ -183,12 +177,8 @@ func validateLicenseKey(licenseKey string) (*ValidateResult, error) {
}
if validateResponse.Valid {
result := &ValidateResult{
- Plan: v1pb.PlanType_PRO,
- Features: []FeatureType{
- FeatureTypeUnlimitedAccounts,
- FeatureTypeUnlimitedCollections,
- FeatureTypeCustomeBranding,
- },
+ Plan: v1pb.PlanType_PRO,
+ Features: getDefaultFeatures(v1pb.PlanType_PRO),
}
if validateResponse.LicenseKey.ExpiresAt != nil && *validateResponse.LicenseKey.ExpiresAt != "" {
expiresTime, err := time.Parse(time.RFC3339Nano, *validateResponse.LicenseKey.ExpiresAt)
@@ -230,3 +220,13 @@ func parseLicenseKey(licenseKey string) (*Claims, error) {
}
return claims, nil
}
+
+func getSubscriptionForFreePlan() *v1pb.Subscription {
+ return &v1pb.Subscription{
+ Plan: v1pb.PlanType_FREE,
+ Seats: 5,
+ ShortcutsLimit: 100,
+ CollectionsLimit: 5,
+ Features: []string{},
+ }
+}