feat: add notification events handling and MQTT integration

- Implemented notification event publishing with a new NotificationEventPublisher interface.
- Created a noopNotificationEventPublisher for testing purposes.
- Added functionality to publish notification created events via MQTT.
- Introduced a new stream event publisher for handling job logs and updates.
- Added database migration for popup_ads table.
- Created tests for notification events and popup ads functionality.
- Established MQTT connection and publishing helpers for event messages.
This commit is contained in:
2026-03-29 15:47:09 +00:00
parent a910e6c624
commit 863a0ea2f6
42 changed files with 4606 additions and 576 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -45,6 +45,11 @@ const (
Admin_CreateAdminAdTemplate_FullMethodName = "/stream.app.v1.Admin/CreateAdminAdTemplate"
Admin_UpdateAdminAdTemplate_FullMethodName = "/stream.app.v1.Admin/UpdateAdminAdTemplate"
Admin_DeleteAdminAdTemplate_FullMethodName = "/stream.app.v1.Admin/DeleteAdminAdTemplate"
Admin_ListAdminPopupAds_FullMethodName = "/stream.app.v1.Admin/ListAdminPopupAds"
Admin_GetAdminPopupAd_FullMethodName = "/stream.app.v1.Admin/GetAdminPopupAd"
Admin_CreateAdminPopupAd_FullMethodName = "/stream.app.v1.Admin/CreateAdminPopupAd"
Admin_UpdateAdminPopupAd_FullMethodName = "/stream.app.v1.Admin/UpdateAdminPopupAd"
Admin_DeleteAdminPopupAd_FullMethodName = "/stream.app.v1.Admin/DeleteAdminPopupAd"
Admin_ListAdminPlayerConfigs_FullMethodName = "/stream.app.v1.Admin/ListAdminPlayerConfigs"
Admin_GetAdminPlayerConfig_FullMethodName = "/stream.app.v1.Admin/GetAdminPlayerConfig"
Admin_CreateAdminPlayerConfig_FullMethodName = "/stream.app.v1.Admin/CreateAdminPlayerConfig"
@@ -91,6 +96,11 @@ type AdminClient interface {
CreateAdminAdTemplate(ctx context.Context, in *CreateAdminAdTemplateRequest, opts ...grpc.CallOption) (*CreateAdminAdTemplateResponse, error)
UpdateAdminAdTemplate(ctx context.Context, in *UpdateAdminAdTemplateRequest, opts ...grpc.CallOption) (*UpdateAdminAdTemplateResponse, error)
DeleteAdminAdTemplate(ctx context.Context, in *DeleteAdminAdTemplateRequest, opts ...grpc.CallOption) (*MessageResponse, error)
ListAdminPopupAds(ctx context.Context, in *ListAdminPopupAdsRequest, opts ...grpc.CallOption) (*ListAdminPopupAdsResponse, error)
GetAdminPopupAd(ctx context.Context, in *GetAdminPopupAdRequest, opts ...grpc.CallOption) (*GetAdminPopupAdResponse, error)
CreateAdminPopupAd(ctx context.Context, in *CreateAdminPopupAdRequest, opts ...grpc.CallOption) (*CreateAdminPopupAdResponse, error)
UpdateAdminPopupAd(ctx context.Context, in *UpdateAdminPopupAdRequest, opts ...grpc.CallOption) (*UpdateAdminPopupAdResponse, error)
DeleteAdminPopupAd(ctx context.Context, in *DeleteAdminPopupAdRequest, opts ...grpc.CallOption) (*MessageResponse, error)
ListAdminPlayerConfigs(ctx context.Context, in *ListAdminPlayerConfigsRequest, opts ...grpc.CallOption) (*ListAdminPlayerConfigsResponse, error)
GetAdminPlayerConfig(ctx context.Context, in *GetAdminPlayerConfigRequest, opts ...grpc.CallOption) (*GetAdminPlayerConfigResponse, error)
CreateAdminPlayerConfig(ctx context.Context, in *CreateAdminPlayerConfigRequest, opts ...grpc.CallOption) (*CreateAdminPlayerConfigResponse, error)
@@ -375,6 +385,56 @@ func (c *adminClient) DeleteAdminAdTemplate(ctx context.Context, in *DeleteAdmin
return out, nil
}
func (c *adminClient) ListAdminPopupAds(ctx context.Context, in *ListAdminPopupAdsRequest, opts ...grpc.CallOption) (*ListAdminPopupAdsResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ListAdminPopupAdsResponse)
err := c.cc.Invoke(ctx, Admin_ListAdminPopupAds_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *adminClient) GetAdminPopupAd(ctx context.Context, in *GetAdminPopupAdRequest, opts ...grpc.CallOption) (*GetAdminPopupAdResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GetAdminPopupAdResponse)
err := c.cc.Invoke(ctx, Admin_GetAdminPopupAd_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *adminClient) CreateAdminPopupAd(ctx context.Context, in *CreateAdminPopupAdRequest, opts ...grpc.CallOption) (*CreateAdminPopupAdResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(CreateAdminPopupAdResponse)
err := c.cc.Invoke(ctx, Admin_CreateAdminPopupAd_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *adminClient) UpdateAdminPopupAd(ctx context.Context, in *UpdateAdminPopupAdRequest, opts ...grpc.CallOption) (*UpdateAdminPopupAdResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(UpdateAdminPopupAdResponse)
err := c.cc.Invoke(ctx, Admin_UpdateAdminPopupAd_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *adminClient) DeleteAdminPopupAd(ctx context.Context, in *DeleteAdminPopupAdRequest, opts ...grpc.CallOption) (*MessageResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(MessageResponse)
err := c.cc.Invoke(ctx, Admin_DeleteAdminPopupAd_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *adminClient) ListAdminPlayerConfigs(ctx context.Context, in *ListAdminPlayerConfigsRequest, opts ...grpc.CallOption) (*ListAdminPlayerConfigsResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ListAdminPlayerConfigsResponse)
@@ -545,6 +605,11 @@ type AdminServer interface {
CreateAdminAdTemplate(context.Context, *CreateAdminAdTemplateRequest) (*CreateAdminAdTemplateResponse, error)
UpdateAdminAdTemplate(context.Context, *UpdateAdminAdTemplateRequest) (*UpdateAdminAdTemplateResponse, error)
DeleteAdminAdTemplate(context.Context, *DeleteAdminAdTemplateRequest) (*MessageResponse, error)
ListAdminPopupAds(context.Context, *ListAdminPopupAdsRequest) (*ListAdminPopupAdsResponse, error)
GetAdminPopupAd(context.Context, *GetAdminPopupAdRequest) (*GetAdminPopupAdResponse, error)
CreateAdminPopupAd(context.Context, *CreateAdminPopupAdRequest) (*CreateAdminPopupAdResponse, error)
UpdateAdminPopupAd(context.Context, *UpdateAdminPopupAdRequest) (*UpdateAdminPopupAdResponse, error)
DeleteAdminPopupAd(context.Context, *DeleteAdminPopupAdRequest) (*MessageResponse, error)
ListAdminPlayerConfigs(context.Context, *ListAdminPlayerConfigsRequest) (*ListAdminPlayerConfigsResponse, error)
GetAdminPlayerConfig(context.Context, *GetAdminPlayerConfigRequest) (*GetAdminPlayerConfigResponse, error)
CreateAdminPlayerConfig(context.Context, *CreateAdminPlayerConfigRequest) (*CreateAdminPlayerConfigResponse, error)
@@ -647,6 +712,21 @@ func (UnimplementedAdminServer) UpdateAdminAdTemplate(context.Context, *UpdateAd
func (UnimplementedAdminServer) DeleteAdminAdTemplate(context.Context, *DeleteAdminAdTemplateRequest) (*MessageResponse, error) {
return nil, status.Error(codes.Unimplemented, "method DeleteAdminAdTemplate not implemented")
}
func (UnimplementedAdminServer) ListAdminPopupAds(context.Context, *ListAdminPopupAdsRequest) (*ListAdminPopupAdsResponse, error) {
return nil, status.Error(codes.Unimplemented, "method ListAdminPopupAds not implemented")
}
func (UnimplementedAdminServer) GetAdminPopupAd(context.Context, *GetAdminPopupAdRequest) (*GetAdminPopupAdResponse, error) {
return nil, status.Error(codes.Unimplemented, "method GetAdminPopupAd not implemented")
}
func (UnimplementedAdminServer) CreateAdminPopupAd(context.Context, *CreateAdminPopupAdRequest) (*CreateAdminPopupAdResponse, error) {
return nil, status.Error(codes.Unimplemented, "method CreateAdminPopupAd not implemented")
}
func (UnimplementedAdminServer) UpdateAdminPopupAd(context.Context, *UpdateAdminPopupAdRequest) (*UpdateAdminPopupAdResponse, error) {
return nil, status.Error(codes.Unimplemented, "method UpdateAdminPopupAd not implemented")
}
func (UnimplementedAdminServer) DeleteAdminPopupAd(context.Context, *DeleteAdminPopupAdRequest) (*MessageResponse, error) {
return nil, status.Error(codes.Unimplemented, "method DeleteAdminPopupAd not implemented")
}
func (UnimplementedAdminServer) ListAdminPlayerConfigs(context.Context, *ListAdminPlayerConfigsRequest) (*ListAdminPlayerConfigsResponse, error) {
return nil, status.Error(codes.Unimplemented, "method ListAdminPlayerConfigs not implemented")
}
@@ -1178,6 +1258,96 @@ func _Admin_DeleteAdminAdTemplate_Handler(srv interface{}, ctx context.Context,
return interceptor(ctx, in, info, handler)
}
func _Admin_ListAdminPopupAds_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListAdminPopupAdsRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AdminServer).ListAdminPopupAds(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Admin_ListAdminPopupAds_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AdminServer).ListAdminPopupAds(ctx, req.(*ListAdminPopupAdsRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Admin_GetAdminPopupAd_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetAdminPopupAdRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AdminServer).GetAdminPopupAd(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Admin_GetAdminPopupAd_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AdminServer).GetAdminPopupAd(ctx, req.(*GetAdminPopupAdRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Admin_CreateAdminPopupAd_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CreateAdminPopupAdRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AdminServer).CreateAdminPopupAd(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Admin_CreateAdminPopupAd_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AdminServer).CreateAdminPopupAd(ctx, req.(*CreateAdminPopupAdRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Admin_UpdateAdminPopupAd_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(UpdateAdminPopupAdRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AdminServer).UpdateAdminPopupAd(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Admin_UpdateAdminPopupAd_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AdminServer).UpdateAdminPopupAd(ctx, req.(*UpdateAdminPopupAdRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Admin_DeleteAdminPopupAd_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(DeleteAdminPopupAdRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AdminServer).DeleteAdminPopupAd(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Admin_DeleteAdminPopupAd_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AdminServer).DeleteAdminPopupAd(ctx, req.(*DeleteAdminPopupAdRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Admin_ListAdminPlayerConfigs_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListAdminPlayerConfigsRequest)
if err := dec(in); err != nil {
@@ -1541,6 +1711,26 @@ var Admin_ServiceDesc = grpc.ServiceDesc{
MethodName: "DeleteAdminAdTemplate",
Handler: _Admin_DeleteAdminAdTemplate_Handler,
},
{
MethodName: "ListAdminPopupAds",
Handler: _Admin_ListAdminPopupAds_Handler,
},
{
MethodName: "GetAdminPopupAd",
Handler: _Admin_GetAdminPopupAd_Handler,
},
{
MethodName: "CreateAdminPopupAd",
Handler: _Admin_CreateAdminPopupAd_Handler,
},
{
MethodName: "UpdateAdminPopupAd",
Handler: _Admin_UpdateAdminPopupAd_Handler,
},
{
MethodName: "DeleteAdminPopupAd",
Handler: _Admin_DeleteAdminPopupAd_Handler,
},
{
MethodName: "ListAdminPlayerConfigs",
Handler: _Admin_ListAdminPlayerConfigs_Handler,

View File

@@ -637,6 +637,498 @@ func (x *DeleteAdTemplateRequest) GetId() string {
return ""
}
type ListPopupAdsRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
Page int32 `protobuf:"varint,1,opt,name=page,proto3" json:"page,omitempty"`
Limit int32 `protobuf:"varint,2,opt,name=limit,proto3" json:"limit,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ListPopupAdsRequest) Reset() {
*x = ListPopupAdsRequest{}
mi := &file_app_v1_catalog_proto_msgTypes[12]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ListPopupAdsRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ListPopupAdsRequest) ProtoMessage() {}
func (x *ListPopupAdsRequest) ProtoReflect() protoreflect.Message {
mi := &file_app_v1_catalog_proto_msgTypes[12]
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 ListPopupAdsRequest.ProtoReflect.Descriptor instead.
func (*ListPopupAdsRequest) Descriptor() ([]byte, []int) {
return file_app_v1_catalog_proto_rawDescGZIP(), []int{12}
}
func (x *ListPopupAdsRequest) GetPage() int32 {
if x != nil {
return x.Page
}
return 0
}
func (x *ListPopupAdsRequest) GetLimit() int32 {
if x != nil {
return x.Limit
}
return 0
}
type ListPopupAdsResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Items []*PopupAd `protobuf:"bytes,1,rep,name=items,proto3" json:"items,omitempty"`
Total int64 `protobuf:"varint,2,opt,name=total,proto3" json:"total,omitempty"`
Page int32 `protobuf:"varint,3,opt,name=page,proto3" json:"page,omitempty"`
Limit int32 `protobuf:"varint,4,opt,name=limit,proto3" json:"limit,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ListPopupAdsResponse) Reset() {
*x = ListPopupAdsResponse{}
mi := &file_app_v1_catalog_proto_msgTypes[13]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ListPopupAdsResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ListPopupAdsResponse) ProtoMessage() {}
func (x *ListPopupAdsResponse) ProtoReflect() protoreflect.Message {
mi := &file_app_v1_catalog_proto_msgTypes[13]
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 ListPopupAdsResponse.ProtoReflect.Descriptor instead.
func (*ListPopupAdsResponse) Descriptor() ([]byte, []int) {
return file_app_v1_catalog_proto_rawDescGZIP(), []int{13}
}
func (x *ListPopupAdsResponse) GetItems() []*PopupAd {
if x != nil {
return x.Items
}
return nil
}
func (x *ListPopupAdsResponse) GetTotal() int64 {
if x != nil {
return x.Total
}
return 0
}
func (x *ListPopupAdsResponse) GetPage() int32 {
if x != nil {
return x.Page
}
return 0
}
func (x *ListPopupAdsResponse) GetLimit() int32 {
if x != nil {
return x.Limit
}
return 0
}
type CreatePopupAdRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"`
Label string `protobuf:"bytes,2,opt,name=label,proto3" json:"label,omitempty"`
Value string `protobuf:"bytes,3,opt,name=value,proto3" json:"value,omitempty"`
IsActive *bool `protobuf:"varint,4,opt,name=is_active,json=isActive,proto3,oneof" json:"is_active,omitempty"`
MaxTriggersPerSession *int32 `protobuf:"varint,5,opt,name=max_triggers_per_session,json=maxTriggersPerSession,proto3,oneof" json:"max_triggers_per_session,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *CreatePopupAdRequest) Reset() {
*x = CreatePopupAdRequest{}
mi := &file_app_v1_catalog_proto_msgTypes[14]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *CreatePopupAdRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CreatePopupAdRequest) ProtoMessage() {}
func (x *CreatePopupAdRequest) ProtoReflect() protoreflect.Message {
mi := &file_app_v1_catalog_proto_msgTypes[14]
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 CreatePopupAdRequest.ProtoReflect.Descriptor instead.
func (*CreatePopupAdRequest) Descriptor() ([]byte, []int) {
return file_app_v1_catalog_proto_rawDescGZIP(), []int{14}
}
func (x *CreatePopupAdRequest) GetType() string {
if x != nil {
return x.Type
}
return ""
}
func (x *CreatePopupAdRequest) GetLabel() string {
if x != nil {
return x.Label
}
return ""
}
func (x *CreatePopupAdRequest) GetValue() string {
if x != nil {
return x.Value
}
return ""
}
func (x *CreatePopupAdRequest) GetIsActive() bool {
if x != nil && x.IsActive != nil {
return *x.IsActive
}
return false
}
func (x *CreatePopupAdRequest) GetMaxTriggersPerSession() int32 {
if x != nil && x.MaxTriggersPerSession != nil {
return *x.MaxTriggersPerSession
}
return 0
}
type CreatePopupAdResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Item *PopupAd `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *CreatePopupAdResponse) Reset() {
*x = CreatePopupAdResponse{}
mi := &file_app_v1_catalog_proto_msgTypes[15]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *CreatePopupAdResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CreatePopupAdResponse) ProtoMessage() {}
func (x *CreatePopupAdResponse) ProtoReflect() protoreflect.Message {
mi := &file_app_v1_catalog_proto_msgTypes[15]
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 CreatePopupAdResponse.ProtoReflect.Descriptor instead.
func (*CreatePopupAdResponse) Descriptor() ([]byte, []int) {
return file_app_v1_catalog_proto_rawDescGZIP(), []int{15}
}
func (x *CreatePopupAdResponse) GetItem() *PopupAd {
if x != nil {
return x.Item
}
return nil
}
type UpdatePopupAdRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"`
Label string `protobuf:"bytes,3,opt,name=label,proto3" json:"label,omitempty"`
Value string `protobuf:"bytes,4,opt,name=value,proto3" json:"value,omitempty"`
IsActive *bool `protobuf:"varint,5,opt,name=is_active,json=isActive,proto3,oneof" json:"is_active,omitempty"`
MaxTriggersPerSession *int32 `protobuf:"varint,6,opt,name=max_triggers_per_session,json=maxTriggersPerSession,proto3,oneof" json:"max_triggers_per_session,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *UpdatePopupAdRequest) Reset() {
*x = UpdatePopupAdRequest{}
mi := &file_app_v1_catalog_proto_msgTypes[16]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *UpdatePopupAdRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*UpdatePopupAdRequest) ProtoMessage() {}
func (x *UpdatePopupAdRequest) ProtoReflect() protoreflect.Message {
mi := &file_app_v1_catalog_proto_msgTypes[16]
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 UpdatePopupAdRequest.ProtoReflect.Descriptor instead.
func (*UpdatePopupAdRequest) Descriptor() ([]byte, []int) {
return file_app_v1_catalog_proto_rawDescGZIP(), []int{16}
}
func (x *UpdatePopupAdRequest) GetId() string {
if x != nil {
return x.Id
}
return ""
}
func (x *UpdatePopupAdRequest) GetType() string {
if x != nil {
return x.Type
}
return ""
}
func (x *UpdatePopupAdRequest) GetLabel() string {
if x != nil {
return x.Label
}
return ""
}
func (x *UpdatePopupAdRequest) GetValue() string {
if x != nil {
return x.Value
}
return ""
}
func (x *UpdatePopupAdRequest) GetIsActive() bool {
if x != nil && x.IsActive != nil {
return *x.IsActive
}
return false
}
func (x *UpdatePopupAdRequest) GetMaxTriggersPerSession() int32 {
if x != nil && x.MaxTriggersPerSession != nil {
return *x.MaxTriggersPerSession
}
return 0
}
type UpdatePopupAdResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Item *PopupAd `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *UpdatePopupAdResponse) Reset() {
*x = UpdatePopupAdResponse{}
mi := &file_app_v1_catalog_proto_msgTypes[17]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *UpdatePopupAdResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*UpdatePopupAdResponse) ProtoMessage() {}
func (x *UpdatePopupAdResponse) ProtoReflect() protoreflect.Message {
mi := &file_app_v1_catalog_proto_msgTypes[17]
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 UpdatePopupAdResponse.ProtoReflect.Descriptor instead.
func (*UpdatePopupAdResponse) Descriptor() ([]byte, []int) {
return file_app_v1_catalog_proto_rawDescGZIP(), []int{17}
}
func (x *UpdatePopupAdResponse) GetItem() *PopupAd {
if x != nil {
return x.Item
}
return nil
}
type DeletePopupAdRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *DeletePopupAdRequest) Reset() {
*x = DeletePopupAdRequest{}
mi := &file_app_v1_catalog_proto_msgTypes[18]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *DeletePopupAdRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*DeletePopupAdRequest) ProtoMessage() {}
func (x *DeletePopupAdRequest) ProtoReflect() protoreflect.Message {
mi := &file_app_v1_catalog_proto_msgTypes[18]
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 DeletePopupAdRequest.ProtoReflect.Descriptor instead.
func (*DeletePopupAdRequest) Descriptor() ([]byte, []int) {
return file_app_v1_catalog_proto_rawDescGZIP(), []int{18}
}
func (x *DeletePopupAdRequest) GetId() string {
if x != nil {
return x.Id
}
return ""
}
type GetActivePopupAdRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetActivePopupAdRequest) Reset() {
*x = GetActivePopupAdRequest{}
mi := &file_app_v1_catalog_proto_msgTypes[19]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GetActivePopupAdRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetActivePopupAdRequest) ProtoMessage() {}
func (x *GetActivePopupAdRequest) ProtoReflect() protoreflect.Message {
mi := &file_app_v1_catalog_proto_msgTypes[19]
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 GetActivePopupAdRequest.ProtoReflect.Descriptor instead.
func (*GetActivePopupAdRequest) Descriptor() ([]byte, []int) {
return file_app_v1_catalog_proto_rawDescGZIP(), []int{19}
}
type GetActivePopupAdResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Item *PopupAd `protobuf:"bytes,1,opt,name=item,proto3,oneof" json:"item,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetActivePopupAdResponse) Reset() {
*x = GetActivePopupAdResponse{}
mi := &file_app_v1_catalog_proto_msgTypes[20]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GetActivePopupAdResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetActivePopupAdResponse) ProtoMessage() {}
func (x *GetActivePopupAdResponse) ProtoReflect() protoreflect.Message {
mi := &file_app_v1_catalog_proto_msgTypes[20]
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 GetActivePopupAdResponse.ProtoReflect.Descriptor instead.
func (*GetActivePopupAdResponse) Descriptor() ([]byte, []int) {
return file_app_v1_catalog_proto_rawDescGZIP(), []int{20}
}
func (x *GetActivePopupAdResponse) GetItem() *PopupAd {
if x != nil {
return x.Item
}
return nil
}
type ListPlayerConfigsRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
@@ -645,7 +1137,7 @@ type ListPlayerConfigsRequest struct {
func (x *ListPlayerConfigsRequest) Reset() {
*x = ListPlayerConfigsRequest{}
mi := &file_app_v1_catalog_proto_msgTypes[12]
mi := &file_app_v1_catalog_proto_msgTypes[21]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -657,7 +1149,7 @@ func (x *ListPlayerConfigsRequest) String() string {
func (*ListPlayerConfigsRequest) ProtoMessage() {}
func (x *ListPlayerConfigsRequest) ProtoReflect() protoreflect.Message {
mi := &file_app_v1_catalog_proto_msgTypes[12]
mi := &file_app_v1_catalog_proto_msgTypes[21]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -670,7 +1162,7 @@ func (x *ListPlayerConfigsRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use ListPlayerConfigsRequest.ProtoReflect.Descriptor instead.
func (*ListPlayerConfigsRequest) Descriptor() ([]byte, []int) {
return file_app_v1_catalog_proto_rawDescGZIP(), []int{12}
return file_app_v1_catalog_proto_rawDescGZIP(), []int{21}
}
type ListPlayerConfigsResponse struct {
@@ -682,7 +1174,7 @@ type ListPlayerConfigsResponse struct {
func (x *ListPlayerConfigsResponse) Reset() {
*x = ListPlayerConfigsResponse{}
mi := &file_app_v1_catalog_proto_msgTypes[13]
mi := &file_app_v1_catalog_proto_msgTypes[22]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -694,7 +1186,7 @@ func (x *ListPlayerConfigsResponse) String() string {
func (*ListPlayerConfigsResponse) ProtoMessage() {}
func (x *ListPlayerConfigsResponse) ProtoReflect() protoreflect.Message {
mi := &file_app_v1_catalog_proto_msgTypes[13]
mi := &file_app_v1_catalog_proto_msgTypes[22]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -707,7 +1199,7 @@ func (x *ListPlayerConfigsResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use ListPlayerConfigsResponse.ProtoReflect.Descriptor instead.
func (*ListPlayerConfigsResponse) Descriptor() ([]byte, []int) {
return file_app_v1_catalog_proto_rawDescGZIP(), []int{13}
return file_app_v1_catalog_proto_rawDescGZIP(), []int{22}
}
func (x *ListPlayerConfigsResponse) GetConfigs() []*PlayerConfig {
@@ -738,7 +1230,7 @@ type CreatePlayerConfigRequest struct {
func (x *CreatePlayerConfigRequest) Reset() {
*x = CreatePlayerConfigRequest{}
mi := &file_app_v1_catalog_proto_msgTypes[14]
mi := &file_app_v1_catalog_proto_msgTypes[23]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -750,7 +1242,7 @@ func (x *CreatePlayerConfigRequest) String() string {
func (*CreatePlayerConfigRequest) ProtoMessage() {}
func (x *CreatePlayerConfigRequest) ProtoReflect() protoreflect.Message {
mi := &file_app_v1_catalog_proto_msgTypes[14]
mi := &file_app_v1_catalog_proto_msgTypes[23]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -763,7 +1255,7 @@ func (x *CreatePlayerConfigRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use CreatePlayerConfigRequest.ProtoReflect.Descriptor instead.
func (*CreatePlayerConfigRequest) Descriptor() ([]byte, []int) {
return file_app_v1_catalog_proto_rawDescGZIP(), []int{14}
return file_app_v1_catalog_proto_rawDescGZIP(), []int{23}
}
func (x *CreatePlayerConfigRequest) GetName() string {
@@ -866,7 +1358,7 @@ type CreatePlayerConfigResponse struct {
func (x *CreatePlayerConfigResponse) Reset() {
*x = CreatePlayerConfigResponse{}
mi := &file_app_v1_catalog_proto_msgTypes[15]
mi := &file_app_v1_catalog_proto_msgTypes[24]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -878,7 +1370,7 @@ func (x *CreatePlayerConfigResponse) String() string {
func (*CreatePlayerConfigResponse) ProtoMessage() {}
func (x *CreatePlayerConfigResponse) ProtoReflect() protoreflect.Message {
mi := &file_app_v1_catalog_proto_msgTypes[15]
mi := &file_app_v1_catalog_proto_msgTypes[24]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -891,7 +1383,7 @@ func (x *CreatePlayerConfigResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use CreatePlayerConfigResponse.ProtoReflect.Descriptor instead.
func (*CreatePlayerConfigResponse) Descriptor() ([]byte, []int) {
return file_app_v1_catalog_proto_rawDescGZIP(), []int{15}
return file_app_v1_catalog_proto_rawDescGZIP(), []int{24}
}
func (x *CreatePlayerConfigResponse) GetConfig() *PlayerConfig {
@@ -923,7 +1415,7 @@ type UpdatePlayerConfigRequest struct {
func (x *UpdatePlayerConfigRequest) Reset() {
*x = UpdatePlayerConfigRequest{}
mi := &file_app_v1_catalog_proto_msgTypes[16]
mi := &file_app_v1_catalog_proto_msgTypes[25]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -935,7 +1427,7 @@ func (x *UpdatePlayerConfigRequest) String() string {
func (*UpdatePlayerConfigRequest) ProtoMessage() {}
func (x *UpdatePlayerConfigRequest) ProtoReflect() protoreflect.Message {
mi := &file_app_v1_catalog_proto_msgTypes[16]
mi := &file_app_v1_catalog_proto_msgTypes[25]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -948,7 +1440,7 @@ func (x *UpdatePlayerConfigRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use UpdatePlayerConfigRequest.ProtoReflect.Descriptor instead.
func (*UpdatePlayerConfigRequest) Descriptor() ([]byte, []int) {
return file_app_v1_catalog_proto_rawDescGZIP(), []int{16}
return file_app_v1_catalog_proto_rawDescGZIP(), []int{25}
}
func (x *UpdatePlayerConfigRequest) GetId() string {
@@ -1058,7 +1550,7 @@ type UpdatePlayerConfigResponse struct {
func (x *UpdatePlayerConfigResponse) Reset() {
*x = UpdatePlayerConfigResponse{}
mi := &file_app_v1_catalog_proto_msgTypes[17]
mi := &file_app_v1_catalog_proto_msgTypes[26]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1070,7 +1562,7 @@ func (x *UpdatePlayerConfigResponse) String() string {
func (*UpdatePlayerConfigResponse) ProtoMessage() {}
func (x *UpdatePlayerConfigResponse) ProtoReflect() protoreflect.Message {
mi := &file_app_v1_catalog_proto_msgTypes[17]
mi := &file_app_v1_catalog_proto_msgTypes[26]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1083,7 +1575,7 @@ func (x *UpdatePlayerConfigResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use UpdatePlayerConfigResponse.ProtoReflect.Descriptor instead.
func (*UpdatePlayerConfigResponse) Descriptor() ([]byte, []int) {
return file_app_v1_catalog_proto_rawDescGZIP(), []int{17}
return file_app_v1_catalog_proto_rawDescGZIP(), []int{26}
}
func (x *UpdatePlayerConfigResponse) GetConfig() *PlayerConfig {
@@ -1102,7 +1594,7 @@ type DeletePlayerConfigRequest struct {
func (x *DeletePlayerConfigRequest) Reset() {
*x = DeletePlayerConfigRequest{}
mi := &file_app_v1_catalog_proto_msgTypes[18]
mi := &file_app_v1_catalog_proto_msgTypes[27]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1114,7 +1606,7 @@ func (x *DeletePlayerConfigRequest) String() string {
func (*DeletePlayerConfigRequest) ProtoMessage() {}
func (x *DeletePlayerConfigRequest) ProtoReflect() protoreflect.Message {
mi := &file_app_v1_catalog_proto_msgTypes[18]
mi := &file_app_v1_catalog_proto_msgTypes[27]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1127,7 +1619,7 @@ func (x *DeletePlayerConfigRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use DeletePlayerConfigRequest.ProtoReflect.Descriptor instead.
func (*DeletePlayerConfigRequest) Descriptor() ([]byte, []int) {
return file_app_v1_catalog_proto_rawDescGZIP(), []int{18}
return file_app_v1_catalog_proto_rawDescGZIP(), []int{27}
}
func (x *DeletePlayerConfigRequest) GetId() string {
@@ -1145,7 +1637,7 @@ type ListPlansRequest struct {
func (x *ListPlansRequest) Reset() {
*x = ListPlansRequest{}
mi := &file_app_v1_catalog_proto_msgTypes[19]
mi := &file_app_v1_catalog_proto_msgTypes[28]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1157,7 +1649,7 @@ func (x *ListPlansRequest) String() string {
func (*ListPlansRequest) ProtoMessage() {}
func (x *ListPlansRequest) ProtoReflect() protoreflect.Message {
mi := &file_app_v1_catalog_proto_msgTypes[19]
mi := &file_app_v1_catalog_proto_msgTypes[28]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1170,7 +1662,7 @@ func (x *ListPlansRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use ListPlansRequest.ProtoReflect.Descriptor instead.
func (*ListPlansRequest) Descriptor() ([]byte, []int) {
return file_app_v1_catalog_proto_rawDescGZIP(), []int{19}
return file_app_v1_catalog_proto_rawDescGZIP(), []int{28}
}
type ListPlansResponse struct {
@@ -1182,7 +1674,7 @@ type ListPlansResponse struct {
func (x *ListPlansResponse) Reset() {
*x = ListPlansResponse{}
mi := &file_app_v1_catalog_proto_msgTypes[20]
mi := &file_app_v1_catalog_proto_msgTypes[29]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1194,7 +1686,7 @@ func (x *ListPlansResponse) String() string {
func (*ListPlansResponse) ProtoMessage() {}
func (x *ListPlansResponse) ProtoReflect() protoreflect.Message {
mi := &file_app_v1_catalog_proto_msgTypes[20]
mi := &file_app_v1_catalog_proto_msgTypes[29]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1207,7 +1699,7 @@ func (x *ListPlansResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use ListPlansResponse.ProtoReflect.Descriptor instead.
func (*ListPlansResponse) Descriptor() ([]byte, []int) {
return file_app_v1_catalog_proto_rawDescGZIP(), []int{20}
return file_app_v1_catalog_proto_rawDescGZIP(), []int{29}
}
func (x *ListPlansResponse) GetPlans() []*Plan {
@@ -1274,7 +1766,44 @@ const file_app_v1_catalog_proto_rawDesc = "" +
"\x18UpdateAdTemplateResponse\x125\n" +
"\btemplate\x18\x01 \x01(\v2\x19.stream.app.v1.AdTemplateR\btemplate\")\n" +
"\x17DeleteAdTemplateRequest\x12\x0e\n" +
"\x02id\x18\x01 \x01(\tR\x02id\"\x1a\n" +
"\x02id\x18\x01 \x01(\tR\x02id\"?\n" +
"\x13ListPopupAdsRequest\x12\x12\n" +
"\x04page\x18\x01 \x01(\x05R\x04page\x12\x14\n" +
"\x05limit\x18\x02 \x01(\x05R\x05limit\"\x84\x01\n" +
"\x14ListPopupAdsResponse\x12,\n" +
"\x05items\x18\x01 \x03(\v2\x16.stream.app.v1.PopupAdR\x05items\x12\x14\n" +
"\x05total\x18\x02 \x01(\x03R\x05total\x12\x12\n" +
"\x04page\x18\x03 \x01(\x05R\x04page\x12\x14\n" +
"\x05limit\x18\x04 \x01(\x05R\x05limit\"\xe1\x01\n" +
"\x14CreatePopupAdRequest\x12\x12\n" +
"\x04type\x18\x01 \x01(\tR\x04type\x12\x14\n" +
"\x05label\x18\x02 \x01(\tR\x05label\x12\x14\n" +
"\x05value\x18\x03 \x01(\tR\x05value\x12 \n" +
"\tis_active\x18\x04 \x01(\bH\x00R\bisActive\x88\x01\x01\x12<\n" +
"\x18max_triggers_per_session\x18\x05 \x01(\x05H\x01R\x15maxTriggersPerSession\x88\x01\x01B\f\n" +
"\n" +
"_is_activeB\x1b\n" +
"\x19_max_triggers_per_session\"C\n" +
"\x15CreatePopupAdResponse\x12*\n" +
"\x04item\x18\x01 \x01(\v2\x16.stream.app.v1.PopupAdR\x04item\"\xf1\x01\n" +
"\x14UpdatePopupAdRequest\x12\x0e\n" +
"\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n" +
"\x04type\x18\x02 \x01(\tR\x04type\x12\x14\n" +
"\x05label\x18\x03 \x01(\tR\x05label\x12\x14\n" +
"\x05value\x18\x04 \x01(\tR\x05value\x12 \n" +
"\tis_active\x18\x05 \x01(\bH\x00R\bisActive\x88\x01\x01\x12<\n" +
"\x18max_triggers_per_session\x18\x06 \x01(\x05H\x01R\x15maxTriggersPerSession\x88\x01\x01B\f\n" +
"\n" +
"_is_activeB\x1b\n" +
"\x19_max_triggers_per_session\"C\n" +
"\x15UpdatePopupAdResponse\x12*\n" +
"\x04item\x18\x01 \x01(\v2\x16.stream.app.v1.PopupAdR\x04item\"&\n" +
"\x14DeletePopupAdRequest\x12\x0e\n" +
"\x02id\x18\x01 \x01(\tR\x02id\"\x19\n" +
"\x17GetActivePopupAdRequest\"T\n" +
"\x18GetActivePopupAdResponse\x12/\n" +
"\x04item\x18\x01 \x01(\v2\x16.stream.app.v1.PopupAdH\x00R\x04item\x88\x01\x01B\a\n" +
"\x05_item\"\x1a\n" +
"\x18ListPlayerConfigsRequest\"R\n" +
"\x19ListPlayerConfigsResponse\x125\n" +
"\aconfigs\x18\x01 \x03(\v2\x1b.stream.app.v1.PlayerConfigR\aconfigs\"\xec\x03\n" +
@@ -1344,7 +1873,13 @@ const file_app_v1_catalog_proto_rawDesc = "" +
"\x0fListAdTemplates\x12%.stream.app.v1.ListAdTemplatesRequest\x1a&.stream.app.v1.ListAdTemplatesResponse\x12c\n" +
"\x10CreateAdTemplate\x12&.stream.app.v1.CreateAdTemplateRequest\x1a'.stream.app.v1.CreateAdTemplateResponse\x12c\n" +
"\x10UpdateAdTemplate\x12&.stream.app.v1.UpdateAdTemplateRequest\x1a'.stream.app.v1.UpdateAdTemplateResponse\x12Z\n" +
"\x10DeleteAdTemplate\x12&.stream.app.v1.DeleteAdTemplateRequest\x1a\x1e.stream.app.v1.MessageResponse2\xad\x03\n" +
"\x10DeleteAdTemplate\x12&.stream.app.v1.DeleteAdTemplateRequest\x1a\x1e.stream.app.v1.MessageResponse2\xd6\x03\n" +
"\bPopupAds\x12W\n" +
"\fListPopupAds\x12\".stream.app.v1.ListPopupAdsRequest\x1a#.stream.app.v1.ListPopupAdsResponse\x12Z\n" +
"\rCreatePopupAd\x12#.stream.app.v1.CreatePopupAdRequest\x1a$.stream.app.v1.CreatePopupAdResponse\x12Z\n" +
"\rUpdatePopupAd\x12#.stream.app.v1.UpdatePopupAdRequest\x1a$.stream.app.v1.UpdatePopupAdResponse\x12T\n" +
"\rDeletePopupAd\x12#.stream.app.v1.DeletePopupAdRequest\x1a\x1e.stream.app.v1.MessageResponse\x12c\n" +
"\x10GetActivePopupAd\x12&.stream.app.v1.GetActivePopupAdRequest\x1a'.stream.app.v1.GetActivePopupAdResponse2\xad\x03\n" +
"\rPlayerConfigs\x12f\n" +
"\x11ListPlayerConfigs\x12'.stream.app.v1.ListPlayerConfigsRequest\x1a(.stream.app.v1.ListPlayerConfigsResponse\x12i\n" +
"\x12CreatePlayerConfig\x12(.stream.app.v1.CreatePlayerConfigRequest\x1a).stream.app.v1.CreatePlayerConfigResponse\x12i\n" +
@@ -1365,7 +1900,7 @@ func file_app_v1_catalog_proto_rawDescGZIP() []byte {
return file_app_v1_catalog_proto_rawDescData
}
var file_app_v1_catalog_proto_msgTypes = make([]protoimpl.MessageInfo, 21)
var file_app_v1_catalog_proto_msgTypes = make([]protoimpl.MessageInfo, 30)
var file_app_v1_catalog_proto_goTypes = []any{
(*ListDomainsRequest)(nil), // 0: stream.app.v1.ListDomainsRequest
(*ListDomainsResponse)(nil), // 1: stream.app.v1.ListDomainsResponse
@@ -1379,60 +1914,84 @@ var file_app_v1_catalog_proto_goTypes = []any{
(*UpdateAdTemplateRequest)(nil), // 9: stream.app.v1.UpdateAdTemplateRequest
(*UpdateAdTemplateResponse)(nil), // 10: stream.app.v1.UpdateAdTemplateResponse
(*DeleteAdTemplateRequest)(nil), // 11: stream.app.v1.DeleteAdTemplateRequest
(*ListPlayerConfigsRequest)(nil), // 12: stream.app.v1.ListPlayerConfigsRequest
(*ListPlayerConfigsResponse)(nil), // 13: stream.app.v1.ListPlayerConfigsResponse
(*CreatePlayerConfigRequest)(nil), // 14: stream.app.v1.CreatePlayerConfigRequest
(*CreatePlayerConfigResponse)(nil), // 15: stream.app.v1.CreatePlayerConfigResponse
(*UpdatePlayerConfigRequest)(nil), // 16: stream.app.v1.UpdatePlayerConfigRequest
(*UpdatePlayerConfigResponse)(nil), // 17: stream.app.v1.UpdatePlayerConfigResponse
(*DeletePlayerConfigRequest)(nil), // 18: stream.app.v1.DeletePlayerConfigRequest
(*ListPlansRequest)(nil), // 19: stream.app.v1.ListPlansRequest
(*ListPlansResponse)(nil), // 20: stream.app.v1.ListPlansResponse
(*Domain)(nil), // 21: stream.app.v1.Domain
(*AdTemplate)(nil), // 22: stream.app.v1.AdTemplate
(*PlayerConfig)(nil), // 23: stream.app.v1.PlayerConfig
(*Plan)(nil), // 24: stream.app.v1.Plan
(*MessageResponse)(nil), // 25: stream.app.v1.MessageResponse
(*ListPopupAdsRequest)(nil), // 12: stream.app.v1.ListPopupAdsRequest
(*ListPopupAdsResponse)(nil), // 13: stream.app.v1.ListPopupAdsResponse
(*CreatePopupAdRequest)(nil), // 14: stream.app.v1.CreatePopupAdRequest
(*CreatePopupAdResponse)(nil), // 15: stream.app.v1.CreatePopupAdResponse
(*UpdatePopupAdRequest)(nil), // 16: stream.app.v1.UpdatePopupAdRequest
(*UpdatePopupAdResponse)(nil), // 17: stream.app.v1.UpdatePopupAdResponse
(*DeletePopupAdRequest)(nil), // 18: stream.app.v1.DeletePopupAdRequest
(*GetActivePopupAdRequest)(nil), // 19: stream.app.v1.GetActivePopupAdRequest
(*GetActivePopupAdResponse)(nil), // 20: stream.app.v1.GetActivePopupAdResponse
(*ListPlayerConfigsRequest)(nil), // 21: stream.app.v1.ListPlayerConfigsRequest
(*ListPlayerConfigsResponse)(nil), // 22: stream.app.v1.ListPlayerConfigsResponse
(*CreatePlayerConfigRequest)(nil), // 23: stream.app.v1.CreatePlayerConfigRequest
(*CreatePlayerConfigResponse)(nil), // 24: stream.app.v1.CreatePlayerConfigResponse
(*UpdatePlayerConfigRequest)(nil), // 25: stream.app.v1.UpdatePlayerConfigRequest
(*UpdatePlayerConfigResponse)(nil), // 26: stream.app.v1.UpdatePlayerConfigResponse
(*DeletePlayerConfigRequest)(nil), // 27: stream.app.v1.DeletePlayerConfigRequest
(*ListPlansRequest)(nil), // 28: stream.app.v1.ListPlansRequest
(*ListPlansResponse)(nil), // 29: stream.app.v1.ListPlansResponse
(*Domain)(nil), // 30: stream.app.v1.Domain
(*AdTemplate)(nil), // 31: stream.app.v1.AdTemplate
(*PopupAd)(nil), // 32: stream.app.v1.PopupAd
(*PlayerConfig)(nil), // 33: stream.app.v1.PlayerConfig
(*Plan)(nil), // 34: stream.app.v1.Plan
(*MessageResponse)(nil), // 35: stream.app.v1.MessageResponse
}
var file_app_v1_catalog_proto_depIdxs = []int32{
21, // 0: stream.app.v1.ListDomainsResponse.domains:type_name -> stream.app.v1.Domain
21, // 1: stream.app.v1.CreateDomainResponse.domain:type_name -> stream.app.v1.Domain
22, // 2: stream.app.v1.ListAdTemplatesResponse.templates:type_name -> stream.app.v1.AdTemplate
22, // 3: stream.app.v1.CreateAdTemplateResponse.template:type_name -> stream.app.v1.AdTemplate
22, // 4: stream.app.v1.UpdateAdTemplateResponse.template:type_name -> stream.app.v1.AdTemplate
23, // 5: stream.app.v1.ListPlayerConfigsResponse.configs:type_name -> stream.app.v1.PlayerConfig
23, // 6: stream.app.v1.CreatePlayerConfigResponse.config:type_name -> stream.app.v1.PlayerConfig
23, // 7: stream.app.v1.UpdatePlayerConfigResponse.config:type_name -> stream.app.v1.PlayerConfig
24, // 8: stream.app.v1.ListPlansResponse.plans:type_name -> stream.app.v1.Plan
0, // 9: stream.app.v1.Domains.ListDomains:input_type -> stream.app.v1.ListDomainsRequest
2, // 10: stream.app.v1.Domains.CreateDomain:input_type -> stream.app.v1.CreateDomainRequest
4, // 11: stream.app.v1.Domains.DeleteDomain:input_type -> stream.app.v1.DeleteDomainRequest
5, // 12: stream.app.v1.AdTemplates.ListAdTemplates:input_type -> stream.app.v1.ListAdTemplatesRequest
7, // 13: stream.app.v1.AdTemplates.CreateAdTemplate:input_type -> stream.app.v1.CreateAdTemplateRequest
9, // 14: stream.app.v1.AdTemplates.UpdateAdTemplate:input_type -> stream.app.v1.UpdateAdTemplateRequest
11, // 15: stream.app.v1.AdTemplates.DeleteAdTemplate:input_type -> stream.app.v1.DeleteAdTemplateRequest
12, // 16: stream.app.v1.PlayerConfigs.ListPlayerConfigs:input_type -> stream.app.v1.ListPlayerConfigsRequest
14, // 17: stream.app.v1.PlayerConfigs.CreatePlayerConfig:input_type -> stream.app.v1.CreatePlayerConfigRequest
16, // 18: stream.app.v1.PlayerConfigs.UpdatePlayerConfig:input_type -> stream.app.v1.UpdatePlayerConfigRequest
18, // 19: stream.app.v1.PlayerConfigs.DeletePlayerConfig:input_type -> stream.app.v1.DeletePlayerConfigRequest
19, // 20: stream.app.v1.Plans.ListPlans:input_type -> stream.app.v1.ListPlansRequest
1, // 21: stream.app.v1.Domains.ListDomains:output_type -> stream.app.v1.ListDomainsResponse
3, // 22: stream.app.v1.Domains.CreateDomain:output_type -> stream.app.v1.CreateDomainResponse
25, // 23: stream.app.v1.Domains.DeleteDomain:output_type -> stream.app.v1.MessageResponse
6, // 24: stream.app.v1.AdTemplates.ListAdTemplates:output_type -> stream.app.v1.ListAdTemplatesResponse
8, // 25: stream.app.v1.AdTemplates.CreateAdTemplate:output_type -> stream.app.v1.CreateAdTemplateResponse
10, // 26: stream.app.v1.AdTemplates.UpdateAdTemplate:output_type -> stream.app.v1.UpdateAdTemplateResponse
25, // 27: stream.app.v1.AdTemplates.DeleteAdTemplate:output_type -> stream.app.v1.MessageResponse
13, // 28: stream.app.v1.PlayerConfigs.ListPlayerConfigs:output_type -> stream.app.v1.ListPlayerConfigsResponse
15, // 29: stream.app.v1.PlayerConfigs.CreatePlayerConfig:output_type -> stream.app.v1.CreatePlayerConfigResponse
17, // 30: stream.app.v1.PlayerConfigs.UpdatePlayerConfig:output_type -> stream.app.v1.UpdatePlayerConfigResponse
25, // 31: stream.app.v1.PlayerConfigs.DeletePlayerConfig:output_type -> stream.app.v1.MessageResponse
20, // 32: stream.app.v1.Plans.ListPlans:output_type -> stream.app.v1.ListPlansResponse
21, // [21:33] is the sub-list for method output_type
9, // [9:21] is the sub-list for method input_type
9, // [9:9] is the sub-list for extension type_name
9, // [9:9] is the sub-list for extension extendee
0, // [0:9] is the sub-list for field type_name
30, // 0: stream.app.v1.ListDomainsResponse.domains:type_name -> stream.app.v1.Domain
30, // 1: stream.app.v1.CreateDomainResponse.domain:type_name -> stream.app.v1.Domain
31, // 2: stream.app.v1.ListAdTemplatesResponse.templates:type_name -> stream.app.v1.AdTemplate
31, // 3: stream.app.v1.CreateAdTemplateResponse.template:type_name -> stream.app.v1.AdTemplate
31, // 4: stream.app.v1.UpdateAdTemplateResponse.template:type_name -> stream.app.v1.AdTemplate
32, // 5: stream.app.v1.ListPopupAdsResponse.items:type_name -> stream.app.v1.PopupAd
32, // 6: stream.app.v1.CreatePopupAdResponse.item:type_name -> stream.app.v1.PopupAd
32, // 7: stream.app.v1.UpdatePopupAdResponse.item:type_name -> stream.app.v1.PopupAd
32, // 8: stream.app.v1.GetActivePopupAdResponse.item:type_name -> stream.app.v1.PopupAd
33, // 9: stream.app.v1.ListPlayerConfigsResponse.configs:type_name -> stream.app.v1.PlayerConfig
33, // 10: stream.app.v1.CreatePlayerConfigResponse.config:type_name -> stream.app.v1.PlayerConfig
33, // 11: stream.app.v1.UpdatePlayerConfigResponse.config:type_name -> stream.app.v1.PlayerConfig
34, // 12: stream.app.v1.ListPlansResponse.plans:type_name -> stream.app.v1.Plan
0, // 13: stream.app.v1.Domains.ListDomains:input_type -> stream.app.v1.ListDomainsRequest
2, // 14: stream.app.v1.Domains.CreateDomain:input_type -> stream.app.v1.CreateDomainRequest
4, // 15: stream.app.v1.Domains.DeleteDomain:input_type -> stream.app.v1.DeleteDomainRequest
5, // 16: stream.app.v1.AdTemplates.ListAdTemplates:input_type -> stream.app.v1.ListAdTemplatesRequest
7, // 17: stream.app.v1.AdTemplates.CreateAdTemplate:input_type -> stream.app.v1.CreateAdTemplateRequest
9, // 18: stream.app.v1.AdTemplates.UpdateAdTemplate:input_type -> stream.app.v1.UpdateAdTemplateRequest
11, // 19: stream.app.v1.AdTemplates.DeleteAdTemplate:input_type -> stream.app.v1.DeleteAdTemplateRequest
12, // 20: stream.app.v1.PopupAds.ListPopupAds:input_type -> stream.app.v1.ListPopupAdsRequest
14, // 21: stream.app.v1.PopupAds.CreatePopupAd:input_type -> stream.app.v1.CreatePopupAdRequest
16, // 22: stream.app.v1.PopupAds.UpdatePopupAd:input_type -> stream.app.v1.UpdatePopupAdRequest
18, // 23: stream.app.v1.PopupAds.DeletePopupAd:input_type -> stream.app.v1.DeletePopupAdRequest
19, // 24: stream.app.v1.PopupAds.GetActivePopupAd:input_type -> stream.app.v1.GetActivePopupAdRequest
21, // 25: stream.app.v1.PlayerConfigs.ListPlayerConfigs:input_type -> stream.app.v1.ListPlayerConfigsRequest
23, // 26: stream.app.v1.PlayerConfigs.CreatePlayerConfig:input_type -> stream.app.v1.CreatePlayerConfigRequest
25, // 27: stream.app.v1.PlayerConfigs.UpdatePlayerConfig:input_type -> stream.app.v1.UpdatePlayerConfigRequest
27, // 28: stream.app.v1.PlayerConfigs.DeletePlayerConfig:input_type -> stream.app.v1.DeletePlayerConfigRequest
28, // 29: stream.app.v1.Plans.ListPlans:input_type -> stream.app.v1.ListPlansRequest
1, // 30: stream.app.v1.Domains.ListDomains:output_type -> stream.app.v1.ListDomainsResponse
3, // 31: stream.app.v1.Domains.CreateDomain:output_type -> stream.app.v1.CreateDomainResponse
35, // 32: stream.app.v1.Domains.DeleteDomain:output_type -> stream.app.v1.MessageResponse
6, // 33: stream.app.v1.AdTemplates.ListAdTemplates:output_type -> stream.app.v1.ListAdTemplatesResponse
8, // 34: stream.app.v1.AdTemplates.CreateAdTemplate:output_type -> stream.app.v1.CreateAdTemplateResponse
10, // 35: stream.app.v1.AdTemplates.UpdateAdTemplate:output_type -> stream.app.v1.UpdateAdTemplateResponse
35, // 36: stream.app.v1.AdTemplates.DeleteAdTemplate:output_type -> stream.app.v1.MessageResponse
13, // 37: stream.app.v1.PopupAds.ListPopupAds:output_type -> stream.app.v1.ListPopupAdsResponse
15, // 38: stream.app.v1.PopupAds.CreatePopupAd:output_type -> stream.app.v1.CreatePopupAdResponse
17, // 39: stream.app.v1.PopupAds.UpdatePopupAd:output_type -> stream.app.v1.UpdatePopupAdResponse
35, // 40: stream.app.v1.PopupAds.DeletePopupAd:output_type -> stream.app.v1.MessageResponse
20, // 41: stream.app.v1.PopupAds.GetActivePopupAd:output_type -> stream.app.v1.GetActivePopupAdResponse
22, // 42: stream.app.v1.PlayerConfigs.ListPlayerConfigs:output_type -> stream.app.v1.ListPlayerConfigsResponse
24, // 43: stream.app.v1.PlayerConfigs.CreatePlayerConfig:output_type -> stream.app.v1.CreatePlayerConfigResponse
26, // 44: stream.app.v1.PlayerConfigs.UpdatePlayerConfig:output_type -> stream.app.v1.UpdatePlayerConfigResponse
35, // 45: stream.app.v1.PlayerConfigs.DeletePlayerConfig:output_type -> stream.app.v1.MessageResponse
29, // 46: stream.app.v1.Plans.ListPlans:output_type -> stream.app.v1.ListPlansResponse
30, // [30:47] is the sub-list for method output_type
13, // [13:30] is the sub-list for method input_type
13, // [13:13] is the sub-list for extension type_name
13, // [13:13] is the sub-list for extension extendee
0, // [0:13] is the sub-list for field type_name
}
func init() { file_app_v1_catalog_proto_init() }
@@ -1445,15 +2004,18 @@ func file_app_v1_catalog_proto_init() {
file_app_v1_catalog_proto_msgTypes[9].OneofWrappers = []any{}
file_app_v1_catalog_proto_msgTypes[14].OneofWrappers = []any{}
file_app_v1_catalog_proto_msgTypes[16].OneofWrappers = []any{}
file_app_v1_catalog_proto_msgTypes[20].OneofWrappers = []any{}
file_app_v1_catalog_proto_msgTypes[23].OneofWrappers = []any{}
file_app_v1_catalog_proto_msgTypes[25].OneofWrappers = []any{}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_v1_catalog_proto_rawDesc), len(file_app_v1_catalog_proto_rawDesc)),
NumEnums: 0,
NumMessages: 21,
NumMessages: 30,
NumExtensions: 0,
NumServices: 4,
NumServices: 5,
},
GoTypes: file_app_v1_catalog_proto_goTypes,
DependencyIndexes: file_app_v1_catalog_proto_depIdxs,

View File

@@ -412,6 +412,260 @@ var AdTemplates_ServiceDesc = grpc.ServiceDesc{
Metadata: "app/v1/catalog.proto",
}
const (
PopupAds_ListPopupAds_FullMethodName = "/stream.app.v1.PopupAds/ListPopupAds"
PopupAds_CreatePopupAd_FullMethodName = "/stream.app.v1.PopupAds/CreatePopupAd"
PopupAds_UpdatePopupAd_FullMethodName = "/stream.app.v1.PopupAds/UpdatePopupAd"
PopupAds_DeletePopupAd_FullMethodName = "/stream.app.v1.PopupAds/DeletePopupAd"
PopupAds_GetActivePopupAd_FullMethodName = "/stream.app.v1.PopupAds/GetActivePopupAd"
)
// PopupAdsClient is the client API for PopupAds service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type PopupAdsClient interface {
ListPopupAds(ctx context.Context, in *ListPopupAdsRequest, opts ...grpc.CallOption) (*ListPopupAdsResponse, error)
CreatePopupAd(ctx context.Context, in *CreatePopupAdRequest, opts ...grpc.CallOption) (*CreatePopupAdResponse, error)
UpdatePopupAd(ctx context.Context, in *UpdatePopupAdRequest, opts ...grpc.CallOption) (*UpdatePopupAdResponse, error)
DeletePopupAd(ctx context.Context, in *DeletePopupAdRequest, opts ...grpc.CallOption) (*MessageResponse, error)
GetActivePopupAd(ctx context.Context, in *GetActivePopupAdRequest, opts ...grpc.CallOption) (*GetActivePopupAdResponse, error)
}
type popupAdsClient struct {
cc grpc.ClientConnInterface
}
func NewPopupAdsClient(cc grpc.ClientConnInterface) PopupAdsClient {
return &popupAdsClient{cc}
}
func (c *popupAdsClient) ListPopupAds(ctx context.Context, in *ListPopupAdsRequest, opts ...grpc.CallOption) (*ListPopupAdsResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ListPopupAdsResponse)
err := c.cc.Invoke(ctx, PopupAds_ListPopupAds_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *popupAdsClient) CreatePopupAd(ctx context.Context, in *CreatePopupAdRequest, opts ...grpc.CallOption) (*CreatePopupAdResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(CreatePopupAdResponse)
err := c.cc.Invoke(ctx, PopupAds_CreatePopupAd_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *popupAdsClient) UpdatePopupAd(ctx context.Context, in *UpdatePopupAdRequest, opts ...grpc.CallOption) (*UpdatePopupAdResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(UpdatePopupAdResponse)
err := c.cc.Invoke(ctx, PopupAds_UpdatePopupAd_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *popupAdsClient) DeletePopupAd(ctx context.Context, in *DeletePopupAdRequest, opts ...grpc.CallOption) (*MessageResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(MessageResponse)
err := c.cc.Invoke(ctx, PopupAds_DeletePopupAd_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *popupAdsClient) GetActivePopupAd(ctx context.Context, in *GetActivePopupAdRequest, opts ...grpc.CallOption) (*GetActivePopupAdResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GetActivePopupAdResponse)
err := c.cc.Invoke(ctx, PopupAds_GetActivePopupAd_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// PopupAdsServer is the server API for PopupAds service.
// All implementations must embed UnimplementedPopupAdsServer
// for forward compatibility.
type PopupAdsServer interface {
ListPopupAds(context.Context, *ListPopupAdsRequest) (*ListPopupAdsResponse, error)
CreatePopupAd(context.Context, *CreatePopupAdRequest) (*CreatePopupAdResponse, error)
UpdatePopupAd(context.Context, *UpdatePopupAdRequest) (*UpdatePopupAdResponse, error)
DeletePopupAd(context.Context, *DeletePopupAdRequest) (*MessageResponse, error)
GetActivePopupAd(context.Context, *GetActivePopupAdRequest) (*GetActivePopupAdResponse, error)
mustEmbedUnimplementedPopupAdsServer()
}
// UnimplementedPopupAdsServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedPopupAdsServer struct{}
func (UnimplementedPopupAdsServer) ListPopupAds(context.Context, *ListPopupAdsRequest) (*ListPopupAdsResponse, error) {
return nil, status.Error(codes.Unimplemented, "method ListPopupAds not implemented")
}
func (UnimplementedPopupAdsServer) CreatePopupAd(context.Context, *CreatePopupAdRequest) (*CreatePopupAdResponse, error) {
return nil, status.Error(codes.Unimplemented, "method CreatePopupAd not implemented")
}
func (UnimplementedPopupAdsServer) UpdatePopupAd(context.Context, *UpdatePopupAdRequest) (*UpdatePopupAdResponse, error) {
return nil, status.Error(codes.Unimplemented, "method UpdatePopupAd not implemented")
}
func (UnimplementedPopupAdsServer) DeletePopupAd(context.Context, *DeletePopupAdRequest) (*MessageResponse, error) {
return nil, status.Error(codes.Unimplemented, "method DeletePopupAd not implemented")
}
func (UnimplementedPopupAdsServer) GetActivePopupAd(context.Context, *GetActivePopupAdRequest) (*GetActivePopupAdResponse, error) {
return nil, status.Error(codes.Unimplemented, "method GetActivePopupAd not implemented")
}
func (UnimplementedPopupAdsServer) mustEmbedUnimplementedPopupAdsServer() {}
func (UnimplementedPopupAdsServer) testEmbeddedByValue() {}
// UnsafePopupAdsServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to PopupAdsServer will
// result in compilation errors.
type UnsafePopupAdsServer interface {
mustEmbedUnimplementedPopupAdsServer()
}
func RegisterPopupAdsServer(s grpc.ServiceRegistrar, srv PopupAdsServer) {
// If the following call panics, it indicates UnimplementedPopupAdsServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&PopupAds_ServiceDesc, srv)
}
func _PopupAds_ListPopupAds_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListPopupAdsRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(PopupAdsServer).ListPopupAds(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: PopupAds_ListPopupAds_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(PopupAdsServer).ListPopupAds(ctx, req.(*ListPopupAdsRequest))
}
return interceptor(ctx, in, info, handler)
}
func _PopupAds_CreatePopupAd_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CreatePopupAdRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(PopupAdsServer).CreatePopupAd(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: PopupAds_CreatePopupAd_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(PopupAdsServer).CreatePopupAd(ctx, req.(*CreatePopupAdRequest))
}
return interceptor(ctx, in, info, handler)
}
func _PopupAds_UpdatePopupAd_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(UpdatePopupAdRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(PopupAdsServer).UpdatePopupAd(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: PopupAds_UpdatePopupAd_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(PopupAdsServer).UpdatePopupAd(ctx, req.(*UpdatePopupAdRequest))
}
return interceptor(ctx, in, info, handler)
}
func _PopupAds_DeletePopupAd_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(DeletePopupAdRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(PopupAdsServer).DeletePopupAd(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: PopupAds_DeletePopupAd_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(PopupAdsServer).DeletePopupAd(ctx, req.(*DeletePopupAdRequest))
}
return interceptor(ctx, in, info, handler)
}
func _PopupAds_GetActivePopupAd_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetActivePopupAdRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(PopupAdsServer).GetActivePopupAd(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: PopupAds_GetActivePopupAd_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(PopupAdsServer).GetActivePopupAd(ctx, req.(*GetActivePopupAdRequest))
}
return interceptor(ctx, in, info, handler)
}
// PopupAds_ServiceDesc is the grpc.ServiceDesc for PopupAds service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var PopupAds_ServiceDesc = grpc.ServiceDesc{
ServiceName: "stream.app.v1.PopupAds",
HandlerType: (*PopupAdsServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "ListPopupAds",
Handler: _PopupAds_ListPopupAds_Handler,
},
{
MethodName: "CreatePopupAd",
Handler: _PopupAds_CreatePopupAd_Handler,
},
{
MethodName: "UpdatePopupAd",
Handler: _PopupAds_UpdatePopupAd_Handler,
},
{
MethodName: "DeletePopupAd",
Handler: _PopupAds_DeletePopupAd_Handler,
},
{
MethodName: "GetActivePopupAd",
Handler: _PopupAds_GetActivePopupAd_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "app/v1/catalog.proto",
}
const (
PlayerConfigs_ListPlayerConfigs_FullMethodName = "/stream.app.v1.PlayerConfigs/ListPlayerConfigs"
PlayerConfigs_CreatePlayerConfig_FullMethodName = "/stream.app.v1.PlayerConfigs/CreatePlayerConfig"

View File

@@ -678,6 +678,106 @@ func (x *AdTemplate) GetUpdatedAt() *timestamppb.Timestamp {
return nil
}
type PopupAd struct {
state protoimpl.MessageState `protogen:"open.v1"`
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"`
Label string `protobuf:"bytes,3,opt,name=label,proto3" json:"label,omitempty"`
Value string `protobuf:"bytes,4,opt,name=value,proto3" json:"value,omitempty"`
IsActive bool `protobuf:"varint,5,opt,name=is_active,json=isActive,proto3" json:"is_active,omitempty"`
MaxTriggersPerSession int32 `protobuf:"varint,6,opt,name=max_triggers_per_session,json=maxTriggersPerSession,proto3" json:"max_triggers_per_session,omitempty"`
CreatedAt *timestamppb.Timestamp `protobuf:"bytes,7,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`
UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,8,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *PopupAd) Reset() {
*x = PopupAd{}
mi := &file_app_v1_common_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *PopupAd) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*PopupAd) ProtoMessage() {}
func (x *PopupAd) ProtoReflect() protoreflect.Message {
mi := &file_app_v1_common_proto_msgTypes[6]
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 PopupAd.ProtoReflect.Descriptor instead.
func (*PopupAd) Descriptor() ([]byte, []int) {
return file_app_v1_common_proto_rawDescGZIP(), []int{6}
}
func (x *PopupAd) GetId() string {
if x != nil {
return x.Id
}
return ""
}
func (x *PopupAd) GetType() string {
if x != nil {
return x.Type
}
return ""
}
func (x *PopupAd) GetLabel() string {
if x != nil {
return x.Label
}
return ""
}
func (x *PopupAd) GetValue() string {
if x != nil {
return x.Value
}
return ""
}
func (x *PopupAd) GetIsActive() bool {
if x != nil {
return x.IsActive
}
return false
}
func (x *PopupAd) GetMaxTriggersPerSession() int32 {
if x != nil {
return x.MaxTriggersPerSession
}
return 0
}
func (x *PopupAd) GetCreatedAt() *timestamppb.Timestamp {
if x != nil {
return x.CreatedAt
}
return nil
}
func (x *PopupAd) GetUpdatedAt() *timestamppb.Timestamp {
if x != nil {
return x.UpdatedAt
}
return nil
}
type PlayerConfig struct {
state protoimpl.MessageState `protogen:"open.v1"`
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
@@ -702,7 +802,7 @@ type PlayerConfig struct {
func (x *PlayerConfig) Reset() {
*x = PlayerConfig{}
mi := &file_app_v1_common_proto_msgTypes[6]
mi := &file_app_v1_common_proto_msgTypes[7]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -714,7 +814,7 @@ func (x *PlayerConfig) String() string {
func (*PlayerConfig) ProtoMessage() {}
func (x *PlayerConfig) ProtoReflect() protoreflect.Message {
mi := &file_app_v1_common_proto_msgTypes[6]
mi := &file_app_v1_common_proto_msgTypes[7]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -727,7 +827,7 @@ func (x *PlayerConfig) ProtoReflect() protoreflect.Message {
// Deprecated: Use PlayerConfig.ProtoReflect.Descriptor instead.
func (*PlayerConfig) Descriptor() ([]byte, []int) {
return file_app_v1_common_proto_rawDescGZIP(), []int{6}
return file_app_v1_common_proto_rawDescGZIP(), []int{7}
}
func (x *PlayerConfig) GetId() string {
@@ -868,7 +968,7 @@ type AdminPlayerConfig struct {
func (x *AdminPlayerConfig) Reset() {
*x = AdminPlayerConfig{}
mi := &file_app_v1_common_proto_msgTypes[7]
mi := &file_app_v1_common_proto_msgTypes[8]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -880,7 +980,7 @@ func (x *AdminPlayerConfig) String() string {
func (*AdminPlayerConfig) ProtoMessage() {}
func (x *AdminPlayerConfig) ProtoReflect() protoreflect.Message {
mi := &file_app_v1_common_proto_msgTypes[7]
mi := &file_app_v1_common_proto_msgTypes[8]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -893,7 +993,7 @@ func (x *AdminPlayerConfig) ProtoReflect() protoreflect.Message {
// Deprecated: Use AdminPlayerConfig.ProtoReflect.Descriptor instead.
func (*AdminPlayerConfig) Descriptor() ([]byte, []int) {
return file_app_v1_common_proto_rawDescGZIP(), []int{7}
return file_app_v1_common_proto_rawDescGZIP(), []int{8}
}
func (x *AdminPlayerConfig) GetId() string {
@@ -1041,7 +1141,7 @@ type Plan struct {
func (x *Plan) Reset() {
*x = Plan{}
mi := &file_app_v1_common_proto_msgTypes[8]
mi := &file_app_v1_common_proto_msgTypes[9]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1053,7 +1153,7 @@ func (x *Plan) String() string {
func (*Plan) ProtoMessage() {}
func (x *Plan) ProtoReflect() protoreflect.Message {
mi := &file_app_v1_common_proto_msgTypes[8]
mi := &file_app_v1_common_proto_msgTypes[9]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1066,7 +1166,7 @@ func (x *Plan) ProtoReflect() protoreflect.Message {
// Deprecated: Use Plan.ProtoReflect.Descriptor instead.
func (*Plan) Descriptor() ([]byte, []int) {
return file_app_v1_common_proto_rawDescGZIP(), []int{8}
return file_app_v1_common_proto_rawDescGZIP(), []int{9}
}
func (x *Plan) GetId() string {
@@ -1164,7 +1264,7 @@ type Payment struct {
func (x *Payment) Reset() {
*x = Payment{}
mi := &file_app_v1_common_proto_msgTypes[9]
mi := &file_app_v1_common_proto_msgTypes[10]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1176,7 +1276,7 @@ func (x *Payment) String() string {
func (*Payment) ProtoMessage() {}
func (x *Payment) ProtoReflect() protoreflect.Message {
mi := &file_app_v1_common_proto_msgTypes[9]
mi := &file_app_v1_common_proto_msgTypes[10]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1189,7 +1289,7 @@ func (x *Payment) ProtoReflect() protoreflect.Message {
// Deprecated: Use Payment.ProtoReflect.Descriptor instead.
func (*Payment) Descriptor() ([]byte, []int) {
return file_app_v1_common_proto_rawDescGZIP(), []int{9}
return file_app_v1_common_proto_rawDescGZIP(), []int{10}
}
func (x *Payment) GetId() string {
@@ -1282,7 +1382,7 @@ type PlanSubscription struct {
func (x *PlanSubscription) Reset() {
*x = PlanSubscription{}
mi := &file_app_v1_common_proto_msgTypes[10]
mi := &file_app_v1_common_proto_msgTypes[11]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1294,7 +1394,7 @@ func (x *PlanSubscription) String() string {
func (*PlanSubscription) ProtoMessage() {}
func (x *PlanSubscription) ProtoReflect() protoreflect.Message {
mi := &file_app_v1_common_proto_msgTypes[10]
mi := &file_app_v1_common_proto_msgTypes[11]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1307,7 +1407,7 @@ func (x *PlanSubscription) ProtoReflect() protoreflect.Message {
// Deprecated: Use PlanSubscription.ProtoReflect.Descriptor instead.
func (*PlanSubscription) Descriptor() ([]byte, []int) {
return file_app_v1_common_proto_rawDescGZIP(), []int{10}
return file_app_v1_common_proto_rawDescGZIP(), []int{11}
}
func (x *PlanSubscription) GetId() string {
@@ -1413,7 +1513,7 @@ type WalletTransaction struct {
func (x *WalletTransaction) Reset() {
*x = WalletTransaction{}
mi := &file_app_v1_common_proto_msgTypes[11]
mi := &file_app_v1_common_proto_msgTypes[12]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1425,7 +1525,7 @@ func (x *WalletTransaction) String() string {
func (*WalletTransaction) ProtoMessage() {}
func (x *WalletTransaction) ProtoReflect() protoreflect.Message {
mi := &file_app_v1_common_proto_msgTypes[11]
mi := &file_app_v1_common_proto_msgTypes[12]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1438,7 +1538,7 @@ func (x *WalletTransaction) ProtoReflect() protoreflect.Message {
// Deprecated: Use WalletTransaction.ProtoReflect.Descriptor instead.
func (*WalletTransaction) Descriptor() ([]byte, []int) {
return file_app_v1_common_proto_rawDescGZIP(), []int{11}
return file_app_v1_common_proto_rawDescGZIP(), []int{12}
}
func (x *WalletTransaction) GetId() string {
@@ -1538,7 +1638,7 @@ type PaymentHistoryItem struct {
func (x *PaymentHistoryItem) Reset() {
*x = PaymentHistoryItem{}
mi := &file_app_v1_common_proto_msgTypes[12]
mi := &file_app_v1_common_proto_msgTypes[13]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1550,7 +1650,7 @@ func (x *PaymentHistoryItem) String() string {
func (*PaymentHistoryItem) ProtoMessage() {}
func (x *PaymentHistoryItem) ProtoReflect() protoreflect.Message {
mi := &file_app_v1_common_proto_msgTypes[12]
mi := &file_app_v1_common_proto_msgTypes[13]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1563,7 +1663,7 @@ func (x *PaymentHistoryItem) ProtoReflect() protoreflect.Message {
// Deprecated: Use PaymentHistoryItem.ProtoReflect.Descriptor instead.
func (*PaymentHistoryItem) Descriptor() ([]byte, []int) {
return file_app_v1_common_proto_rawDescGZIP(), []int{12}
return file_app_v1_common_proto_rawDescGZIP(), []int{13}
}
func (x *PaymentHistoryItem) GetId() string {
@@ -1673,7 +1773,7 @@ type Video struct {
func (x *Video) Reset() {
*x = Video{}
mi := &file_app_v1_common_proto_msgTypes[13]
mi := &file_app_v1_common_proto_msgTypes[14]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1685,7 +1785,7 @@ func (x *Video) String() string {
func (*Video) ProtoMessage() {}
func (x *Video) ProtoReflect() protoreflect.Message {
mi := &file_app_v1_common_proto_msgTypes[13]
mi := &file_app_v1_common_proto_msgTypes[14]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1698,7 +1798,7 @@ func (x *Video) ProtoReflect() protoreflect.Message {
// Deprecated: Use Video.ProtoReflect.Descriptor instead.
func (*Video) Descriptor() ([]byte, []int) {
return file_app_v1_common_proto_rawDescGZIP(), []int{13}
return file_app_v1_common_proto_rawDescGZIP(), []int{14}
}
func (x *Video) GetId() string {
@@ -1823,7 +1923,7 @@ type AdminDashboard struct {
func (x *AdminDashboard) Reset() {
*x = AdminDashboard{}
mi := &file_app_v1_common_proto_msgTypes[14]
mi := &file_app_v1_common_proto_msgTypes[15]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1835,7 +1935,7 @@ func (x *AdminDashboard) String() string {
func (*AdminDashboard) ProtoMessage() {}
func (x *AdminDashboard) ProtoReflect() protoreflect.Message {
mi := &file_app_v1_common_proto_msgTypes[14]
mi := &file_app_v1_common_proto_msgTypes[15]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1848,7 +1948,7 @@ func (x *AdminDashboard) ProtoReflect() protoreflect.Message {
// Deprecated: Use AdminDashboard.ProtoReflect.Descriptor instead.
func (*AdminDashboard) Descriptor() ([]byte, []int) {
return file_app_v1_common_proto_rawDescGZIP(), []int{14}
return file_app_v1_common_proto_rawDescGZIP(), []int{15}
}
func (x *AdminDashboard) GetTotalUsers() int64 {
@@ -1934,7 +2034,7 @@ type AdminUser struct {
func (x *AdminUser) Reset() {
*x = AdminUser{}
mi := &file_app_v1_common_proto_msgTypes[15]
mi := &file_app_v1_common_proto_msgTypes[16]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1946,7 +2046,7 @@ func (x *AdminUser) String() string {
func (*AdminUser) ProtoMessage() {}
func (x *AdminUser) ProtoReflect() protoreflect.Message {
mi := &file_app_v1_common_proto_msgTypes[15]
mi := &file_app_v1_common_proto_msgTypes[16]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1959,7 +2059,7 @@ func (x *AdminUser) ProtoReflect() protoreflect.Message {
// Deprecated: Use AdminUser.ProtoReflect.Descriptor instead.
func (*AdminUser) Descriptor() ([]byte, []int) {
return file_app_v1_common_proto_rawDescGZIP(), []int{15}
return file_app_v1_common_proto_rawDescGZIP(), []int{16}
}
func (x *AdminUser) GetId() string {
@@ -2057,7 +2157,7 @@ type ReferralUserSummary struct {
func (x *ReferralUserSummary) Reset() {
*x = ReferralUserSummary{}
mi := &file_app_v1_common_proto_msgTypes[16]
mi := &file_app_v1_common_proto_msgTypes[17]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -2069,7 +2169,7 @@ func (x *ReferralUserSummary) String() string {
func (*ReferralUserSummary) ProtoMessage() {}
func (x *ReferralUserSummary) ProtoReflect() protoreflect.Message {
mi := &file_app_v1_common_proto_msgTypes[16]
mi := &file_app_v1_common_proto_msgTypes[17]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -2082,7 +2182,7 @@ func (x *ReferralUserSummary) ProtoReflect() protoreflect.Message {
// Deprecated: Use ReferralUserSummary.ProtoReflect.Descriptor instead.
func (*ReferralUserSummary) Descriptor() ([]byte, []int) {
return file_app_v1_common_proto_rawDescGZIP(), []int{16}
return file_app_v1_common_proto_rawDescGZIP(), []int{17}
}
func (x *ReferralUserSummary) GetId() string {
@@ -2123,7 +2223,7 @@ type AdminUserReferralInfo struct {
func (x *AdminUserReferralInfo) Reset() {
*x = AdminUserReferralInfo{}
mi := &file_app_v1_common_proto_msgTypes[17]
mi := &file_app_v1_common_proto_msgTypes[18]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -2135,7 +2235,7 @@ func (x *AdminUserReferralInfo) String() string {
func (*AdminUserReferralInfo) ProtoMessage() {}
func (x *AdminUserReferralInfo) ProtoReflect() protoreflect.Message {
mi := &file_app_v1_common_proto_msgTypes[17]
mi := &file_app_v1_common_proto_msgTypes[18]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -2148,7 +2248,7 @@ func (x *AdminUserReferralInfo) ProtoReflect() protoreflect.Message {
// Deprecated: Use AdminUserReferralInfo.ProtoReflect.Descriptor instead.
func (*AdminUserReferralInfo) Descriptor() ([]byte, []int) {
return file_app_v1_common_proto_rawDescGZIP(), []int{17}
return file_app_v1_common_proto_rawDescGZIP(), []int{18}
}
func (x *AdminUserReferralInfo) GetReferrer() *ReferralUserSummary {
@@ -2225,7 +2325,7 @@ type AdminUserDetail struct {
func (x *AdminUserDetail) Reset() {
*x = AdminUserDetail{}
mi := &file_app_v1_common_proto_msgTypes[18]
mi := &file_app_v1_common_proto_msgTypes[19]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -2237,7 +2337,7 @@ func (x *AdminUserDetail) String() string {
func (*AdminUserDetail) ProtoMessage() {}
func (x *AdminUserDetail) ProtoReflect() protoreflect.Message {
mi := &file_app_v1_common_proto_msgTypes[18]
mi := &file_app_v1_common_proto_msgTypes[19]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -2250,7 +2350,7 @@ func (x *AdminUserDetail) ProtoReflect() protoreflect.Message {
// Deprecated: Use AdminUserDetail.ProtoReflect.Descriptor instead.
func (*AdminUserDetail) Descriptor() ([]byte, []int) {
return file_app_v1_common_proto_rawDescGZIP(), []int{18}
return file_app_v1_common_proto_rawDescGZIP(), []int{19}
}
func (x *AdminUserDetail) GetUser() *AdminUser {
@@ -2298,7 +2398,7 @@ type AdminVideo struct {
func (x *AdminVideo) Reset() {
*x = AdminVideo{}
mi := &file_app_v1_common_proto_msgTypes[19]
mi := &file_app_v1_common_proto_msgTypes[20]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -2310,7 +2410,7 @@ func (x *AdminVideo) String() string {
func (*AdminVideo) ProtoMessage() {}
func (x *AdminVideo) ProtoReflect() protoreflect.Message {
mi := &file_app_v1_common_proto_msgTypes[19]
mi := &file_app_v1_common_proto_msgTypes[20]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -2323,7 +2423,7 @@ func (x *AdminVideo) ProtoReflect() protoreflect.Message {
// Deprecated: Use AdminVideo.ProtoReflect.Descriptor instead.
func (*AdminVideo) Descriptor() ([]byte, []int) {
return file_app_v1_common_proto_rawDescGZIP(), []int{19}
return file_app_v1_common_proto_rawDescGZIP(), []int{20}
}
func (x *AdminVideo) GetId() string {
@@ -2464,7 +2564,7 @@ type AdminPayment struct {
func (x *AdminPayment) Reset() {
*x = AdminPayment{}
mi := &file_app_v1_common_proto_msgTypes[20]
mi := &file_app_v1_common_proto_msgTypes[21]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -2476,7 +2576,7 @@ func (x *AdminPayment) String() string {
func (*AdminPayment) ProtoMessage() {}
func (x *AdminPayment) ProtoReflect() protoreflect.Message {
mi := &file_app_v1_common_proto_msgTypes[20]
mi := &file_app_v1_common_proto_msgTypes[21]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -2489,7 +2589,7 @@ func (x *AdminPayment) ProtoReflect() protoreflect.Message {
// Deprecated: Use AdminPayment.ProtoReflect.Descriptor instead.
func (*AdminPayment) Descriptor() ([]byte, []int) {
return file_app_v1_common_proto_rawDescGZIP(), []int{20}
return file_app_v1_common_proto_rawDescGZIP(), []int{21}
}
func (x *AdminPayment) GetId() string {
@@ -2640,7 +2740,7 @@ type AdminPlan struct {
func (x *AdminPlan) Reset() {
*x = AdminPlan{}
mi := &file_app_v1_common_proto_msgTypes[21]
mi := &file_app_v1_common_proto_msgTypes[22]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -2652,7 +2752,7 @@ func (x *AdminPlan) String() string {
func (*AdminPlan) ProtoMessage() {}
func (x *AdminPlan) ProtoReflect() protoreflect.Message {
mi := &file_app_v1_common_proto_msgTypes[21]
mi := &file_app_v1_common_proto_msgTypes[22]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -2665,7 +2765,7 @@ func (x *AdminPlan) ProtoReflect() protoreflect.Message {
// Deprecated: Use AdminPlan.ProtoReflect.Descriptor instead.
func (*AdminPlan) Descriptor() ([]byte, []int) {
return file_app_v1_common_proto_rawDescGZIP(), []int{21}
return file_app_v1_common_proto_rawDescGZIP(), []int{22}
}
func (x *AdminPlan) GetId() string {
@@ -2786,7 +2886,7 @@ type AdminAdTemplate struct {
func (x *AdminAdTemplate) Reset() {
*x = AdminAdTemplate{}
mi := &file_app_v1_common_proto_msgTypes[22]
mi := &file_app_v1_common_proto_msgTypes[23]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -2798,7 +2898,7 @@ func (x *AdminAdTemplate) String() string {
func (*AdminAdTemplate) ProtoMessage() {}
func (x *AdminAdTemplate) ProtoReflect() protoreflect.Message {
mi := &file_app_v1_common_proto_msgTypes[22]
mi := &file_app_v1_common_proto_msgTypes[23]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -2811,7 +2911,7 @@ func (x *AdminAdTemplate) ProtoReflect() protoreflect.Message {
// Deprecated: Use AdminAdTemplate.ProtoReflect.Descriptor instead.
func (*AdminAdTemplate) Descriptor() ([]byte, []int) {
return file_app_v1_common_proto_rawDescGZIP(), []int{22}
return file_app_v1_common_proto_rawDescGZIP(), []int{23}
}
func (x *AdminAdTemplate) GetId() string {
@@ -2898,6 +2998,122 @@ func (x *AdminAdTemplate) GetUpdatedAt() *timestamppb.Timestamp {
return nil
}
type AdminPopupAd struct {
state protoimpl.MessageState `protogen:"open.v1"`
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
UserId string `protobuf:"bytes,2,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
Type string `protobuf:"bytes,3,opt,name=type,proto3" json:"type,omitempty"`
Label string `protobuf:"bytes,4,opt,name=label,proto3" json:"label,omitempty"`
Value string `protobuf:"bytes,5,opt,name=value,proto3" json:"value,omitempty"`
IsActive bool `protobuf:"varint,6,opt,name=is_active,json=isActive,proto3" json:"is_active,omitempty"`
MaxTriggersPerSession int32 `protobuf:"varint,7,opt,name=max_triggers_per_session,json=maxTriggersPerSession,proto3" json:"max_triggers_per_session,omitempty"`
OwnerEmail *string `protobuf:"bytes,8,opt,name=owner_email,json=ownerEmail,proto3,oneof" json:"owner_email,omitempty"`
CreatedAt *timestamppb.Timestamp `protobuf:"bytes,9,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`
UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,10,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *AdminPopupAd) Reset() {
*x = AdminPopupAd{}
mi := &file_app_v1_common_proto_msgTypes[24]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *AdminPopupAd) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AdminPopupAd) ProtoMessage() {}
func (x *AdminPopupAd) ProtoReflect() protoreflect.Message {
mi := &file_app_v1_common_proto_msgTypes[24]
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 AdminPopupAd.ProtoReflect.Descriptor instead.
func (*AdminPopupAd) Descriptor() ([]byte, []int) {
return file_app_v1_common_proto_rawDescGZIP(), []int{24}
}
func (x *AdminPopupAd) GetId() string {
if x != nil {
return x.Id
}
return ""
}
func (x *AdminPopupAd) GetUserId() string {
if x != nil {
return x.UserId
}
return ""
}
func (x *AdminPopupAd) GetType() string {
if x != nil {
return x.Type
}
return ""
}
func (x *AdminPopupAd) GetLabel() string {
if x != nil {
return x.Label
}
return ""
}
func (x *AdminPopupAd) GetValue() string {
if x != nil {
return x.Value
}
return ""
}
func (x *AdminPopupAd) GetIsActive() bool {
if x != nil {
return x.IsActive
}
return false
}
func (x *AdminPopupAd) GetMaxTriggersPerSession() int32 {
if x != nil {
return x.MaxTriggersPerSession
}
return 0
}
func (x *AdminPopupAd) GetOwnerEmail() string {
if x != nil && x.OwnerEmail != nil {
return *x.OwnerEmail
}
return ""
}
func (x *AdminPopupAd) GetCreatedAt() *timestamppb.Timestamp {
if x != nil {
return x.CreatedAt
}
return nil
}
func (x *AdminPopupAd) GetUpdatedAt() *timestamppb.Timestamp {
if x != nil {
return x.UpdatedAt
}
return nil
}
type AdminJob struct {
state protoimpl.MessageState `protogen:"open.v1"`
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
@@ -2926,7 +3142,7 @@ type AdminJob struct {
func (x *AdminJob) Reset() {
*x = AdminJob{}
mi := &file_app_v1_common_proto_msgTypes[23]
mi := &file_app_v1_common_proto_msgTypes[25]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -2938,7 +3154,7 @@ func (x *AdminJob) String() string {
func (*AdminJob) ProtoMessage() {}
func (x *AdminJob) ProtoReflect() protoreflect.Message {
mi := &file_app_v1_common_proto_msgTypes[23]
mi := &file_app_v1_common_proto_msgTypes[25]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -2951,7 +3167,7 @@ func (x *AdminJob) ProtoReflect() protoreflect.Message {
// Deprecated: Use AdminJob.ProtoReflect.Descriptor instead.
func (*AdminJob) Descriptor() ([]byte, []int) {
return file_app_v1_common_proto_rawDescGZIP(), []int{23}
return file_app_v1_common_proto_rawDescGZIP(), []int{25}
}
func (x *AdminJob) GetId() string {
@@ -3114,7 +3330,7 @@ type AdminAgent struct {
func (x *AdminAgent) Reset() {
*x = AdminAgent{}
mi := &file_app_v1_common_proto_msgTypes[24]
mi := &file_app_v1_common_proto_msgTypes[26]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -3126,7 +3342,7 @@ func (x *AdminAgent) String() string {
func (*AdminAgent) ProtoMessage() {}
func (x *AdminAgent) ProtoReflect() protoreflect.Message {
mi := &file_app_v1_common_proto_msgTypes[24]
mi := &file_app_v1_common_proto_msgTypes[26]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -3139,7 +3355,7 @@ func (x *AdminAgent) ProtoReflect() protoreflect.Message {
// Deprecated: Use AdminAgent.ProtoReflect.Descriptor instead.
func (*AdminAgent) Descriptor() ([]byte, []int) {
return file_app_v1_common_proto_rawDescGZIP(), []int{24}
return file_app_v1_common_proto_rawDescGZIP(), []int{26}
}
func (x *AdminAgent) GetId() string {
@@ -3321,7 +3537,18 @@ const file_app_v1_common_proto_rawDesc = "" +
"updated_at\x18\n" +
" \x01(\v2\x1a.google.protobuf.TimestampR\tupdatedAtB\x0e\n" +
"\f_descriptionB\v\n" +
"\t_duration\"\xa6\x04\n" +
"\t_duration\"\xa5\x02\n" +
"\aPopupAd\x12\x0e\n" +
"\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n" +
"\x04type\x18\x02 \x01(\tR\x04type\x12\x14\n" +
"\x05label\x18\x03 \x01(\tR\x05label\x12\x14\n" +
"\x05value\x18\x04 \x01(\tR\x05value\x12\x1b\n" +
"\tis_active\x18\x05 \x01(\bR\bisActive\x127\n" +
"\x18max_triggers_per_session\x18\x06 \x01(\x05R\x15maxTriggersPerSession\x129\n" +
"\n" +
"created_at\x18\a \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\x129\n" +
"\n" +
"updated_at\x18\b \x01(\v2\x1a.google.protobuf.TimestampR\tupdatedAt\"\xa6\x04\n" +
"\fPlayerConfig\x12\x0e\n" +
"\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n" +
"\x04name\x18\x02 \x01(\tR\x04name\x12%\n" +
@@ -3664,6 +3891,22 @@ const file_app_v1_common_proto_rawDesc = "" +
"updated_at\x18\f \x01(\v2\x1a.google.protobuf.TimestampR\tupdatedAtB\x0e\n" +
"\f_descriptionB\v\n" +
"\t_durationB\x0e\n" +
"\f_owner_email\"\xf9\x02\n" +
"\fAdminPopupAd\x12\x0e\n" +
"\x02id\x18\x01 \x01(\tR\x02id\x12\x17\n" +
"\auser_id\x18\x02 \x01(\tR\x06userId\x12\x12\n" +
"\x04type\x18\x03 \x01(\tR\x04type\x12\x14\n" +
"\x05label\x18\x04 \x01(\tR\x05label\x12\x14\n" +
"\x05value\x18\x05 \x01(\tR\x05value\x12\x1b\n" +
"\tis_active\x18\x06 \x01(\bR\bisActive\x127\n" +
"\x18max_triggers_per_session\x18\a \x01(\x05R\x15maxTriggersPerSession\x12$\n" +
"\vowner_email\x18\b \x01(\tH\x00R\n" +
"ownerEmail\x88\x01\x01\x129\n" +
"\n" +
"created_at\x18\t \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\x129\n" +
"\n" +
"updated_at\x18\n" +
" \x01(\v2\x1a.google.protobuf.TimestampR\tupdatedAtB\x0e\n" +
"\f_owner_email\"\x98\x05\n" +
"\bAdminJob\x12\x0e\n" +
"\x02id\x18\x01 \x01(\tR\x02id\x12\x16\n" +
@@ -3725,7 +3968,7 @@ func file_app_v1_common_proto_rawDescGZIP() []byte {
return file_app_v1_common_proto_rawDescData
}
var file_app_v1_common_proto_msgTypes = make([]protoimpl.MessageInfo, 25)
var file_app_v1_common_proto_msgTypes = make([]protoimpl.MessageInfo, 27)
var file_app_v1_common_proto_goTypes = []any{
(*MessageResponse)(nil), // 0: stream.app.v1.MessageResponse
(*User)(nil), // 1: stream.app.v1.User
@@ -3733,76 +3976,82 @@ var file_app_v1_common_proto_goTypes = []any{
(*Notification)(nil), // 3: stream.app.v1.Notification
(*Domain)(nil), // 4: stream.app.v1.Domain
(*AdTemplate)(nil), // 5: stream.app.v1.AdTemplate
(*PlayerConfig)(nil), // 6: stream.app.v1.PlayerConfig
(*AdminPlayerConfig)(nil), // 7: stream.app.v1.AdminPlayerConfig
(*Plan)(nil), // 8: stream.app.v1.Plan
(*Payment)(nil), // 9: stream.app.v1.Payment
(*PlanSubscription)(nil), // 10: stream.app.v1.PlanSubscription
(*WalletTransaction)(nil), // 11: stream.app.v1.WalletTransaction
(*PaymentHistoryItem)(nil), // 12: stream.app.v1.PaymentHistoryItem
(*Video)(nil), // 13: stream.app.v1.Video
(*AdminDashboard)(nil), // 14: stream.app.v1.AdminDashboard
(*AdminUser)(nil), // 15: stream.app.v1.AdminUser
(*ReferralUserSummary)(nil), // 16: stream.app.v1.ReferralUserSummary
(*AdminUserReferralInfo)(nil), // 17: stream.app.v1.AdminUserReferralInfo
(*AdminUserDetail)(nil), // 18: stream.app.v1.AdminUserDetail
(*AdminVideo)(nil), // 19: stream.app.v1.AdminVideo
(*AdminPayment)(nil), // 20: stream.app.v1.AdminPayment
(*AdminPlan)(nil), // 21: stream.app.v1.AdminPlan
(*AdminAdTemplate)(nil), // 22: stream.app.v1.AdminAdTemplate
(*AdminJob)(nil), // 23: stream.app.v1.AdminJob
(*AdminAgent)(nil), // 24: stream.app.v1.AdminAgent
(*timestamppb.Timestamp)(nil), // 25: google.protobuf.Timestamp
(*PopupAd)(nil), // 6: stream.app.v1.PopupAd
(*PlayerConfig)(nil), // 7: stream.app.v1.PlayerConfig
(*AdminPlayerConfig)(nil), // 8: stream.app.v1.AdminPlayerConfig
(*Plan)(nil), // 9: stream.app.v1.Plan
(*Payment)(nil), // 10: stream.app.v1.Payment
(*PlanSubscription)(nil), // 11: stream.app.v1.PlanSubscription
(*WalletTransaction)(nil), // 12: stream.app.v1.WalletTransaction
(*PaymentHistoryItem)(nil), // 13: stream.app.v1.PaymentHistoryItem
(*Video)(nil), // 14: stream.app.v1.Video
(*AdminDashboard)(nil), // 15: stream.app.v1.AdminDashboard
(*AdminUser)(nil), // 16: stream.app.v1.AdminUser
(*ReferralUserSummary)(nil), // 17: stream.app.v1.ReferralUserSummary
(*AdminUserReferralInfo)(nil), // 18: stream.app.v1.AdminUserReferralInfo
(*AdminUserDetail)(nil), // 19: stream.app.v1.AdminUserDetail
(*AdminVideo)(nil), // 20: stream.app.v1.AdminVideo
(*AdminPayment)(nil), // 21: stream.app.v1.AdminPayment
(*AdminPlan)(nil), // 22: stream.app.v1.AdminPlan
(*AdminAdTemplate)(nil), // 23: stream.app.v1.AdminAdTemplate
(*AdminPopupAd)(nil), // 24: stream.app.v1.AdminPopupAd
(*AdminJob)(nil), // 25: stream.app.v1.AdminJob
(*AdminAgent)(nil), // 26: stream.app.v1.AdminAgent
(*timestamppb.Timestamp)(nil), // 27: google.protobuf.Timestamp
}
var file_app_v1_common_proto_depIdxs = []int32{
25, // 0: stream.app.v1.User.plan_started_at:type_name -> google.protobuf.Timestamp
25, // 1: stream.app.v1.User.plan_expires_at:type_name -> google.protobuf.Timestamp
25, // 2: stream.app.v1.User.created_at:type_name -> google.protobuf.Timestamp
25, // 3: stream.app.v1.User.updated_at:type_name -> google.protobuf.Timestamp
25, // 4: stream.app.v1.Notification.created_at:type_name -> google.protobuf.Timestamp
25, // 5: stream.app.v1.Domain.created_at:type_name -> google.protobuf.Timestamp
25, // 6: stream.app.v1.Domain.updated_at:type_name -> google.protobuf.Timestamp
25, // 7: stream.app.v1.AdTemplate.created_at:type_name -> google.protobuf.Timestamp
25, // 8: stream.app.v1.AdTemplate.updated_at:type_name -> google.protobuf.Timestamp
25, // 9: stream.app.v1.PlayerConfig.created_at:type_name -> google.protobuf.Timestamp
25, // 10: stream.app.v1.PlayerConfig.updated_at:type_name -> google.protobuf.Timestamp
25, // 11: stream.app.v1.AdminPlayerConfig.created_at:type_name -> google.protobuf.Timestamp
25, // 12: stream.app.v1.AdminPlayerConfig.updated_at:type_name -> google.protobuf.Timestamp
25, // 13: stream.app.v1.Payment.created_at:type_name -> google.protobuf.Timestamp
25, // 14: stream.app.v1.Payment.updated_at:type_name -> google.protobuf.Timestamp
25, // 15: stream.app.v1.PlanSubscription.started_at:type_name -> google.protobuf.Timestamp
25, // 16: stream.app.v1.PlanSubscription.expires_at:type_name -> google.protobuf.Timestamp
25, // 17: stream.app.v1.PlanSubscription.created_at:type_name -> google.protobuf.Timestamp
25, // 18: stream.app.v1.PlanSubscription.updated_at:type_name -> google.protobuf.Timestamp
25, // 19: stream.app.v1.WalletTransaction.created_at:type_name -> google.protobuf.Timestamp
25, // 20: stream.app.v1.WalletTransaction.updated_at:type_name -> google.protobuf.Timestamp
25, // 21: stream.app.v1.PaymentHistoryItem.expires_at:type_name -> google.protobuf.Timestamp
25, // 22: stream.app.v1.PaymentHistoryItem.created_at:type_name -> google.protobuf.Timestamp
25, // 23: stream.app.v1.Video.created_at:type_name -> google.protobuf.Timestamp
25, // 24: stream.app.v1.Video.updated_at:type_name -> google.protobuf.Timestamp
25, // 25: stream.app.v1.AdminUser.created_at:type_name -> google.protobuf.Timestamp
25, // 26: stream.app.v1.AdminUser.updated_at:type_name -> google.protobuf.Timestamp
16, // 27: stream.app.v1.AdminUserReferralInfo.referrer:type_name -> stream.app.v1.ReferralUserSummary
25, // 28: stream.app.v1.AdminUserReferralInfo.reward_granted_at:type_name -> google.protobuf.Timestamp
15, // 29: stream.app.v1.AdminUserDetail.user:type_name -> stream.app.v1.AdminUser
10, // 30: stream.app.v1.AdminUserDetail.subscription:type_name -> stream.app.v1.PlanSubscription
17, // 31: stream.app.v1.AdminUserDetail.referral:type_name -> stream.app.v1.AdminUserReferralInfo
25, // 32: stream.app.v1.AdminVideo.created_at:type_name -> google.protobuf.Timestamp
25, // 33: stream.app.v1.AdminVideo.updated_at:type_name -> google.protobuf.Timestamp
25, // 34: stream.app.v1.AdminPayment.created_at:type_name -> google.protobuf.Timestamp
25, // 35: stream.app.v1.AdminPayment.updated_at:type_name -> google.protobuf.Timestamp
25, // 36: stream.app.v1.AdminAdTemplate.created_at:type_name -> google.protobuf.Timestamp
25, // 37: stream.app.v1.AdminAdTemplate.updated_at:type_name -> google.protobuf.Timestamp
25, // 38: stream.app.v1.AdminJob.created_at:type_name -> google.protobuf.Timestamp
25, // 39: stream.app.v1.AdminJob.updated_at:type_name -> google.protobuf.Timestamp
25, // 40: stream.app.v1.AdminAgent.last_heartbeat:type_name -> google.protobuf.Timestamp
25, // 41: stream.app.v1.AdminAgent.created_at:type_name -> google.protobuf.Timestamp
25, // 42: stream.app.v1.AdminAgent.updated_at:type_name -> google.protobuf.Timestamp
43, // [43:43] is the sub-list for method output_type
43, // [43:43] 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
27, // 0: stream.app.v1.User.plan_started_at:type_name -> google.protobuf.Timestamp
27, // 1: stream.app.v1.User.plan_expires_at:type_name -> google.protobuf.Timestamp
27, // 2: stream.app.v1.User.created_at:type_name -> google.protobuf.Timestamp
27, // 3: stream.app.v1.User.updated_at:type_name -> google.protobuf.Timestamp
27, // 4: stream.app.v1.Notification.created_at:type_name -> google.protobuf.Timestamp
27, // 5: stream.app.v1.Domain.created_at:type_name -> google.protobuf.Timestamp
27, // 6: stream.app.v1.Domain.updated_at:type_name -> google.protobuf.Timestamp
27, // 7: stream.app.v1.AdTemplate.created_at:type_name -> google.protobuf.Timestamp
27, // 8: stream.app.v1.AdTemplate.updated_at:type_name -> google.protobuf.Timestamp
27, // 9: stream.app.v1.PopupAd.created_at:type_name -> google.protobuf.Timestamp
27, // 10: stream.app.v1.PopupAd.updated_at:type_name -> google.protobuf.Timestamp
27, // 11: stream.app.v1.PlayerConfig.created_at:type_name -> google.protobuf.Timestamp
27, // 12: stream.app.v1.PlayerConfig.updated_at:type_name -> google.protobuf.Timestamp
27, // 13: stream.app.v1.AdminPlayerConfig.created_at:type_name -> google.protobuf.Timestamp
27, // 14: stream.app.v1.AdminPlayerConfig.updated_at:type_name -> google.protobuf.Timestamp
27, // 15: stream.app.v1.Payment.created_at:type_name -> google.protobuf.Timestamp
27, // 16: stream.app.v1.Payment.updated_at:type_name -> google.protobuf.Timestamp
27, // 17: stream.app.v1.PlanSubscription.started_at:type_name -> google.protobuf.Timestamp
27, // 18: stream.app.v1.PlanSubscription.expires_at:type_name -> google.protobuf.Timestamp
27, // 19: stream.app.v1.PlanSubscription.created_at:type_name -> google.protobuf.Timestamp
27, // 20: stream.app.v1.PlanSubscription.updated_at:type_name -> google.protobuf.Timestamp
27, // 21: stream.app.v1.WalletTransaction.created_at:type_name -> google.protobuf.Timestamp
27, // 22: stream.app.v1.WalletTransaction.updated_at:type_name -> google.protobuf.Timestamp
27, // 23: stream.app.v1.PaymentHistoryItem.expires_at:type_name -> google.protobuf.Timestamp
27, // 24: stream.app.v1.PaymentHistoryItem.created_at:type_name -> google.protobuf.Timestamp
27, // 25: stream.app.v1.Video.created_at:type_name -> google.protobuf.Timestamp
27, // 26: stream.app.v1.Video.updated_at:type_name -> google.protobuf.Timestamp
27, // 27: stream.app.v1.AdminUser.created_at:type_name -> google.protobuf.Timestamp
27, // 28: stream.app.v1.AdminUser.updated_at:type_name -> google.protobuf.Timestamp
17, // 29: stream.app.v1.AdminUserReferralInfo.referrer:type_name -> stream.app.v1.ReferralUserSummary
27, // 30: stream.app.v1.AdminUserReferralInfo.reward_granted_at:type_name -> google.protobuf.Timestamp
16, // 31: stream.app.v1.AdminUserDetail.user:type_name -> stream.app.v1.AdminUser
11, // 32: stream.app.v1.AdminUserDetail.subscription:type_name -> stream.app.v1.PlanSubscription
18, // 33: stream.app.v1.AdminUserDetail.referral:type_name -> stream.app.v1.AdminUserReferralInfo
27, // 34: stream.app.v1.AdminVideo.created_at:type_name -> google.protobuf.Timestamp
27, // 35: stream.app.v1.AdminVideo.updated_at:type_name -> google.protobuf.Timestamp
27, // 36: stream.app.v1.AdminPayment.created_at:type_name -> google.protobuf.Timestamp
27, // 37: stream.app.v1.AdminPayment.updated_at:type_name -> google.protobuf.Timestamp
27, // 38: stream.app.v1.AdminAdTemplate.created_at:type_name -> google.protobuf.Timestamp
27, // 39: stream.app.v1.AdminAdTemplate.updated_at:type_name -> google.protobuf.Timestamp
27, // 40: stream.app.v1.AdminPopupAd.created_at:type_name -> google.protobuf.Timestamp
27, // 41: stream.app.v1.AdminPopupAd.updated_at:type_name -> google.protobuf.Timestamp
27, // 42: stream.app.v1.AdminJob.created_at:type_name -> google.protobuf.Timestamp
27, // 43: stream.app.v1.AdminJob.updated_at:type_name -> google.protobuf.Timestamp
27, // 44: stream.app.v1.AdminAgent.last_heartbeat:type_name -> google.protobuf.Timestamp
27, // 45: stream.app.v1.AdminAgent.created_at:type_name -> google.protobuf.Timestamp
27, // 46: stream.app.v1.AdminAgent.updated_at:type_name -> google.protobuf.Timestamp
47, // [47:47] is the sub-list for method output_type
47, // [47:47] 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_app_v1_common_proto_init() }
@@ -3813,14 +4062,13 @@ func file_app_v1_common_proto_init() {
file_app_v1_common_proto_msgTypes[1].OneofWrappers = []any{}
file_app_v1_common_proto_msgTypes[3].OneofWrappers = []any{}
file_app_v1_common_proto_msgTypes[5].OneofWrappers = []any{}
file_app_v1_common_proto_msgTypes[6].OneofWrappers = []any{}
file_app_v1_common_proto_msgTypes[7].OneofWrappers = []any{}
file_app_v1_common_proto_msgTypes[8].OneofWrappers = []any{}
file_app_v1_common_proto_msgTypes[9].OneofWrappers = []any{}
file_app_v1_common_proto_msgTypes[11].OneofWrappers = []any{}
file_app_v1_common_proto_msgTypes[10].OneofWrappers = []any{}
file_app_v1_common_proto_msgTypes[12].OneofWrappers = []any{}
file_app_v1_common_proto_msgTypes[13].OneofWrappers = []any{}
file_app_v1_common_proto_msgTypes[15].OneofWrappers = []any{}
file_app_v1_common_proto_msgTypes[14].OneofWrappers = []any{}
file_app_v1_common_proto_msgTypes[16].OneofWrappers = []any{}
file_app_v1_common_proto_msgTypes[17].OneofWrappers = []any{}
file_app_v1_common_proto_msgTypes[18].OneofWrappers = []any{}
@@ -3829,13 +4077,15 @@ func file_app_v1_common_proto_init() {
file_app_v1_common_proto_msgTypes[21].OneofWrappers = []any{}
file_app_v1_common_proto_msgTypes[22].OneofWrappers = []any{}
file_app_v1_common_proto_msgTypes[23].OneofWrappers = []any{}
file_app_v1_common_proto_msgTypes[24].OneofWrappers = []any{}
file_app_v1_common_proto_msgTypes[25].OneofWrappers = []any{}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_v1_common_proto_rawDesc), len(file_app_v1_common_proto_rawDesc)),
NumEnums: 0,
NumMessages: 25,
NumMessages: 27,
NumExtensions: 0,
NumServices: 0,
},

View File

@@ -24,7 +24,7 @@ type PlayerConfig struct {
Airplay *bool `gorm:"column:airplay;type:boolean;not null;default:true" json:"airplay"`
Chromecast *bool `gorm:"column:chromecast;type:boolean;not null;default:true" json:"chromecast"`
IsActive *bool `gorm:"column:is_active;type:boolean;not null;default:true" json:"is_active"`
IsDefault bool `gorm:"column:is_default;type:boolean;not null;index:idx_player_configs_user_default,priority:1;index:idx_player_configs_is_default,priority:1" json:"is_default"`
IsDefault bool `gorm:"column:is_default;type:boolean;not null;index:idx_player_configs_is_default,priority:1;index:idx_player_configs_user_default,priority:1" json:"is_default"`
CreatedAt *time.Time `gorm:"column:created_at;type:timestamp(3) without time zone;not null;default:CURRENT_TIMESTAMP" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at;type:timestamp(3) without time zone;not null" json:"updated_at"`
Version *int64 `gorm:"column:version;type:bigint;not null;default:1;version" json:"-"`

View File

@@ -0,0 +1,30 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package model
import (
"time"
)
const TableNamePopupAd = "popup_ads"
// PopupAd mapped from table <popup_ads>
type PopupAd struct {
ID string `gorm:"column:id;type:uuid;primaryKey" json:"id"`
UserID string `gorm:"column:user_id;type:uuid;not null;index:idx_popup_ads_user_id,priority:1;index:idx_popup_ads_user_active,priority:2" json:"user_id"`
Type string `gorm:"column:type;type:character varying(20);not null" json:"type"`
Label string `gorm:"column:label;type:text;not null" json:"label"`
Value string `gorm:"column:value;type:text;not null" json:"value"`
IsActive *bool `gorm:"column:is_active;type:boolean;not null;index:idx_popup_ads_user_active,priority:1;default:true" json:"is_active"`
MaxTriggersPerSession *int32 `gorm:"column:max_triggers_per_session;type:integer;not null;default:3" json:"max_triggers_per_session"`
CreatedAt *time.Time `gorm:"column:created_at;type:timestamp with time zone;default:CURRENT_TIMESTAMP" json:"created_at"`
UpdatedAt *time.Time `gorm:"column:updated_at;type:timestamp with time zone" json:"updated_at"`
Version *int64 `gorm:"column:version;type:bigint;not null;default:1;version" json:"-"`
}
// TableName PopupAd's table name
func (*PopupAd) TableName() string {
return TableNamePopupAd
}

View File

@@ -25,6 +25,7 @@ var (
Plan *plan
PlanSubscription *planSubscription
PlayerConfig *playerConfig
PopupAd *popupAd
User *user
UserPreference *userPreference
Video *video
@@ -41,6 +42,7 @@ func SetDefault(db *gorm.DB, opts ...gen.DOOption) {
Plan = &Q.Plan
PlanSubscription = &Q.PlanSubscription
PlayerConfig = &Q.PlayerConfig
PopupAd = &Q.PopupAd
User = &Q.User
UserPreference = &Q.UserPreference
Video = &Q.Video
@@ -58,6 +60,7 @@ func Use(db *gorm.DB, opts ...gen.DOOption) *Query {
Plan: newPlan(db, opts...),
PlanSubscription: newPlanSubscription(db, opts...),
PlayerConfig: newPlayerConfig(db, opts...),
PopupAd: newPopupAd(db, opts...),
User: newUser(db, opts...),
UserPreference: newUserPreference(db, opts...),
Video: newVideo(db, opts...),
@@ -76,6 +79,7 @@ type Query struct {
Plan plan
PlanSubscription planSubscription
PlayerConfig playerConfig
PopupAd popupAd
User user
UserPreference userPreference
Video video
@@ -95,6 +99,7 @@ func (q *Query) clone(db *gorm.DB) *Query {
Plan: q.Plan.clone(db),
PlanSubscription: q.PlanSubscription.clone(db),
PlayerConfig: q.PlayerConfig.clone(db),
PopupAd: q.PopupAd.clone(db),
User: q.User.clone(db),
UserPreference: q.UserPreference.clone(db),
Video: q.Video.clone(db),
@@ -121,6 +126,7 @@ func (q *Query) ReplaceDB(db *gorm.DB) *Query {
Plan: q.Plan.replaceDB(db),
PlanSubscription: q.PlanSubscription.replaceDB(db),
PlayerConfig: q.PlayerConfig.replaceDB(db),
PopupAd: q.PopupAd.replaceDB(db),
User: q.User.replaceDB(db),
UserPreference: q.UserPreference.replaceDB(db),
Video: q.Video.replaceDB(db),
@@ -137,6 +143,7 @@ type queryCtx struct {
Plan IPlanDo
PlanSubscription IPlanSubscriptionDo
PlayerConfig IPlayerConfigDo
PopupAd IPopupAdDo
User IUserDo
UserPreference IUserPreferenceDo
Video IVideoDo
@@ -153,6 +160,7 @@ func (q *Query) WithContext(ctx context.Context) *queryCtx {
Plan: q.Plan.WithContext(ctx),
PlanSubscription: q.PlanSubscription.WithContext(ctx),
PlayerConfig: q.PlayerConfig.WithContext(ctx),
PopupAd: q.PopupAd.WithContext(ctx),
User: q.User.WithContext(ctx),
UserPreference: q.UserPreference.WithContext(ctx),
Video: q.Video.WithContext(ctx),

View File

@@ -0,0 +1,427 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package query
import (
"context"
"database/sql"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"gorm.io/gorm/schema"
"gorm.io/gen"
"gorm.io/gen/field"
"gorm.io/plugin/dbresolver"
"stream.api/internal/database/model"
)
func newPopupAd(db *gorm.DB, opts ...gen.DOOption) popupAd {
_popupAd := popupAd{}
_popupAd.popupAdDo.UseDB(db, opts...)
_popupAd.popupAdDo.UseModel(&model.PopupAd{})
tableName := _popupAd.popupAdDo.TableName()
_popupAd.ALL = field.NewAsterisk(tableName)
_popupAd.ID = field.NewString(tableName, "id")
_popupAd.UserID = field.NewString(tableName, "user_id")
_popupAd.Type = field.NewString(tableName, "type")
_popupAd.Label = field.NewString(tableName, "label")
_popupAd.Value = field.NewString(tableName, "value")
_popupAd.IsActive = field.NewBool(tableName, "is_active")
_popupAd.MaxTriggersPerSession = field.NewInt32(tableName, "max_triggers_per_session")
_popupAd.CreatedAt = field.NewTime(tableName, "created_at")
_popupAd.UpdatedAt = field.NewTime(tableName, "updated_at")
_popupAd.Version = field.NewInt64(tableName, "version")
_popupAd.fillFieldMap()
return _popupAd
}
type popupAd struct {
popupAdDo popupAdDo
ALL field.Asterisk
ID field.String
UserID field.String
Type field.String
Label field.String
Value field.String
IsActive field.Bool
MaxTriggersPerSession field.Int32
CreatedAt field.Time
UpdatedAt field.Time
Version field.Int64
fieldMap map[string]field.Expr
}
func (p popupAd) Table(newTableName string) *popupAd {
p.popupAdDo.UseTable(newTableName)
return p.updateTableName(newTableName)
}
func (p popupAd) As(alias string) *popupAd {
p.popupAdDo.DO = *(p.popupAdDo.As(alias).(*gen.DO))
return p.updateTableName(alias)
}
func (p *popupAd) updateTableName(table string) *popupAd {
p.ALL = field.NewAsterisk(table)
p.ID = field.NewString(table, "id")
p.UserID = field.NewString(table, "user_id")
p.Type = field.NewString(table, "type")
p.Label = field.NewString(table, "label")
p.Value = field.NewString(table, "value")
p.IsActive = field.NewBool(table, "is_active")
p.MaxTriggersPerSession = field.NewInt32(table, "max_triggers_per_session")
p.CreatedAt = field.NewTime(table, "created_at")
p.UpdatedAt = field.NewTime(table, "updated_at")
p.Version = field.NewInt64(table, "version")
p.fillFieldMap()
return p
}
func (p *popupAd) WithContext(ctx context.Context) IPopupAdDo { return p.popupAdDo.WithContext(ctx) }
func (p popupAd) TableName() string { return p.popupAdDo.TableName() }
func (p popupAd) Alias() string { return p.popupAdDo.Alias() }
func (p popupAd) Columns(cols ...field.Expr) gen.Columns { return p.popupAdDo.Columns(cols...) }
func (p *popupAd) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
_f, ok := p.fieldMap[fieldName]
if !ok || _f == nil {
return nil, false
}
_oe, ok := _f.(field.OrderExpr)
return _oe, ok
}
func (p *popupAd) fillFieldMap() {
p.fieldMap = make(map[string]field.Expr, 10)
p.fieldMap["id"] = p.ID
p.fieldMap["user_id"] = p.UserID
p.fieldMap["type"] = p.Type
p.fieldMap["label"] = p.Label
p.fieldMap["value"] = p.Value
p.fieldMap["is_active"] = p.IsActive
p.fieldMap["max_triggers_per_session"] = p.MaxTriggersPerSession
p.fieldMap["created_at"] = p.CreatedAt
p.fieldMap["updated_at"] = p.UpdatedAt
p.fieldMap["version"] = p.Version
}
func (p popupAd) clone(db *gorm.DB) popupAd {
p.popupAdDo.ReplaceConnPool(db.Statement.ConnPool)
return p
}
func (p popupAd) replaceDB(db *gorm.DB) popupAd {
p.popupAdDo.ReplaceDB(db)
return p
}
type popupAdDo struct{ gen.DO }
type IPopupAdDo interface {
gen.SubQuery
Debug() IPopupAdDo
WithContext(ctx context.Context) IPopupAdDo
WithResult(fc func(tx gen.Dao)) gen.ResultInfo
ReplaceDB(db *gorm.DB)
ReadDB() IPopupAdDo
WriteDB() IPopupAdDo
As(alias string) gen.Dao
Session(config *gorm.Session) IPopupAdDo
Columns(cols ...field.Expr) gen.Columns
Clauses(conds ...clause.Expression) IPopupAdDo
Not(conds ...gen.Condition) IPopupAdDo
Or(conds ...gen.Condition) IPopupAdDo
Select(conds ...field.Expr) IPopupAdDo
Where(conds ...gen.Condition) IPopupAdDo
Order(conds ...field.Expr) IPopupAdDo
Distinct(cols ...field.Expr) IPopupAdDo
Omit(cols ...field.Expr) IPopupAdDo
Join(table schema.Tabler, on ...field.Expr) IPopupAdDo
LeftJoin(table schema.Tabler, on ...field.Expr) IPopupAdDo
RightJoin(table schema.Tabler, on ...field.Expr) IPopupAdDo
Group(cols ...field.Expr) IPopupAdDo
Having(conds ...gen.Condition) IPopupAdDo
Limit(limit int) IPopupAdDo
Offset(offset int) IPopupAdDo
Count() (count int64, err error)
Scopes(funcs ...func(gen.Dao) gen.Dao) IPopupAdDo
Unscoped() IPopupAdDo
Create(values ...*model.PopupAd) error
CreateInBatches(values []*model.PopupAd, batchSize int) error
Save(values ...*model.PopupAd) error
First() (*model.PopupAd, error)
Take() (*model.PopupAd, error)
Last() (*model.PopupAd, error)
Find() ([]*model.PopupAd, error)
FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.PopupAd, err error)
FindInBatches(result *[]*model.PopupAd, batchSize int, fc func(tx gen.Dao, batch int) error) error
Pluck(column field.Expr, dest interface{}) error
Delete(...*model.PopupAd) (info gen.ResultInfo, err error)
Update(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
UpdateSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
Updates(value interface{}) (info gen.ResultInfo, err error)
UpdateColumn(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
UpdateColumnSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
UpdateColumns(value interface{}) (info gen.ResultInfo, err error)
UpdateFrom(q gen.SubQuery) gen.Dao
Attrs(attrs ...field.AssignExpr) IPopupAdDo
Assign(attrs ...field.AssignExpr) IPopupAdDo
Joins(fields ...field.RelationField) IPopupAdDo
Preload(fields ...field.RelationField) IPopupAdDo
FirstOrInit() (*model.PopupAd, error)
FirstOrCreate() (*model.PopupAd, error)
FindByPage(offset int, limit int) (result []*model.PopupAd, count int64, err error)
ScanByPage(result interface{}, offset int, limit int) (count int64, err error)
Rows() (*sql.Rows, error)
Row() *sql.Row
Scan(result interface{}) (err error)
Returning(value interface{}, columns ...string) IPopupAdDo
UnderlyingDB() *gorm.DB
schema.Tabler
}
func (p popupAdDo) Debug() IPopupAdDo {
return p.withDO(p.DO.Debug())
}
func (p popupAdDo) WithContext(ctx context.Context) IPopupAdDo {
return p.withDO(p.DO.WithContext(ctx))
}
func (p popupAdDo) ReadDB() IPopupAdDo {
return p.Clauses(dbresolver.Read)
}
func (p popupAdDo) WriteDB() IPopupAdDo {
return p.Clauses(dbresolver.Write)
}
func (p popupAdDo) Session(config *gorm.Session) IPopupAdDo {
return p.withDO(p.DO.Session(config))
}
func (p popupAdDo) Clauses(conds ...clause.Expression) IPopupAdDo {
return p.withDO(p.DO.Clauses(conds...))
}
func (p popupAdDo) Returning(value interface{}, columns ...string) IPopupAdDo {
return p.withDO(p.DO.Returning(value, columns...))
}
func (p popupAdDo) Not(conds ...gen.Condition) IPopupAdDo {
return p.withDO(p.DO.Not(conds...))
}
func (p popupAdDo) Or(conds ...gen.Condition) IPopupAdDo {
return p.withDO(p.DO.Or(conds...))
}
func (p popupAdDo) Select(conds ...field.Expr) IPopupAdDo {
return p.withDO(p.DO.Select(conds...))
}
func (p popupAdDo) Where(conds ...gen.Condition) IPopupAdDo {
return p.withDO(p.DO.Where(conds...))
}
func (p popupAdDo) Order(conds ...field.Expr) IPopupAdDo {
return p.withDO(p.DO.Order(conds...))
}
func (p popupAdDo) Distinct(cols ...field.Expr) IPopupAdDo {
return p.withDO(p.DO.Distinct(cols...))
}
func (p popupAdDo) Omit(cols ...field.Expr) IPopupAdDo {
return p.withDO(p.DO.Omit(cols...))
}
func (p popupAdDo) Join(table schema.Tabler, on ...field.Expr) IPopupAdDo {
return p.withDO(p.DO.Join(table, on...))
}
func (p popupAdDo) LeftJoin(table schema.Tabler, on ...field.Expr) IPopupAdDo {
return p.withDO(p.DO.LeftJoin(table, on...))
}
func (p popupAdDo) RightJoin(table schema.Tabler, on ...field.Expr) IPopupAdDo {
return p.withDO(p.DO.RightJoin(table, on...))
}
func (p popupAdDo) Group(cols ...field.Expr) IPopupAdDo {
return p.withDO(p.DO.Group(cols...))
}
func (p popupAdDo) Having(conds ...gen.Condition) IPopupAdDo {
return p.withDO(p.DO.Having(conds...))
}
func (p popupAdDo) Limit(limit int) IPopupAdDo {
return p.withDO(p.DO.Limit(limit))
}
func (p popupAdDo) Offset(offset int) IPopupAdDo {
return p.withDO(p.DO.Offset(offset))
}
func (p popupAdDo) Scopes(funcs ...func(gen.Dao) gen.Dao) IPopupAdDo {
return p.withDO(p.DO.Scopes(funcs...))
}
func (p popupAdDo) Unscoped() IPopupAdDo {
return p.withDO(p.DO.Unscoped())
}
func (p popupAdDo) Create(values ...*model.PopupAd) error {
if len(values) == 0 {
return nil
}
return p.DO.Create(values)
}
func (p popupAdDo) CreateInBatches(values []*model.PopupAd, batchSize int) error {
return p.DO.CreateInBatches(values, batchSize)
}
// Save : !!! underlying implementation is different with GORM
// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
func (p popupAdDo) Save(values ...*model.PopupAd) error {
if len(values) == 0 {
return nil
}
return p.DO.Save(values)
}
func (p popupAdDo) First() (*model.PopupAd, error) {
if result, err := p.DO.First(); err != nil {
return nil, err
} else {
return result.(*model.PopupAd), nil
}
}
func (p popupAdDo) Take() (*model.PopupAd, error) {
if result, err := p.DO.Take(); err != nil {
return nil, err
} else {
return result.(*model.PopupAd), nil
}
}
func (p popupAdDo) Last() (*model.PopupAd, error) {
if result, err := p.DO.Last(); err != nil {
return nil, err
} else {
return result.(*model.PopupAd), nil
}
}
func (p popupAdDo) Find() ([]*model.PopupAd, error) {
result, err := p.DO.Find()
return result.([]*model.PopupAd), err
}
func (p popupAdDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.PopupAd, err error) {
buf := make([]*model.PopupAd, 0, batchSize)
err = p.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
defer func() { results = append(results, buf...) }()
return fc(tx, batch)
})
return results, err
}
func (p popupAdDo) FindInBatches(result *[]*model.PopupAd, batchSize int, fc func(tx gen.Dao, batch int) error) error {
return p.DO.FindInBatches(result, batchSize, fc)
}
func (p popupAdDo) Attrs(attrs ...field.AssignExpr) IPopupAdDo {
return p.withDO(p.DO.Attrs(attrs...))
}
func (p popupAdDo) Assign(attrs ...field.AssignExpr) IPopupAdDo {
return p.withDO(p.DO.Assign(attrs...))
}
func (p popupAdDo) Joins(fields ...field.RelationField) IPopupAdDo {
for _, _f := range fields {
p = *p.withDO(p.DO.Joins(_f))
}
return &p
}
func (p popupAdDo) Preload(fields ...field.RelationField) IPopupAdDo {
for _, _f := range fields {
p = *p.withDO(p.DO.Preload(_f))
}
return &p
}
func (p popupAdDo) FirstOrInit() (*model.PopupAd, error) {
if result, err := p.DO.FirstOrInit(); err != nil {
return nil, err
} else {
return result.(*model.PopupAd), nil
}
}
func (p popupAdDo) FirstOrCreate() (*model.PopupAd, error) {
if result, err := p.DO.FirstOrCreate(); err != nil {
return nil, err
} else {
return result.(*model.PopupAd), nil
}
}
func (p popupAdDo) FindByPage(offset int, limit int) (result []*model.PopupAd, count int64, err error) {
result, err = p.Offset(offset).Limit(limit).Find()
if err != nil {
return
}
if size := len(result); 0 < limit && 0 < size && size < limit {
count = int64(size + offset)
return
}
count, err = p.Offset(-1).Limit(-1).Count()
return
}
func (p popupAdDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
count, err = p.Count()
if err != nil {
return
}
err = p.Offset(offset).Limit(limit).Scan(result)
return
}
func (p popupAdDo) Scan(result interface{}) (err error) {
return p.DO.Scan(result)
}
func (p popupAdDo) Delete(models ...*model.PopupAd) (result gen.ResultInfo, err error) {
return p.DO.Delete(models)
}
func (p *popupAdDo) withDO(do gen.Dao) *popupAdDo {
p.DO = *do.(*gen.DO)
return p
}

View File

@@ -36,17 +36,23 @@ type Actor struct {
Role string
}
type Authenticator struct {
db *gorm.DB
logger logger.Logger
trustedMarker string
type NotificationEventPublisher interface {
PublishNotificationCreated(ctx context.Context, notification *model.Notification) error
}
func NewAuthenticator(db *gorm.DB, l logger.Logger, trustedMarker string) *Authenticator {
type Authenticator struct {
db *gorm.DB
logger logger.Logger
trustedMarker string
notificationEvents NotificationEventPublisher
}
func NewAuthenticator(db *gorm.DB, l logger.Logger, trustedMarker string, notificationEvents NotificationEventPublisher) *Authenticator {
return &Authenticator{
db: db,
logger: l,
trustedMarker: strings.TrimSpace(trustedMarker),
db: db,
logger: l,
trustedMarker: strings.TrimSpace(trustedMarker),
notificationEvents: notificationEvents,
}
}
@@ -210,6 +216,11 @@ func (a *Authenticator) maybeCreateSubscriptionReminderPostAuth(ctx context.Cont
if err := tx.WithContext(ctx).Create(notification).Error; err != nil {
return err
}
if a.notificationEvents != nil {
if err := a.notificationEvents.PublishNotificationCreated(ctx, notification); err != nil {
a.logger.Error("Failed to publish notification MQTT event", "error", err, "notification_id", notification.ID, "user_id", notification.UserID)
}
}
return tx.WithContext(ctx).
Model(&model.PlanSubscription{}).
Where("id = ?", subscription.ID).

View File

@@ -106,7 +106,7 @@ func newTrustedContext(userID, role string) context.Context {
func TestRequireActor(t *testing.T) {
auth := NewAuthenticator(newAuthenticatorTestDB(t), testLogger{}, "trusted-marker")
auth := NewAuthenticator(newAuthenticatorTestDB(t), testLogger{}, "trusted-marker", nil)
t.Run("thiếu metadata", func(t *testing.T) {
_, err := auth.RequireActor(context.Background())
@@ -146,7 +146,7 @@ func TestAuthenticate(t *testing.T) {
t.Run("user không tồn tại", func(t *testing.T) {
db := newAuthenticatorTestDB(t)
auth := NewAuthenticator(db, testLogger{}, "trusted-marker")
auth := NewAuthenticator(db, testLogger{}, "trusted-marker", nil)
_, err := auth.Authenticate(newTrustedContext(uuid.NewString(), "USER"))
if status.Code(err) != codes.Unauthenticated {
t.Fatalf("code = %v, want %v", status.Code(err), codes.Unauthenticated)
@@ -159,7 +159,7 @@ func TestAuthenticate(t *testing.T) {
if err := db.Create(&blocked).Error; err != nil {
t.Fatalf("create blocked user: %v", err)
}
auth := NewAuthenticator(db, testLogger{}, "trusted-marker")
auth := NewAuthenticator(db, testLogger{}, "trusted-marker", nil)
_, err := auth.Authenticate(newTrustedContext(blocked.ID, "USER"))
if status.Code(err) != codes.PermissionDenied {
t.Fatalf("code = %v, want %v", status.Code(err), codes.PermissionDenied)
@@ -188,7 +188,7 @@ func TestAuthenticate(t *testing.T) {
if err := db.Create(&subscription).Error; err != nil {
t.Fatalf("create subscription: %v", err)
}
auth := NewAuthenticator(db, testLogger{}, "trusted-marker")
auth := NewAuthenticator(db, testLogger{}, "trusted-marker", nil)
result, err := auth.Authenticate(newTrustedContext(user.ID, "USER"))
if err != nil {
@@ -221,7 +221,7 @@ func TestAuthenticate(t *testing.T) {
if err := db.Create(&subscription).Error; err != nil {
t.Fatalf("create subscription: %v", err)
}
auth := NewAuthenticator(db, testLogger{}, "trusted-marker")
auth := NewAuthenticator(db, testLogger{}, "trusted-marker", nil)
result, err := auth.Authenticate(newTrustedContext(user.ID, "USER"))
if err != nil {
@@ -268,7 +268,7 @@ func TestAuthenticate(t *testing.T) {
t.Fatalf("create subscription: %v", err)
}
auth := NewAuthenticator(db, testLogger{}, "trusted-marker")
auth := NewAuthenticator(db, testLogger{}, "trusted-marker", nil)
for range 2 {
if _, err := auth.Authenticate(newTrustedContext(user.ID, "USER")); err != nil {
t.Fatalf("Authenticate() error = %v", err)

View File

@@ -0,0 +1,105 @@
package repository
import (
"context"
"strings"
"gorm.io/gorm"
"stream.api/internal/database/model"
)
type popupAdRepository struct {
db *gorm.DB
}
func NewPopupAdRepository(db *gorm.DB) *popupAdRepository {
return &popupAdRepository{db: db}
}
func (r *popupAdRepository) ListByUser(ctx context.Context, userID string, limit int32, offset int) ([]model.PopupAd, int64, error) {
db := r.baseQuery(ctx).Model(&model.PopupAd{}).Where("user_id = ?", strings.TrimSpace(userID))
var total int64
if err := db.Count(&total).Error; err != nil {
return nil, 0, err
}
var items []model.PopupAd
if err := db.Order("created_at DESC").Offset(offset).Limit(int(limit)).Find(&items).Error; err != nil {
return nil, 0, err
}
return items, total, nil
}
func (r *popupAdRepository) ListForAdmin(ctx context.Context, search string, userID string, limit int32, offset int) ([]model.PopupAd, int64, error) {
db := r.baseQuery(ctx).Model(&model.PopupAd{})
if trimmedSearch := strings.TrimSpace(search); trimmedSearch != "" {
like := "%" + trimmedSearch + "%"
db = db.Where("label ILIKE ?", like)
}
if trimmedUserID := strings.TrimSpace(userID); trimmedUserID != "" {
db = db.Where("user_id = ?", trimmedUserID)
}
var total int64
if err := db.Count(&total).Error; err != nil {
return nil, 0, err
}
var items []model.PopupAd
if err := db.Order("created_at DESC").Offset(offset).Limit(int(limit)).Find(&items).Error; err != nil {
return nil, 0, err
}
return items, total, nil
}
func (r *popupAdRepository) GetByID(ctx context.Context, id string) (*model.PopupAd, error) {
var item model.PopupAd
if err := r.baseQuery(ctx).Where("id = ?", strings.TrimSpace(id)).First(&item).Error; err != nil {
return nil, err
}
return &item, nil
}
func (r *popupAdRepository) GetByIDAndUser(ctx context.Context, id string, userID string) (*model.PopupAd, error) {
var item model.PopupAd
if err := r.baseQuery(ctx).Where("id = ? AND user_id = ?", strings.TrimSpace(id), strings.TrimSpace(userID)).First(&item).Error; err != nil {
return nil, err
}
return &item, nil
}
func (r *popupAdRepository) GetActiveByUser(ctx context.Context, userID string) (*model.PopupAd, error) {
var item model.PopupAd
err := r.baseQuery(ctx).
Where("user_id = ?", strings.TrimSpace(userID)).
Where("is_active = ?", true).
Order("created_at DESC").
First(&item).Error
if err != nil {
return nil, err
}
return &item, nil
}
func (r *popupAdRepository) Create(ctx context.Context, item *model.PopupAd) error {
return r.baseQuery(ctx).Create(item).Error
}
func (r *popupAdRepository) Save(ctx context.Context, item *model.PopupAd) error {
return r.baseQuery(ctx).Save(item).Error
}
func (r *popupAdRepository) DeleteByIDAndUser(ctx context.Context, id string, userID string) (int64, error) {
res := r.baseQuery(ctx).Where("id = ? AND user_id = ?", strings.TrimSpace(id), strings.TrimSpace(userID)).Delete(&model.PopupAd{})
return res.RowsAffected, res.Error
}
func (r *popupAdRepository) DeleteByID(ctx context.Context, id string) (int64, error) {
res := r.baseQuery(ctx).Where("id = ?", strings.TrimSpace(id)).Delete(&model.PopupAd{})
return res.RowsAffected, res.Error
}
func (r *popupAdRepository) baseQuery(ctx context.Context) *gorm.DB {
return r.db.WithContext(ctx)
}

View File

@@ -0,0 +1,96 @@
package service
import (
"context"
"testing"
"time"
"github.com/google/uuid"
appv1 "stream.api/internal/api/proto/app/v1"
"stream.api/internal/database/model"
)
type publishedNotificationEvent struct {
notification *model.Notification
}
type fakeNotificationEventPublisher struct {
events []publishedNotificationEvent
}
func (f *fakeNotificationEventPublisher) PublishNotificationCreated(_ context.Context, notification *model.Notification) error {
copyNotification := *notification
f.events = append(f.events, publishedNotificationEvent{notification: &copyNotification})
return nil
}
func TestExecutePaymentFlow_PublishesNotificationEvent(t *testing.T) {
db := newTestDB(t)
services := newTestAppServices(t, db)
publisher := &fakeNotificationEventPublisher{}
services.notificationEvents = publisher
user := seedTestUser(t, db, model.User{ID: uuid.NewString(), Email: "payer@example.com", Role: ptrString("USER")})
plan := seedTestPlan(t, db, model.Plan{ID: uuid.NewString(), Name: "Pro", Price: 10, Cycle: "monthly", StorageLimit: 100, UploadLimit: 10, QualityLimit: "1080p", IsActive: ptrBool(true)})
seedWalletTransaction(t, db, model.WalletTransaction{ID: uuid.NewString(), UserID: user.ID, Type: walletTransactionTypeTopup, Amount: 5, Currency: ptrString("USD")})
result, err := services.executePaymentFlow(context.Background(), paymentExecutionInput{
UserID: user.ID,
Plan: &plan,
TermMonths: 1,
PaymentMethod: paymentMethodTopup,
TopupAmount: ptrFloat64(5),
})
if err != nil {
t.Fatalf("executePaymentFlow() error = %v", err)
}
if result == nil {
t.Fatal("executePaymentFlow() result is nil")
}
if len(publisher.events) != 1 {
t.Fatalf("published events = %d, want 1", len(publisher.events))
}
if publisher.events[0].notification == nil || publisher.events[0].notification.Type != "billing.subscription" {
t.Fatalf("published notification = %#v", publisher.events[0].notification)
}
}
func TestTopupWallet_PublishesNotificationEvent(t *testing.T) {
db := newTestDB(t)
services := newTestAppServices(t, db)
publisher := &fakeNotificationEventPublisher{}
services.notificationEvents = publisher
user := seedTestUser(t, db, model.User{ID: uuid.NewString(), Email: "user@example.com", Role: ptrString("USER")})
_, err := (&paymentsAppService{appServices: services}).TopupWallet(testActorIncomingContext(user.ID, "USER"), &appv1.TopupWalletRequest{Amount: 12})
if err != nil {
t.Fatalf("TopupWallet() error = %v", err)
}
if len(publisher.events) != 1 {
t.Fatalf("published events = %d, want 1", len(publisher.events))
}
if publisher.events[0].notification == nil || publisher.events[0].notification.Type != "billing.topup" {
t.Fatalf("published notification = %#v", publisher.events[0].notification)
}
}
func TestBuildNotificationCreatedPayload(t *testing.T) {
now := time.Now().UTC()
notification := &model.Notification{
ID: uuid.NewString(),
UserID: uuid.NewString(),
Type: "billing.subscription",
Title: "Subscription activated",
Message: "Your subscription is active.",
ActionURL: ptrString("/settings/billing"),
ActionLabel: ptrString("Renew plan"),
CreatedAt: &now,
}
payload := BuildNotificationCreatedPayload(notification)
if payload.ID != notification.ID || payload.UserID != notification.UserID || payload.Type != notification.Type {
t.Fatalf("payload = %#v", payload)
}
if payload.CreatedAt == "" {
t.Fatal("payload created_at should not be empty")
}
}

View File

@@ -0,0 +1,177 @@
package service
import (
"testing"
"time"
"github.com/google/uuid"
"google.golang.org/grpc/codes"
appv1 "stream.api/internal/api/proto/app/v1"
"stream.api/internal/database/model"
)
func TestPopupAdsUserFlow(t *testing.T) {
t.Run("create list update delete popup ad", func(t *testing.T) {
db := newTestDB(t)
services := newTestAppServices(t, db)
user := seedTestUser(t, db, model.User{ID: uuid.NewString(), Email: "user@example.com", Role: ptrString("USER")})
startAt := time.Now().UTC().Add(-time.Hour)
endAt := time.Now().UTC().Add(2 * time.Hour)
createResp, err := services.CreatePopupAd(testActorIncomingContext(user.ID, "USER"), &appv1.CreatePopupAdRequest{
Title: "Homepage Campaign",
ImageUrl: "https://cdn.example.com/banner.jpg",
TargetUrl: "https://example.com/landing",
IsActive: ptrBool(true),
StartAt: timeToProto(&startAt),
EndAt: timeToProto(&endAt),
Priority: int32Ptr(5),
CloseCooldownMinutes: int32Ptr(90),
})
if err != nil {
t.Fatalf("CreatePopupAd() error = %v", err)
}
if createResp.Item == nil || createResp.Item.Title != "Homepage Campaign" {
t.Fatalf("CreatePopupAd() unexpected response: %#v", createResp)
}
listResp, err := services.ListPopupAds(testActorIncomingContext(user.ID, "USER"), &appv1.ListPopupAdsRequest{})
if err != nil {
t.Fatalf("ListPopupAds() error = %v", err)
}
if len(listResp.Items) != 1 {
t.Fatalf("ListPopupAds() count = %d, want 1", len(listResp.Items))
}
updateResp, err := services.UpdatePopupAd(testActorIncomingContext(user.ID, "USER"), &appv1.UpdatePopupAdRequest{
Id: createResp.Item.Id,
Title: "Homepage Campaign v2",
ImageUrl: "https://cdn.example.com/banner-v2.jpg",
TargetUrl: "https://example.com/landing-v2",
IsActive: ptrBool(false),
Priority: int32Ptr(8),
CloseCooldownMinutes: int32Ptr(30),
})
if err != nil {
t.Fatalf("UpdatePopupAd() error = %v", err)
}
if updateResp.Item == nil || updateResp.Item.Title != "Homepage Campaign v2" || updateResp.Item.IsActive {
t.Fatalf("UpdatePopupAd() unexpected response: %#v", updateResp)
}
items := mustListPopupAdsByUser(t, db, user.ID)
if len(items) != 1 {
t.Fatalf("popup ad count = %d, want 1", len(items))
}
if items[0].Priority != 8 || items[0].CloseCooldownMinutes != 30 {
t.Fatalf("popup ad values = %#v", items[0])
}
_, err = services.DeletePopupAd(testActorIncomingContext(user.ID, "USER"), &appv1.DeletePopupAdRequest{Id: createResp.Item.Id})
if err != nil {
t.Fatalf("DeletePopupAd() error = %v", err)
}
items = mustListPopupAdsByUser(t, db, user.ID)
if len(items) != 0 {
t.Fatalf("popup ad count after delete = %d, want 0", len(items))
}
})
t.Run("reject invalid schedule", func(t *testing.T) {
db := newTestDB(t)
services := newTestAppServices(t, db)
user := seedTestUser(t, db, model.User{ID: uuid.NewString(), Email: "user@example.com", Role: ptrString("USER")})
startAt := time.Now().UTC().Add(time.Hour)
endAt := time.Now().UTC()
_, err := services.CreatePopupAd(testActorIncomingContext(user.ID, "USER"), &appv1.CreatePopupAdRequest{
Title: "Invalid",
ImageUrl: "https://cdn.example.com/banner.jpg",
TargetUrl: "https://example.com/landing",
StartAt: timeToProto(&startAt),
EndAt: timeToProto(&endAt),
})
assertGRPCCode(t, err, codes.InvalidArgument)
})
t.Run("get active popup ad picks highest priority valid item", func(t *testing.T) {
db := newTestDB(t)
services := newTestAppServices(t, db)
user := seedTestUser(t, db, model.User{ID: uuid.NewString(), Email: "user@example.com", Role: ptrString("USER")})
now := time.Now().UTC()
seedTestPopupAd(t, db, model.PopupAd{ID: uuid.NewString(), UserID: user.ID, Title: "inactive", ImageURL: "https://cdn.example.com/1.jpg", TargetURL: "https://example.com/1", IsActive: ptrBool(false), Priority: 99})
seedTestPopupAd(t, db, model.PopupAd{ID: uuid.NewString(), UserID: user.ID, Title: "expired", ImageURL: "https://cdn.example.com/2.jpg", TargetURL: "https://example.com/2", IsActive: ptrBool(true), Priority: 50, StartAt: ptrTime(now.Add(-2 * time.Hour)), EndAt: ptrTime(now.Add(-time.Hour))})
seedTestPopupAd(t, db, model.PopupAd{ID: uuid.NewString(), UserID: user.ID, Title: "low", ImageURL: "https://cdn.example.com/3.jpg", TargetURL: "https://example.com/3", IsActive: ptrBool(true), Priority: 1, StartAt: ptrTime(now.Add(-time.Hour)), EndAt: ptrTime(now.Add(time.Hour))})
winner := seedTestPopupAd(t, db, model.PopupAd{ID: uuid.NewString(), UserID: user.ID, Title: "winner", ImageURL: "https://cdn.example.com/4.jpg", TargetURL: "https://example.com/4", IsActive: ptrBool(true), Priority: 10, StartAt: ptrTime(now.Add(-time.Hour)), EndAt: ptrTime(now.Add(time.Hour)), CloseCooldownMinutes: 15})
resp, err := services.GetActivePopupAd(testActorIncomingContext(user.ID, "USER"), &appv1.GetActivePopupAdRequest{})
if err != nil {
t.Fatalf("GetActivePopupAd() error = %v", err)
}
if resp.Item == nil || resp.Item.Id != winner.ID {
t.Fatalf("GetActivePopupAd() = %#v, want winner %q", resp.Item, winner.ID)
}
})
}
func TestPopupAdsAdminFlow(t *testing.T) {
t.Run("admin create list update delete popup ad", func(t *testing.T) {
db := newTestDB(t)
services := newTestAppServices(t, db)
admin := seedTestUser(t, db, model.User{ID: uuid.NewString(), Email: "admin@example.com", Role: ptrString("ADMIN")})
user := seedTestUser(t, db, model.User{ID: uuid.NewString(), Email: "user@example.com", Role: ptrString("USER")})
conn, cleanup := newTestGRPCServer(t, services)
defer cleanup()
client := newAdminClient(conn)
createResp, err := client.CreateAdminPopupAd(testActorOutgoingContext(admin.ID, "ADMIN"), &appv1.CreateAdminPopupAdRequest{
UserId: user.ID,
Title: "Admin Campaign",
ImageUrl: "https://cdn.example.com/admin.jpg",
TargetUrl: "https://example.com/admin",
IsActive: ptrBool(true),
Priority: int32Ptr(7),
CloseCooldownMinutes: int32Ptr(45),
})
if err != nil {
t.Fatalf("CreateAdminPopupAd() error = %v", err)
}
if createResp.Item == nil || createResp.Item.UserId != user.ID {
t.Fatalf("CreateAdminPopupAd() unexpected response: %#v", createResp)
}
listResp, err := client.ListAdminPopupAds(testActorOutgoingContext(admin.ID, "ADMIN"), &appv1.ListAdminPopupAdsRequest{UserId: &user.ID})
if err != nil {
t.Fatalf("ListAdminPopupAds() error = %v", err)
}
if len(listResp.Items) != 1 {
t.Fatalf("ListAdminPopupAds() count = %d, want 1", len(listResp.Items))
}
updateResp, err := client.UpdateAdminPopupAd(testActorOutgoingContext(admin.ID, "ADMIN"), &appv1.UpdateAdminPopupAdRequest{
Id: createResp.Item.Id,
UserId: user.ID,
Title: "Admin Campaign v2",
ImageUrl: "https://cdn.example.com/admin-v2.jpg",
TargetUrl: "https://example.com/admin-v2",
IsActive: ptrBool(false),
Priority: int32Ptr(11),
CloseCooldownMinutes: int32Ptr(10),
})
if err != nil {
t.Fatalf("UpdateAdminPopupAd() error = %v", err)
}
if updateResp.Item == nil || updateResp.Item.Title != "Admin Campaign v2" || updateResp.Item.IsActive {
t.Fatalf("UpdateAdminPopupAd() unexpected response: %#v", updateResp)
}
_, err = client.DeleteAdminPopupAd(testActorOutgoingContext(admin.ID, "ADMIN"), &appv1.DeleteAdminPopupAdRequest{Id: createResp.Item.Id})
if err != nil {
t.Fatalf("DeleteAdminPopupAd() error = %v", err)
}
items := mustListPopupAdsByUser(t, db, user.ID)
if len(items) != 0 {
t.Fatalf("popup ad count after delete = %d, want 0", len(items))
}
})
}

View File

@@ -15,11 +15,12 @@ import (
"google.golang.org/grpc/test/bufconn"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
_ "modernc.org/sqlite"
_ "github.com/mattn/go-sqlite3"
appv1 "stream.api/internal/api/proto/app/v1"
"stream.api/internal/database/model"
"stream.api/internal/database/query"
"stream.api/internal/middleware"
"stream.api/internal/repository"
"stream.api/pkg/logger"
)
@@ -74,7 +75,7 @@ func newTestDB(t *testing.T) *gorm.DB {
t.Helper()
dsn := fmt.Sprintf("file:%s?mode=memory&cache=shared", uuid.NewString())
db, err := gorm.Open(sqlite.Dialector{DriverName: "sqlite", DSN: dsn}, &gorm.Config{})
db, err := gorm.Open(sqlite.Dialector{DriverName: "sqlite3", DSN: dsn}, &gorm.Config{})
if err != nil {
t.Fatalf("open sqlite db: %v", err)
}
@@ -206,6 +207,21 @@ func newTestDB(t *testing.T) *gorm.DB {
encrytion_m3u8 BOOLEAN NOT NULL DEFAULT 1,
logo_url TEXT
)`,
`CREATE TABLE popup_ads (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
title TEXT NOT NULL,
image_url TEXT NOT NULL,
target_url TEXT NOT NULL,
is_active BOOLEAN NOT NULL DEFAULT 1,
start_at DATETIME,
end_at DATETIME,
priority INTEGER NOT NULL DEFAULT 0,
close_cooldown_minutes INTEGER NOT NULL DEFAULT 60,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME,
version INTEGER NOT NULL DEFAULT 1
)`,
} {
if err := db.Exec(stmt).Error; err != nil {
t.Fatalf("create test schema: %v", err)
@@ -226,9 +242,17 @@ func newTestAppServices(t *testing.T, db *gorm.DB) *appServices {
return &appServices{
db: db,
logger: testLogger{},
authenticator: middleware.NewAuthenticator(db, testLogger{}, testTrustedMarker),
authenticator: middleware.NewAuthenticator(db, testLogger{}, testTrustedMarker, nil),
// cache: &fakeCache{values: map[string]string{}},
googleUserInfoURL: defaultGoogleUserInfoURL,
googleUserInfoURL: defaultGoogleUserInfoURL,
userRepository: repository.NewUserRepository(db),
planRepository: repository.NewPlanRepository(db),
paymentRepository: repository.NewPaymentRepository(db),
notificationRepo: repository.NewNotificationRepository(db),
domainRepository: repository.NewDomainRepository(db),
adTemplateRepository: repository.NewAdTemplateRepository(db),
popupAdRepository: repository.NewPopupAdRepository(db),
playerConfigRepo: repository.NewPlayerConfigRepository(db),
}
}
@@ -244,6 +268,7 @@ func newTestGRPCServer(t *testing.T, services *appServices) (*grpc.ClientConn, f
NotificationsServer: services,
DomainsServer: services,
AdTemplatesServer: services,
PopupAdsServer: services,
PlayerConfigsServer: services,
PlansServer: services,
PaymentsServer: services,
@@ -382,4 +407,33 @@ func newAdminClient(conn *grpc.ClientConn) appv1.AdminClient {
return appv1.NewAdminClient(conn)
}
func ptrTime(v time.Time) *time.Time { return &v }
func seedTestPopupAd(t *testing.T, db *gorm.DB, item model.PopupAd) model.PopupAd {
t.Helper()
if item.IsActive == nil {
item.IsActive = ptrBool(true)
}
if item.CreatedAt == nil {
now := time.Now().UTC()
item.CreatedAt = &now
}
if item.CloseCooldownMinutes == 0 {
item.CloseCooldownMinutes = 60
}
if err := db.Create(&item).Error; err != nil {
t.Fatalf("create popup ad: %v", err)
}
return item
}
func mustListPopupAdsByUser(t *testing.T, db *gorm.DB, userID string) []model.PopupAd {
t.Helper()
var items []model.PopupAd
if err := db.Order("priority DESC, created_at DESC").Find(&items, "user_id = ?", userID).Error; err != nil {
t.Fatalf("list popup ads for user %s: %v", userID, err)
}
return items
}
var _ logger.Logger = testLogger{}

View File

@@ -464,6 +464,23 @@ func validateAdminAdTemplateInput(userID, name, vastTagURL, adFormat string, dur
return ""
}
func validateAdminPopupAdInput(userID, popupType, label, value string, maxTriggersPerSession *int32) string {
if strings.TrimSpace(userID) == "" {
return "User ID is required"
}
popupType = strings.ToLower(strings.TrimSpace(popupType))
if popupType != "url" && popupType != "script" {
return "Popup ad type must be url or script"
}
if strings.TrimSpace(label) == "" || strings.TrimSpace(value) == "" {
return "Label and value are required"
}
if maxTriggersPerSession != nil && *maxTriggersPerSession < 1 {
return "Max triggers per session must be greater than 0"
}
return ""
}
func validateAdminPlayerConfigInput(userID, name string) string {
if strings.TrimSpace(userID) == "" {
return "User ID is required"
@@ -503,6 +520,32 @@ func (s *appServices) buildAdminPlan(ctx context.Context, plan *model.Plan) (*ap
return payload, nil
}
func (s *appServices) buildAdminPopupAd(ctx context.Context, item *model.PopupAd) (*appv1.AdminPopupAd, error) {
if item == nil {
return nil, nil
}
payload := &appv1.AdminPopupAd{
Id: item.ID,
UserId: item.UserID,
Type: item.Type,
Label: item.Label,
Value: item.Value,
IsActive: boolValue(item.IsActive),
MaxTriggersPerSession: func() int32 { if item.MaxTriggersPerSession != nil { return *item.MaxTriggersPerSession }; return 0 }(),
CreatedAt: timeToProto(item.CreatedAt),
UpdatedAt: timeToProto(item.UpdatedAt),
}
ownerEmail, err := s.loadAdminUserEmail(ctx, item.UserID)
if err != nil {
return nil, err
}
payload.OwnerEmail = ownerEmail
return payload, nil
}
func (s *appServices) buildAdminAdTemplate(ctx context.Context, item *model.AdTemplate) (*appv1.AdminAdTemplate, error) {
if item == nil {
return nil, nil

View File

@@ -1,7 +1,9 @@
package service
import appv1 "stream.api/internal/api/proto/app/v1"
import "stream.api/internal/database/model"
import (
appv1 "stream.api/internal/api/proto/app/v1"
"stream.api/internal/database/model"
)
func toProtoDomain(item *model.Domain) *appv1.Domain {
if item == nil {
@@ -33,6 +35,22 @@ func toProtoAdTemplate(item *model.AdTemplate) *appv1.AdTemplate {
}
}
func toProtoPopupAd(item *model.PopupAd) *appv1.PopupAd {
if item == nil {
return nil
}
return &appv1.PopupAd{
Id: item.ID,
Type: item.Type,
Label: item.Label,
Value: item.Value,
IsActive: boolValue(item.IsActive),
MaxTriggersPerSession: func() int32 { if item.MaxTriggersPerSession != nil { return *item.MaxTriggersPerSession }; return 0 }(),
CreatedAt: timeToProto(item.CreatedAt),
UpdatedAt: timeToProto(item.UpdatedAt),
}
}
func toProtoPlayerConfig(item *model.PlayerConfig) *appv1.PlayerConfig {
if item == nil {
return nil

View File

@@ -43,6 +43,10 @@ type PaymentRepository interface {
ExecuteSubscriptionPayment(ctx context.Context, userID string, plan *model.Plan, termMonths int32, paymentMethod string, paymentRecord *model.Payment, invoiceID string, now time.Time, validateFunding func(currentWalletBalance float64) (float64, error)) (*model.PlanSubscription, float64, error)
}
type NotificationEventPublisher interface {
PublishNotificationCreated(ctx context.Context, notification *model.Notification) error
}
type AccountRepository interface {
DeleteUserAccount(ctx context.Context, userID string) error
ClearUserData(ctx context.Context, userID string) error
@@ -76,6 +80,18 @@ type AdTemplateRepository interface {
DeleteByIDAndClearVideos(ctx context.Context, id string) error
}
type PopupAdRepository interface {
ListByUser(ctx context.Context, userID string, limit int32, offset int) ([]model.PopupAd, int64, error)
ListForAdmin(ctx context.Context, search string, userID string, limit int32, offset int) ([]model.PopupAd, int64, error)
GetByID(ctx context.Context, id string) (*model.PopupAd, error)
GetByIDAndUser(ctx context.Context, id string, userID string) (*model.PopupAd, error)
GetActiveByUser(ctx context.Context, userID string) (*model.PopupAd, error)
Create(ctx context.Context, item *model.PopupAd) error
Save(ctx context.Context, item *model.PopupAd) error
DeleteByIDAndUser(ctx context.Context, id string, userID string) (int64, error)
DeleteByID(ctx context.Context, id string) (int64, error)
}
type PlayerConfigRepository interface {
ListByUser(ctx context.Context, userID string) ([]model.PlayerConfig, error)
ListForAdmin(ctx context.Context, search string, userID string, limit int32, offset int) ([]model.PlayerConfig, int64, error)

View File

@@ -0,0 +1,57 @@
package service
import (
"context"
"encoding/json"
"time"
"stream.api/internal/database/model"
)
type noopNotificationEventPublisher struct{}
func (noopNotificationEventPublisher) PublishNotificationCreated(context.Context, *model.Notification) error {
return nil
}
type NotificationCreatedPayload struct {
ID string `json:"id"`
UserID string `json:"user_id"`
Type string `json:"type"`
Title string `json:"title"`
Message string `json:"message"`
ActionURL *string `json:"action_url,omitempty"`
ActionLabel *string `json:"action_label,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
}
func (s *appServices) publishNotificationCreated(ctx context.Context, notification *model.Notification) {
if s == nil || s.notificationEvents == nil || notification == nil {
return
}
if err := s.notificationEvents.PublishNotificationCreated(ctx, notification); err != nil {
s.logger.Error("Failed to publish notification MQTT event", "error", err, "notification_id", notification.ID, "user_id", notification.UserID)
}
}
func BuildNotificationCreatedPayload(notification *model.Notification) NotificationCreatedPayload {
createdAt := ""
if notification != nil && notification.CreatedAt != nil {
createdAt = notification.CreatedAt.UTC().Format(time.RFC3339)
}
return NotificationCreatedPayload{
ID: notification.ID,
UserID: notification.UserID,
Type: notification.Type,
Title: notification.Title,
Message: notification.Message,
ActionURL: nullableTrimmedString(notification.ActionURL),
ActionLabel: nullableTrimmedString(notification.ActionLabel),
CreatedAt: createdAt,
}
}
func mustMarshalNotificationPayload(notification *model.Notification) []byte {
encoded, _ := json.Marshal(BuildNotificationCreatedPayload(notification))
return encoded
}

View File

@@ -0,0 +1,175 @@
package service
import (
"context"
"fmt"
"testing"
"time"
"github.com/google/uuid"
"google.golang.org/grpc/metadata"
_ "modernc.org/sqlite"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
appv1 "stream.api/internal/api/proto/app/v1"
"stream.api/internal/database/model"
"stream.api/internal/database/query"
"stream.api/internal/middleware"
"stream.api/internal/repository"
)
type serviceTestLogger struct{}
func (serviceTestLogger) Info(string, ...any) {}
func (serviceTestLogger) Error(string, ...any) {}
func (serviceTestLogger) Debug(string, ...any) {}
func (serviceTestLogger) Warn(string, ...any) {}
const notificationTestTrustedMarker = "trusted-notification-test-marker"
type publishedNotificationEvent struct {
notification *model.Notification
}
type fakeNotificationEventPublisher struct {
events []publishedNotificationEvent
}
func (f *fakeNotificationEventPublisher) PublishNotificationCreated(_ context.Context, notification *model.Notification) error {
copyNotification := *notification
f.events = append(f.events, publishedNotificationEvent{notification: &copyNotification})
return nil
}
func newNotificationTestDB(t *testing.T) *gorm.DB {
t.Helper()
dsn := fmt.Sprintf("file:%s?mode=memory&cache=shared", uuid.NewString())
db, err := gorm.Open(sqlite.Dialector{DriverName: "sqlite", DSN: dsn}, &gorm.Config{})
if err != nil {
t.Fatalf("open sqlite db: %v", err)
}
for _, stmt := range []string{
`CREATE TABLE user (id TEXT PRIMARY KEY,email TEXT NOT NULL,password TEXT,username TEXT,avatar TEXT,role TEXT NOT NULL,google_id TEXT,storage_used INTEGER NOT NULL DEFAULT 0,plan_id TEXT,referred_by_user_id TEXT,referral_eligible BOOLEAN NOT NULL DEFAULT 1,referral_reward_bps INTEGER,referral_reward_granted_at DATETIME,referral_reward_payment_id TEXT,referral_reward_amount REAL,created_at DATETIME DEFAULT CURRENT_TIMESTAMP,updated_at DATETIME,version INTEGER NOT NULL DEFAULT 1,telegram_id TEXT)`,
`CREATE TABLE plan (id TEXT PRIMARY KEY,name TEXT NOT NULL,description TEXT,price REAL NOT NULL,cycle TEXT NOT NULL,storage_limit INTEGER NOT NULL,upload_limit INTEGER NOT NULL,duration_limit INTEGER NOT NULL,quality_limit TEXT NOT NULL,features JSON,is_active BOOLEAN NOT NULL DEFAULT 1,version INTEGER NOT NULL DEFAULT 1)`,
`CREATE TABLE payment (id TEXT PRIMARY KEY,user_id TEXT NOT NULL,plan_id TEXT,amount REAL NOT NULL,currency TEXT,status TEXT,provider TEXT,transaction_id TEXT,created_at DATETIME DEFAULT CURRENT_TIMESTAMP,updated_at DATETIME,version INTEGER NOT NULL DEFAULT 1)`,
`CREATE TABLE plan_subscriptions (id TEXT PRIMARY KEY,user_id TEXT NOT NULL,payment_id TEXT NOT NULL,plan_id TEXT NOT NULL,term_months INTEGER NOT NULL,payment_method TEXT NOT NULL,wallet_amount REAL NOT NULL,topup_amount REAL NOT NULL,started_at DATETIME NOT NULL,expires_at DATETIME NOT NULL,reminder_7d_sent_at DATETIME,reminder_3d_sent_at DATETIME,reminder_1d_sent_at DATETIME,created_at DATETIME DEFAULT CURRENT_TIMESTAMP,updated_at DATETIME,version INTEGER NOT NULL DEFAULT 1)`,
`CREATE TABLE wallet_transactions (id TEXT PRIMARY KEY,user_id TEXT NOT NULL,type TEXT NOT NULL,amount REAL NOT NULL,currency TEXT,note TEXT,created_at DATETIME DEFAULT CURRENT_TIMESTAMP,updated_at DATETIME,payment_id TEXT,plan_id TEXT,term_months INTEGER,version INTEGER NOT NULL DEFAULT 1)`,
`CREATE TABLE notifications (id TEXT PRIMARY KEY,user_id TEXT NOT NULL,type TEXT NOT NULL,title TEXT NOT NULL,message TEXT NOT NULL,metadata TEXT,action_url TEXT,action_label TEXT,is_read BOOLEAN NOT NULL DEFAULT 0,created_at DATETIME DEFAULT CURRENT_TIMESTAMP,updated_at DATETIME,version INTEGER NOT NULL DEFAULT 1)`,
} {
if err := db.Exec(stmt).Error; err != nil {
t.Fatalf("create test schema: %v", err)
}
}
query.SetDefault(db)
return db
}
func newNotificationTestServices(t *testing.T, db *gorm.DB, publisher NotificationEventPublisher) *appServices {
t.Helper()
return &appServices{
db: db,
logger: serviceTestLogger{},
authenticator: middleware.NewAuthenticator(db, serviceTestLogger{}, notificationTestTrustedMarker, publisher),
googleUserInfoURL: defaultGoogleUserInfoURL,
userRepository: repository.NewUserRepository(db),
planRepository: repository.NewPlanRepository(db),
paymentRepository: repository.NewPaymentRepository(db),
billingRepository: repository.NewBillingRepository(db),
notificationRepo: repository.NewNotificationRepository(db),
notificationEvents: publisher,
}
}
func notificationTestContext(userID, role string) context.Context {
return metadata.NewIncomingContext(context.Background(), metadata.Pairs(
middleware.ActorMarkerMetadataKey, notificationTestTrustedMarker,
middleware.ActorIDMetadataKey, userID,
middleware.ActorRoleMetadataKey, role,
middleware.ActorEmailMetadataKey, "actor@example.com",
))
}
func notificationPtrString(v string) *string { return &v }
func notificationPtrBool(v bool) *bool { return &v }
func notificationPtrFloat64(v float64) *float64 { return &v }
func seedNotificationUser(t *testing.T, db *gorm.DB, user model.User) model.User {
t.Helper()
if user.Role == nil {
user.Role = notificationPtrString("USER")
}
if err := db.Create(&user).Error; err != nil {
t.Fatalf("create user: %v", err)
}
return user
}
func seedNotificationPlan(t *testing.T, db *gorm.DB, plan model.Plan) model.Plan {
t.Helper()
if plan.IsActive == nil {
plan.IsActive = notificationPtrBool(true)
}
if err := db.Create(&plan).Error; err != nil {
t.Fatalf("create plan: %v", err)
}
return plan
}
func seedNotificationWalletTransaction(t *testing.T, db *gorm.DB, tx model.WalletTransaction) model.WalletTransaction {
t.Helper()
if err := db.Create(&tx).Error; err != nil {
t.Fatalf("create wallet transaction: %v", err)
}
return tx
}
func TestExecutePaymentFlowPublishesNotificationEvent(t *testing.T) {
db := newNotificationTestDB(t)
publisher := &fakeNotificationEventPublisher{}
services := newNotificationTestServices(t, db, publisher)
user := seedNotificationUser(t, db, model.User{ID: uuid.NewString(), Email: "payer@example.com", Role: notificationPtrString("USER")})
plan := seedNotificationPlan(t, db, model.Plan{ID: uuid.NewString(), Name: "Pro", Price: 10, Cycle: "monthly", StorageLimit: 100, UploadLimit: 10, QualityLimit: "1080p", IsActive: notificationPtrBool(true)})
seedNotificationWalletTransaction(t, db, model.WalletTransaction{ID: uuid.NewString(), UserID: user.ID, Type: walletTransactionTypeTopup, Amount: 5, Currency: notificationPtrString("USD")})
_, err := services.executePaymentFlow(context.Background(), paymentExecutionInput{UserID: user.ID, Plan: &plan, TermMonths: 1, PaymentMethod: paymentMethodTopup, TopupAmount: notificationPtrFloat64(5)})
if err != nil {
t.Fatalf("executePaymentFlow() error = %v", err)
}
if len(publisher.events) != 1 {
t.Fatalf("published events = %d, want 1", len(publisher.events))
}
if publisher.events[0].notification == nil || publisher.events[0].notification.Type != "billing.subscription" {
t.Fatalf("published notification = %#v", publisher.events[0].notification)
}
}
func TestTopupWalletPublishesNotificationEvent(t *testing.T) {
db := newNotificationTestDB(t)
publisher := &fakeNotificationEventPublisher{}
services := newNotificationTestServices(t, db, publisher)
user := seedNotificationUser(t, db, model.User{ID: uuid.NewString(), Email: "user@example.com", Role: notificationPtrString("USER")})
_, err := (&paymentsAppService{appServices: services}).TopupWallet(notificationTestContext(user.ID, "USER"), &appv1.TopupWalletRequest{Amount: 12})
if err != nil {
t.Fatalf("TopupWallet() error = %v", err)
}
if len(publisher.events) != 1 {
t.Fatalf("published events = %d, want 1", len(publisher.events))
}
if publisher.events[0].notification == nil || publisher.events[0].notification.Type != "billing.topup" {
t.Fatalf("published notification = %#v", publisher.events[0].notification)
}
}
func TestBuildNotificationCreatedPayload(t *testing.T) {
now := time.Now().UTC()
notification := &model.Notification{ID: uuid.NewString(), UserID: uuid.NewString(), Type: "billing.subscription", Title: "Subscription activated", Message: "Your subscription is active.", ActionURL: notificationPtrString("/settings/billing"), ActionLabel: notificationPtrString("Renew plan"), CreatedAt: &now}
payload := BuildNotificationCreatedPayload(notification)
if payload.ID != notification.ID || payload.UserID != notification.UserID || payload.Type != notification.Type {
t.Fatalf("payload = %#v", payload)
}
if payload.CreatedAt == "" {
t.Fatal("payload created_at should not be empty")
}
}

View File

@@ -118,6 +118,9 @@ func (s *appServices) executePaymentFlow(ctx context.Context, input paymentExecu
}
result.Subscription = subscription
result.WalletBalance = walletBalance
if notification := latestNotificationForPayment(result.Payment, subscription, input.Plan, invoiceID); notification != nil {
s.publishNotificationCreated(ctx, notification)
}
return result, nil
}

View File

@@ -0,0 +1,31 @@
package service
import (
"fmt"
"time"
"stream.api/internal/database/model"
)
func latestNotificationForPayment(paymentRecord *model.Payment, subscription *model.PlanSubscription, plan *model.Plan, invoiceID string) *model.Notification {
if paymentRecord == nil || subscription == nil || plan == nil {
return nil
}
return &model.Notification{
ID: "",
UserID: paymentRecord.UserID,
Type: "billing.subscription",
Title: "Subscription activated",
Message: fmt.Sprintf("Your subscription to %s is active until %s.", plan.Name, subscription.ExpiresAt.UTC().Format("2006-01-02")),
Metadata: model.StringPtr(mustMarshalJSON(map[string]any{
"payment_id": paymentRecord.ID,
"invoice_id": invoiceID,
"plan_id": plan.ID,
"term_months": subscription.TermMonths,
"payment_method": subscription.PaymentMethod,
"wallet_amount": subscription.WalletAmount,
"topup_amount": subscription.TopupAmount,
"plan_expires_at": subscription.ExpiresAt.UTC().Format(time.RFC3339),
})),
}
}

View File

@@ -205,9 +205,11 @@ func (s *appServices) maybeGrantReferralReward(ctx context.Context, tx *gorm.DB,
if err := s.paymentRepository.CreateWalletTransactionTx(tx, ctx, rewardTransaction); err != nil {
return nil, err
}
if err := s.paymentRepository.CreateNotificationTx(tx, ctx, buildReferralRewardNotification(referrer.ID, rewardAmount, referee, paymentRecord)); err != nil {
rewardNotification := buildReferralRewardNotification(referrer.ID, rewardAmount, referee, paymentRecord)
if err := s.paymentRepository.CreateNotificationTx(tx, ctx, rewardNotification); err != nil {
return nil, err
}
s.publishNotificationCreated(ctx, rewardNotification)
now := time.Now().UTC()
updates := map[string]any{

View File

@@ -11,6 +11,7 @@ func Register(server grpc.ServiceRegistrar, services *Services) {
appv1.RegisterNotificationsServer(server, services.NotificationsServer)
appv1.RegisterDomainsServer(server, services.DomainsServer)
appv1.RegisterAdTemplatesServer(server, services.AdTemplatesServer)
appv1.RegisterPopupAdsServer(server, services.PopupAdsServer)
appv1.RegisterPlayerConfigsServer(server, services.PlayerConfigsServer)
appv1.RegisterPlansServer(server, services.PlansServer)
appv1.RegisterPaymentsServer(server, services.PaymentsServer)

View File

@@ -461,6 +461,150 @@ func (s *appServices) DeleteAdminAdTemplate(ctx context.Context, req *appv1.Dele
return &appv1.MessageResponse{Message: "Ad template deleted"}, nil
}
func (s *appServices) ListAdminPopupAds(ctx context.Context, req *appv1.ListAdminPopupAdsRequest) (*appv1.ListAdminPopupAdsResponse, error) {
if _, err := s.requireAdmin(ctx); err != nil {
return nil, err
}
page, limit, offset := adminPageLimitOffset(req.GetPage(), req.GetLimit())
search := strings.TrimSpace(protoStringValue(req.Search))
userID := strings.TrimSpace(protoStringValue(req.UserId))
items, total, err := s.popupAdRepository.ListForAdmin(ctx, search, userID, limit, offset)
if err != nil {
return nil, status.Error(codes.Internal, "Failed to list popup ads")
}
payload := make([]*appv1.AdminPopupAd, 0, len(items))
for i := range items {
mapped, err := s.buildAdminPopupAd(ctx, &items[i])
if err != nil {
return nil, status.Error(codes.Internal, "Failed to list popup ads")
}
payload = append(payload, mapped)
}
return &appv1.ListAdminPopupAdsResponse{Items: payload, Total: total, Page: page, Limit: limit}, nil
}
func (s *appServices) GetAdminPopupAd(ctx context.Context, req *appv1.GetAdminPopupAdRequest) (*appv1.GetAdminPopupAdResponse, error) {
if _, err := s.requireAdmin(ctx); err != nil {
return nil, err
}
id := strings.TrimSpace(req.GetId())
if id == "" {
return nil, status.Error(codes.NotFound, "Popup ad not found")
}
item, err := s.popupAdRepository.GetByID(ctx, id)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, status.Error(codes.NotFound, "Popup ad not found")
}
return nil, status.Error(codes.Internal, "Failed to load popup ad")
}
payload, err := s.buildAdminPopupAd(ctx, item)
if err != nil {
return nil, status.Error(codes.Internal, "Failed to load popup ad")
}
return &appv1.GetAdminPopupAdResponse{Item: payload}, nil
}
func (s *appServices) CreateAdminPopupAd(ctx context.Context, req *appv1.CreateAdminPopupAdRequest) (*appv1.CreateAdminPopupAdResponse, error) {
if _, err := s.requireAdmin(ctx); err != nil {
return nil, err
}
if msg := validateAdminPopupAdInput(req.GetUserId(), req.GetType(), req.GetLabel(), req.GetValue(), req.MaxTriggersPerSession); msg != "" {
return nil, status.Error(codes.InvalidArgument, msg)
}
user, err := s.userRepository.GetByID(ctx, strings.TrimSpace(req.GetUserId()))
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, status.Error(codes.InvalidArgument, "User not found")
}
return nil, status.Error(codes.Internal, "Failed to save popup ad")
}
maxTriggers := int32(3)
if req.MaxTriggersPerSession != nil {
maxTriggers = *req.MaxTriggersPerSession
}
item := &model.PopupAd{
ID: uuid.New().String(),
UserID: user.ID,
Type: strings.ToLower(strings.TrimSpace(req.GetType())),
Label: strings.TrimSpace(req.GetLabel()),
Value: strings.TrimSpace(req.GetValue()),
IsActive: model.BoolPtr(req.IsActive == nil || *req.IsActive),
MaxTriggersPerSession: int32Ptr(maxTriggers),
}
if err := s.popupAdRepository.Create(ctx, item); err != nil {
return nil, status.Error(codes.Internal, "Failed to save popup ad")
}
payload, err := s.buildAdminPopupAd(ctx, item)
if err != nil {
return nil, status.Error(codes.Internal, "Failed to save popup ad")
}
return &appv1.CreateAdminPopupAdResponse{Item: payload}, nil
}
func (s *appServices) UpdateAdminPopupAd(ctx context.Context, req *appv1.UpdateAdminPopupAdRequest) (*appv1.UpdateAdminPopupAdResponse, error) {
if _, err := s.requireAdmin(ctx); err != nil {
return nil, err
}
id := strings.TrimSpace(req.GetId())
if id == "" {
return nil, status.Error(codes.NotFound, "Popup ad not found")
}
if msg := validateAdminPopupAdInput(req.GetUserId(), req.GetType(), req.GetLabel(), req.GetValue(), req.MaxTriggersPerSession); msg != "" {
return nil, status.Error(codes.InvalidArgument, msg)
}
user, err := s.userRepository.GetByID(ctx, strings.TrimSpace(req.GetUserId()))
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, status.Error(codes.InvalidArgument, "User not found")
}
return nil, status.Error(codes.Internal, "Failed to save popup ad")
}
item, err := s.popupAdRepository.GetByID(ctx, id)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, status.Error(codes.NotFound, "Popup ad not found")
}
return nil, status.Error(codes.Internal, "Failed to save popup ad")
}
item.UserID = user.ID
item.Type = strings.ToLower(strings.TrimSpace(req.GetType()))
item.Label = strings.TrimSpace(req.GetLabel())
item.Value = strings.TrimSpace(req.GetValue())
if req.IsActive != nil {
item.IsActive = model.BoolPtr(*req.IsActive)
}
if req.MaxTriggersPerSession != nil {
item.MaxTriggersPerSession = int32Ptr(*req.MaxTriggersPerSession)
}
if err := s.popupAdRepository.Save(ctx, item); err != nil {
return nil, status.Error(codes.Internal, "Failed to save popup ad")
}
payload, err := s.buildAdminPopupAd(ctx, item)
if err != nil {
return nil, status.Error(codes.Internal, "Failed to save popup ad")
}
return &appv1.UpdateAdminPopupAdResponse{Item: payload}, nil
}
func (s *appServices) DeleteAdminPopupAd(ctx context.Context, req *appv1.DeleteAdminPopupAdRequest) (*appv1.MessageResponse, error) {
if _, err := s.requireAdmin(ctx); err != nil {
return nil, err
}
id := strings.TrimSpace(req.GetId())
if id == "" {
return nil, status.Error(codes.NotFound, "Popup ad not found")
}
rowsAffected, err := s.popupAdRepository.DeleteByID(ctx, id)
if err != nil {
return nil, status.Error(codes.Internal, "Failed to delete popup ad")
}
if rowsAffected == 0 {
return nil, status.Error(codes.NotFound, "Popup ad not found")
}
return &appv1.MessageResponse{Message: "Popup ad deleted"}, nil
}
func (s *appServices) ListAdminPlayerConfigs(ctx context.Context, req *appv1.ListAdminPlayerConfigsRequest) (*appv1.ListAdminPlayerConfigsResponse, error) {
if _, err := s.requireAdmin(ctx); err != nil {
return nil, err

View File

@@ -48,6 +48,7 @@ type Services struct {
appv1.NotificationsServer
appv1.DomainsServer
appv1.AdTemplatesServer
appv1.PopupAdsServer
appv1.PlayerConfigsServer
appv1.PlansServer
appv1.PaymentsServer
@@ -60,6 +61,7 @@ type accountAppService struct{ *appServices }
type notificationsAppService struct{ *appServices }
type domainsAppService struct{ *appServices }
type adTemplatesAppService struct{ *appServices }
type popupAdsAppService struct{ *appServices }
type playerConfigsAppService struct{ *appServices }
type plansAppService struct{ *appServices }
type paymentsAppService struct{ *appServices }
@@ -72,6 +74,7 @@ type appServices struct {
appv1.UnimplementedNotificationsServer
appv1.UnimplementedDomainsServer
appv1.UnimplementedAdTemplatesServer
appv1.UnimplementedPopupAdsServer
appv1.UnimplementedPlayerConfigsServer
appv1.UnimplementedPlansServer
appv1.UnimplementedPaymentsServer
@@ -92,8 +95,10 @@ type appServices struct {
paymentRepository PaymentRepository
accountRepository AccountRepository
notificationRepo NotificationRepository
notificationEvents NotificationEventPublisher
domainRepository DomainRepository
adTemplateRepository AdTemplateRepository
popupAdRepository PopupAdRepository
playerConfigRepo PlayerConfigRepository
agentRuntime AgentRuntime
googleOauth *oauth2.Config
@@ -138,7 +143,7 @@ type apiErrorBody struct {
Data any `json:"data,omitempty"`
}
func NewServices(c *redis.RedisAdapter, db *gorm.DB, l logger.Logger, cfg *config.Config, videoWorkflowService VideoWorkflow, agentRuntime AgentRuntime) *Services {
func NewServices(c *redis.RedisAdapter, db *gorm.DB, l logger.Logger, cfg *config.Config, videoWorkflowService VideoWorkflow, agentRuntime AgentRuntime, notificationEvents NotificationEventPublisher) *Services {
var storageProvider storage.Provider
if cfg != nil {
provider, err := storage.NewS3Provider(cfg)
@@ -175,7 +180,7 @@ func NewServices(c *redis.RedisAdapter, db *gorm.DB, l logger.Logger, cfg *confi
service := &appServices{
db: db,
logger: l,
authenticator: middleware.NewAuthenticator(db, l, cfg.Internal.Marker),
authenticator: middleware.NewAuthenticator(db, l, cfg.Internal.Marker, notificationEvents),
cache: c,
storageProvider: storageProvider,
videoWorkflowService: videoWorkflowService,
@@ -187,8 +192,10 @@ func NewServices(c *redis.RedisAdapter, db *gorm.DB, l logger.Logger, cfg *confi
paymentRepository: repository.NewPaymentRepository(db),
accountRepository: repository.NewAccountRepository(db),
notificationRepo: repository.NewNotificationRepository(db),
notificationEvents: notificationEvents,
domainRepository: repository.NewDomainRepository(db),
adTemplateRepository: repository.NewAdTemplateRepository(db),
popupAdRepository: repository.NewPopupAdRepository(db),
playerConfigRepo: repository.NewPlayerConfigRepository(db),
jobRepository: repository.NewJobRepository(db),
agentRuntime: agentRuntime,
@@ -203,6 +210,7 @@ func NewServices(c *redis.RedisAdapter, db *gorm.DB, l logger.Logger, cfg *confi
NotificationsServer: &notificationsAppService{appServices: service},
DomainsServer: &domainsAppService{appServices: service},
AdTemplatesServer: &adTemplatesAppService{appServices: service},
PopupAdsServer: &popupAdsAppService{appServices: service},
PlayerConfigsServer: &playerConfigsAppService{appServices: service},
PlansServer: &plansAppService{appServices: service},
PaymentsServer: &paymentsAppService{appServices: service},

View File

@@ -141,6 +141,7 @@ func (s *paymentsAppService) TopupWallet(ctx context.Context, req *appv1.TopupWa
s.logger.Error("Failed to top up wallet", "error", err)
return nil, status.Error(codes.Internal, "Failed to top up wallet")
}
s.publishNotificationCreated(ctx, notification)
balance, err := s.billingRepository.GetWalletBalance(ctx, result.UserID)
if err != nil {

View File

@@ -0,0 +1,354 @@
package service
import (
"context"
"fmt"
"testing"
"time"
"github.com/google/uuid"
_ "modernc.org/sqlite"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
appv1 "stream.api/internal/api/proto/app/v1"
"stream.api/internal/database/model"
"stream.api/internal/database/query"
"stream.api/internal/middleware"
"stream.api/internal/repository"
"stream.api/pkg/logger"
)
const popupTestTrustedMarker = "trusted-popup-test-marker"
type popupTestLogger struct{}
func (popupTestLogger) Info(string, ...any) {}
func (popupTestLogger) Error(string, ...any) {}
func (popupTestLogger) Debug(string, ...any) {}
func (popupTestLogger) Warn(string, ...any) {}
func newPopupTestDB(t *testing.T) *gorm.DB {
t.Helper()
dsn := fmt.Sprintf("file:%s?mode=memory&cache=shared", uuid.NewString())
db, err := gorm.Open(sqlite.Dialector{DriverName: "sqlite", DSN: dsn}, &gorm.Config{})
if err != nil {
t.Fatalf("open sqlite db: %v", err)
}
for _, stmt := range []string{
`CREATE TABLE user (
id TEXT PRIMARY KEY,
email TEXT NOT NULL,
password TEXT,
username TEXT,
avatar TEXT,
role TEXT NOT NULL,
google_id TEXT,
storage_used INTEGER NOT NULL DEFAULT 0,
plan_id TEXT,
referred_by_user_id TEXT,
referral_eligible BOOLEAN NOT NULL DEFAULT 1,
referral_reward_bps INTEGER,
referral_reward_granted_at DATETIME,
referral_reward_payment_id TEXT,
referral_reward_amount REAL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME,
version INTEGER NOT NULL DEFAULT 1,
telegram_id TEXT
)`,
`CREATE TABLE plan (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
description TEXT,
price REAL NOT NULL,
cycle TEXT NOT NULL,
storage_limit INTEGER NOT NULL,
upload_limit INTEGER NOT NULL,
duration_limit INTEGER NOT NULL,
quality_limit TEXT NOT NULL,
features JSON,
is_active BOOLEAN NOT NULL DEFAULT 1,
version INTEGER NOT NULL DEFAULT 1
)`,
`CREATE TABLE plan_subscriptions (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
payment_id TEXT NOT NULL,
plan_id TEXT NOT NULL,
term_months INTEGER NOT NULL,
payment_method TEXT NOT NULL,
wallet_amount REAL NOT NULL,
topup_amount REAL NOT NULL,
started_at DATETIME NOT NULL,
expires_at DATETIME NOT NULL,
reminder_7d_sent_at DATETIME,
reminder_3d_sent_at DATETIME,
reminder_1d_sent_at DATETIME,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME,
version INTEGER NOT NULL DEFAULT 1
)`,
`CREATE TABLE notifications (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
type TEXT NOT NULL,
title TEXT NOT NULL,
message TEXT NOT NULL,
metadata TEXT,
action_url TEXT,
action_label TEXT,
is_read BOOLEAN NOT NULL DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME,
version INTEGER NOT NULL DEFAULT 1
)`,
`CREATE TABLE popup_ads (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
type TEXT NOT NULL,
label TEXT NOT NULL,
value TEXT NOT NULL,
is_active BOOLEAN NOT NULL DEFAULT 1,
max_triggers_per_session INTEGER NOT NULL DEFAULT 3,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME,
version INTEGER NOT NULL DEFAULT 1
)`,
} {
if err := db.Exec(stmt).Error; err != nil {
t.Fatalf("create test schema: %v", err)
}
}
query.SetDefault(db)
return db
}
func newPopupTestServices(t *testing.T, db *gorm.DB) *appServices {
t.Helper()
return &appServices{
db: db,
logger: popupTestLogger{},
authenticator: middleware.NewAuthenticator(db, popupTestLogger{}, popupTestTrustedMarker, nil),
userRepository: repository.NewUserRepository(db),
planRepository: repository.NewPlanRepository(db),
notificationRepo: repository.NewNotificationRepository(db),
popupAdRepository: repository.NewPopupAdRepository(db),
googleUserInfoURL: defaultGoogleUserInfoURL,
}
}
func popupTestContext(userID, role string) context.Context {
return metadata.NewIncomingContext(context.Background(), metadata.Pairs(
middleware.ActorMarkerMetadataKey, popupTestTrustedMarker,
middleware.ActorIDMetadataKey, userID,
middleware.ActorRoleMetadataKey, role,
middleware.ActorEmailMetadataKey, "actor@example.com",
))
}
func popupPtrString(v string) *string { return &v }
func popupPtrBool(v bool) *bool { return &v }
func popupPtrInt32(v int32) *int32 { return &v }
func popupPtrTime(v time.Time) *time.Time { return &v }
func popupSeedUser(t *testing.T, db *gorm.DB, user model.User) model.User {
t.Helper()
if user.Role == nil {
user.Role = popupPtrString("USER")
}
if err := db.Create(&user).Error; err != nil {
t.Fatalf("create user: %v", err)
}
return user
}
func popupSeedAd(t *testing.T, db *gorm.DB, item model.PopupAd) model.PopupAd {
t.Helper()
if item.IsActive == nil {
item.IsActive = popupPtrBool(true)
}
if *item.MaxTriggersPerSession == 0 {
*item.MaxTriggersPerSession = 60
}
if item.CreatedAt == nil {
now := time.Now().UTC()
item.CreatedAt = &now
}
if err := db.Create(&item).Error; err != nil {
t.Fatalf("create popup ad: %v", err)
}
return item
}
func popupMustListAds(t *testing.T, db *gorm.DB, userID string) []model.PopupAd {
t.Helper()
var items []model.PopupAd
if err := db.Order("created_at DESC").Find(&items, "user_id = ?", userID).Error; err != nil {
t.Fatalf("list popup ads: %v", err)
}
return items
}
func popupAssertGRPCCode(t *testing.T, err error, code codes.Code) {
t.Helper()
if status.Code(err) != code {
t.Fatalf("grpc code = %v, want %v (err=%v)", status.Code(err), code, err)
}
}
func TestPopupAdsUserFlow(t *testing.T) {
t.Run("create list update delete popup ad", func(t *testing.T) {
db := newPopupTestDB(t)
services := newPopupTestServices(t, db)
user := popupSeedUser(t, db, model.User{ID: uuid.NewString(), Email: "user@example.com", Role: popupPtrString("USER")})
createResp, err := (&popupAdsAppService{appServices: services}).CreatePopupAd(popupTestContext(user.ID, "USER"), &appv1.CreatePopupAdRequest{
Type: "url",
Label: "Homepage Campaign",
Value: "https://example.com/landing",
IsActive: popupPtrBool(true),
MaxTriggersPerSession: popupPtrInt32(5),
})
if err != nil {
t.Fatalf("CreatePopupAd() error = %v", err)
}
if createResp.Item == nil || createResp.Item.Label != "Homepage Campaign" {
t.Fatalf("CreatePopupAd() unexpected response: %#v", createResp)
}
listResp, err := (&popupAdsAppService{appServices: services}).ListPopupAds(popupTestContext(user.ID, "USER"), &appv1.ListPopupAdsRequest{})
if err != nil {
t.Fatalf("ListPopupAds() error = %v", err)
}
if len(listResp.Items) != 1 {
t.Fatalf("ListPopupAds() count = %d, want 1", len(listResp.Items))
}
updateResp, err := (&popupAdsAppService{appServices: services}).UpdatePopupAd(popupTestContext(user.ID, "USER"), &appv1.UpdatePopupAdRequest{
Id: createResp.Item.Id,
Type: "script",
Label: "Homepage Campaign v2",
Value: `<script async src="//example.com/ad.js"></script>`,
IsActive: popupPtrBool(false),
MaxTriggersPerSession: popupPtrInt32(8),
})
if err != nil {
t.Fatalf("UpdatePopupAd() error = %v", err)
}
if updateResp.Item == nil || updateResp.Item.Label != "Homepage Campaign v2" || updateResp.Item.IsActive {
t.Fatalf("UpdatePopupAd() unexpected response: %#v", updateResp)
}
items := popupMustListAds(t, db, user.ID)
if len(items) != 1 {
t.Fatalf("popup ad count = %d, want 1", len(items))
}
if items[0].Type != "script" || items[0].Label != "Homepage Campaign v2" || *items[0].MaxTriggersPerSession != 8 {
t.Fatalf("popup ad values = %#v", items[0])
}
_, err = (&popupAdsAppService{appServices: services}).DeletePopupAd(popupTestContext(user.ID, "USER"), &appv1.DeletePopupAdRequest{Id: createResp.Item.Id})
if err != nil {
t.Fatalf("DeletePopupAd() error = %v", err)
}
items = popupMustListAds(t, db, user.ID)
if len(items) != 0 {
t.Fatalf("popup ad count after delete = %d, want 0", len(items))
}
})
t.Run("reject invalid type", func(t *testing.T) {
db := newPopupTestDB(t)
services := newPopupTestServices(t, db)
user := popupSeedUser(t, db, model.User{ID: uuid.NewString(), Email: "user@example.com", Role: popupPtrString("USER")})
_, err := (&popupAdsAppService{appServices: services}).CreatePopupAd(popupTestContext(user.ID, "USER"), &appv1.CreatePopupAdRequest{
Type: "image",
Label: "Invalid",
Value: "https://example.com/landing",
})
popupAssertGRPCCode(t, err, codes.InvalidArgument)
})
t.Run("get active popup ad returns newest active item", func(t *testing.T) {
db := newPopupTestDB(t)
services := newPopupTestServices(t, db)
user := popupSeedUser(t, db, model.User{ID: uuid.NewString(), Email: "user@example.com", Role: popupPtrString("USER")})
popupSeedAd(t, db, model.PopupAd{ID: uuid.NewString(), UserID: user.ID, Type: "url", Label: "inactive", Value: "https://example.com/1", IsActive: popupPtrBool(false), MaxTriggersPerSession: popupPtrInt32(2)})
popupSeedAd(t, db, model.PopupAd{ID: uuid.NewString(), UserID: user.ID, Type: "url", Label: "older", Value: "https://example.com/3", IsActive: popupPtrBool(true), MaxTriggersPerSession: popupPtrInt32(1), CreatedAt: popupPtrTime(time.Now().UTC().Add(-time.Minute))})
winner := popupSeedAd(t, db, model.PopupAd{ID: uuid.NewString(), UserID: user.ID, Type: "script", Label: "winner", Value: `<script async src="//example.com/win.js"></script>`, IsActive: popupPtrBool(true), MaxTriggersPerSession: popupPtrInt32(3), CreatedAt: popupPtrTime(time.Now().UTC())})
resp, err := (&popupAdsAppService{appServices: services}).GetActivePopupAd(popupTestContext(user.ID, "USER"), &appv1.GetActivePopupAdRequest{})
if err != nil {
t.Fatalf("GetActivePopupAd() error = %v", err)
}
if resp.Item == nil || resp.Item.Id != winner.ID {
t.Fatalf("GetActivePopupAd() = %#v, want winner %q", resp.Item, winner.ID)
}
})
}
func TestPopupAdsAdminFlow(t *testing.T) {
t.Run("admin create list update delete popup ad", func(t *testing.T) {
db := newPopupTestDB(t)
services := newPopupTestServices(t, db)
admin := popupSeedUser(t, db, model.User{ID: uuid.NewString(), Email: "admin@example.com", Role: popupPtrString("ADMIN")})
user := popupSeedUser(t, db, model.User{ID: uuid.NewString(), Email: "user@example.com", Role: popupPtrString("USER")})
createResp, err := services.CreateAdminPopupAd(popupTestContext(admin.ID, "ADMIN"), &appv1.CreateAdminPopupAdRequest{
UserId: user.ID,
Type: "url",
Label: "Admin Campaign",
Value: "https://example.com/admin",
IsActive: popupPtrBool(true),
MaxTriggersPerSession: popupPtrInt32(7),
})
if err != nil {
t.Fatalf("CreateAdminPopupAd() error = %v", err)
}
if createResp.Item == nil || createResp.Item.UserId != user.ID {
t.Fatalf("CreateAdminPopupAd() unexpected response: %#v", createResp)
}
listResp, err := services.ListAdminPopupAds(popupTestContext(admin.ID, "ADMIN"), &appv1.ListAdminPopupAdsRequest{UserId: &user.ID})
if err != nil {
t.Fatalf("ListAdminPopupAds() error = %v", err)
}
if len(listResp.Items) != 1 {
t.Fatalf("ListAdminPopupAds() count = %d, want 1", len(listResp.Items))
}
updateResp, err := services.UpdateAdminPopupAd(popupTestContext(admin.ID, "ADMIN"), &appv1.UpdateAdminPopupAdRequest{
Id: createResp.Item.Id,
UserId: user.ID,
Type: "script",
Label: "Admin Campaign v2",
Value: `<script async src="//example.com/admin-v2.js"></script>`,
IsActive: popupPtrBool(false),
MaxTriggersPerSession: popupPtrInt32(11),
})
if err != nil {
t.Fatalf("UpdateAdminPopupAd() error = %v", err)
}
if updateResp.Item == nil || updateResp.Item.Label != "Admin Campaign v2" || updateResp.Item.IsActive {
t.Fatalf("UpdateAdminPopupAd() unexpected response: %#v", updateResp)
}
_, err = services.DeleteAdminPopupAd(popupTestContext(admin.ID, "ADMIN"), &appv1.DeleteAdminPopupAdRequest{Id: createResp.Item.Id})
if err != nil {
t.Fatalf("DeleteAdminPopupAd() error = %v", err)
}
items := popupMustListAds(t, db, user.ID)
if len(items) != 0 {
t.Fatalf("popup ad count after delete = %d, want 0", len(items))
}
})
}
var _ logger.Logger = popupTestLogger{}

View File

@@ -318,6 +318,150 @@ func (s *adTemplatesAppService) DeleteAdTemplate(ctx context.Context, req *appv1
return messageResponse("Ad template deleted"), nil
}
func (s *popupAdsAppService) ListPopupAds(ctx context.Context, req *appv1.ListPopupAdsRequest) (*appv1.ListPopupAdsResponse, error) {
result, err := s.authenticate(ctx)
if err != nil {
return nil, err
}
page, limit, offset := adminPageLimitOffset(req.GetPage(), req.GetLimit())
items, total, err := s.popupAdRepository.ListByUser(ctx, result.UserID, limit, offset)
if err != nil {
s.logger.Error("Failed to list popup ads", "error", err)
return nil, status.Error(codes.Internal, "Failed to load popup ads")
}
payload := make([]*appv1.PopupAd, 0, len(items))
for _, item := range items {
copyItem := item
payload = append(payload, toProtoPopupAd(&copyItem))
}
return &appv1.ListPopupAdsResponse{Items: payload, Total: total, Page: page, Limit: limit}, nil
}
func (s *popupAdsAppService) CreatePopupAd(ctx context.Context, req *appv1.CreatePopupAdRequest) (*appv1.CreatePopupAdResponse, error) {
result, err := s.authenticate(ctx)
if err != nil {
return nil, err
}
popupType := strings.ToLower(strings.TrimSpace(req.GetType()))
label := strings.TrimSpace(req.GetLabel())
value := strings.TrimSpace(req.GetValue())
maxTriggers := int32(3)
if req.MaxTriggersPerSession != nil {
maxTriggers = *req.MaxTriggersPerSession
}
if popupType != "url" && popupType != "script" {
return nil, status.Error(codes.InvalidArgument, "Popup ad type must be url or script")
}
if label == "" || value == "" {
return nil, status.Error(codes.InvalidArgument, "Label and value are required")
}
if maxTriggers < 1 {
return nil, status.Error(codes.InvalidArgument, "Max triggers per session must be greater than 0")
}
item := &model.PopupAd{
ID: uuid.New().String(),
UserID: result.UserID,
Type: popupType,
Label: label,
Value: value,
IsActive: model.BoolPtr(req.IsActive == nil || *req.IsActive),
MaxTriggersPerSession: int32Ptr(maxTriggers),
}
if err := s.popupAdRepository.Create(ctx, item); err != nil {
s.logger.Error("Failed to create popup ad", "error", err)
return nil, status.Error(codes.Internal, "Failed to save popup ad")
}
return &appv1.CreatePopupAdResponse{Item: toProtoPopupAd(item)}, nil
}
func (s *popupAdsAppService) UpdatePopupAd(ctx context.Context, req *appv1.UpdatePopupAdRequest) (*appv1.UpdatePopupAdResponse, error) {
result, err := s.authenticate(ctx)
if err != nil {
return nil, err
}
id := strings.TrimSpace(req.GetId())
if id == "" {
return nil, status.Error(codes.NotFound, "Popup ad not found")
}
popupType := strings.ToLower(strings.TrimSpace(req.GetType()))
label := strings.TrimSpace(req.GetLabel())
value := strings.TrimSpace(req.GetValue())
if popupType != "url" && popupType != "script" {
return nil, status.Error(codes.InvalidArgument, "Popup ad type must be url or script")
}
if label == "" || value == "" {
return nil, status.Error(codes.InvalidArgument, "Label and value are required")
}
if req.MaxTriggersPerSession != nil && *req.MaxTriggersPerSession < 1 {
return nil, status.Error(codes.InvalidArgument, "Max triggers per session must be greater than 0")
}
item, err := s.popupAdRepository.GetByIDAndUser(ctx, id, result.UserID)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, status.Error(codes.NotFound, "Popup ad not found")
}
s.logger.Error("Failed to load popup ad", "error", err)
return nil, status.Error(codes.Internal, "Failed to save popup ad")
}
item.Type = popupType
item.Label = label
item.Value = value
if req.IsActive != nil {
item.IsActive = model.BoolPtr(*req.IsActive)
}
if req.MaxTriggersPerSession != nil {
item.MaxTriggersPerSession = int32Ptr(*req.MaxTriggersPerSession)
}
if err := s.popupAdRepository.Save(ctx, item); err != nil {
s.logger.Error("Failed to update popup ad", "error", err)
return nil, status.Error(codes.Internal, "Failed to save popup ad")
}
return &appv1.UpdatePopupAdResponse{Item: toProtoPopupAd(item)}, nil
}
func (s *popupAdsAppService) DeletePopupAd(ctx context.Context, req *appv1.DeletePopupAdRequest) (*appv1.MessageResponse, error) {
result, err := s.authenticate(ctx)
if err != nil {
return nil, err
}
id := strings.TrimSpace(req.GetId())
if id == "" {
return nil, status.Error(codes.NotFound, "Popup ad not found")
}
rowsAffected, err := s.popupAdRepository.DeleteByIDAndUser(ctx, id, result.UserID)
if err != nil {
s.logger.Error("Failed to delete popup ad", "error", err)
return nil, status.Error(codes.Internal, "Failed to delete popup ad")
}
if rowsAffected == 0 {
return nil, status.Error(codes.NotFound, "Popup ad not found")
}
return messageResponse("Popup ad deleted"), nil
}
func (s *popupAdsAppService) GetActivePopupAd(ctx context.Context, _ *appv1.GetActivePopupAdRequest) (*appv1.GetActivePopupAdResponse, error) {
result, err := s.authenticate(ctx)
if err != nil {
return nil, err
}
item, err := s.popupAdRepository.GetActiveByUser(ctx, result.UserID)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return &appv1.GetActivePopupAdResponse{}, nil
}
s.logger.Error("Failed to load active popup ad", "error", err)
return nil, status.Error(codes.Internal, "Failed to load popup ad")
}
return &appv1.GetActivePopupAdResponse{Item: toProtoPopupAd(item)}, nil
}
func (s *plansAppService) ListPlans(ctx context.Context, _ *appv1.ListPlansRequest) (*appv1.ListPlansResponse, error) {
if _, err := s.authenticate(ctx); err != nil {
return nil, err

View File

@@ -53,6 +53,14 @@ func int32Ptr(value int32) *int32 {
return &value
}
func protoTimestampToTime(value *timestamppb.Timestamp) *time.Time {
if value == nil {
return nil
}
timeValue := value.AsTime().UTC()
return &timeValue
}
func protoStringValue(value *string) string {
if value == nil {
return ""

View File

@@ -36,17 +36,19 @@ func NewGRPCModule(ctx context.Context, cfg *config.Config, db *gorm.DB, rds *re
cfg: cfg,
}
var notificationPublisher service.NotificationEventPublisher = nil
if publisher, err := mqtt.NewMQTTBootstrap(jobService, agentRuntime, appLogger); err != nil {
appLogger.Error("Failed to initialize MQTT publisher", "error", err)
} else {
module.mqttPublisher = publisher
notificationPublisher = mqtt.NewNotificationPublisher(publisher.Client(), appLogger)
agentRuntime.SetAgentEventHandler(func(eventType string, agent *dto.AgentWithStats) {
mqtt.PublishAgentMQTTEvent(publisher.Client(), appLogger, eventType, agent)
})
}
agentRuntime.Register(grpcServer)
service.Register(grpcServer, service.NewServices(rds, db, appLogger, cfg, videoService, agentRuntime))
service.Register(grpcServer, service.NewServices(rds, db, appLogger, cfg, videoService, agentRuntime, notificationPublisher))
if module.mqttPublisher != nil {
module.mqttPublisher.Start(ctx)
}

View File

@@ -0,0 +1,41 @@
package mqtt
import (
"context"
pahomqtt "github.com/eclipse/paho.mqtt.golang"
"stream.api/internal/dto"
"stream.api/internal/service"
"stream.api/pkg/logger"
)
type MQTTBootstrap struct{ *streamEventPublisher }
func NewMQTTBootstrap(jobService *service.JobService, agentRT agentRuntime, appLogger logger.Logger) (*MQTTBootstrap, error) {
publisher, err := newStreamEventPublisher(jobService, agentRT, appLogger)
if err != nil {
return nil, err
}
return &MQTTBootstrap{streamEventPublisher: publisher}, nil
}
func (b *MQTTBootstrap) Start(ctx context.Context) {
if b == nil || b.streamEventPublisher == nil {
return
}
b.streamEventPublisher.start(ctx)
}
func (b *MQTTBootstrap) Client() pahomqtt.Client {
if b == nil || b.streamEventPublisher == nil {
return nil
}
return b.client
}
func PublishAgentMQTTEvent(client pahomqtt.Client, appLogger logger.Logger, eventType string, agent *dto.AgentWithStats) {
publishMQTTEvent(client, appLogger, defaultMQTTPrefix, mqttEvent{
Type: eventType,
Payload: mapAgentPayload(agent),
})
}

View File

@@ -0,0 +1,14 @@
package mqtt
import "time"
const (
defaultMQTTBrokerURL = "tcp://broker.mqtt-dashboard.com:1883"
defaultMQTTPrefix = "picpic"
defaultPublishWait = 5 * time.Second
)
type mqttEvent struct {
Type string `json:"type"`
Payload any `json:"payload"`
}

View File

@@ -0,0 +1,44 @@
package mqtt
import (
"context"
"fmt"
pahomqtt "github.com/eclipse/paho.mqtt.golang"
"stream.api/internal/database/model"
"stream.api/internal/service"
"stream.api/pkg/logger"
)
type notificationPublisher struct {
client pahomqtt.Client
logger logger.Logger
prefix string
}
func NewNotificationPublisher(client pahomqtt.Client, appLogger logger.Logger) service.NotificationEventPublisher {
if client == nil {
return service.NotificationEventPublisher(serviceNotificationNoop{})
}
return &notificationPublisher{client: client, logger: appLogger, prefix: defaultMQTTPrefix}
}
type serviceNotificationNoop struct{}
func (serviceNotificationNoop) PublishNotificationCreated(context.Context, *model.Notification) error { return nil }
func (p *notificationPublisher) PublishNotificationCreated(_ context.Context, notification *model.Notification) error {
if p == nil || notification == nil {
return nil
}
message := mqttEvent{
Type: "notification.created",
Payload: service.BuildNotificationCreatedPayload(notification),
}
return publishMQTTJSON(p.client, p.notificationTopic(notification.UserID), message)
}
func (p *notificationPublisher) notificationTopic(userID string) string {
return fmt.Sprintf("%s/notifications/%s", p.prefix, userID)
}

View File

@@ -36,17 +36,22 @@ func publishMQTTEvent(client pahomqtt.Client, appLogger logger.Logger, prefix st
return
}
encoded, err := json.Marshal(event)
if err != nil {
appLogger.Error("Failed to marshal MQTT event", "error", err, "type", event.Type)
return
}
if err := publishPahoMessage(client, fmt.Sprintf("%s/events", prefix), encoded); err != nil {
if err := publishMQTTJSON(client, fmt.Sprintf("%s/events", prefix), event); err != nil {
appLogger.Error("Failed to publish MQTT event", "error", err, "type", event.Type)
}
}
func publishMQTTJSON(client pahomqtt.Client, topic string, payload any) error {
if client == nil {
return nil
}
encoded, err := json.Marshal(payload)
if err != nil {
return err
}
return publishPahoMessage(client, topic, encoded)
}
func publishPahoMessage(client pahomqtt.Client, topic string, payload []byte) error {
if client == nil {
return nil

View File

@@ -12,17 +12,11 @@ import (
"stream.api/pkg/logger"
)
const (
defaultMQTTBrokerURL = "tcp://broker.mqtt-dashboard.com:1883"
defaultMQTTPrefix = "picpic"
defaultPublishWait = 5 * time.Second
)
type agentRuntime interface {
ListAgentsWithStats() []*dto.AgentWithStats
}
type mqttPublisher struct {
type streamEventPublisher struct {
client pahomqtt.Client
jobService *service.JobService
agentRT agentRuntime
@@ -30,13 +24,13 @@ type mqttPublisher struct {
prefix string
}
func newMQTTPublisher(jobService *service.JobService, agentRT agentRuntime, appLogger logger.Logger) (*mqttPublisher, error) {
func newStreamEventPublisher(jobService *service.JobService, agentRT agentRuntime, appLogger logger.Logger) (*streamEventPublisher, error) {
client, err := connectPahoClient(defaultMQTTBrokerURL, fmt.Sprintf("stream-api-%d", time.Now().UnixNano()))
if err != nil {
return nil, err
}
return &mqttPublisher{
return &streamEventPublisher{
client: client,
jobService: jobService,
agentRT: agentRT,
@@ -45,7 +39,7 @@ func newMQTTPublisher(jobService *service.JobService, agentRT agentRuntime, appL
}, nil
}
func (p *mqttPublisher) start(ctx context.Context) {
func (p *streamEventPublisher) start(ctx context.Context) {
if p == nil || p.jobService == nil {
return
}
@@ -54,7 +48,7 @@ func (p *mqttPublisher) start(ctx context.Context) {
go p.consumeResources(ctx)
}
func (p *mqttPublisher) consumeLogs(ctx context.Context) {
func (p *streamEventPublisher) consumeLogs(ctx context.Context) {
ch, err := p.jobService.SubscribeJobLogs(ctx, "")
if err != nil {
p.logger.Error("Failed to subscribe job logs for MQTT", "error", err)
@@ -69,14 +63,14 @@ func (p *mqttPublisher) consumeLogs(ctx context.Context) {
if !ok {
return
}
if err := p.publishJSON(p.logTopic(entry.JobID), entry); err != nil {
if err := publishMQTTJSON(p.client, p.logTopic(entry.JobID), entry); err != nil {
p.logger.Error("Failed to publish MQTT job log", "error", err, "job_id", entry.JobID)
}
}
}
}
func (p *mqttPublisher) consumeJobUpdates(ctx context.Context) {
func (p *streamEventPublisher) consumeJobUpdates(ctx context.Context) {
ch, err := p.jobService.SubscribeJobUpdates(ctx)
if err != nil {
p.logger.Error("Failed to subscribe job updates for MQTT", "error", err)
@@ -106,7 +100,7 @@ func (p *mqttPublisher) consumeJobUpdates(ctx context.Context) {
}
}
func (p *mqttPublisher) consumeResources(ctx context.Context) {
func (p *streamEventPublisher) consumeResources(ctx context.Context) {
ch, err := p.jobService.SubscribeSystemResources(ctx)
if err != nil {
p.logger.Error("Failed to subscribe resources for MQTT", "error", err)
@@ -137,7 +131,7 @@ func (p *mqttPublisher) consumeResources(ctx context.Context) {
}
}
func (p *mqttPublisher) findAgent(agentID string) *dto.AgentWithStats {
func (p *streamEventPublisher) findAgent(agentID string) *dto.AgentWithStats {
if p == nil || p.agentRT == nil {
return nil
}
@@ -149,40 +143,25 @@ func (p *mqttPublisher) findAgent(agentID string) *dto.AgentWithStats {
return nil
}
func (p *mqttPublisher) publishEvent(eventType string, payload any, jobID string) error {
func (p *streamEventPublisher) publishEvent(eventType string, payload any, jobID string) error {
message := mqttEvent{Type: eventType, Payload: payload}
if jobID != "" {
if err := p.publishJSON(p.jobTopic(jobID), message); err != nil {
if err := publishMQTTJSON(p.client, p.jobTopic(jobID), message); err != nil {
return err
}
}
return p.publishJSON(p.eventsTopic(), message)
return publishMQTTJSON(p.client, p.eventsTopic(), message)
}
func (p *mqttPublisher) publishJSON(topic string, payload any) error {
encoded, err := json.Marshal(payload)
if err != nil {
return err
}
return p.publish(topic, encoded)
}
func (p *mqttPublisher) publish(topic string, payload []byte) error {
if p == nil {
return nil
}
return publishPahoMessage(p.client, topic, payload)
}
func (p *mqttPublisher) logTopic(jobID string) string {
func (p *streamEventPublisher) logTopic(jobID string) string {
return fmt.Sprintf("%s/logs/%s", p.prefix, jobID)
}
func (p *mqttPublisher) jobTopic(jobID string) string {
func (p *streamEventPublisher) jobTopic(jobID string) string {
return fmt.Sprintf("%s/job/%s", p.prefix, jobID)
}
func (p *mqttPublisher) eventsTopic() string {
func (p *streamEventPublisher) eventsTopic() string {
return fmt.Sprintf("%s/events", p.prefix)
}
@@ -206,39 +185,3 @@ func mapAgentPayload(agent *dto.AgentWithStats) map[string]any {
"active_job_count": agent.ActiveJobCount,
}
}
type mqttEvent struct {
Type string `json:"type"`
Payload any `json:"payload"`
}
type MQTTBootstrap struct{ *mqttPublisher }
func NewMQTTBootstrap(jobService *service.JobService, agentRT agentRuntime, appLogger logger.Logger) (*MQTTBootstrap, error) {
publisher, err := newMQTTPublisher(jobService, agentRT, appLogger)
if err != nil {
return nil, err
}
return &MQTTBootstrap{mqttPublisher: publisher}, nil
}
func (b *MQTTBootstrap) Start(ctx context.Context) {
if b == nil || b.mqttPublisher == nil {
return
}
b.mqttPublisher.start(ctx)
}
func (b *MQTTBootstrap) Client() pahomqtt.Client {
if b == nil || b.mqttPublisher == nil {
return nil
}
return b.client
}
func PublishAgentMQTTEvent(client pahomqtt.Client, appLogger logger.Logger, eventType string, agent *dto.AgentWithStats) {
publishMQTTEvent(client, appLogger, defaultMQTTPrefix, mqttEvent{
Type: eventType,
Payload: mapAgentPayload(agent),
})
}