diff --git a/api b/api new file mode 100755 index 0000000..a1b9cc8 Binary files /dev/null and b/api differ diff --git a/bin/api b/bin/api new file mode 100755 index 0000000..8bcd5dc Binary files /dev/null and b/bin/api differ diff --git a/buf.gen.yaml b/buf.gen.yaml new file mode 100644 index 0000000..4f8d0b1 --- /dev/null +++ b/buf.gen.yaml @@ -0,0 +1,19 @@ +version: v2 +plugins: + - remote: buf.build/protocolbuffers/go + out: internal/gen/proto + opt: + - paths=source_relative + - remote: buf.build/grpc/go + out: internal/gen/proto + opt: + - paths=source_relative + - remote: buf.build/community/stephenh-ts-proto + out: ../stream-ui/src/server/gen/proto + opt: + - env=node + - esModuleInterop=true + - outputServices=grpc-js + - useOptionals=all + - forceLong=number + - useDate=string diff --git a/buf.yaml b/buf.yaml new file mode 100644 index 0000000..c7e30e3 --- /dev/null +++ b/buf.yaml @@ -0,0 +1,9 @@ +version: v2 +modules: + - path: proto +lint: + use: + - STANDARD +breaking: + use: + - FILE diff --git a/cmd/api/main.go b/cmd/api/main.go index 4ed9050..3490a56 100644 --- a/cmd/api/main.go +++ b/cmd/api/main.go @@ -3,38 +3,20 @@ package main import ( "context" "log" - "net/http" + "net" "os" "os/signal" "syscall" - "time" - "stream.api/internal/app" "stream.api/internal/config" "stream.api/internal/database/query" + videoruntime "stream.api/internal/video/runtime" "stream.api/pkg/cache" "stream.api/pkg/database" "stream.api/pkg/logger" "stream.api/pkg/token" ) -// @title Stream API -// @version 1.0 -// @description This is the API server for Stream application. -// @termsOfService http://swagger.io/terms/ - -// @contact.name API Support -// @contact.url http://www.swagger.io/support -// @contact.email support@swagger.io - -// @license.name Apache 2.0 -// @license.url http://www.apache.org/licenses/LICENSE-2.0.html - -// @host localhost:8080 -// @BasePath / -// @securityDefinitions.apikey BearerAuth -// @in header -// @name Authorization func main() { // 1. Load Config cfg, err := config.LoadConfig() @@ -52,6 +34,8 @@ func main() { // Initialize generated query query.SetDefault(db) + // TODO: Tách database migration ra luồng riêng nếu cần. + // 3. Connect Redis (Cache Interface) rdb, err := cache.NewRedisCache(cfg.Redis.Addr, cfg.Redis.Password, cfg.Redis.DB) if err != nil { @@ -63,39 +47,30 @@ func main() { tokenProvider := token.NewJWTProvider(cfg.JWT.Secret) appLogger := logger.NewLogger(cfg.Server.Mode) - // 5. Setup Router - r := app.SetupRouter(cfg, db, rdb, tokenProvider, appLogger) + module, err := videoruntime.NewModule(context.Background(), cfg, db, rdb, tokenProvider, appLogger) + if err != nil { + log.Fatalf("Failed to setup gRPC runtime module: %v", err) + } - // 5. Run Server with Graceful Shutdown - srv := &http.Server{ - Addr: ":" + cfg.Server.Port, - Handler: r, + grpcListener, err := net.Listen("tcp", ":"+cfg.Server.GRPCPort) + if err != nil { + log.Fatalf("Failed to listen on gRPC port %s: %v", cfg.Server.GRPCPort, err) } go func() { - log.Printf("Starting server on port %s", cfg.Server.Port) - if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { - log.Fatalf("Failed to run server: %v", err) + log.Printf("Starting gRPC server on port %s", cfg.Server.GRPCPort) + if err := module.ServeGRPC(grpcListener); err != nil { + log.Fatalf("Failed to run gRPC server: %v", err) } }() - // Wait for interrupt signal to gracefully shutdown the server with - // a timeout of 5 seconds. quit := make(chan os.Signal, 1) - // kill (no param) default send syscall.SIGTERM - // kill -2 is syscall.SIGINT - // kill -9 is syscall.SIGKILL but can't be caught, so don't need to add it signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) <-quit - log.Println("Shutting down server...") + log.Println("Shutting down gRPC server...") - // The context is used to inform the server it has 5 seconds to finish - // the request it is currently handling - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - if err := srv.Shutdown(ctx); err != nil { - log.Fatal("Server forced to shutdown: ", err) - } + module.Shutdown() + _ = grpcListener.Close() log.Println("Server exiting") } diff --git a/cmd/gendb/main.go b/cmd/gendb/main.go new file mode 100644 index 0000000..5b31af9 --- /dev/null +++ b/cmd/gendb/main.go @@ -0,0 +1,89 @@ +package main + +import ( + "log" + "os" + + "gorm.io/driver/postgres" + "gorm.io/gen" + "gorm.io/gen/field" + "gorm.io/gorm" +) + +func main() { + dsn := os.Getenv("APP_DATABASE_DSN") + if dsn == "" { + log.Fatal("APP_DATABASE_DSN is required") + } + + db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{}) + if err != nil { + log.Fatal("Không thể kết nối database:", err) + } + + // 2. CẤU HÌNH GENERATOR + g := gen.NewGenerator(gen.Config{ + OutPath: "internal/database/query", + ModelPkgPath: "internal/database/model", + Mode: gen.WithDefaultQuery | gen.WithQueryInterface, // Tạo cả query mặc định và interface để dễ mock test + + // Tùy chọn sinh code cho Model + FieldNullable: true, // Sinh pointer (*) cho các field có thể NULL trong DB + FieldCoverable: true, // Sinh pointer cho tất cả field (hữu ích khi dùng hàm Update zero value) + FieldSignable: true, // Hỗ trợ unsigned integer + FieldWithIndexTag: true, // Sinh tag gorm:index + FieldWithTypeTag: true, // Sinh tag gorm:type + }) + + g.UseDB(db) + + // 3. XỬ LÝ KIỂU DỮ LIỆU (Data Mapping) + // Ví dụ: Map decimal sang float64 thay vì string hoặc mảng byte + dataMap := map[string]func(gorm.ColumnType) (dataType string){ + "decimal": func(columnType gorm.ColumnType) (dataType string) { + return "float64" // Hoặc "decimal.Decimal" nếu dùng shopspring/decimal + }, + "numeric": func(columnType gorm.ColumnType) (dataType string) { + return "float64" + }, + "text[]": func(columnType gorm.ColumnType) (dataType string) { + return "[]string" + }, + "_text": func(columnType gorm.ColumnType) (dataType string) { + return "[]string" + }, + } + g.WithDataTypeMap(dataMap) + g.WithImportPkgPath("github.com/lib/pq") + + // 4. CÁC TÙY CHỌN (OPTIONS) + g.WithOpts( + gen.FieldType("id", "string"), + gen.FieldType("features", "pq.StringArray"), + gen.FieldGenType("features", "Field"), + gen.FieldGORMTag("version", func(tag field.GormTag) field.GormTag { + return tag.Set("version", "") + }), + + gen.FieldJSONTag("password", "-"), + gen.FieldJSONTag("version", "-"), + ) + + // 5. CHỌN TABLE ĐỂ GENERATE + // GenerateAllTable() sẽ lấy tất cả, bao gồm cả bảng migration nếu có. + // Nếu muốn loại trừ bảng nào đó, hãy dùng logic filter hoặc liệt kê cụ thể. + + // Cách 1: Lấy tất cả (như code cũ) + allTables := g.GenerateAllTable() + + // Cách 2: (Khuyên dùng) Lọc bỏ các bảng rác hoặc hệ thống + // var tableModels []interface{} + // for _, tbl := range allTables { + // // Logic lọc bảng ở đây nếu cần + // tableModels = append(tableModels, tbl) + // } + + g.ApplyBasic(allTables...) + + g.Execute() +} diff --git a/config.example.yaml b/config.example.yaml index 678ea1e..58ebc1b 100644 --- a/config.example.yaml +++ b/config.example.yaml @@ -1,5 +1,6 @@ server: port: "8080" + grpc_port: "9000" mode: "debug" # debug or release database: @@ -17,6 +18,20 @@ google: client_id: "your_google_client_id" client_secret: "your_google_client_secret" redirect_url: "http://localhost:8080/auth/google/callback" + state_ttl_minutes: 10 + +frontend: + base_url: "http://localhost:5173" + google_auth_finalize_path: "/auth/google/finalize" + +internal: + marker: "your_shared_internal_auth_marker" + +cors: + allow_origins: + - "http://localhost:5173" + - "http://localhost:8080" + - "http://localhost:8081" email: from: "no-reply@picpic.com" @@ -26,3 +41,10 @@ aws: bucket: "your-bucket-name" access_key: "your_access_key" secret_key: "your_secret_key" + +render: + agent_secret: "your_render_agent_secret" + enable_metrics: true + enable_tracing: false + otlp_endpoint: "" + service_name: "stream-api-render" diff --git a/config.yaml b/config.yaml index 709e78d..59a40fd 100644 --- a/config.yaml +++ b/config.yaml @@ -20,3 +20,6 @@ google: email: from: "no-reply@picpic.com" + +internal: + marker: "your-secret-marker" diff --git a/docs/docs.go b/docs/docs.go index 2fa978b..32061d5 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -1,3 +1,6 @@ +//go:build ignore +// +build ignore + // Package docs Code generated by swaggo/swag. DO NOT EDIT package docs @@ -24,6 +27,2694 @@ const docTemplate = `{ "host": "{{.Host}}", "basePath": "{{.BasePath}}", "paths": { + "/ad-templates": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get all VAST ad templates for the current user", + "produces": [ + "application/json" + ], + "tags": [ + "ad-templates" + ], + "summary": "List Ad Templates", + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/adtemplates.TemplateListPayload" + } + } + } + ] + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Create a VAST ad template for the current user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "ad-templates" + ], + "summary": "Create Ad Template", + "parameters": [ + { + "description": "Ad template payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/adtemplates.SaveAdTemplateRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/adtemplates.TemplatePayload" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/ad-templates/{id}": { + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Update a VAST ad template for the current user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "ad-templates" + ], + "summary": "Update Ad Template", + "parameters": [ + { + "type": "string", + "description": "Ad Template ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Ad template payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/adtemplates.SaveAdTemplateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/adtemplates.TemplatePayload" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Delete a VAST ad template for the current user", + "produces": [ + "application/json" + ], + "tags": [ + "ad-templates" + ], + "summary": "Delete Ad Template", + "parameters": [ + { + "type": "string", + "description": "Ad Template ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/admin/ad-templates": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get paginated list of all ad templates across users (admin only)", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "List All Ad Templates", + "parameters": [ + { + "type": "integer", + "default": 1, + "description": "Page", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "default": 20, + "description": "Limit", + "name": "limit", + "in": "query" + }, + { + "type": "string", + "description": "Filter by user ID", + "name": "user_id", + "in": "query" + }, + { + "type": "string", + "description": "Search by name", + "name": "search", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Create an ad template for any user (admin only)", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Create Ad Template", + "parameters": [ + { + "description": "Ad template payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/admin.SaveAdminAdTemplateRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/admin/ad-templates/{id}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get ad template detail (admin only)", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Get Ad Template Detail", + "parameters": [ + { + "type": "string", + "description": "Ad Template ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Update an ad template for any user (admin only)", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Update Ad Template", + "parameters": [ + { + "type": "string", + "description": "Ad Template ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Ad template payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/admin.SaveAdminAdTemplateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Delete any ad template by ID (admin only)", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Delete Ad Template (Admin)", + "parameters": [ + { + "type": "string", + "description": "Ad Template ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/admin/agents": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Returns currently connected render agents and current runtime stats", + "produces": [ + "application/json" + ], + "tags": [ + "admin-render" + ], + "summary": "List connected render agents", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/admin/agents/{id}/restart": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Sends a restart command to a currently connected render agent", + "produces": [ + "application/json" + ], + "tags": [ + "admin-render" + ], + "summary": "Restart connected render agent", + "parameters": [ + { + "type": "string", + "description": "Agent ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "503": { + "description": "Service Unavailable", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/admin/agents/{id}/update": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Sends an update command to a currently connected render agent", + "produces": [ + "application/json" + ], + "tags": [ + "admin-render" + ], + "summary": "Update connected render agent", + "parameters": [ + { + "type": "string", + "description": "Agent ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "503": { + "description": "Service Unavailable", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/admin/dashboard": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get system-wide statistics for the admin dashboard", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Admin Dashboard", + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/admin.DashboardPayload" + } + } + } + ] + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/admin/jobs": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Returns paginated render jobs for admin management", + "produces": [ + "application/json" + ], + "tags": [ + "admin-render" + ], + "summary": "List render jobs", + "parameters": [ + { + "type": "integer", + "description": "Offset", + "name": "offset", + "in": "query" + }, + { + "type": "integer", + "description": "Limit", + "name": "limit", + "in": "query" + }, + { + "type": "string", + "description": "Agent ID", + "name": "agent_id", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Queues a new render job for agents", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "admin-render" + ], + "summary": "Create render job", + "parameters": [ + { + "description": "Job payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/admin.createJobRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/admin/jobs/{id}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Returns a render job by ID", + "produces": [ + "application/json" + ], + "tags": [ + "admin-render" + ], + "summary": "Get render job detail", + "parameters": [ + { + "type": "string", + "description": "Job ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/admin/jobs/{id}/cancel": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Cancels a pending or running render job", + "produces": [ + "application/json" + ], + "tags": [ + "admin-render" + ], + "summary": "Cancel render job", + "parameters": [ + { + "type": "string", + "description": "Job ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/admin/jobs/{id}/logs": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Returns plain text logs for a render job", + "produces": [ + "text/plain" + ], + "tags": [ + "admin-render" + ], + "summary": "Get render job logs", + "parameters": [ + { + "type": "string", + "description": "Job ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/admin/jobs/{id}/retry": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retries a failed or cancelled render job", + "produces": [ + "application/json" + ], + "tags": [ + "admin-render" + ], + "summary": "Retry render job", + "parameters": [ + { + "type": "string", + "description": "Job ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/admin/payments": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get paginated list of all payments across users (admin only)", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "List All Payments", + "parameters": [ + { + "type": "integer", + "default": 1, + "description": "Page", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "default": 20, + "description": "Limit", + "name": "limit", + "in": "query" + }, + { + "type": "string", + "description": "Filter by user ID", + "name": "user_id", + "in": "query" + }, + { + "type": "string", + "description": "Filter by status", + "name": "status", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Create a model subscription charge for a user (admin only)", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Create Payment", + "parameters": [ + { + "description": "Payment payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/admin.CreateAdminPaymentRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/admin/payments/{id}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get payment detail (admin only)", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Get Payment Detail", + "parameters": [ + { + "type": "string", + "description": "Payment ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Update payment status safely without hard delete (admin only)", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Update Payment", + "parameters": [ + { + "type": "string", + "description": "Payment ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Payment update payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/admin.UpdateAdminPaymentRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/admin/plans": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get all plans with usage counts (admin only)", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "List Plans", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Create a plan (admin only)", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Create Plan", + "parameters": [ + { + "description": "Plan payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/admin.SavePlanRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/admin/plans/{id}": { + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Update a plan (admin only)", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Update Plan", + "parameters": [ + { + "type": "string", + "description": "Plan ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Plan payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/admin.SavePlanRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Delete a plan, or deactivate it if already used (admin only)", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Delete Plan", + "parameters": [ + { + "type": "string", + "description": "Plan ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/admin/users": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get paginated list of all users (admin only)", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "List Users", + "parameters": [ + { + "type": "integer", + "default": 1, + "description": "Page", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "default": 20, + "description": "Limit", + "name": "limit", + "in": "query" + }, + { + "type": "string", + "description": "Search by email or username", + "name": "search", + "in": "query" + }, + { + "type": "string", + "description": "Filter by role", + "name": "role", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Create a user from admin panel (admin only)", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Create User", + "parameters": [ + { + "description": "User payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/admin.CreateAdminUserRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/admin/users/{id}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get detailed info about a single user (admin only)", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Get User Detail", + "parameters": [ + { + "type": "string", + "description": "User ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Update a user from admin panel (admin only)", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Update User", + "parameters": [ + { + "type": "string", + "description": "User ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "User payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/admin.UpdateAdminUserRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Delete a user and their data (admin only)", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Delete User", + "parameters": [ + { + "type": "string", + "description": "User ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/admin/users/{id}/role": { + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Change user role (admin only). Valid: USER, ADMIN, BLOCK", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Update User Role", + "parameters": [ + { + "type": "string", + "description": "User ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Role payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/admin.UpdateUserRoleRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/admin/videos": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get paginated list of all videos across users (admin only)", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "List All Videos", + "parameters": [ + { + "type": "integer", + "default": 1, + "description": "Page", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "default": 20, + "description": "Limit", + "name": "limit", + "in": "query" + }, + { + "type": "string", + "description": "Search by title", + "name": "search", + "in": "query" + }, + { + "type": "string", + "description": "Filter by user ID", + "name": "user_id", + "in": "query" + }, + { + "type": "string", + "description": "Filter by status", + "name": "status", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Create a model video record for a user (admin only)", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Create Video", + "parameters": [ + { + "description": "Video payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/admin.SaveAdminVideoRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/admin/videos/{id}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get video detail by ID (admin only)", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Get Video Detail", + "parameters": [ + { + "type": "string", + "description": "Video ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Update video metadata and status (admin only)", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Update Video", + "parameters": [ + { + "type": "string", + "description": "Video ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Video payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/admin.SaveAdminVideoRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Delete any video by ID (admin only)", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Delete Video (Admin)", + "parameters": [ + { + "type": "string", + "description": "Video ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/admin/ws": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Upgrade to websocket for authenticated admin realtime job and agent updates", + "produces": [ + "application/json" + ], + "tags": [ + "admin-render" + ], + "summary": "Admin realtime websocket", + "responses": { + "101": { + "description": "Switching Protocols", + "schema": { + "type": "string" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/auth/change-password": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Change the authenticated user's local password", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Change Password", + "parameters": [ + { + "description": "Password payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/auth.ChangePasswordRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/auth/forgot-password": { + "post": { + "description": "Request password reset link", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Forgot Password", + "parameters": [ + { + "description": "Forgot password payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/auth.ForgotPasswordRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/auth/google/callback": { + "get": { + "description": "Callback for Google Login", + "tags": [ + "auth" + ], + "summary": "Google Callback", + "responses": { + "307": { + "description": "Temporary Redirect" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/auth/google/login": { + "get": { + "description": "Redirect to Google for Login", + "tags": [ + "auth" + ], + "summary": "Google Login", + "responses": {} + } + }, + "/auth/login": { + "post": { + "description": "Login with email and password", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Login", + "parameters": [ + { + "description": "Login payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/auth.LoginRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/auth.UserPayload" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/auth/logout": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Logout user and clear cookies", + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Logout", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/auth/register": { + "post": { + "description": "Register a new user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Register", + "parameters": [ + { + "description": "Registration payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/auth.RegisterRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/auth/reset-password": { + "post": { + "description": "Reset password using token", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Reset Password", + "parameters": [ + { + "description": "Reset password payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/auth.ResetPasswordRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/domains": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get all whitelisted domains for the current user", + "produces": [ + "application/json" + ], + "tags": [ + "domains" + ], + "summary": "List Domains", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Add a domain to the current user's whitelist", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "domains" + ], + "summary": "Create Domain", + "parameters": [ + { + "description": "Domain payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/domains.CreateDomainRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/domains/{id}": { + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Remove a domain from the current user's whitelist", + "produces": [ + "application/json" + ], + "tags": [ + "domains" + ], + "summary": "Delete Domain", + "parameters": [ + { + "type": "string", + "description": "Domain ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/health/detailed": { + "get": { + "description": "Returns detailed health state for database, redis, and render dependencies", + "produces": [ + "application/json" + ], + "tags": [ + "health" + ], + "summary": "Detailed health check", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/services.HealthReport" + } + } + } + } + }, + "/health/live": { + "get": { + "description": "Returns liveness status for the API and render module", + "produces": [ + "application/json" + ], + "tags": [ + "health" + ], + "summary": "Liveness health check", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "503": { + "description": "Service Unavailable", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/health/ready": { + "get": { + "description": "Returns readiness status including render gRPC availability flag", + "produces": [ + "application/json" + ], + "tags": [ + "health" + ], + "summary": "Readiness health check", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "503": { + "description": "Service Unavailable", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "/me": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get the authenticated user's profile payload", + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Get Current User", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Update the authenticated user's profile information", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Update Current User", + "parameters": [ + { + "description": "Profile payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/auth.UpdateMeRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Permanently delete the authenticated user's account and related data", + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Delete My Account", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/me/clear-data": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Remove videos and settings-related resources for the authenticated user", + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Clear My Data", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/notifications": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get notifications for the current user", + "produces": [ + "application/json" + ], + "tags": [ + "notifications" + ], + "summary": "List Notifications", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Delete all notifications for the current user", + "produces": [ + "application/json" + ], + "tags": [ + "notifications" + ], + "summary": "Clear Notifications", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/notifications/read-all": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Mark all notifications as read for the current user", + "produces": [ + "application/json" + ], + "tags": [ + "notifications" + ], + "summary": "Mark All Notifications Read", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/notifications/{id}": { + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Delete a single notification for the current user", + "produces": [ + "application/json" + ], + "tags": [ + "notifications" + ], + "summary": "Delete Notification", + "parameters": [ + { + "type": "string", + "description": "Notification ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/notifications/{id}/read": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Mark a single notification as read for the current user", + "produces": [ + "application/json" + ], + "tags": [ + "notifications" + ], + "summary": "Mark Notification Read", + "parameters": [ + { + "type": "string", + "description": "Notification ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, "/payments": { "post": { "security": [ @@ -31,7 +2722,7 @@ const docTemplate = `{ "BearerAuth": [] } ], - "description": "Create a new payment", + "description": "Create a new payment for buying or renewing a plan", "consumes": [ "application/json" ], @@ -72,6 +2763,101 @@ const docTemplate = `{ "$ref": "#/definitions/response.Response" } }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/payments/history": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get payment history for the current user", + "produces": [ + "application/json" + ], + "tags": [ + "payment" + ], + "summary": "List Payment History", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/payments/{id}/invoice": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Download invoice text for a payment or wallet top-up", + "produces": [ + "text/plain" + ], + "tags": [ + "payment" + ], + "summary": "Download Invoice", + "parameters": [ + { + "type": "string", + "description": "Payment ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, "500": { "description": "Internal Server Error", "schema": { @@ -96,6 +2882,129 @@ const docTemplate = `{ "plan" ], "summary": "List Plans", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/settings/preferences": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get notification, player, and locale preferences for the current user", + "produces": [ + "application/json" + ], + "tags": [ + "settings" + ], + "summary": "Get Preferences", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Update notification, player, and locale preferences for the current user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "settings" + ], + "summary": "Update Preferences", + "parameters": [ + { + "description": "Preferences payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/preferences.SettingsPreferencesRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/usage": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get the authenticated user's total video count and total storage usage", + "produces": [ + "application/json" + ], + "tags": [ + "usage" + ], + "summary": "Get Usage", "responses": { "200": { "description": "OK", @@ -108,16 +3017,19 @@ const docTemplate = `{ "type": "object", "properties": { "data": { - "type": "array", - "items": { - "$ref": "#/definitions/model.Plan" - } + "$ref": "#/definitions/usage.UsagePayload" } } } ] } }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, "500": { "description": "Internal Server Error", "schema": { @@ -336,12 +3248,349 @@ const docTemplate = `{ } } } + }, + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Update title and description for a video owned by the current user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "video" + ], + "summary": "Update Video", + "parameters": [ + { + "type": "string", + "description": "Video ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Video payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/video.UpdateVideoRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Delete a video owned by the current user", + "produces": [ + "application/json" + ], + "tags": [ + "video" + ], + "summary": "Delete Video", + "parameters": [ + { + "type": "string", + "description": "Video ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/wallet/topups": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Add funds to wallet balance for the current user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "payment" + ], + "summary": "Top Up Wallet", + "parameters": [ + { + "description": "Topup Info", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/payment.TopupWalletRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } } } }, "definitions": { - "model.Plan": { + "admin.CreateAdminPaymentRequest": { "type": "object", + "required": [ + "payment_method", + "plan_id", + "term_months", + "user_id" + ], + "properties": { + "payment_method": { + "type": "string" + }, + "plan_id": { + "type": "string" + }, + "term_months": { + "type": "integer" + }, + "topup_amount": { + "type": "number" + }, + "user_id": { + "type": "string" + } + } + }, + "admin.CreateAdminUserRequest": { + "type": "object", + "required": [ + "email", + "password" + ], + "properties": { + "email": { + "type": "string" + }, + "password": { + "type": "string", + "minLength": 6 + }, + "plan_id": { + "type": "string" + }, + "role": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + "admin.DashboardPayload": { + "type": "object", + "properties": { + "active_subscriptions": { + "type": "integer" + }, + "new_users_today": { + "type": "integer" + }, + "new_videos_today": { + "type": "integer" + }, + "total_ad_templates": { + "type": "integer" + }, + "total_payments": { + "type": "integer" + }, + "total_revenue": { + "type": "number" + }, + "total_storage_used": { + "type": "integer" + }, + "total_users": { + "type": "integer" + }, + "total_videos": { + "type": "integer" + } + } + }, + "admin.SaveAdminAdTemplateRequest": { + "type": "object", + "required": [ + "name", + "user_id", + "vast_tag_url" + ], + "properties": { + "ad_format": { + "type": "string" + }, + "description": { + "type": "string" + }, + "duration": { + "type": "integer" + }, + "is_active": { + "type": "boolean" + }, + "is_default": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "user_id": { + "type": "string" + }, + "vast_tag_url": { + "type": "string" + } + } + }, + "admin.SaveAdminVideoRequest": { + "type": "object", + "required": [ + "size", + "title", + "url", + "user_id" + ], + "properties": { + "ad_template_id": { + "type": "string" + }, + "description": { + "type": "string" + }, + "duration": { + "type": "integer" + }, + "format": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "status": { + "type": "string" + }, + "title": { + "type": "string" + }, + "url": { + "type": "string" + }, + "user_id": { + "type": "string" + } + } + }, + "admin.SavePlanRequest": { + "type": "object", + "required": [ + "cycle", + "name", + "price", + "storage_limit", + "upload_limit" + ], "properties": { "cycle": { "type": "string" @@ -349,14 +3598,11 @@ const docTemplate = `{ "description": { "type": "string" }, - "duration_limit": { - "type": "integer" - }, "features": { - "type": "string" - }, - "id": { - "type": "string" + "type": "array", + "items": { + "type": "string" + } }, "is_active": { "type": "boolean" @@ -367,9 +3613,6 @@ const docTemplate = `{ "price": { "type": "number" }, - "quality_limit": { - "type": "string" - }, "storage_limit": { "type": "integer" }, @@ -378,6 +3621,329 @@ const docTemplate = `{ } } }, + "admin.UpdateAdminPaymentRequest": { + "type": "object", + "required": [ + "status" + ], + "properties": { + "status": { + "type": "string" + } + } + }, + "admin.UpdateAdminUserRequest": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "password": { + "type": "string" + }, + "plan_id": { + "type": "string" + }, + "role": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + "admin.UpdateUserRoleRequest": { + "type": "object", + "required": [ + "role" + ], + "properties": { + "role": { + "type": "string" + } + } + }, + "admin.createJobRequest": { + "type": "object", + "properties": { + "command": { + "type": "string" + }, + "env": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "image": { + "type": "string" + }, + "name": { + "type": "string" + }, + "priority": { + "type": "integer" + }, + "time_limit": { + "type": "integer" + }, + "user_id": { + "type": "string" + } + } + }, + "adtemplates.SaveAdTemplateRequest": { + "type": "object", + "required": [ + "name", + "vast_tag_url" + ], + "properties": { + "ad_format": { + "type": "string" + }, + "description": { + "type": "string" + }, + "duration": { + "type": "integer" + }, + "is_active": { + "type": "boolean" + }, + "is_default": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "vast_tag_url": { + "type": "string" + } + } + }, + "adtemplates.TemplateListPayload": { + "type": "object", + "properties": { + "templates": { + "type": "array", + "items": { + "$ref": "#/definitions/model.AdTemplate" + } + } + } + }, + "adtemplates.TemplatePayload": { + "type": "object", + "properties": { + "template": { + "$ref": "#/definitions/model.AdTemplate" + } + } + }, + "auth.ChangePasswordRequest": { + "type": "object", + "required": [ + "current_password", + "new_password" + ], + "properties": { + "current_password": { + "type": "string" + }, + "new_password": { + "type": "string", + "minLength": 6 + } + } + }, + "auth.ForgotPasswordRequest": { + "type": "object", + "required": [ + "email" + ], + "properties": { + "email": { + "type": "string" + } + } + }, + "auth.LoginRequest": { + "type": "object", + "required": [ + "email", + "password" + ], + "properties": { + "email": { + "type": "string" + }, + "password": { + "type": "string" + } + } + }, + "auth.RegisterRequest": { + "type": "object", + "required": [ + "email", + "password", + "username" + ], + "properties": { + "email": { + "type": "string" + }, + "password": { + "type": "string", + "minLength": 6 + }, + "username": { + "type": "string" + } + } + }, + "auth.ResetPasswordRequest": { + "type": "object", + "required": [ + "new_password", + "token" + ], + "properties": { + "new_password": { + "type": "string", + "minLength": 6 + }, + "token": { + "type": "string" + } + } + }, + "auth.UpdateMeRequest": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "language": { + "type": "string" + }, + "locale": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + "auth.UserPayload": { + "type": "object", + "properties": { + "avatar": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "email": { + "type": "string" + }, + "google_id": { + "type": "string" + }, + "id": { + "type": "string" + }, + "language": { + "type": "string" + }, + "locale": { + "type": "string" + }, + "plan_expires_at": { + "type": "string" + }, + "plan_expiring_soon": { + "type": "boolean" + }, + "plan_id": { + "type": "string" + }, + "plan_payment_method": { + "type": "string" + }, + "plan_started_at": { + "type": "string" + }, + "plan_term_months": { + "type": "integer" + }, + "role": { + "type": "string" + }, + "storage_used": { + "type": "integer" + }, + "updated_at": { + "type": "string" + }, + "username": { + "type": "string" + }, + "wallet_balance": { + "type": "number" + } + } + }, + "domains.CreateDomainRequest": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string" + } + } + }, + "model.AdTemplate": { + "type": "object", + "properties": { + "ad_format": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "description": { + "type": "string" + }, + "duration": { + "type": "integer" + }, + "id": { + "type": "string" + }, + "is_active": { + "type": "boolean" + }, + "is_default": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "user_id": { + "type": "string" + }, + "vast_tag_url": { + "type": "string" + } + } + }, "model.Video": { "type": "object", "properties": { @@ -440,15 +4006,77 @@ const docTemplate = `{ "payment.CreatePaymentRequest": { "type": "object", "required": [ - "amount", - "plan_id" + "payment_method", + "plan_id", + "term_months" + ], + "properties": { + "payment_method": { + "type": "string" + }, + "plan_id": { + "type": "string" + }, + "term_months": { + "type": "integer" + }, + "topup_amount": { + "type": "number" + } + } + }, + "payment.TopupWalletRequest": { + "type": "object", + "required": [ + "amount" ], "properties": { "amount": { "type": "number" + } + } + }, + "preferences.SettingsPreferencesRequest": { + "type": "object", + "properties": { + "airplay": { + "type": "boolean" }, - "plan_id": { + "autoplay": { + "type": "boolean" + }, + "chromecast": { + "type": "boolean" + }, + "email_notifications": { + "type": "boolean" + }, + "language": { "type": "string" + }, + "locale": { + "type": "string" + }, + "loop": { + "type": "boolean" + }, + "marketing_notifications": { + "type": "boolean" + }, + "muted": { + "type": "boolean" + }, + "pip": { + "type": "boolean" + }, + "push_notifications": { + "type": "boolean" + }, + "show_controls": { + "type": "boolean" + }, + "telegram_notifications": { + "type": "boolean" } } }, @@ -464,6 +4092,70 @@ const docTemplate = `{ } } }, + "services.ComponentHealth": { + "type": "object", + "properties": { + "checked_at": { + "type": "string" + }, + "latency": { + "type": "string" + }, + "message": { + "type": "string" + }, + "status": { + "$ref": "#/definitions/services.HealthStatus" + } + } + }, + "services.HealthReport": { + "type": "object", + "properties": { + "checked_at": { + "type": "string" + }, + "components": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/services.ComponentHealth" + } + }, + "status": { + "$ref": "#/definitions/services.HealthStatus" + }, + "version": { + "type": "string" + } + } + }, + "services.HealthStatus": { + "type": "string", + "enum": [ + "healthy", + "unhealthy", + "degraded" + ], + "x-enum-varnames": [ + "HealthStatusHealthy", + "HealthStatusUnhealthy", + "HealthStatusDegraded" + ] + }, + "usage.UsagePayload": { + "type": "object", + "properties": { + "total_storage": { + "type": "integer" + }, + "total_videos": { + "type": "integer" + }, + "user_id": { + "type": "string" + } + } + }, "video.CreateVideoRequest": { "type": "object", "required": [ @@ -494,6 +4186,23 @@ const docTemplate = `{ } } }, + "video.UpdateVideoRequest": { + "type": "object", + "required": [ + "title" + ], + "properties": { + "ad_template_id": { + "type": "string" + }, + "description": { + "type": "string" + }, + "title": { + "type": "string" + } + } + }, "video.UploadURLRequest": { "type": "object", "required": [ diff --git a/docs/swagger.json b/docs/swagger.json index 08db3c7..dfc0795 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -18,6 +18,2694 @@ "host": "localhost:8080", "basePath": "/", "paths": { + "/ad-templates": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get all VAST ad templates for the current user", + "produces": [ + "application/json" + ], + "tags": [ + "ad-templates" + ], + "summary": "List Ad Templates", + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/adtemplates.TemplateListPayload" + } + } + } + ] + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Create a VAST ad template for the current user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "ad-templates" + ], + "summary": "Create Ad Template", + "parameters": [ + { + "description": "Ad template payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/adtemplates.SaveAdTemplateRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/adtemplates.TemplatePayload" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/ad-templates/{id}": { + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Update a VAST ad template for the current user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "ad-templates" + ], + "summary": "Update Ad Template", + "parameters": [ + { + "type": "string", + "description": "Ad Template ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Ad template payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/adtemplates.SaveAdTemplateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/adtemplates.TemplatePayload" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Delete a VAST ad template for the current user", + "produces": [ + "application/json" + ], + "tags": [ + "ad-templates" + ], + "summary": "Delete Ad Template", + "parameters": [ + { + "type": "string", + "description": "Ad Template ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/admin/ad-templates": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get paginated list of all ad templates across users (admin only)", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "List All Ad Templates", + "parameters": [ + { + "type": "integer", + "default": 1, + "description": "Page", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "default": 20, + "description": "Limit", + "name": "limit", + "in": "query" + }, + { + "type": "string", + "description": "Filter by user ID", + "name": "user_id", + "in": "query" + }, + { + "type": "string", + "description": "Search by name", + "name": "search", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Create an ad template for any user (admin only)", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Create Ad Template", + "parameters": [ + { + "description": "Ad template payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/admin.SaveAdminAdTemplateRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/admin/ad-templates/{id}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get ad template detail (admin only)", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Get Ad Template Detail", + "parameters": [ + { + "type": "string", + "description": "Ad Template ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Update an ad template for any user (admin only)", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Update Ad Template", + "parameters": [ + { + "type": "string", + "description": "Ad Template ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Ad template payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/admin.SaveAdminAdTemplateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Delete any ad template by ID (admin only)", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Delete Ad Template (Admin)", + "parameters": [ + { + "type": "string", + "description": "Ad Template ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/admin/agents": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Returns currently connected render agents and current runtime stats", + "produces": [ + "application/json" + ], + "tags": [ + "admin-render" + ], + "summary": "List connected render agents", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/admin/agents/{id}/restart": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Sends a restart command to a currently connected render agent", + "produces": [ + "application/json" + ], + "tags": [ + "admin-render" + ], + "summary": "Restart connected render agent", + "parameters": [ + { + "type": "string", + "description": "Agent ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "503": { + "description": "Service Unavailable", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/admin/agents/{id}/update": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Sends an update command to a currently connected render agent", + "produces": [ + "application/json" + ], + "tags": [ + "admin-render" + ], + "summary": "Update connected render agent", + "parameters": [ + { + "type": "string", + "description": "Agent ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "503": { + "description": "Service Unavailable", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/admin/dashboard": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get system-wide statistics for the admin dashboard", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Admin Dashboard", + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/admin.DashboardPayload" + } + } + } + ] + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/admin/jobs": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Returns paginated render jobs for admin management", + "produces": [ + "application/json" + ], + "tags": [ + "admin-render" + ], + "summary": "List render jobs", + "parameters": [ + { + "type": "integer", + "description": "Offset", + "name": "offset", + "in": "query" + }, + { + "type": "integer", + "description": "Limit", + "name": "limit", + "in": "query" + }, + { + "type": "string", + "description": "Agent ID", + "name": "agent_id", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Queues a new render job for agents", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "admin-render" + ], + "summary": "Create render job", + "parameters": [ + { + "description": "Job payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/admin.createJobRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/admin/jobs/{id}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Returns a render job by ID", + "produces": [ + "application/json" + ], + "tags": [ + "admin-render" + ], + "summary": "Get render job detail", + "parameters": [ + { + "type": "string", + "description": "Job ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/admin/jobs/{id}/cancel": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Cancels a pending or running render job", + "produces": [ + "application/json" + ], + "tags": [ + "admin-render" + ], + "summary": "Cancel render job", + "parameters": [ + { + "type": "string", + "description": "Job ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/admin/jobs/{id}/logs": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Returns plain text logs for a render job", + "produces": [ + "text/plain" + ], + "tags": [ + "admin-render" + ], + "summary": "Get render job logs", + "parameters": [ + { + "type": "string", + "description": "Job ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/admin/jobs/{id}/retry": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retries a failed or cancelled render job", + "produces": [ + "application/json" + ], + "tags": [ + "admin-render" + ], + "summary": "Retry render job", + "parameters": [ + { + "type": "string", + "description": "Job ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/admin/payments": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get paginated list of all payments across users (admin only)", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "List All Payments", + "parameters": [ + { + "type": "integer", + "default": 1, + "description": "Page", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "default": 20, + "description": "Limit", + "name": "limit", + "in": "query" + }, + { + "type": "string", + "description": "Filter by user ID", + "name": "user_id", + "in": "query" + }, + { + "type": "string", + "description": "Filter by status", + "name": "status", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Create a model subscription charge for a user (admin only)", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Create Payment", + "parameters": [ + { + "description": "Payment payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/admin.CreateAdminPaymentRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/admin/payments/{id}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get payment detail (admin only)", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Get Payment Detail", + "parameters": [ + { + "type": "string", + "description": "Payment ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Update payment status safely without hard delete (admin only)", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Update Payment", + "parameters": [ + { + "type": "string", + "description": "Payment ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Payment update payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/admin.UpdateAdminPaymentRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/admin/plans": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get all plans with usage counts (admin only)", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "List Plans", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Create a plan (admin only)", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Create Plan", + "parameters": [ + { + "description": "Plan payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/admin.SavePlanRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/admin/plans/{id}": { + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Update a plan (admin only)", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Update Plan", + "parameters": [ + { + "type": "string", + "description": "Plan ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Plan payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/admin.SavePlanRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Delete a plan, or deactivate it if already used (admin only)", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Delete Plan", + "parameters": [ + { + "type": "string", + "description": "Plan ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/admin/users": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get paginated list of all users (admin only)", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "List Users", + "parameters": [ + { + "type": "integer", + "default": 1, + "description": "Page", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "default": 20, + "description": "Limit", + "name": "limit", + "in": "query" + }, + { + "type": "string", + "description": "Search by email or username", + "name": "search", + "in": "query" + }, + { + "type": "string", + "description": "Filter by role", + "name": "role", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Create a user from admin panel (admin only)", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Create User", + "parameters": [ + { + "description": "User payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/admin.CreateAdminUserRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/admin/users/{id}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get detailed info about a single user (admin only)", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Get User Detail", + "parameters": [ + { + "type": "string", + "description": "User ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Update a user from admin panel (admin only)", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Update User", + "parameters": [ + { + "type": "string", + "description": "User ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "User payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/admin.UpdateAdminUserRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Delete a user and their data (admin only)", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Delete User", + "parameters": [ + { + "type": "string", + "description": "User ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/admin/users/{id}/role": { + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Change user role (admin only). Valid: USER, ADMIN, BLOCK", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Update User Role", + "parameters": [ + { + "type": "string", + "description": "User ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Role payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/admin.UpdateUserRoleRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/admin/videos": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get paginated list of all videos across users (admin only)", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "List All Videos", + "parameters": [ + { + "type": "integer", + "default": 1, + "description": "Page", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "default": 20, + "description": "Limit", + "name": "limit", + "in": "query" + }, + { + "type": "string", + "description": "Search by title", + "name": "search", + "in": "query" + }, + { + "type": "string", + "description": "Filter by user ID", + "name": "user_id", + "in": "query" + }, + { + "type": "string", + "description": "Filter by status", + "name": "status", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Create a model video record for a user (admin only)", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Create Video", + "parameters": [ + { + "description": "Video payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/admin.SaveAdminVideoRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/admin/videos/{id}": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get video detail by ID (admin only)", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Get Video Detail", + "parameters": [ + { + "type": "string", + "description": "Video ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Update video metadata and status (admin only)", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Update Video", + "parameters": [ + { + "type": "string", + "description": "Video ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Video payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/admin.SaveAdminVideoRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Delete any video by ID (admin only)", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Delete Video (Admin)", + "parameters": [ + { + "type": "string", + "description": "Video ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/admin/ws": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Upgrade to websocket for authenticated admin realtime job and agent updates", + "produces": [ + "application/json" + ], + "tags": [ + "admin-render" + ], + "summary": "Admin realtime websocket", + "responses": { + "101": { + "description": "Switching Protocols", + "schema": { + "type": "string" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/auth/change-password": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Change the authenticated user's local password", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Change Password", + "parameters": [ + { + "description": "Password payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/auth.ChangePasswordRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/auth/forgot-password": { + "post": { + "description": "Request password reset link", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Forgot Password", + "parameters": [ + { + "description": "Forgot password payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/auth.ForgotPasswordRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/auth/google/callback": { + "get": { + "description": "Callback for Google Login", + "tags": [ + "auth" + ], + "summary": "Google Callback", + "responses": { + "307": { + "description": "Temporary Redirect" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/auth/google/login": { + "get": { + "description": "Redirect to Google for Login", + "tags": [ + "auth" + ], + "summary": "Google Login", + "responses": {} + } + }, + "/auth/login": { + "post": { + "description": "Login with email and password", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Login", + "parameters": [ + { + "description": "Login payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/auth.LoginRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/auth.UserPayload" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/auth/logout": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Logout user and clear cookies", + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Logout", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/auth/register": { + "post": { + "description": "Register a new user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Register", + "parameters": [ + { + "description": "Registration payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/auth.RegisterRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/auth/reset-password": { + "post": { + "description": "Reset password using token", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Reset Password", + "parameters": [ + { + "description": "Reset password payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/auth.ResetPasswordRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/domains": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get all whitelisted domains for the current user", + "produces": [ + "application/json" + ], + "tags": [ + "domains" + ], + "summary": "List Domains", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Add a domain to the current user's whitelist", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "domains" + ], + "summary": "Create Domain", + "parameters": [ + { + "description": "Domain payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/domains.CreateDomainRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/domains/{id}": { + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Remove a domain from the current user's whitelist", + "produces": [ + "application/json" + ], + "tags": [ + "domains" + ], + "summary": "Delete Domain", + "parameters": [ + { + "type": "string", + "description": "Domain ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/health/detailed": { + "get": { + "description": "Returns detailed health state for database, redis, and render dependencies", + "produces": [ + "application/json" + ], + "tags": [ + "health" + ], + "summary": "Detailed health check", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/services.HealthReport" + } + } + } + } + }, + "/health/live": { + "get": { + "description": "Returns liveness status for the API and render module", + "produces": [ + "application/json" + ], + "tags": [ + "health" + ], + "summary": "Liveness health check", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "503": { + "description": "Service Unavailable", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "/health/ready": { + "get": { + "description": "Returns readiness status including render gRPC availability flag", + "produces": [ + "application/json" + ], + "tags": [ + "health" + ], + "summary": "Readiness health check", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "503": { + "description": "Service Unavailable", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "/me": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get the authenticated user's profile payload", + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Get Current User", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Update the authenticated user's profile information", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Update Current User", + "parameters": [ + { + "description": "Profile payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/auth.UpdateMeRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Permanently delete the authenticated user's account and related data", + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Delete My Account", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/me/clear-data": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Remove videos and settings-related resources for the authenticated user", + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Clear My Data", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/notifications": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get notifications for the current user", + "produces": [ + "application/json" + ], + "tags": [ + "notifications" + ], + "summary": "List Notifications", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Delete all notifications for the current user", + "produces": [ + "application/json" + ], + "tags": [ + "notifications" + ], + "summary": "Clear Notifications", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/notifications/read-all": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Mark all notifications as read for the current user", + "produces": [ + "application/json" + ], + "tags": [ + "notifications" + ], + "summary": "Mark All Notifications Read", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/notifications/{id}": { + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Delete a single notification for the current user", + "produces": [ + "application/json" + ], + "tags": [ + "notifications" + ], + "summary": "Delete Notification", + "parameters": [ + { + "type": "string", + "description": "Notification ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/notifications/{id}/read": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Mark a single notification as read for the current user", + "produces": [ + "application/json" + ], + "tags": [ + "notifications" + ], + "summary": "Mark Notification Read", + "parameters": [ + { + "type": "string", + "description": "Notification ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, "/payments": { "post": { "security": [ @@ -25,7 +2713,7 @@ "BearerAuth": [] } ], - "description": "Create a new payment", + "description": "Create a new payment for buying or renewing a plan", "consumes": [ "application/json" ], @@ -66,6 +2754,101 @@ "$ref": "#/definitions/response.Response" } }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/payments/history": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get payment history for the current user", + "produces": [ + "application/json" + ], + "tags": [ + "payment" + ], + "summary": "List Payment History", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/payments/{id}/invoice": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Download invoice text for a payment or wallet top-up", + "produces": [ + "text/plain" + ], + "tags": [ + "payment" + ], + "summary": "Download Invoice", + "parameters": [ + { + "type": "string", + "description": "Payment ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, "500": { "description": "Internal Server Error", "schema": { @@ -90,6 +2873,129 @@ "plan" ], "summary": "List Plans", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/settings/preferences": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get notification, player, and locale preferences for the current user", + "produces": [ + "application/json" + ], + "tags": [ + "settings" + ], + "summary": "Get Preferences", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Update notification, player, and locale preferences for the current user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "settings" + ], + "summary": "Update Preferences", + "parameters": [ + { + "description": "Preferences payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/preferences.SettingsPreferencesRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/usage": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Get the authenticated user's total video count and total storage usage", + "produces": [ + "application/json" + ], + "tags": [ + "usage" + ], + "summary": "Get Usage", "responses": { "200": { "description": "OK", @@ -102,16 +3008,19 @@ "type": "object", "properties": { "data": { - "type": "array", - "items": { - "$ref": "#/definitions/model.Plan" - } + "$ref": "#/definitions/usage.UsagePayload" } } } ] } }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, "500": { "description": "Internal Server Error", "schema": { @@ -330,12 +3239,349 @@ } } } + }, + "put": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Update title and description for a video owned by the current user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "video" + ], + "summary": "Update Video", + "parameters": [ + { + "type": "string", + "description": "Video ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Video payload", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/video.UpdateVideoRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "delete": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Delete a video owned by the current user", + "produces": [ + "application/json" + ], + "tags": [ + "video" + ], + "summary": "Delete Video", + "parameters": [ + { + "type": "string", + "description": "Video ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/wallet/topups": { + "post": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Add funds to wallet balance for the current user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "payment" + ], + "summary": "Top Up Wallet", + "parameters": [ + { + "description": "Topup Info", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/payment.TopupWalletRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } } } }, "definitions": { - "model.Plan": { + "admin.CreateAdminPaymentRequest": { "type": "object", + "required": [ + "payment_method", + "plan_id", + "term_months", + "user_id" + ], + "properties": { + "payment_method": { + "type": "string" + }, + "plan_id": { + "type": "string" + }, + "term_months": { + "type": "integer" + }, + "topup_amount": { + "type": "number" + }, + "user_id": { + "type": "string" + } + } + }, + "admin.CreateAdminUserRequest": { + "type": "object", + "required": [ + "email", + "password" + ], + "properties": { + "email": { + "type": "string" + }, + "password": { + "type": "string", + "minLength": 6 + }, + "plan_id": { + "type": "string" + }, + "role": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + "admin.DashboardPayload": { + "type": "object", + "properties": { + "active_subscriptions": { + "type": "integer" + }, + "new_users_today": { + "type": "integer" + }, + "new_videos_today": { + "type": "integer" + }, + "total_ad_templates": { + "type": "integer" + }, + "total_payments": { + "type": "integer" + }, + "total_revenue": { + "type": "number" + }, + "total_storage_used": { + "type": "integer" + }, + "total_users": { + "type": "integer" + }, + "total_videos": { + "type": "integer" + } + } + }, + "admin.SaveAdminAdTemplateRequest": { + "type": "object", + "required": [ + "name", + "user_id", + "vast_tag_url" + ], + "properties": { + "ad_format": { + "type": "string" + }, + "description": { + "type": "string" + }, + "duration": { + "type": "integer" + }, + "is_active": { + "type": "boolean" + }, + "is_default": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "user_id": { + "type": "string" + }, + "vast_tag_url": { + "type": "string" + } + } + }, + "admin.SaveAdminVideoRequest": { + "type": "object", + "required": [ + "size", + "title", + "url", + "user_id" + ], + "properties": { + "ad_template_id": { + "type": "string" + }, + "description": { + "type": "string" + }, + "duration": { + "type": "integer" + }, + "format": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "status": { + "type": "string" + }, + "title": { + "type": "string" + }, + "url": { + "type": "string" + }, + "user_id": { + "type": "string" + } + } + }, + "admin.SavePlanRequest": { + "type": "object", + "required": [ + "cycle", + "name", + "price", + "storage_limit", + "upload_limit" + ], "properties": { "cycle": { "type": "string" @@ -343,14 +3589,11 @@ "description": { "type": "string" }, - "duration_limit": { - "type": "integer" - }, "features": { - "type": "string" - }, - "id": { - "type": "string" + "type": "array", + "items": { + "type": "string" + } }, "is_active": { "type": "boolean" @@ -361,9 +3604,6 @@ "price": { "type": "number" }, - "quality_limit": { - "type": "string" - }, "storage_limit": { "type": "integer" }, @@ -372,6 +3612,329 @@ } } }, + "admin.UpdateAdminPaymentRequest": { + "type": "object", + "required": [ + "status" + ], + "properties": { + "status": { + "type": "string" + } + } + }, + "admin.UpdateAdminUserRequest": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "password": { + "type": "string" + }, + "plan_id": { + "type": "string" + }, + "role": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + "admin.UpdateUserRoleRequest": { + "type": "object", + "required": [ + "role" + ], + "properties": { + "role": { + "type": "string" + } + } + }, + "admin.createJobRequest": { + "type": "object", + "properties": { + "command": { + "type": "string" + }, + "env": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "image": { + "type": "string" + }, + "name": { + "type": "string" + }, + "priority": { + "type": "integer" + }, + "time_limit": { + "type": "integer" + }, + "user_id": { + "type": "string" + } + } + }, + "adtemplates.SaveAdTemplateRequest": { + "type": "object", + "required": [ + "name", + "vast_tag_url" + ], + "properties": { + "ad_format": { + "type": "string" + }, + "description": { + "type": "string" + }, + "duration": { + "type": "integer" + }, + "is_active": { + "type": "boolean" + }, + "is_default": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "vast_tag_url": { + "type": "string" + } + } + }, + "adtemplates.TemplateListPayload": { + "type": "object", + "properties": { + "templates": { + "type": "array", + "items": { + "$ref": "#/definitions/model.AdTemplate" + } + } + } + }, + "adtemplates.TemplatePayload": { + "type": "object", + "properties": { + "template": { + "$ref": "#/definitions/model.AdTemplate" + } + } + }, + "auth.ChangePasswordRequest": { + "type": "object", + "required": [ + "current_password", + "new_password" + ], + "properties": { + "current_password": { + "type": "string" + }, + "new_password": { + "type": "string", + "minLength": 6 + } + } + }, + "auth.ForgotPasswordRequest": { + "type": "object", + "required": [ + "email" + ], + "properties": { + "email": { + "type": "string" + } + } + }, + "auth.LoginRequest": { + "type": "object", + "required": [ + "email", + "password" + ], + "properties": { + "email": { + "type": "string" + }, + "password": { + "type": "string" + } + } + }, + "auth.RegisterRequest": { + "type": "object", + "required": [ + "email", + "password", + "username" + ], + "properties": { + "email": { + "type": "string" + }, + "password": { + "type": "string", + "minLength": 6 + }, + "username": { + "type": "string" + } + } + }, + "auth.ResetPasswordRequest": { + "type": "object", + "required": [ + "new_password", + "token" + ], + "properties": { + "new_password": { + "type": "string", + "minLength": 6 + }, + "token": { + "type": "string" + } + } + }, + "auth.UpdateMeRequest": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "language": { + "type": "string" + }, + "locale": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + "auth.UserPayload": { + "type": "object", + "properties": { + "avatar": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "email": { + "type": "string" + }, + "google_id": { + "type": "string" + }, + "id": { + "type": "string" + }, + "language": { + "type": "string" + }, + "locale": { + "type": "string" + }, + "plan_expires_at": { + "type": "string" + }, + "plan_expiring_soon": { + "type": "boolean" + }, + "plan_id": { + "type": "string" + }, + "plan_payment_method": { + "type": "string" + }, + "plan_started_at": { + "type": "string" + }, + "plan_term_months": { + "type": "integer" + }, + "role": { + "type": "string" + }, + "storage_used": { + "type": "integer" + }, + "updated_at": { + "type": "string" + }, + "username": { + "type": "string" + }, + "wallet_balance": { + "type": "number" + } + } + }, + "domains.CreateDomainRequest": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string" + } + } + }, + "model.AdTemplate": { + "type": "object", + "properties": { + "ad_format": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "description": { + "type": "string" + }, + "duration": { + "type": "integer" + }, + "id": { + "type": "string" + }, + "is_active": { + "type": "boolean" + }, + "is_default": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "user_id": { + "type": "string" + }, + "vast_tag_url": { + "type": "string" + } + } + }, "model.Video": { "type": "object", "properties": { @@ -434,15 +3997,77 @@ "payment.CreatePaymentRequest": { "type": "object", "required": [ - "amount", - "plan_id" + "payment_method", + "plan_id", + "term_months" + ], + "properties": { + "payment_method": { + "type": "string" + }, + "plan_id": { + "type": "string" + }, + "term_months": { + "type": "integer" + }, + "topup_amount": { + "type": "number" + } + } + }, + "payment.TopupWalletRequest": { + "type": "object", + "required": [ + "amount" ], "properties": { "amount": { "type": "number" + } + } + }, + "preferences.SettingsPreferencesRequest": { + "type": "object", + "properties": { + "airplay": { + "type": "boolean" }, - "plan_id": { + "autoplay": { + "type": "boolean" + }, + "chromecast": { + "type": "boolean" + }, + "email_notifications": { + "type": "boolean" + }, + "language": { "type": "string" + }, + "locale": { + "type": "string" + }, + "loop": { + "type": "boolean" + }, + "marketing_notifications": { + "type": "boolean" + }, + "muted": { + "type": "boolean" + }, + "pip": { + "type": "boolean" + }, + "push_notifications": { + "type": "boolean" + }, + "show_controls": { + "type": "boolean" + }, + "telegram_notifications": { + "type": "boolean" } } }, @@ -458,6 +4083,70 @@ } } }, + "services.ComponentHealth": { + "type": "object", + "properties": { + "checked_at": { + "type": "string" + }, + "latency": { + "type": "string" + }, + "message": { + "type": "string" + }, + "status": { + "$ref": "#/definitions/services.HealthStatus" + } + } + }, + "services.HealthReport": { + "type": "object", + "properties": { + "checked_at": { + "type": "string" + }, + "components": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/services.ComponentHealth" + } + }, + "status": { + "$ref": "#/definitions/services.HealthStatus" + }, + "version": { + "type": "string" + } + } + }, + "services.HealthStatus": { + "type": "string", + "enum": [ + "healthy", + "unhealthy", + "degraded" + ], + "x-enum-varnames": [ + "HealthStatusHealthy", + "HealthStatusUnhealthy", + "HealthStatusDegraded" + ] + }, + "usage.UsagePayload": { + "type": "object", + "properties": { + "total_storage": { + "type": "integer" + }, + "total_videos": { + "type": "integer" + }, + "user_id": { + "type": "string" + } + } + }, "video.CreateVideoRequest": { "type": "object", "required": [ @@ -488,6 +4177,23 @@ } } }, + "video.UpdateVideoRequest": { + "type": "object", + "required": [ + "title" + ], + "properties": { + "ad_template_id": { + "type": "string" + }, + "description": { + "type": "string" + }, + "title": { + "type": "string" + } + } + }, "video.UploadURLRequest": { "type": "object", "required": [ diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 24e53c6..d4f8e9f 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -1,29 +1,349 @@ basePath: / definitions: - model.Plan: + admin.CreateAdminPaymentRequest: + properties: + payment_method: + type: string + plan_id: + type: string + term_months: + type: integer + topup_amount: + type: number + user_id: + type: string + required: + - payment_method + - plan_id + - term_months + - user_id + type: object + admin.CreateAdminUserRequest: + properties: + email: + type: string + password: + minLength: 6 + type: string + plan_id: + type: string + role: + type: string + username: + type: string + required: + - email + - password + type: object + admin.DashboardPayload: + properties: + active_subscriptions: + type: integer + new_users_today: + type: integer + new_videos_today: + type: integer + total_ad_templates: + type: integer + total_payments: + type: integer + total_revenue: + type: number + total_storage_used: + type: integer + total_users: + type: integer + total_videos: + type: integer + type: object + admin.SaveAdminAdTemplateRequest: + properties: + ad_format: + type: string + description: + type: string + duration: + type: integer + is_active: + type: boolean + is_default: + type: boolean + name: + type: string + user_id: + type: string + vast_tag_url: + type: string + required: + - name + - user_id + - vast_tag_url + type: object + admin.SaveAdminVideoRequest: + properties: + ad_template_id: + type: string + description: + type: string + duration: + type: integer + format: + type: string + size: + type: integer + status: + type: string + title: + type: string + url: + type: string + user_id: + type: string + required: + - size + - title + - url + - user_id + type: object + admin.SavePlanRequest: properties: cycle: type: string description: type: string - duration_limit: - type: integer features: - type: string - id: - type: string + items: + type: string + type: array is_active: type: boolean name: type: string price: type: number - quality_limit: - type: string storage_limit: type: integer upload_limit: type: integer + required: + - cycle + - name + - price + - storage_limit + - upload_limit + type: object + admin.UpdateAdminPaymentRequest: + properties: + status: + type: string + required: + - status + type: object + admin.UpdateAdminUserRequest: + properties: + email: + type: string + password: + type: string + plan_id: + type: string + role: + type: string + username: + type: string + type: object + admin.UpdateUserRoleRequest: + properties: + role: + type: string + required: + - role + type: object + admin.createJobRequest: + properties: + command: + type: string + env: + additionalProperties: + type: string + type: object + image: + type: string + name: + type: string + priority: + type: integer + time_limit: + type: integer + user_id: + type: string + type: object + adtemplates.SaveAdTemplateRequest: + properties: + ad_format: + type: string + description: + type: string + duration: + type: integer + is_active: + type: boolean + is_default: + type: boolean + name: + type: string + vast_tag_url: + type: string + required: + - name + - vast_tag_url + type: object + adtemplates.TemplateListPayload: + properties: + templates: + items: + $ref: '#/definitions/model.AdTemplate' + type: array + type: object + adtemplates.TemplatePayload: + properties: + template: + $ref: '#/definitions/model.AdTemplate' + type: object + auth.ChangePasswordRequest: + properties: + current_password: + type: string + new_password: + minLength: 6 + type: string + required: + - current_password + - new_password + type: object + auth.ForgotPasswordRequest: + properties: + email: + type: string + required: + - email + type: object + auth.LoginRequest: + properties: + email: + type: string + password: + type: string + required: + - email + - password + type: object + auth.RegisterRequest: + properties: + email: + type: string + password: + minLength: 6 + type: string + username: + type: string + required: + - email + - password + - username + type: object + auth.ResetPasswordRequest: + properties: + new_password: + minLength: 6 + type: string + token: + type: string + required: + - new_password + - token + type: object + auth.UpdateMeRequest: + properties: + email: + type: string + language: + type: string + locale: + type: string + username: + type: string + type: object + auth.UserPayload: + properties: + avatar: + type: string + created_at: + type: string + email: + type: string + google_id: + type: string + id: + type: string + language: + type: string + locale: + type: string + plan_expires_at: + type: string + plan_expiring_soon: + type: boolean + plan_id: + type: string + plan_payment_method: + type: string + plan_started_at: + type: string + plan_term_months: + type: integer + role: + type: string + storage_used: + type: integer + updated_at: + type: string + username: + type: string + wallet_balance: + type: number + type: object + domains.CreateDomainRequest: + properties: + name: + type: string + required: + - name + type: object + model.AdTemplate: + properties: + ad_format: + type: string + created_at: + type: string + description: + type: string + duration: + type: integer + id: + type: string + is_active: + type: boolean + is_default: + type: boolean + name: + type: string + updated_at: + type: string + user_id: + type: string + vast_tag_url: + type: string type: object model.Video: properties: @@ -66,13 +386,54 @@ definitions: type: object payment.CreatePaymentRequest: properties: - amount: - type: number + payment_method: + type: string plan_id: type: string + term_months: + type: integer + topup_amount: + type: number + required: + - payment_method + - plan_id + - term_months + type: object + payment.TopupWalletRequest: + properties: + amount: + type: number required: - amount - - plan_id + type: object + preferences.SettingsPreferencesRequest: + properties: + airplay: + type: boolean + autoplay: + type: boolean + chromecast: + type: boolean + email_notifications: + type: boolean + language: + type: string + locale: + type: string + loop: + type: boolean + marketing_notifications: + type: boolean + muted: + type: boolean + pip: + type: boolean + push_notifications: + type: boolean + show_controls: + type: boolean + telegram_notifications: + type: boolean type: object response.Response: properties: @@ -82,6 +443,49 @@ definitions: message: type: string type: object + services.ComponentHealth: + properties: + checked_at: + type: string + latency: + type: string + message: + type: string + status: + $ref: '#/definitions/services.HealthStatus' + type: object + services.HealthReport: + properties: + checked_at: + type: string + components: + additionalProperties: + $ref: '#/definitions/services.ComponentHealth' + type: object + status: + $ref: '#/definitions/services.HealthStatus' + version: + type: string + type: object + services.HealthStatus: + enum: + - healthy + - unhealthy + - degraded + type: string + x-enum-varnames: + - HealthStatusHealthy + - HealthStatusUnhealthy + - HealthStatusDegraded + usage.UsagePayload: + properties: + total_storage: + type: integer + total_videos: + type: integer + user_id: + type: string + type: object video.CreateVideoRequest: properties: description: @@ -103,6 +507,17 @@ definitions: - title - url type: object + video.UpdateVideoRequest: + properties: + ad_template_id: + type: string + description: + type: string + title: + type: string + required: + - title + type: object video.UploadURLRequest: properties: content_type: @@ -130,11 +545,1704 @@ info: title: Stream API version: "1.0" paths: + /ad-templates: + get: + description: Get all VAST ad templates for the current user + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/adtemplates.TemplateListPayload' + type: object + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: List Ad Templates + tags: + - ad-templates + post: + consumes: + - application/json + description: Create a VAST ad template for the current user + parameters: + - description: Ad template payload + in: body + name: request + required: true + schema: + $ref: '#/definitions/adtemplates.SaveAdTemplateRequest' + produces: + - application/json + responses: + "201": + description: Created + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/adtemplates.TemplatePayload' + type: object + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "403": + description: Forbidden + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Create Ad Template + tags: + - ad-templates + /ad-templates/{id}: + delete: + description: Delete a VAST ad template for the current user + parameters: + - description: Ad Template ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "403": + description: Forbidden + schema: + $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Delete Ad Template + tags: + - ad-templates + put: + consumes: + - application/json + description: Update a VAST ad template for the current user + parameters: + - description: Ad Template ID + in: path + name: id + required: true + type: string + - description: Ad template payload + in: body + name: request + required: true + schema: + $ref: '#/definitions/adtemplates.SaveAdTemplateRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/adtemplates.TemplatePayload' + type: object + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "403": + description: Forbidden + schema: + $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Update Ad Template + tags: + - ad-templates + /admin/ad-templates: + get: + description: Get paginated list of all ad templates across users (admin only) + parameters: + - default: 1 + description: Page + in: query + name: page + type: integer + - default: 20 + description: Limit + in: query + name: limit + type: integer + - description: Filter by user ID + in: query + name: user_id + type: string + - description: Search by name + in: query + name: search + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "403": + description: Forbidden + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: List All Ad Templates + tags: + - admin + post: + consumes: + - application/json + description: Create an ad template for any user (admin only) + parameters: + - description: Ad template payload + in: body + name: request + required: true + schema: + $ref: '#/definitions/admin.SaveAdminAdTemplateRequest' + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Create Ad Template + tags: + - admin + /admin/ad-templates/{id}: + delete: + description: Delete any ad template by ID (admin only) + parameters: + - description: Ad Template ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Delete Ad Template (Admin) + tags: + - admin + get: + description: Get ad template detail (admin only) + parameters: + - description: Ad Template ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Get Ad Template Detail + tags: + - admin + put: + consumes: + - application/json + description: Update an ad template for any user (admin only) + parameters: + - description: Ad Template ID + in: path + name: id + required: true + type: string + - description: Ad template payload + in: body + name: request + required: true + schema: + $ref: '#/definitions/admin.SaveAdminAdTemplateRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Update Ad Template + tags: + - admin + /admin/agents: + get: + description: Returns currently connected render agents and current runtime stats + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: List connected render agents + tags: + - admin-render + /admin/agents/{id}/restart: + post: + description: Sends a restart command to a currently connected render agent + parameters: + - description: Agent ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "503": + description: Service Unavailable + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Restart connected render agent + tags: + - admin-render + /admin/agents/{id}/update: + post: + description: Sends an update command to a currently connected render agent + parameters: + - description: Agent ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "503": + description: Service Unavailable + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Update connected render agent + tags: + - admin-render + /admin/dashboard: + get: + description: Get system-wide statistics for the admin dashboard + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/admin.DashboardPayload' + type: object + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "403": + description: Forbidden + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Admin Dashboard + tags: + - admin + /admin/jobs: + get: + description: Returns paginated render jobs for admin management + parameters: + - description: Offset + in: query + name: offset + type: integer + - description: Limit + in: query + name: limit + type: integer + - description: Agent ID + in: query + name: agent_id + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: List render jobs + tags: + - admin-render + post: + consumes: + - application/json + description: Queues a new render job for agents + parameters: + - description: Job payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/admin.createJobRequest' + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Create render job + tags: + - admin-render + /admin/jobs/{id}: + get: + description: Returns a render job by ID + parameters: + - description: Job ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Get render job detail + tags: + - admin-render + /admin/jobs/{id}/cancel: + post: + description: Cancels a pending or running render job + parameters: + - description: Job ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Cancel render job + tags: + - admin-render + /admin/jobs/{id}/logs: + get: + description: Returns plain text logs for a render job + parameters: + - description: Job ID + in: path + name: id + required: true + type: string + produces: + - text/plain + responses: + "200": + description: OK + schema: + type: string + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Get render job logs + tags: + - admin-render + /admin/jobs/{id}/retry: + post: + description: Retries a failed or cancelled render job + parameters: + - description: Job ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Retry render job + tags: + - admin-render + /admin/payments: + get: + description: Get paginated list of all payments across users (admin only) + parameters: + - default: 1 + description: Page + in: query + name: page + type: integer + - default: 20 + description: Limit + in: query + name: limit + type: integer + - description: Filter by user ID + in: query + name: user_id + type: string + - description: Filter by status + in: query + name: status + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "403": + description: Forbidden + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: List All Payments + tags: + - admin + post: + consumes: + - application/json + description: Create a model subscription charge for a user (admin only) + parameters: + - description: Payment payload + in: body + name: request + required: true + schema: + $ref: '#/definitions/admin.CreateAdminPaymentRequest' + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Create Payment + tags: + - admin + /admin/payments/{id}: + get: + description: Get payment detail (admin only) + parameters: + - description: Payment ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Get Payment Detail + tags: + - admin + put: + consumes: + - application/json + description: Update payment status safely without hard delete (admin only) + parameters: + - description: Payment ID + in: path + name: id + required: true + type: string + - description: Payment update payload + in: body + name: request + required: true + schema: + $ref: '#/definitions/admin.UpdateAdminPaymentRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Update Payment + tags: + - admin + /admin/plans: + get: + description: Get all plans with usage counts (admin only) + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: List Plans + tags: + - admin + post: + consumes: + - application/json + description: Create a plan (admin only) + parameters: + - description: Plan payload + in: body + name: request + required: true + schema: + $ref: '#/definitions/admin.SavePlanRequest' + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Create Plan + tags: + - admin + /admin/plans/{id}: + delete: + description: Delete a plan, or deactivate it if already used (admin only) + parameters: + - description: Plan ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Delete Plan + tags: + - admin + put: + consumes: + - application/json + description: Update a plan (admin only) + parameters: + - description: Plan ID + in: path + name: id + required: true + type: string + - description: Plan payload + in: body + name: request + required: true + schema: + $ref: '#/definitions/admin.SavePlanRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Update Plan + tags: + - admin + /admin/users: + get: + description: Get paginated list of all users (admin only) + parameters: + - default: 1 + description: Page + in: query + name: page + type: integer + - default: 20 + description: Limit + in: query + name: limit + type: integer + - description: Search by email or username + in: query + name: search + type: string + - description: Filter by role + in: query + name: role + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "403": + description: Forbidden + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: List Users + tags: + - admin + post: + consumes: + - application/json + description: Create a user from admin panel (admin only) + parameters: + - description: User payload + in: body + name: request + required: true + schema: + $ref: '#/definitions/admin.CreateAdminUserRequest' + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Create User + tags: + - admin + /admin/users/{id}: + delete: + description: Delete a user and their data (admin only) + parameters: + - description: User ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Delete User + tags: + - admin + get: + description: Get detailed info about a single user (admin only) + parameters: + - description: User ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Get User Detail + tags: + - admin + put: + consumes: + - application/json + description: Update a user from admin panel (admin only) + parameters: + - description: User ID + in: path + name: id + required: true + type: string + - description: User payload + in: body + name: request + required: true + schema: + $ref: '#/definitions/admin.UpdateAdminUserRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Update User + tags: + - admin + /admin/users/{id}/role: + put: + consumes: + - application/json + description: 'Change user role (admin only). Valid: USER, ADMIN, BLOCK' + parameters: + - description: User ID + in: path + name: id + required: true + type: string + - description: Role payload + in: body + name: request + required: true + schema: + $ref: '#/definitions/admin.UpdateUserRoleRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Update User Role + tags: + - admin + /admin/videos: + get: + description: Get paginated list of all videos across users (admin only) + parameters: + - default: 1 + description: Page + in: query + name: page + type: integer + - default: 20 + description: Limit + in: query + name: limit + type: integer + - description: Search by title + in: query + name: search + type: string + - description: Filter by user ID + in: query + name: user_id + type: string + - description: Filter by status + in: query + name: status + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "403": + description: Forbidden + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: List All Videos + tags: + - admin + post: + consumes: + - application/json + description: Create a model video record for a user (admin only) + parameters: + - description: Video payload + in: body + name: request + required: true + schema: + $ref: '#/definitions/admin.SaveAdminVideoRequest' + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Create Video + tags: + - admin + /admin/videos/{id}: + delete: + description: Delete any video by ID (admin only) + parameters: + - description: Video ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Delete Video (Admin) + tags: + - admin + get: + description: Get video detail by ID (admin only) + parameters: + - description: Video ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Get Video Detail + tags: + - admin + put: + consumes: + - application/json + description: Update video metadata and status (admin only) + parameters: + - description: Video ID + in: path + name: id + required: true + type: string + - description: Video payload + in: body + name: request + required: true + schema: + $ref: '#/definitions/admin.SaveAdminVideoRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Update Video + tags: + - admin + /admin/ws: + get: + description: Upgrade to websocket for authenticated admin realtime job and agent + updates + produces: + - application/json + responses: + "101": + description: Switching Protocols + schema: + type: string + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "403": + description: Forbidden + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Admin realtime websocket + tags: + - admin-render + /auth/change-password: + post: + consumes: + - application/json + description: Change the authenticated user's local password + parameters: + - description: Password payload + in: body + name: request + required: true + schema: + $ref: '#/definitions/auth.ChangePasswordRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Change Password + tags: + - auth + /auth/forgot-password: + post: + consumes: + - application/json + description: Request password reset link + parameters: + - description: Forgot password payload + in: body + name: request + required: true + schema: + $ref: '#/definitions/auth.ForgotPasswordRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + summary: Forgot Password + tags: + - auth + /auth/google/callback: + get: + description: Callback for Google Login + responses: + "307": + description: Temporary Redirect + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + summary: Google Callback + tags: + - auth + /auth/google/login: + get: + description: Redirect to Google for Login + responses: {} + summary: Google Login + tags: + - auth + /auth/login: + post: + consumes: + - application/json + description: Login with email and password + parameters: + - description: Login payload + in: body + name: request + required: true + schema: + $ref: '#/definitions/auth.LoginRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/auth.UserPayload' + type: object + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + summary: Login + tags: + - auth + /auth/logout: + post: + description: Logout user and clear cookies + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Logout + tags: + - auth + /auth/register: + post: + consumes: + - application/json + description: Register a new user + parameters: + - description: Registration payload + in: body + name: request + required: true + schema: + $ref: '#/definitions/auth.RegisterRequest' + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + summary: Register + tags: + - auth + /auth/reset-password: + post: + consumes: + - application/json + description: Reset password using token + parameters: + - description: Reset password payload + in: body + name: request + required: true + schema: + $ref: '#/definitions/auth.ResetPasswordRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + summary: Reset Password + tags: + - auth + /domains: + get: + description: Get all whitelisted domains for the current user + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: List Domains + tags: + - domains + post: + consumes: + - application/json + description: Add a domain to the current user's whitelist + parameters: + - description: Domain payload + in: body + name: request + required: true + schema: + $ref: '#/definitions/domains.CreateDomainRequest' + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Create Domain + tags: + - domains + /domains/{id}: + delete: + description: Remove a domain from the current user's whitelist + parameters: + - description: Domain ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Delete Domain + tags: + - domains + /health/detailed: + get: + description: Returns detailed health state for database, redis, and render dependencies + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/services.HealthReport' + summary: Detailed health check + tags: + - health + /health/live: + get: + description: Returns liveness status for the API and render module + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: + type: string + type: object + "503": + description: Service Unavailable + schema: + additionalProperties: + type: string + type: object + summary: Liveness health check + tags: + - health + /health/ready: + get: + description: Returns readiness status including render gRPC availability flag + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: true + type: object + "503": + description: Service Unavailable + schema: + additionalProperties: true + type: object + summary: Readiness health check + tags: + - health + /me: + delete: + description: Permanently delete the authenticated user's account and related + data + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Delete My Account + tags: + - auth + get: + description: Get the authenticated user's profile payload + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Get Current User + tags: + - auth + put: + consumes: + - application/json + description: Update the authenticated user's profile information + parameters: + - description: Profile payload + in: body + name: request + required: true + schema: + $ref: '#/definitions/auth.UpdateMeRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Update Current User + tags: + - auth + /me/clear-data: + post: + description: Remove videos and settings-related resources for the authenticated + user + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Clear My Data + tags: + - auth + /notifications: + delete: + description: Delete all notifications for the current user + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Clear Notifications + tags: + - notifications + get: + description: Get notifications for the current user + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: List Notifications + tags: + - notifications + /notifications/{id}: + delete: + description: Delete a single notification for the current user + parameters: + - description: Notification ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Delete Notification + tags: + - notifications + /notifications/{id}/read: + post: + description: Mark a single notification as read for the current user + parameters: + - description: Notification ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Mark Notification Read + tags: + - notifications + /notifications/read-all: + post: + description: Mark all notifications as read for the current user + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Mark All Notifications Read + tags: + - notifications /payments: post: consumes: - application/json - description: Create a new payment + description: Create a new payment for buying or renewing a plan parameters: - description: Payment Info in: body @@ -157,6 +2265,10 @@ paths: description: Unauthorized schema: $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' "500": description: Internal Server Error schema: @@ -166,6 +2278,62 @@ paths: summary: Create Payment tags: - payment + /payments/{id}/invoice: + get: + description: Download invoice text for a payment or wallet top-up + parameters: + - description: Payment ID + in: path + name: id + required: true + type: string + produces: + - text/plain + responses: + "200": + description: OK + schema: + type: string + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Download Invoice + tags: + - payment + /payments/history: + get: + description: Get payment history for the current user + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: List Payment History + tags: + - payment /plans: get: description: Get all active plans @@ -175,14 +2343,7 @@ paths: "200": description: OK schema: - allOf: - - $ref: '#/definitions/response.Response' - - properties: - data: - items: - $ref: '#/definitions/model.Plan' - type: array - type: object + $ref: '#/definitions/response.Response' "500": description: Internal Server Error schema: @@ -192,6 +2353,95 @@ paths: summary: List Plans tags: - plan + /settings/preferences: + get: + description: Get notification, player, and locale preferences for the current + user + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Get Preferences + tags: + - settings + put: + consumes: + - application/json + description: Update notification, player, and locale preferences for the current + user + parameters: + - description: Preferences payload + in: body + name: request + required: true + schema: + $ref: '#/definitions/preferences.SettingsPreferencesRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Update Preferences + tags: + - settings + /usage: + get: + description: Get the authenticated user's total video count and total storage + usage + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/usage.UsagePayload' + type: object + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Get Usage + tags: + - usage /videos: get: description: Get paginated videos @@ -259,6 +2509,38 @@ paths: tags: - video /videos/{id}: + delete: + description: Delete a video owned by the current user + parameters: + - description: Video ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Delete Video + tags: + - video get: description: Get video details by ID parameters: @@ -288,6 +2570,50 @@ paths: summary: Get Video tags: - video + put: + consumes: + - application/json + description: Update title and description for a video owned by the current user + parameters: + - description: Video ID + in: path + name: id + required: true + type: string + - description: Video payload + in: body + name: request + required: true + schema: + $ref: '#/definitions/video.UpdateVideoRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Update Video + tags: + - video /videos/upload-url: post: consumes: @@ -320,6 +2646,42 @@ paths: summary: Get Upload URL tags: - video + /wallet/topups: + post: + consumes: + - application/json + description: Add funds to wallet balance for the current user + parameters: + - description: Topup Info + in: body + name: request + required: true + schema: + $ref: '#/definitions/payment.TopupWalletRequest' + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - BearerAuth: [] + summary: Top Up Wallet + tags: + - payment securityDefinitions: BearerAuth: in: header diff --git a/go.mod b/go.mod index 0d321b5..261f1d7 100644 --- a/go.mod +++ b/go.mod @@ -7,18 +7,17 @@ require ( github.com/aws/aws-sdk-go-v2/config v1.32.7 github.com/aws/aws-sdk-go-v2/credentials v1.19.7 github.com/aws/aws-sdk-go-v2/service/s3 v1.95.1 - github.com/gin-contrib/cors v1.7.6 - github.com/gin-gonic/gin v1.11.0 + github.com/eclipse/paho.mqtt.golang v1.5.1 github.com/golang-jwt/jwt/v5 v5.3.0 github.com/google/uuid v1.6.0 + github.com/lib/pq v1.11.2 github.com/redis/go-redis/v9 v9.17.2 github.com/spf13/viper v1.21.0 - github.com/swaggo/files v1.0.1 - github.com/swaggo/gin-swagger v1.6.1 - github.com/swaggo/swag v1.16.6 go.uber.org/zap v1.27.1 golang.org/x/crypto v0.47.0 golang.org/x/oauth2 v0.34.0 + google.golang.org/grpc v1.79.2 + google.golang.org/protobuf v1.36.11 gorm.io/driver/postgres v1.6.0 gorm.io/gen v0.3.27 gorm.io/gorm v1.31.1 @@ -28,7 +27,6 @@ require ( require ( cloud.google.com/go/compute/metadata v0.9.0 // indirect filippo.io/edwards25519 v1.1.0 // indirect - github.com/KyleBanks/depth v1.2.1 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.17 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17 // indirect @@ -44,47 +42,19 @@ require ( github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.13 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.41.6 // indirect github.com/aws/smithy-go v1.24.0 // indirect - github.com/bytedance/gopkg v0.1.3 // indirect - github.com/bytedance/sonic v1.14.2 // indirect - github.com/bytedance/sonic/loader v0.4.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/cloudwego/base64x v0.1.6 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect - github.com/gabriel-vasile/mimetype v1.4.12 // indirect - github.com/gin-contrib/sse v1.1.0 // indirect - github.com/go-openapi/jsonpointer v0.22.4 // indirect - github.com/go-openapi/jsonreference v0.21.4 // indirect - github.com/go-openapi/spec v0.22.3 // indirect - github.com/go-openapi/swag/conv v0.25.4 // indirect - github.com/go-openapi/swag/jsonname v0.25.4 // indirect - github.com/go-openapi/swag/jsonutils v0.25.4 // indirect - github.com/go-openapi/swag/loading v0.25.4 // indirect - github.com/go-openapi/swag/stringutils v0.25.4 // indirect - github.com/go-openapi/swag/typeutils v0.25.4 // indirect - github.com/go-openapi/swag/yamlutils v0.25.4 // indirect - github.com/go-playground/locales v0.14.1 // indirect - github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.30.1 // indirect github.com/go-sql-driver/mysql v1.8.1 // indirect github.com/go-viper/mapstructure/v2 v2.4.0 // indirect - github.com/goccy/go-json v0.10.5 // indirect - github.com/goccy/go-yaml v1.19.2 // indirect + github.com/gorilla/websocket v1.5.3 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/jackc/pgx/v5 v5.6.0 // indirect github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect - github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/cpuid/v2 v2.3.0 // indirect - github.com/leodido/go-urn v1.4.0 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect - github.com/quic-go/qpack v0.6.0 // indirect - github.com/quic-go/quic-go v0.59.0 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/sagikazarmark/locafero v0.11.0 // indirect github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect @@ -92,19 +62,15 @@ require ( github.com/spf13/cast v1.10.0 // indirect github.com/spf13/pflag v1.0.10 // indirect github.com/subosito/gotenv v1.6.0 // indirect - github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - github.com/ugorji/go/codec v1.3.1 // indirect - go.uber.org/mock v0.6.0 // indirect go.uber.org/multierr v1.10.0 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/arch v0.23.0 // indirect golang.org/x/mod v0.32.0 // indirect golang.org/x/net v0.49.0 // indirect golang.org/x/sync v0.19.0 // indirect golang.org/x/sys v0.40.0 // indirect golang.org/x/text v0.33.0 // indirect golang.org/x/tools v0.41.0 // indirect - google.golang.org/protobuf v1.36.11 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect gorm.io/datatypes v1.2.4 // indirect gorm.io/driver/mysql v1.5.7 // indirect gorm.io/hints v1.1.0 // indirect diff --git a/go.sum b/go.sum index 0825014..e5b2c50 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,6 @@ cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdB cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= -github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= -github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= github.com/aws/aws-sdk-go-v2 v1.41.1 h1:ABlyEARCDLN034NhxlRUSZr4l71mh+T5KAeGh6cerhU= github.com/aws/aws-sdk-go-v2 v1.41.1/go.mod h1:MayyLB8y+buD9hZqkCW3kX1AKq07Y5pXxtgB+rRFhz0= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4 h1:489krEF9xIGkOaaX3CE/Be2uWjiXrkCH6gUX+bZA/BU= @@ -46,90 +44,42 @@ github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= -github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M= -github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM= -github.com/bytedance/sonic v1.14.2 h1:k1twIoe97C1DtYUo+fZQy865IuHia4PR5RPiuGPPIIE= -github.com/bytedance/sonic v1.14.2/go.mod h1:T80iDELeHiHKSc0C9tubFygiuXoGzrkjKzX2quAx980= -github.com/bytedance/sonic/loader v0.4.0 h1:olZ7lEqcxtZygCK9EKYKADnpQoYkRQxaeY2NYzevs+o= -github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= -github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/eclipse/paho.mqtt.golang v1.5.1 h1:/VSOv3oDLlpqR2Epjn1Q7b2bSTplJIeV2ISgCl2W7nE= +github.com/eclipse/paho.mqtt.golang v1.5.1/go.mod h1:1/yJCneuyOoCOzKSsOTUc0AJfpsItBGWvYpBLimhArU= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= -github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw= -github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= -github.com/gin-contrib/cors v1.7.6 h1:3gQ8GMzs1Ylpf70y8bMw4fVpycXIeX1ZemuSQIsnQQY= -github.com/gin-contrib/cors v1.7.6/go.mod h1:Ulcl+xN4jel9t1Ry8vqph23a60FwH9xVLd+3ykmTjOk= -github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4= -github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk= -github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= -github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= -github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk= -github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls= -github.com/go-openapi/jsonpointer v0.22.4 h1:dZtK82WlNpVLDW2jlA1YCiVJFVqkED1MegOUy9kR5T4= -github.com/go-openapi/jsonpointer v0.22.4/go.mod h1:elX9+UgznpFhgBuaMQ7iu4lvvX1nvNsesQ3oxmYTw80= -github.com/go-openapi/jsonreference v0.21.4 h1:24qaE2y9bx/q3uRK/qN+TDwbok1NhbSmGjjySRCHtC8= -github.com/go-openapi/jsonreference v0.21.4/go.mod h1:rIENPTjDbLpzQmQWCj5kKj3ZlmEh+EFVbz3RTUh30/4= -github.com/go-openapi/spec v0.22.3 h1:qRSmj6Smz2rEBxMnLRBMeBWxbbOvuOoElvSvObIgwQc= -github.com/go-openapi/spec v0.22.3/go.mod h1:iIImLODL2loCh3Vnox8TY2YWYJZjMAKYyLH2Mu8lOZs= -github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= -github.com/go-openapi/swag/conv v0.25.4 h1:/Dd7p0LZXczgUcC/Ikm1+YqVzkEeCc9LnOWjfkpkfe4= -github.com/go-openapi/swag/conv v0.25.4/go.mod h1:3LXfie/lwoAv0NHoEuY1hjoFAYkvlqI/Bn5EQDD3PPU= -github.com/go-openapi/swag/jsonname v0.25.4 h1:bZH0+MsS03MbnwBXYhuTttMOqk+5KcQ9869Vye1bNHI= -github.com/go-openapi/swag/jsonname v0.25.4/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag= -github.com/go-openapi/swag/jsonutils v0.25.4 h1:VSchfbGhD4UTf4vCdR2F4TLBdLwHyUDTd1/q4i+jGZA= -github.com/go-openapi/swag/jsonutils v0.25.4/go.mod h1:7OYGXpvVFPn4PpaSdPHJBtF0iGnbEaTk8AvBkoWnaAY= -github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4 h1:IACsSvBhiNJwlDix7wq39SS2Fh7lUOCJRmx/4SN4sVo= -github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4/go.mod h1:Mt0Ost9l3cUzVv4OEZG+WSeoHwjWLnarzMePNDAOBiM= -github.com/go-openapi/swag/loading v0.25.4 h1:jN4MvLj0X6yhCDduRsxDDw1aHe+ZWoLjW+9ZQWIKn2s= -github.com/go-openapi/swag/loading v0.25.4/go.mod h1:rpUM1ZiyEP9+mNLIQUdMiD7dCETXvkkC30z53i+ftTE= -github.com/go-openapi/swag/stringutils v0.25.4 h1:O6dU1Rd8bej4HPA3/CLPciNBBDwZj9HiEpdVsb8B5A8= -github.com/go-openapi/swag/stringutils v0.25.4/go.mod h1:GTsRvhJW5xM5gkgiFe0fV3PUlFm0dr8vki6/VSRaZK0= -github.com/go-openapi/swag/typeutils v0.25.4 h1:1/fbZOUN472NTc39zpa+YGHn3jzHWhv42wAJSN91wRw= -github.com/go-openapi/swag/typeutils v0.25.4/go.mod h1:Ou7g//Wx8tTLS9vG0UmzfCsjZjKhpjxayRKTHXf2pTE= -github.com/go-openapi/swag/yamlutils v0.25.4 h1:6jdaeSItEUb7ioS9lFoCZ65Cne1/RZtPBZ9A56h92Sw= -github.com/go-openapi/swag/yamlutils v0.25.4/go.mod h1:MNzq1ulQu+yd8Kl7wPOut/YHAAU/H6hL91fF+E2RFwc= -github.com/go-openapi/testify/enable/yaml/v2 v2.0.2 h1:0+Y41Pz1NkbTHz8NngxTuAXxEodtNSI1WG1c/m5Akw4= -github.com/go-openapi/testify/enable/yaml/v2 v2.0.2/go.mod h1:kme83333GCtJQHXQ8UKX3IBZu6z8T5Dvy5+CW3NLUUg= -github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls= -github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54= -github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= -github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= -github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= -github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= -github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w= -github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= -github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= -github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= -github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= -github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA= github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= @@ -143,36 +93,21 @@ github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkr github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= -github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= -github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/lib/pq v1.11.2 h1:x6gxUeu39V0BHZiugWe8LXZYZ+Utk7hSJGThs8sdzfs= +github.com/lib/pq v1.11.2/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA= github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/microsoft/go-mssqldb v0.17.0 h1:Fto83dMZPnYv1Zwx5vHHxpNraeEaUlQ/hhHLgZiaenE= github.com/microsoft/go-mssqldb v0.17.0/go.mod h1:OkoNGhGEs8EZqchVTtochlXruEhEOaO4S0d2sB5aeGQ= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= -github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= -github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw= -github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU= github.com/redis/go-redis/v9 v9.17.2 h1:P2EGsA4qVIM3Pp+aPocCJ7DguDHhqrXNhVcEp4ViluI= github.com/redis/go-redis/v9 v9.17.2/go.mod h1:u410H11HMLoB+TP67dz8rL9s6QW2j76l0//kSOd3370= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= @@ -190,85 +125,54 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= -github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE= -github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg= -github.com/swaggo/gin-swagger v1.6.1 h1:Ri06G4gc9N4t4k8hekMigJ9zKTFSlqj/9paAQCQs7cY= -github.com/swaggo/gin-swagger v1.6.1/go.mod h1:LQ+hJStHakCWRiK/YNYtJOu4mR2FP+pxLnILT/qNiTw= -github.com/swaggo/swag v1.16.6 h1:qBNcx53ZaX+M5dxVyTrgQ0PJ/ACK+NzhwcbieTt+9yI= -github.com/swaggo/swag v1.16.6/go.mod h1:ngP2etMK5a0P3QBizic5MEwpRmluJZPHjXcMoj4Xesg= -github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= -github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= -github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY= -github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= +go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= +go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= +go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= +go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= +go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= +go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= +go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= +go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= +go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= -go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= -golang.org/x/arch v0.23.0 h1:lKF64A2jF6Zd8L0knGltUnegD62JMFBiCPBmQpToHhg= -golang.org/x/arch v0.23.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c= golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc= golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= +google.golang.org/grpc v1.79.2 h1:fRMD94s2tITpyJGtBBn7MkMseNpOZU8ZxgC3MMBaXRU= +google.golang.org/grpc v1.79.2/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/internal/api/admin/ad_templates.go b/internal/api/admin/ad_templates.go new file mode 100644 index 0000000..dfa19e6 --- /dev/null +++ b/internal/api/admin/ad_templates.go @@ -0,0 +1,390 @@ +//go:build ignore +// +build ignore + +package admin + +import ( + "errors" + "net/http" + "strconv" + "strings" + + "github.com/gin-gonic/gin" + "github.com/google/uuid" + "gorm.io/gorm" + + // "stream.api/internal/database/model" + "stream.api/internal/database/model" + "stream.api/pkg/response" +) + +type AdminAdTemplatePayload struct { + ID string `json:"id"` + UserID string `json:"user_id"` + Name string `json:"name"` + Description string `json:"description"` + VastTagURL string `json:"vast_tag_url"` + AdFormat string `json:"ad_format"` + Duration *int64 `json:"duration,omitempty"` + IsActive bool `json:"is_active"` + IsDefault bool `json:"is_default"` + OwnerEmail string `json:"owner_email,omitempty"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` +} + +type SaveAdminAdTemplateRequest struct { + UserID string `json:"user_id" binding:"required"` + Name string `json:"name" binding:"required"` + Description string `json:"description"` + VASTTagURL string `json:"vast_tag_url" binding:"required"` + AdFormat string `json:"ad_format"` + Duration *int64 `json:"duration"` + IsActive *bool `json:"is_active"` + IsDefault *bool `json:"is_default"` +} + +func normalizeAdminAdFormat(value string) string { + switch strings.TrimSpace(strings.ToLower(value)) { + case "mid-roll", "post-roll": + return strings.TrimSpace(strings.ToLower(value)) + default: + return "pre-roll" + } +} + +func unsetAdminDefaultTemplates(tx *gorm.DB, userID, excludeID string) error { + query := tx.Model(&model.AdTemplate{}).Where("user_id = ?", userID) + if excludeID != "" { + query = query.Where("id <> ?", excludeID) + } + return query.Update("is_default", false).Error +} + +func validateAdminAdTemplateRequest(req *SaveAdminAdTemplateRequest) string { + if strings.TrimSpace(req.UserID) == "" { + return "User ID is required" + } + if strings.TrimSpace(req.Name) == "" || strings.TrimSpace(req.VASTTagURL) == "" { + return "Name and VAST URL are required" + } + format := normalizeAdminAdFormat(req.AdFormat) + if format == "mid-roll" && (req.Duration == nil || *req.Duration <= 0) { + return "Duration is required for mid-roll templates" + } + return "" +} + +func (h *Handler) buildAdminAdTemplatePayload(ctx *gin.Context, item model.AdTemplate, ownerEmail string) AdminAdTemplatePayload { + return AdminAdTemplatePayload{ + ID: item.ID, + UserID: item.UserID, + Name: item.Name, + Description: adminStringValue(item.Description), + VastTagURL: item.VastTagURL, + AdFormat: adminStringValue(item.AdFormat), + Duration: item.Duration, + IsActive: adminBoolValue(item.IsActive, true), + IsDefault: item.IsDefault, + OwnerEmail: ownerEmail, + CreatedAt: adminFormatTime(item.CreatedAt), + UpdatedAt: adminFormatTime(item.UpdatedAt), + } +} + +// @Summary List All Ad Templates +// @Description Get paginated list of all ad templates across users (admin only) +// @Tags admin +// @Produce json +// @Param page query int false "Page" default(1) +// @Param limit query int false "Limit" default(20) +// @Param user_id query string false "Filter by user ID" +// @Param search query string false "Search by name" +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 403 {object} response.Response +// @Router /admin/ad-templates [get] +// @Security BearerAuth +func (h *Handler) ListAdTemplates(c *gin.Context) { + ctx := c.Request.Context() + page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) + if page < 1 { + page = 1 + } + limit, _ := strconv.Atoi(c.DefaultQuery("limit", "20")) + if limit <= 0 { + limit = 20 + } + if limit > 100 { + limit = 100 + } + offset := (page - 1) * limit + + search := strings.TrimSpace(c.Query("search")) + userID := strings.TrimSpace(c.Query("user_id")) + + db := h.db.WithContext(ctx).Model(&model.AdTemplate{}) + if search != "" { + like := "%" + search + "%" + db = db.Where("name ILIKE ?", like) + } + if userID != "" { + db = db.Where("user_id = ?", userID) + } + + var total int64 + if err := db.Count(&total).Error; err != nil { + response.Error(c, http.StatusInternalServerError, "Failed to list ad templates") + return + } + + var templates []model.AdTemplate + if err := db.Order("created_at DESC").Offset(offset).Limit(limit).Find(&templates).Error; err != nil { + h.logger.Error("Failed to list ad templates", "error", err) + response.Error(c, http.StatusInternalServerError, "Failed to list ad templates") + return + } + + ownerIDs := map[string]bool{} + for _, t := range templates { + ownerIDs[t.UserID] = true + } + ownerEmails := map[string]string{} + if len(ownerIDs) > 0 { + ids := make([]string, 0, len(ownerIDs)) + for id := range ownerIDs { + ids = append(ids, id) + } + var users []struct{ ID, Email string } + h.db.WithContext(ctx).Table("\"user\"").Select("id, email").Where("id IN ?", ids).Find(&users) + for _, u := range users { + ownerEmails[u.ID] = u.Email + } + } + + result := make([]AdminAdTemplatePayload, 0, len(templates)) + for _, t := range templates { + result = append(result, h.buildAdminAdTemplatePayload(c, t, ownerEmails[t.UserID])) + } + + response.Success(c, gin.H{ + "templates": result, + "total": total, + "page": page, + "limit": limit, + }) +} + +// @Summary Get Ad Template Detail +// @Description Get ad template detail (admin only) +// @Tags admin +// @Produce json +// @Param id path string true "Ad Template ID" +// @Success 200 {object} response.Response +// @Router /admin/ad-templates/{id} [get] +// @Security BearerAuth +func (h *Handler) GetAdTemplate(c *gin.Context) { + id := strings.TrimSpace(c.Param("id")) + if id == "" { + response.Error(c, http.StatusNotFound, "Ad template not found") + return + } + + var item model.AdTemplate + if err := h.db.WithContext(c.Request.Context()).Where("id = ?", id).First(&item).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + response.Error(c, http.StatusNotFound, "Ad template not found") + return + } + response.Error(c, http.StatusInternalServerError, "Failed to load ad template") + return + } + + ownerEmail := "" + var user model.User + if err := h.db.WithContext(c.Request.Context()).Select("id, email").Where("id = ?", item.UserID).First(&user).Error; err == nil { + ownerEmail = user.Email + } + + response.Success(c, gin.H{"template": h.buildAdminAdTemplatePayload(c, item, ownerEmail)}) +} + +// @Summary Create Ad Template +// @Description Create an ad template for any user (admin only) +// @Tags admin +// @Accept json +// @Produce json +// @Param request body SaveAdminAdTemplateRequest true "Ad template payload" +// @Success 201 {object} response.Response +// @Router /admin/ad-templates [post] +// @Security BearerAuth +func (h *Handler) CreateAdTemplate(c *gin.Context) { + var req SaveAdminAdTemplateRequest + if err := c.ShouldBindJSON(&req); err != nil { + response.Error(c, http.StatusBadRequest, err.Error()) + return + } + if msg := validateAdminAdTemplateRequest(&req); msg != "" { + response.Error(c, http.StatusBadRequest, msg) + return + } + + ctx := c.Request.Context() + var user model.User + if err := h.db.WithContext(ctx).Where("id = ?", strings.TrimSpace(req.UserID)).First(&user).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + response.Error(c, http.StatusBadRequest, "User not found") + return + } + response.Error(c, http.StatusInternalServerError, "Failed to save ad template") + return + } + + item := &model.AdTemplate{ + ID: uuid.New().String(), + UserID: user.ID, + Name: strings.TrimSpace(req.Name), + Description: adminStringPtr(req.Description), + VastTagURL: strings.TrimSpace(req.VASTTagURL), + AdFormat: model.StringPtr(normalizeAdminAdFormat(req.AdFormat)), + Duration: req.Duration, + IsActive: model.BoolPtr(req.IsActive == nil || *req.IsActive), + IsDefault: req.IsDefault != nil && *req.IsDefault, + } + if !adminBoolValue(item.IsActive, true) { + item.IsDefault = false + } + + if err := h.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + if item.IsDefault { + if err := unsetAdminDefaultTemplates(tx, item.UserID, ""); err != nil { + return err + } + } + return tx.Create(item).Error + }); err != nil { + h.logger.Error("Failed to create ad template", "error", err) + response.Error(c, http.StatusInternalServerError, "Failed to save ad template") + return + } + + response.Created(c, gin.H{"template": h.buildAdminAdTemplatePayload(c, *item, user.Email)}) +} + +// @Summary Update Ad Template +// @Description Update an ad template for any user (admin only) +// @Tags admin +// @Accept json +// @Produce json +// @Param id path string true "Ad Template ID" +// @Param request body SaveAdminAdTemplateRequest true "Ad template payload" +// @Success 200 {object} response.Response +// @Router /admin/ad-templates/{id} [put] +// @Security BearerAuth +func (h *Handler) UpdateAdTemplate(c *gin.Context) { + id := strings.TrimSpace(c.Param("id")) + if id == "" { + response.Error(c, http.StatusNotFound, "Ad template not found") + return + } + + var req SaveAdminAdTemplateRequest + if err := c.ShouldBindJSON(&req); err != nil { + response.Error(c, http.StatusBadRequest, err.Error()) + return + } + if msg := validateAdminAdTemplateRequest(&req); msg != "" { + response.Error(c, http.StatusBadRequest, msg) + return + } + + ctx := c.Request.Context() + var user model.User + if err := h.db.WithContext(ctx).Where("id = ?", strings.TrimSpace(req.UserID)).First(&user).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + response.Error(c, http.StatusBadRequest, "User not found") + return + } + response.Error(c, http.StatusInternalServerError, "Failed to save ad template") + return + } + + var item model.AdTemplate + if err := h.db.WithContext(ctx).Where("id = ?", id).First(&item).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + response.Error(c, http.StatusNotFound, "Ad template not found") + return + } + response.Error(c, http.StatusInternalServerError, "Failed to save ad template") + return + } + + item.UserID = user.ID + item.Name = strings.TrimSpace(req.Name) + item.Description = adminStringPtr(req.Description) + item.VastTagURL = strings.TrimSpace(req.VASTTagURL) + item.AdFormat = model.StringPtr(normalizeAdminAdFormat(req.AdFormat)) + item.Duration = req.Duration + if req.IsActive != nil { + item.IsActive = model.BoolPtr(*req.IsActive) + } + if req.IsDefault != nil { + item.IsDefault = *req.IsDefault + } + if !adminBoolValue(item.IsActive, true) { + item.IsDefault = false + } + + if err := h.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + if item.IsDefault { + if err := unsetAdminDefaultTemplates(tx, item.UserID, item.ID); err != nil { + return err + } + } + return tx.Save(&item).Error + }); err != nil { + h.logger.Error("Failed to update ad template", "error", err) + response.Error(c, http.StatusInternalServerError, "Failed to save ad template") + return + } + + response.Success(c, gin.H{"template": h.buildAdminAdTemplatePayload(c, item, user.Email)}) +} + +// @Summary Delete Ad Template (Admin) +// @Description Delete any ad template by ID (admin only) +// @Tags admin +// @Produce json +// @Param id path string true "Ad Template ID" +// @Success 200 {object} response.Response +// @Failure 404 {object} response.Response +// @Router /admin/ad-templates/{id} [delete] +// @Security BearerAuth +func (h *Handler) DeleteAdTemplate(c *gin.Context) { + id := c.Param("id") + + err := h.db.WithContext(c.Request.Context()).Transaction(func(tx *gorm.DB) error { + if err := tx.Where("ad_template_id = ?", id).Delete(&model.VideoAdConfig{}).Error; err != nil { + return err + } + res := tx.Where("id = ?", id).Delete(&model.AdTemplate{}) + if res.Error != nil { + return res.Error + } + if res.RowsAffected == 0 { + return gorm.ErrRecordNotFound + } + return nil + }) + if err != nil { + if err == gorm.ErrRecordNotFound { + response.Error(c, http.StatusNotFound, "Ad template not found") + return + } + h.logger.Error("Failed to delete ad template", "error", err) + response.Error(c, http.StatusInternalServerError, "Failed to delete ad template") + return + } + + response.Success(c, gin.H{"message": "Ad template deleted"}) +} diff --git a/internal/api/admin/common.go b/internal/api/admin/common.go new file mode 100644 index 0000000..fa9f893 --- /dev/null +++ b/internal/api/admin/common.go @@ -0,0 +1,94 @@ +//go:build ignore +// +build ignore + +package admin + +import ( + "fmt" + "strings" + "time" +) + +func adminFormatTime(value *time.Time) string { + if value == nil { + return "" + } + return value.UTC().Format(time.RFC3339) +} + +func adminFormatTimeValue(value time.Time) string { + if value.IsZero() { + return "" + } + return value.UTC().Format(time.RFC3339) +} + +func adminStringPtr(value string) *string { + trimmed := strings.TrimSpace(value) + if trimmed == "" { + return nil + } + return &trimmed +} + +func adminStringValue(value *string) string { + if value == nil { + return "" + } + return strings.TrimSpace(*value) +} + +func adminInt64Ptr(value *int64) *int64 { + if value == nil { + return nil + } + return value +} + +func adminStringSlice(values []string) []string { + if len(values) == 0 { + return nil + } + + result := make([]string, 0, len(values)) + for _, value := range values { + trimmed := strings.TrimSpace(value) + if trimmed == "" { + continue + } + result = append(result, trimmed) + } + + if len(result) == 0 { + return nil + } + + return result +} + +func adminStringSliceValue(values []string) []string { + if len(values) == 0 { + return []string{} + } + + return append([]string(nil), values...) +} + +func adminBoolValue(value *bool, fallback bool) bool { + if value == nil { + return fallback + } + return *value +} + +func adminInvoiceID(id string) string { + trimmed := strings.ReplaceAll(strings.TrimSpace(id), "-", "") + if len(trimmed) > 12 { + trimmed = trimmed[:12] + } + return "INV-" + strings.ToUpper(trimmed) +} + +func adminTransactionID(prefix string) string { + return fmt.Sprintf("%s_%d", prefix, time.Now().UnixNano()) +} diff --git a/internal/api/admin/dashboard.go b/internal/api/admin/dashboard.go new file mode 100644 index 0000000..b7148e6 --- /dev/null +++ b/internal/api/admin/dashboard.go @@ -0,0 +1,68 @@ +//go:build ignore +// +build ignore + +package admin + +import ( + "time" + + "github.com/gin-gonic/gin" + "stream.api/internal/database/model" + "stream.api/pkg/response" +) + +type DashboardPayload struct { + TotalUsers int64 `json:"total_users"` + TotalVideos int64 `json:"total_videos"` + TotalStorageUsed int64 `json:"total_storage_used"` + TotalPayments int64 `json:"total_payments"` + TotalRevenue float64 `json:"total_revenue"` + ActiveSubscriptions int64 `json:"active_subscriptions"` + TotalAdTemplates int64 `json:"total_ad_templates"` + NewUsersToday int64 `json:"new_users_today"` + NewVideosToday int64 `json:"new_videos_today"` +} + +// @Summary Admin Dashboard +// @Description Get system-wide statistics for the admin dashboard +// @Tags admin +// @Produce json +// @Success 200 {object} response.Response{data=DashboardPayload} +// @Failure 401 {object} response.Response +// @Failure 403 {object} response.Response +// @Router /admin/dashboard [get] +// @Security BearerAuth +func (h *Handler) Dashboard(c *gin.Context) { + ctx := c.Request.Context() + var payload DashboardPayload + + h.db.WithContext(ctx).Model(&model.User{}).Count(&payload.TotalUsers) + h.db.WithContext(ctx).Model(&model.Video{}).Count(&payload.TotalVideos) + + h.db.WithContext(ctx).Model(&model.User{}). + Select("COALESCE(SUM(storage_used), 0)"). + Row().Scan(&payload.TotalStorageUsed) + + h.db.WithContext(ctx).Model(&model.Payment{}).Count(&payload.TotalPayments) + + h.db.WithContext(ctx).Model(&model.Payment{}). + Where("status = ?", "SUCCESS"). + Select("COALESCE(SUM(amount), 0)"). + Row().Scan(&payload.TotalRevenue) + + h.db.WithContext(ctx).Model(&model.PlanSubscription{}). + Where("expires_at > ?", time.Now()). + Count(&payload.ActiveSubscriptions) + + h.db.WithContext(ctx).Model(&model.AdTemplate{}).Count(&payload.TotalAdTemplates) + + today := time.Now().Truncate(24 * time.Hour) + h.db.WithContext(ctx).Model(&model.User{}). + Where("created_at >= ?", today). + Count(&payload.NewUsersToday) + h.db.WithContext(ctx).Model(&model.Video{}). + Where("created_at >= ?", today). + Count(&payload.NewVideosToday) + + response.Success(c, payload) +} diff --git a/internal/api/admin/handler.go b/internal/api/admin/handler.go new file mode 100644 index 0000000..5616bcd --- /dev/null +++ b/internal/api/admin/handler.go @@ -0,0 +1,26 @@ +//go:build ignore +// +build ignore + +package admin + +import ( + "gorm.io/gorm" + runtimegrpc "stream.api/internal/video/runtime/grpc" + "stream.api/internal/video/runtime/services" + "stream.api/pkg/logger" +) + +type RenderRuntime interface { + JobService() *services.JobService + AgentRuntime() *runtimegrpc.Server +} + +type Handler struct { + logger logger.Logger + db *gorm.DB + runtime RenderRuntime +} + +func NewHandler(l logger.Logger, db *gorm.DB, renderRuntime RenderRuntime) *Handler { + return &Handler{logger: l, db: db, runtime: renderRuntime} +} diff --git a/internal/api/admin/payments.go b/internal/api/admin/payments.go new file mode 100644 index 0000000..d05f254 --- /dev/null +++ b/internal/api/admin/payments.go @@ -0,0 +1,521 @@ +//go:build ignore +// +build ignore + +package admin + +import ( + "errors" + "fmt" + "net/http" + "strconv" + "strings" + "time" + + "github.com/gin-gonic/gin" + "github.com/google/uuid" + "gorm.io/gorm" + "gorm.io/gorm/clause" + "stream.api/internal/database/model" + "stream.api/pkg/response" +) + +const ( + adminWalletTransactionTypeTopup = "topup" + adminWalletTransactionTypeSubscriptionDebit = "subscription_debit" + adminPaymentMethodWallet = "wallet" + adminPaymentMethodTopup = "topup" +) + +var adminAllowedTermMonths = map[int32]struct{}{ + 1: {}, 3: {}, 6: {}, 12: {}, +} + +type AdminPaymentPayload struct { + ID string `json:"id"` + UserID string `json:"user_id"` + PlanID *string `json:"plan_id,omitempty"` + Amount float64 `json:"amount"` + Currency string `json:"currency"` + Status string `json:"status"` + Provider string `json:"provider"` + TransactionID string `json:"transaction_id,omitempty"` + UserEmail string `json:"user_email,omitempty"` + PlanName string `json:"plan_name,omitempty"` + InvoiceID string `json:"invoice_id"` + TermMonths *int32 `json:"term_months,omitempty"` + PaymentMethod *string `json:"payment_method,omitempty"` + ExpiresAt *string `json:"expires_at,omitempty"` + WalletAmount *float64 `json:"wallet_amount,omitempty"` + TopupAmount *float64 `json:"topup_amount,omitempty"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` +} + +type CreateAdminPaymentRequest struct { + UserID string `json:"user_id" binding:"required"` + PlanID string `json:"plan_id" binding:"required"` + TermMonths int32 `json:"term_months" binding:"required"` + PaymentMethod string `json:"payment_method" binding:"required"` + TopupAmount *float64 `json:"topup_amount,omitempty"` +} + +type UpdateAdminPaymentRequest struct { + Status string `json:"status" binding:"required"` +} + +func normalizeAdminPaymentMethod(value string) string { + switch strings.ToLower(strings.TrimSpace(value)) { + case adminPaymentMethodWallet: + return adminPaymentMethodWallet + case adminPaymentMethodTopup: + return adminPaymentMethodTopup + default: + return "" + } +} + +func normalizeAdminPaymentStatus(value string) string { + switch strings.ToUpper(strings.TrimSpace(value)) { + case "PENDING": + return "PENDING" + case "FAILED": + return "FAILED" + default: + return "SUCCESS" + } +} + +func adminIsAllowedTermMonths(value int32) bool { + _, ok := adminAllowedTermMonths[value] + return ok +} + +func (h *Handler) loadAdminPaymentPayload(ctx *gin.Context, payment model.Payment) (AdminPaymentPayload, error) { + payload := AdminPaymentPayload{ + ID: payment.ID, + UserID: payment.UserID, + PlanID: payment.PlanID, + Amount: payment.Amount, + Currency: strings.ToUpper(adminStringValue(payment.Currency)), + Status: normalizeAdminPaymentStatus(adminStringValue(payment.Status)), + Provider: strings.ToUpper(adminStringValue(payment.Provider)), + TransactionID: adminStringValue(payment.TransactionID), + InvoiceID: adminInvoiceID(payment.ID), + CreatedAt: adminFormatTime(payment.CreatedAt), + UpdatedAt: adminFormatTimeValue(payment.UpdatedAt), + } + if payload.Currency == "" { + payload.Currency = "USD" + } + + var user model.User + if err := h.db.WithContext(ctx.Request.Context()).Select("id, email").Where("id = ?", payment.UserID).First(&user).Error; err == nil { + payload.UserEmail = user.Email + } + + if payment.PlanID != nil && strings.TrimSpace(*payment.PlanID) != "" { + var plan model.Plan + if err := h.db.WithContext(ctx.Request.Context()).Where("id = ?", *payment.PlanID).First(&plan).Error; err == nil { + payload.PlanName = plan.Name + } + } + + var subscription model.PlanSubscription + if err := h.db.WithContext(ctx.Request.Context()).Where("payment_id = ?", payment.ID).Order("created_at DESC").First(&subscription).Error; err == nil { + payload.TermMonths = &subscription.TermMonths + method := subscription.PaymentMethod + payload.PaymentMethod = &method + expiresAt := subscription.ExpiresAt.UTC().Format(time.RFC3339) + payload.ExpiresAt = &expiresAt + payload.WalletAmount = &subscription.WalletAmount + payload.TopupAmount = &subscription.TopupAmount + } + + return payload, nil +} + +func (h *Handler) adminLockUserForUpdate(ctx *gin.Context, tx *gorm.DB, userID string) (*model.User, error) { + var user model.User + if err := tx.WithContext(ctx.Request.Context()).Clauses(clause.Locking{Strength: "UPDATE"}).Where("id = ?", userID).First(&user).Error; err != nil { + return nil, err + } + return &user, nil +} + +func (h *Handler) adminCreateSubscriptionPayment(ctx *gin.Context, req CreateAdminPaymentRequest) (*model.Payment, *model.PlanSubscription, float64, error) { + planID := strings.TrimSpace(req.PlanID) + userID := strings.TrimSpace(req.UserID) + paymentMethod := normalizeAdminPaymentMethod(req.PaymentMethod) + if paymentMethod == "" { + return nil, nil, 0, errors.New("Payment method must be wallet or topup") + } + if !adminIsAllowedTermMonths(req.TermMonths) { + return nil, nil, 0, errors.New("Term months must be one of 1, 3, 6, or 12") + } + + var planRecord model.Plan + if err := h.db.WithContext(ctx.Request.Context()).Where("id = ?", planID).First(&planRecord).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil, 0, errors.New("Plan not found") + } + return nil, nil, 0, err + } + if !adminBoolValue(planRecord.IsActive, true) { + return nil, nil, 0, errors.New("Plan is not active") + } + + var user model.User + if err := h.db.WithContext(ctx.Request.Context()).Where("id = ?", userID).First(&user).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil, 0, errors.New("User not found") + } + return nil, nil, 0, err + } + + totalAmount := planRecord.Price * float64(req.TermMonths) + status := "SUCCESS" + provider := "INTERNAL" + currency := "USD" + transactionID := adminTransactionID("sub") + now := time.Now().UTC() + payment := &model.Payment{ + ID: uuid.New().String(), + UserID: user.ID, + PlanID: &planRecord.ID, + Amount: totalAmount, + Currency: ¤cy, + Status: &status, + Provider: &provider, + TransactionID: &transactionID, + } + + var subscription *model.PlanSubscription + var walletBalance float64 + err := h.db.WithContext(ctx.Request.Context()).Transaction(func(tx *gorm.DB) error { + if _, err := h.adminLockUserForUpdate(ctx, tx, user.ID); err != nil { + return err + } + + var currentSubscription model.PlanSubscription + hasCurrentSubscription := false + if err := tx.Where("user_id = ?", user.ID).Order("created_at DESC").First(¤tSubscription).Error; err == nil { + hasCurrentSubscription = true + } else if !errors.Is(err, gorm.ErrRecordNotFound) { + return err + } + + baseExpiry := now + if hasCurrentSubscription && currentSubscription.ExpiresAt.After(baseExpiry) { + baseExpiry = currentSubscription.ExpiresAt.UTC() + } + newExpiry := baseExpiry.AddDate(0, int(req.TermMonths), 0) + + currentWalletBalance, err := model.GetWalletBalance(ctx.Request.Context(), tx, user.ID) + if err != nil { + return err + } + shortfall := totalAmount - currentWalletBalance + if shortfall < 0 { + shortfall = 0 + } + if paymentMethod == adminPaymentMethodWallet && shortfall > 0 { + return fmt.Errorf("Insufficient wallet balance") + } + + topupAmount := 0.0 + if paymentMethod == adminPaymentMethodTopup { + if req.TopupAmount == nil { + return fmt.Errorf("Top-up amount is required when payment method is topup") + } + topupAmount = *req.TopupAmount + if topupAmount <= 0 { + return fmt.Errorf("Top-up amount must be greater than 0") + } + if topupAmount < shortfall { + return fmt.Errorf("Top-up amount must be greater than or equal to the required shortfall") + } + } + + if err := tx.Create(payment).Error; err != nil { + return err + } + + if paymentMethod == adminPaymentMethodTopup { + topupTransaction := &model.WalletTransaction{ + ID: uuid.New().String(), + UserID: user.ID, + Type: adminWalletTransactionTypeTopup, + Amount: topupAmount, + Currency: model.StringPtr(currency), + Note: model.StringPtr(fmt.Sprintf("Wallet top-up for %s (%d months)", planRecord.Name, req.TermMonths)), + PaymentID: &payment.ID, + PlanID: &planRecord.ID, + TermMonths: &req.TermMonths, + } + if err := tx.Create(topupTransaction).Error; err != nil { + return err + } + } + + debitTransaction := &model.WalletTransaction{ + ID: uuid.New().String(), + UserID: user.ID, + Type: adminWalletTransactionTypeSubscriptionDebit, + Amount: -totalAmount, + Currency: model.StringPtr(currency), + Note: model.StringPtr(fmt.Sprintf("Subscription payment for %s (%d months)", planRecord.Name, req.TermMonths)), + PaymentID: &payment.ID, + PlanID: &planRecord.ID, + TermMonths: &req.TermMonths, + } + if err := tx.Create(debitTransaction).Error; err != nil { + return err + } + + subscription = &model.PlanSubscription{ + ID: uuid.New().String(), + UserID: user.ID, + PaymentID: payment.ID, + PlanID: planRecord.ID, + TermMonths: req.TermMonths, + PaymentMethod: paymentMethod, + WalletAmount: totalAmount, + TopupAmount: topupAmount, + StartedAt: now, + ExpiresAt: newExpiry, + } + if err := tx.Create(subscription).Error; err != nil { + return err + } + + if err := tx.Model(&model.User{}).Where("id = ?", user.ID).Update("plan_id", planRecord.ID).Error; err != nil { + return err + } + + notification := &model.Notification{ + ID: uuid.New().String(), + UserID: user.ID, + Type: "billing.subscription", + Title: "Subscription activated", + Message: fmt.Sprintf("Your subscription to %s is active until %s.", planRecord.Name, subscription.ExpiresAt.UTC().Format("2006-01-02")), + Metadata: model.StringPtr("{}"), + } + if err := tx.Create(notification).Error; err != nil { + return err + } + + walletBalance, err = model.GetWalletBalance(ctx.Request.Context(), tx, user.ID) + if err != nil { + return err + } + return nil + }) + if err != nil { + return nil, nil, 0, err + } + return payment, subscription, walletBalance, nil +} + +// @Summary List All Payments +// @Description Get paginated list of all payments across users (admin only) +// @Tags admin +// @Produce json +// @Param page query int false "Page" default(1) +// @Param limit query int false "Limit" default(20) +// @Param user_id query string false "Filter by user ID" +// @Param status query string false "Filter by status" +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 403 {object} response.Response +// @Router /admin/payments [get] +// @Security BearerAuth +func (h *Handler) ListPayments(c *gin.Context) { + ctx := c.Request.Context() + page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) + if page < 1 { + page = 1 + } + limit, _ := strconv.Atoi(c.DefaultQuery("limit", "20")) + if limit <= 0 { + limit = 20 + } + if limit > 100 { + limit = 100 + } + offset := (page - 1) * limit + + userID := strings.TrimSpace(c.Query("user_id")) + status := strings.TrimSpace(c.Query("status")) + + db := h.db.WithContext(ctx).Model(&model.Payment{}) + if userID != "" { + db = db.Where("user_id = ?", userID) + } + if status != "" { + db = db.Where("UPPER(status) = ?", strings.ToUpper(status)) + } + + var total int64 + if err := db.Count(&total).Error; err != nil { + response.Error(c, http.StatusInternalServerError, "Failed to list payments") + return + } + + var payments []model.Payment + if err := db.Order("created_at DESC").Offset(offset).Limit(limit).Find(&payments).Error; err != nil { + h.logger.Error("Failed to list payments", "error", err) + response.Error(c, http.StatusInternalServerError, "Failed to list payments") + return + } + + result := make([]AdminPaymentPayload, 0, len(payments)) + for _, p := range payments { + payload, err := h.loadAdminPaymentPayload(c, p) + if err != nil { + response.Error(c, http.StatusInternalServerError, "Failed to list payments") + return + } + result = append(result, payload) + } + + response.Success(c, gin.H{ + "payments": result, + "total": total, + "page": page, + "limit": limit, + }) +} + +// @Summary Get Payment Detail +// @Description Get payment detail (admin only) +// @Tags admin +// @Produce json +// @Param id path string true "Payment ID" +// @Success 200 {object} response.Response +// @Router /admin/payments/{id} [get] +// @Security BearerAuth +func (h *Handler) GetPayment(c *gin.Context) { + id := strings.TrimSpace(c.Param("id")) + if id == "" { + response.Error(c, http.StatusNotFound, "Payment not found") + return + } + + var payment model.Payment + if err := h.db.WithContext(c.Request.Context()).Where("id = ?", id).First(&payment).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + response.Error(c, http.StatusNotFound, "Payment not found") + return + } + response.Error(c, http.StatusInternalServerError, "Failed to get payment") + return + } + + payload, err := h.loadAdminPaymentPayload(c, payment) + if err != nil { + response.Error(c, http.StatusInternalServerError, "Failed to get payment") + return + } + + response.Success(c, gin.H{"payment": payload}) +} + +// @Summary Create Payment +// @Description Create a model subscription charge for a user (admin only) +// @Tags admin +// @Accept json +// @Produce json +// @Param request body CreateAdminPaymentRequest true "Payment payload" +// @Success 201 {object} response.Response +// @Router /admin/payments [post] +// @Security BearerAuth +func (h *Handler) CreatePayment(c *gin.Context) { + var req CreateAdminPaymentRequest + if err := c.ShouldBindJSON(&req); err != nil { + response.Error(c, http.StatusBadRequest, err.Error()) + return + } + + payment, subscription, walletBalance, err := h.adminCreateSubscriptionPayment(c, req) + if err != nil { + switch err.Error() { + case "User not found", "Plan not found", "Plan is not active", "Payment method must be wallet or topup", "Term months must be one of 1, 3, 6, or 12", "Insufficient wallet balance", "Top-up amount is required when payment method is topup", "Top-up amount must be greater than 0", "Top-up amount must be greater than or equal to the required shortfall": + response.Error(c, http.StatusBadRequest, err.Error()) + return + default: + h.logger.Error("Failed to create admin payment", "error", err) + response.Error(c, http.StatusInternalServerError, "Failed to create payment") + return + } + } + + payload, err := h.loadAdminPaymentPayload(c, *payment) + if err != nil { + response.Error(c, http.StatusInternalServerError, "Failed to create payment") + return + } + + response.Created(c, gin.H{ + "payment": payload, + "subscription": subscription, + "wallet_balance": walletBalance, + "invoice_id": adminInvoiceID(payment.ID), + }) +} + +// @Summary Update Payment +// @Description Update payment status safely without hard delete (admin only) +// @Tags admin +// @Accept json +// @Produce json +// @Param id path string true "Payment ID" +// @Param request body UpdateAdminPaymentRequest true "Payment update payload" +// @Success 200 {object} response.Response +// @Router /admin/payments/{id} [put] +// @Security BearerAuth +func (h *Handler) UpdatePayment(c *gin.Context) { + id := strings.TrimSpace(c.Param("id")) + if id == "" { + response.Error(c, http.StatusNotFound, "Payment not found") + return + } + + var req UpdateAdminPaymentRequest + if err := c.ShouldBindJSON(&req); err != nil { + response.Error(c, http.StatusBadRequest, err.Error()) + return + } + + newStatus := normalizeAdminPaymentStatus(req.Status) + var payment model.Payment + if err := h.db.WithContext(c.Request.Context()).Where("id = ?", id).First(&payment).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + response.Error(c, http.StatusNotFound, "Payment not found") + return + } + response.Error(c, http.StatusInternalServerError, "Failed to update payment") + return + } + + currentStatus := normalizeAdminPaymentStatus(adminStringValue(payment.Status)) + if currentStatus != newStatus { + if (currentStatus == "FAILED" || currentStatus == "PENDING") && newStatus == "SUCCESS" { + response.Error(c, http.StatusBadRequest, "Cannot transition payment to SUCCESS from admin update; recreate through the payment flow instead") + return + } + payment.Status = &newStatus + if err := h.db.WithContext(c.Request.Context()).Save(&payment).Error; err != nil { + h.logger.Error("Failed to update payment", "error", err) + response.Error(c, http.StatusInternalServerError, "Failed to update payment") + return + } + } + + payload, err := h.loadAdminPaymentPayload(c, payment) + if err != nil { + response.Error(c, http.StatusInternalServerError, "Failed to update payment") + return + } + + response.Success(c, gin.H{"payment": payload}) +} diff --git a/internal/api/admin/plans.go b/internal/api/admin/plans.go new file mode 100644 index 0000000..2505cb1 --- /dev/null +++ b/internal/api/admin/plans.go @@ -0,0 +1,302 @@ +//go:build ignore +// +build ignore + +package admin + +import ( + "errors" + "net/http" + "strings" + + "github.com/gin-gonic/gin" + "github.com/google/uuid" + "gorm.io/gorm" + "stream.api/internal/database/model" + "stream.api/pkg/response" +) + +type AdminPlanPayload struct { + ID string `json:"id"` + Name string `json:"name"` + Description string `json:"description,omitempty"` + Features []string `json:"features,omitempty"` + Price float64 `json:"price"` + Cycle string `json:"cycle"` + StorageLimit int64 `json:"storage_limit"` + UploadLimit int32 `json:"upload_limit"` + DurationLimit int32 `json:"duration_limit"` + QualityLimit string `json:"quality_limit"` + IsActive bool `json:"is_active"` + UserCount int64 `json:"user_count"` + PaymentCount int64 `json:"payment_count"` + SubscriptionCount int64 `json:"subscription_count"` +} + +type SavePlanRequest struct { + Name string `json:"name" binding:"required"` + Description string `json:"description"` + Features []string `json:"features"` + Price float64 `json:"price" binding:"required"` + Cycle string `json:"cycle" binding:"required"` + StorageLimit int64 `json:"storage_limit" binding:"required"` + UploadLimit int32 `json:"upload_limit" binding:"required"` + IsActive *bool `json:"is_active"` +} + +func buildAdminPlanPayload(plan model.Plan, userCount, paymentCount, subscriptionCount int64) AdminPlanPayload { + return AdminPlanPayload{ + ID: plan.ID, + Name: plan.Name, + Description: adminStringValue(plan.Description), + Features: adminStringSliceValue(plan.Features), + Price: plan.Price, + Cycle: plan.Cycle, + StorageLimit: plan.StorageLimit, + UploadLimit: plan.UploadLimit, + DurationLimit: plan.DurationLimit, + QualityLimit: plan.QualityLimit, + IsActive: adminBoolValue(plan.IsActive, true), + UserCount: userCount, + PaymentCount: paymentCount, + SubscriptionCount: subscriptionCount, + } +} + +func (h *Handler) loadPlanUsageCounts(ctx *gin.Context, planID string) (int64, int64, int64, error) { + var userCount int64 + if err := h.db.WithContext(ctx.Request.Context()).Model(&model.User{}).Where("plan_id = ?", planID).Count(&userCount).Error; err != nil { + return 0, 0, 0, err + } + + var paymentCount int64 + if err := h.db.WithContext(ctx.Request.Context()).Model(&model.Payment{}).Where("plan_id = ?", planID).Count(&paymentCount).Error; err != nil { + return 0, 0, 0, err + } + + var subscriptionCount int64 + if err := h.db.WithContext(ctx.Request.Context()).Model(&model.PlanSubscription{}).Where("plan_id = ?", planID).Count(&subscriptionCount).Error; err != nil { + return 0, 0, 0, err + } + + return userCount, paymentCount, subscriptionCount, nil +} + +func validatePlanRequest(req *SavePlanRequest) string { + if strings.TrimSpace(req.Name) == "" { + return "Name is required" + } + if strings.TrimSpace(req.Cycle) == "" { + return "Cycle is required" + } + if req.Price < 0 { + return "Price must be greater than or equal to 0" + } + if req.StorageLimit <= 0 { + return "Storage limit must be greater than 0" + } + if req.UploadLimit <= 0 { + return "Upload limit must be greater than 0" + } + return "" +} + +// @Summary List Plans +// @Description Get all plans with usage counts (admin only) +// @Tags admin +// @Produce json +// @Success 200 {object} response.Response +// @Router /admin/plans [get] +// @Security BearerAuth +func (h *Handler) ListPlans(c *gin.Context) { + ctx := c.Request.Context() + var plans []model.Plan + if err := h.db.WithContext(ctx).Order("price ASC").Find(&plans).Error; err != nil { + h.logger.Error("Failed to list plans", "error", err) + response.Error(c, http.StatusInternalServerError, "Failed to list plans") + return + } + + result := make([]AdminPlanPayload, 0, len(plans)) + for _, plan := range plans { + userCount, paymentCount, subscriptionCount, err := h.loadPlanUsageCounts(c, plan.ID) + if err != nil { + h.logger.Error("Failed to load plan usage", "error", err, "plan_id", plan.ID) + response.Error(c, http.StatusInternalServerError, "Failed to list plans") + return + } + result = append(result, buildAdminPlanPayload(plan, userCount, paymentCount, subscriptionCount)) + } + + response.Success(c, gin.H{"plans": result}) +} + +// @Summary Create Plan +// @Description Create a plan (admin only) +// @Tags admin +// @Accept json +// @Produce json +// @Param request body SavePlanRequest true "Plan payload" +// @Success 201 {object} response.Response +// @Router /admin/plans [post] +// @Security BearerAuth +func (h *Handler) CreatePlan(c *gin.Context) { + var req SavePlanRequest + if err := c.ShouldBindJSON(&req); err != nil { + response.Error(c, http.StatusBadRequest, err.Error()) + return + } + if msg := validatePlanRequest(&req); msg != "" { + response.Error(c, http.StatusBadRequest, msg) + return + } + + plan := &model.Plan{ + ID: uuid.New().String(), + Name: strings.TrimSpace(req.Name), + Description: adminStringPtr(req.Description), + Features: adminStringSlice(req.Features), + Price: req.Price, + Cycle: strings.TrimSpace(req.Cycle), + StorageLimit: req.StorageLimit, + UploadLimit: req.UploadLimit, + DurationLimit: 0, + QualityLimit: "", + IsActive: func() *bool { + value := true + if req.IsActive != nil { + value = *req.IsActive + } + return &value + }(), + } + + if err := h.db.WithContext(c.Request.Context()).Create(plan).Error; err != nil { + h.logger.Error("Failed to create plan", "error", err) + response.Error(c, http.StatusInternalServerError, "Failed to create plan") + return + } + + response.Created(c, gin.H{"plan": buildAdminPlanPayload(*plan, 0, 0, 0)}) +} + +// @Summary Update Plan +// @Description Update a plan (admin only) +// @Tags admin +// @Accept json +// @Produce json +// @Param id path string true "Plan ID" +// @Param request body SavePlanRequest true "Plan payload" +// @Success 200 {object} response.Response +// @Router /admin/plans/{id} [put] +// @Security BearerAuth +func (h *Handler) UpdatePlan(c *gin.Context) { + id := strings.TrimSpace(c.Param("id")) + if id == "" { + response.Error(c, http.StatusNotFound, "Plan not found") + return + } + + var req SavePlanRequest + if err := c.ShouldBindJSON(&req); err != nil { + response.Error(c, http.StatusBadRequest, err.Error()) + return + } + if msg := validatePlanRequest(&req); msg != "" { + response.Error(c, http.StatusBadRequest, msg) + return + } + + ctx := c.Request.Context() + var plan model.Plan + if err := h.db.WithContext(ctx).Where("id = ?", id).First(&plan).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + response.Error(c, http.StatusNotFound, "Plan not found") + return + } + response.Error(c, http.StatusInternalServerError, "Failed to update plan") + return + } + + plan.Name = strings.TrimSpace(req.Name) + plan.Description = adminStringPtr(req.Description) + plan.Features = adminStringSlice(req.Features) + plan.Price = req.Price + plan.Cycle = strings.TrimSpace(req.Cycle) + plan.StorageLimit = req.StorageLimit + plan.UploadLimit = req.UploadLimit + if req.IsActive != nil { + plan.IsActive = req.IsActive + } + + if err := h.db.WithContext(ctx).Save(&plan).Error; err != nil { + h.logger.Error("Failed to update plan", "error", err) + response.Error(c, http.StatusInternalServerError, "Failed to update plan") + return + } + + userCount, paymentCount, subscriptionCount, err := h.loadPlanUsageCounts(c, plan.ID) + if err != nil { + h.logger.Error("Failed to load plan usage", "error", err) + response.Error(c, http.StatusInternalServerError, "Failed to update plan") + return + } + + response.Success(c, gin.H{"plan": buildAdminPlanPayload(plan, userCount, paymentCount, subscriptionCount)}) +} + +// @Summary Delete Plan +// @Description Delete a plan, or deactivate it if already used (admin only) +// @Tags admin +// @Produce json +// @Param id path string true "Plan ID" +// @Success 200 {object} response.Response +// @Router /admin/plans/{id} [delete] +// @Security BearerAuth +func (h *Handler) DeletePlan(c *gin.Context) { + id := strings.TrimSpace(c.Param("id")) + if id == "" { + response.Error(c, http.StatusNotFound, "Plan not found") + return + } + + ctx := c.Request.Context() + var plan model.Plan + if err := h.db.WithContext(ctx).Where("id = ?", id).First(&plan).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + response.Error(c, http.StatusNotFound, "Plan not found") + return + } + response.Error(c, http.StatusInternalServerError, "Failed to delete plan") + return + } + + var paymentCount int64 + if err := h.db.WithContext(ctx).Model(&model.Payment{}).Where("plan_id = ?", id).Count(&paymentCount).Error; err != nil { + response.Error(c, http.StatusInternalServerError, "Failed to delete plan") + return + } + var subscriptionCount int64 + if err := h.db.WithContext(ctx).Model(&model.PlanSubscription{}).Where("plan_id = ?", id).Count(&subscriptionCount).Error; err != nil { + response.Error(c, http.StatusInternalServerError, "Failed to delete plan") + return + } + + if paymentCount > 0 || subscriptionCount > 0 { + inactive := false + if err := h.db.WithContext(ctx).Model(&model.Plan{}).Where("id = ?", id).Update("is_active", inactive).Error; err != nil { + h.logger.Error("Failed to deactivate plan", "error", err) + response.Error(c, http.StatusInternalServerError, "Failed to deactivate plan") + return + } + response.Success(c, gin.H{"message": "Plan deactivated", "mode": "deactivated"}) + return + } + + if err := h.db.WithContext(ctx).Where("id = ?", id).Delete(&model.Plan{}).Error; err != nil { + h.logger.Error("Failed to delete plan", "error", err) + response.Error(c, http.StatusInternalServerError, "Failed to delete plan") + return + } + + response.Success(c, gin.H{"message": "Plan deleted", "mode": "deleted"}) +} diff --git a/internal/api/admin/render.go b/internal/api/admin/render.go new file mode 100644 index 0000000..f59cc96 --- /dev/null +++ b/internal/api/admin/render.go @@ -0,0 +1,218 @@ +//go:build ignore +// +build ignore + +package admin + +import ( + "encoding/json" + "fmt" + "net/http" + + "github.com/gin-gonic/gin" + "stream.api/pkg/response" +) + +type createJobRequest struct { + Command string `json:"command"` + Image string `json:"image"` + Env map[string]string `json:"env"` + Priority int `json:"priority"` + UserID string `json:"user_id"` + Name string `json:"name"` + TimeLimit int64 `json:"time_limit"` +} + +// @Summary List render jobs +// @Description Returns paginated render jobs for admin management +// @Tags admin-render +// @Security BearerAuth +// @Produce json +// @Param offset query int false "Offset" +// @Param limit query int false "Limit" +// @Param agent_id query string false "Agent ID" +// @Success 200 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /admin/jobs [get] +func (h *Handler) ListJobs(c *gin.Context) { + offset := parseInt(c.Query("offset"), 0) + limit := parseInt(c.Query("limit"), 20) + if agentID := c.Query("agent_id"); agentID != "" { + items, err := h.runtime.JobService().ListJobsByAgent(c.Request.Context(), agentID, offset, limit) + if err != nil { + response.Error(c, http.StatusInternalServerError, "Failed to list jobs") + return + } + response.Success(c, items) + return + } + items, err := h.runtime.JobService().ListJobs(c.Request.Context(), offset, limit) + if err != nil { + response.Error(c, http.StatusInternalServerError, "Failed to list jobs") + return + } + response.Success(c, items) +} + +// @Summary Get render job detail +// @Description Returns a render job by ID +// @Tags admin-render +// @Security BearerAuth +// @Produce json +// @Param id path string true "Job ID" +// @Success 200 {object} response.Response +// @Failure 404 {object} response.Response +// @Router /admin/jobs/{id} [get] +func (h *Handler) GetJob(c *gin.Context) { + job, err := h.runtime.JobService().GetJob(c.Request.Context(), c.Param("id")) + if err != nil { + response.Error(c, http.StatusNotFound, "Job not found") + return + } + response.Success(c, gin.H{"job": job}) +} + +// @Summary Get render job logs +// @Description Returns plain text logs for a render job +// @Tags admin-render +// @Security BearerAuth +// @Produce plain +// @Param id path string true "Job ID" +// @Success 200 {string} string +// @Failure 404 {object} response.Response +// @Router /admin/jobs/{id}/logs [get] +func (h *Handler) GetJobLogs(c *gin.Context) { + job, err := h.runtime.JobService().GetJob(c.Request.Context(), c.Param("id")) + if err != nil { + response.Error(c, http.StatusNotFound, "Job not found") + return + } + c.String(http.StatusOK, job.Logs) +} + +// @Summary Create render job +// @Description Queues a new render job for agents +// @Tags admin-render +// @Security BearerAuth +// @Accept json +// @Produce json +// @Param payload body createJobRequest true "Job payload" +// @Success 201 {object} response.Response +// @Failure 400 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /admin/jobs [post] +func (h *Handler) CreateJob(c *gin.Context) { + var req createJobRequest + if err := c.ShouldBindJSON(&req); err != nil { + response.Error(c, http.StatusBadRequest, err.Error()) + return + } + if req.Command == "" { + response.Error(c, http.StatusBadRequest, "Command is required") + return + } + if req.Image == "" { + req.Image = "alpine" + } + if req.Name == "" { + req.Name = req.Command + } + payload, _ := json.Marshal(map[string]interface{}{"image": req.Image, "commands": []string{req.Command}, "environment": req.Env}) + job, err := h.runtime.JobService().CreateJob(c.Request.Context(), req.UserID, req.Name, payload, req.Priority, req.TimeLimit) + if err != nil { + response.Error(c, http.StatusInternalServerError, "Failed to create job") + return + } + response.Created(c, gin.H{"job": job}) +} + +// @Summary Cancel render job +// @Description Cancels a pending or running render job +// @Tags admin-render +// @Security BearerAuth +// @Produce json +// @Param id path string true "Job ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.Response +// @Router /admin/jobs/{id}/cancel [post] +func (h *Handler) CancelJob(c *gin.Context) { + if err := h.runtime.JobService().CancelJob(c.Request.Context(), c.Param("id")); err != nil { + response.Error(c, http.StatusBadRequest, err.Error()) + return + } + response.Success(c, gin.H{"status": "cancelled", "job_id": c.Param("id")}) +} + +// @Summary Retry render job +// @Description Retries a failed or cancelled render job +// @Tags admin-render +// @Security BearerAuth +// @Produce json +// @Param id path string true "Job ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.Response +// @Router /admin/jobs/{id}/retry [post] +func (h *Handler) RetryJob(c *gin.Context) { + job, err := h.runtime.JobService().RetryJob(c.Request.Context(), c.Param("id")) + if err != nil { + response.Error(c, http.StatusBadRequest, err.Error()) + return + } + response.Success(c, gin.H{"job": job}) +} + +// @Summary List connected render agents +// @Description Returns currently connected render agents and current runtime stats +// @Tags admin-render +// @Security BearerAuth +// @Produce json +// @Success 200 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /admin/agents [get] +func (h *Handler) ListAgents(c *gin.Context) { + response.Success(c, gin.H{"agents": h.runtime.AgentRuntime().ListAgentsWithStats()}) +} + +// @Summary Restart connected render agent +// @Description Sends a restart command to a currently connected render agent +// @Tags admin-render +// @Security BearerAuth +// @Produce json +// @Param id path string true "Agent ID" +// @Success 200 {object} response.Response +// @Failure 503 {object} response.Response +// @Router /admin/agents/{id}/restart [post] +func (h *Handler) RestartAgent(c *gin.Context) { + if ok := h.runtime.AgentRuntime().SendCommand(c.Param("id"), "restart"); !ok { + response.Error(c, http.StatusServiceUnavailable, "Agent not active or command channel full") + return + } + response.Success(c, gin.H{"status": "restart command sent"}) +} + +// @Summary Update connected render agent +// @Description Sends an update command to a currently connected render agent +// @Tags admin-render +// @Security BearerAuth +// @Produce json +// @Param id path string true "Agent ID" +// @Success 200 {object} response.Response +// @Failure 503 {object} response.Response +// @Router /admin/agents/{id}/update [post] +func (h *Handler) UpdateAgent(c *gin.Context) { + if ok := h.runtime.AgentRuntime().SendCommand(c.Param("id"), "update"); !ok { + response.Error(c, http.StatusServiceUnavailable, "Agent not active or command channel full") + return + } + response.Success(c, gin.H{"status": "update command sent"}) +} + +func parseInt(value string, fallback int) int { + if value == "" { + return fallback + } + var result int + if _, err := fmt.Sscanf(value, "%d", &result); err != nil { + return fallback + } + return result +} diff --git a/internal/api/admin/users.go b/internal/api/admin/users.go new file mode 100644 index 0000000..67d12da --- /dev/null +++ b/internal/api/admin/users.go @@ -0,0 +1,522 @@ +//go:build ignore +// +build ignore + +package admin + +import ( + "errors" + "net/http" + "strconv" + "strings" + + "github.com/gin-gonic/gin" + "github.com/google/uuid" + "golang.org/x/crypto/bcrypt" + "gorm.io/gorm" + "stream.api/internal/database/model" + "stream.api/pkg/response" +) + +type AdminUserPayload struct { + ID string `json:"id"` + Email string `json:"email"` + Username *string `json:"username"` + Avatar *string `json:"avatar"` + Role *string `json:"role"` + PlanID *string `json:"plan_id"` + PlanName string `json:"plan_name,omitempty"` + StorageUsed int64 `json:"storage_used"` + VideoCount int64 `json:"video_count"` + WalletBalance float64 `json:"wallet_balance"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` +} + +type CreateAdminUserRequest struct { + Email string `json:"email" binding:"required,email"` + Username string `json:"username"` + Password string `json:"password" binding:"required,min=6"` + Role string `json:"role"` + PlanID *string `json:"plan_id"` +} + +type UpdateAdminUserRequest struct { + Email *string `json:"email"` + Username *string `json:"username"` + Password *string `json:"password"` + Role *string `json:"role"` + PlanID *string `json:"plan_id"` +} + +type UpdateUserRoleRequest struct { + Role string `json:"role" binding:"required"` +} + +func normalizeAdminRole(value string) string { + role := strings.ToUpper(strings.TrimSpace(value)) + if role == "" { + return "USER" + } + return role +} + +func isValidAdminRole(role string) bool { + switch normalizeAdminRole(role) { + case "USER", "ADMIN", "BLOCK": + return true + default: + return false + } +} + +func (h *Handler) ensurePlanExists(ctx *gin.Context, planID *string) error { + if planID == nil { + return nil + } + trimmed := strings.TrimSpace(*planID) + if trimmed == "" { + return nil + } + var count int64 + if err := h.db.WithContext(ctx.Request.Context()).Model(&model.Plan{}).Where("id = ?", trimmed).Count(&count).Error; err != nil { + return err + } + if count == 0 { + return gorm.ErrRecordNotFound + } + return nil +} + +// @Summary List Users +// @Description Get paginated list of all users (admin only) +// @Tags admin +// @Produce json +// @Param page query int false "Page" default(1) +// @Param limit query int false "Limit" default(20) +// @Param search query string false "Search by email or username" +// @Param role query string false "Filter by role" +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 403 {object} response.Response +// @Router /admin/users [get] +// @Security BearerAuth +func (h *Handler) ListUsers(c *gin.Context) { + ctx := c.Request.Context() + page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) + if page < 1 { + page = 1 + } + limit, _ := strconv.Atoi(c.DefaultQuery("limit", "20")) + if limit <= 0 { + limit = 20 + } + if limit > 100 { + limit = 100 + } + offset := (page - 1) * limit + + search := strings.TrimSpace(c.Query("search")) + role := strings.TrimSpace(c.Query("role")) + + db := h.db.WithContext(ctx).Model(&model.User{}) + if search != "" { + like := "%" + search + "%" + db = db.Where("email ILIKE ? OR username ILIKE ?", like, like) + } + if role != "" { + db = db.Where("UPPER(role) = ?", strings.ToUpper(role)) + } + + var total int64 + if err := db.Count(&total).Error; err != nil { + h.logger.Error("Failed to count users", "error", err) + response.Error(c, http.StatusInternalServerError, "Failed to list users") + return + } + + var users []model.User + if err := db.Order("created_at DESC").Offset(offset).Limit(limit).Find(&users).Error; err != nil { + h.logger.Error("Failed to list users", "error", err) + response.Error(c, http.StatusInternalServerError, "Failed to list users") + return + } + + planIDs := map[string]bool{} + for _, u := range users { + if u.PlanID != nil && strings.TrimSpace(*u.PlanID) != "" { + planIDs[*u.PlanID] = true + } + } + planNames := map[string]string{} + if len(planIDs) > 0 { + ids := make([]string, 0, len(planIDs)) + for id := range planIDs { + ids = append(ids, id) + } + var plans []model.Plan + h.db.WithContext(ctx).Where("id IN ?", ids).Find(&plans) + for _, p := range plans { + planNames[p.ID] = p.Name + } + } + + result := make([]AdminUserPayload, 0, len(users)) + for _, u := range users { + payload := AdminUserPayload{ + ID: u.ID, + Email: u.Email, + Username: u.Username, + Avatar: u.Avatar, + Role: u.Role, + PlanID: u.PlanID, + StorageUsed: u.StorageUsed, + CreatedAt: adminFormatTime(u.CreatedAt), + UpdatedAt: adminFormatTimeValue(u.UpdatedAt), + } + if u.PlanID != nil { + payload.PlanName = planNames[*u.PlanID] + } + h.db.WithContext(ctx).Model(&model.Video{}).Where("user_id = ?", u.ID).Count(&payload.VideoCount) + payload.WalletBalance, _ = model.GetWalletBalance(ctx, h.db, u.ID) + result = append(result, payload) + } + + response.Success(c, gin.H{ + "users": result, + "total": total, + "page": page, + "limit": limit, + }) +} + +// @Summary Create User +// @Description Create a user from admin panel (admin only) +// @Tags admin +// @Accept json +// @Produce json +// @Param request body CreateAdminUserRequest true "User payload" +// @Success 201 {object} response.Response +// @Router /admin/users [post] +// @Security BearerAuth +func (h *Handler) CreateUser(c *gin.Context) { + var req CreateAdminUserRequest + if err := c.ShouldBindJSON(&req); err != nil { + response.Error(c, http.StatusBadRequest, err.Error()) + return + } + + role := normalizeAdminRole(req.Role) + if !isValidAdminRole(role) { + response.Error(c, http.StatusBadRequest, "Invalid role. Must be USER, ADMIN, or BLOCK") + return + } + if err := h.ensurePlanExists(c, req.PlanID); err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + response.Error(c, http.StatusBadRequest, "Plan not found") + return + } + response.Error(c, http.StatusInternalServerError, "Failed to create user") + return + } + + email := strings.TrimSpace(req.Email) + username := strings.TrimSpace(req.Username) + hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost) + if err != nil { + response.Error(c, http.StatusInternalServerError, "Failed to hash password") + return + } + password := string(hashedPassword) + + user := &model.User{ + ID: uuid.New().String(), + Email: email, + Password: &password, + Username: adminStringPtr(username), + Role: &role, + PlanID: nil, + } + if req.PlanID != nil && strings.TrimSpace(*req.PlanID) != "" { + planID := strings.TrimSpace(*req.PlanID) + user.PlanID = &planID + } + + if err := h.db.WithContext(c.Request.Context()).Create(user).Error; err != nil { + if errors.Is(err, gorm.ErrDuplicatedKey) { + response.Error(c, http.StatusBadRequest, "Email already registered") + return + } + h.logger.Error("Failed to create user", "error", err) + response.Error(c, http.StatusInternalServerError, "Failed to create user") + return + } + + response.Created(c, gin.H{"user": user}) +} + +// @Summary Get User Detail +// @Description Get detailed info about a single user (admin only) +// @Tags admin +// @Produce json +// @Param id path string true "User ID" +// @Success 200 {object} response.Response +// @Failure 404 {object} response.Response +// @Router /admin/users/{id} [get] +// @Security BearerAuth +func (h *Handler) GetUser(c *gin.Context) { + ctx := c.Request.Context() + id := c.Param("id") + + var user model.User + if err := h.db.WithContext(ctx).Where("id = ?", id).First(&user).Error; err != nil { + if err == gorm.ErrRecordNotFound { + response.Error(c, http.StatusNotFound, "User not found") + return + } + response.Error(c, http.StatusInternalServerError, "Failed to get user") + return + } + + var videoCount int64 + h.db.WithContext(ctx).Model(&model.Video{}).Where("user_id = ?", id).Count(&videoCount) + balance, _ := model.GetWalletBalance(ctx, h.db, id) + + planName := "" + if user.PlanID != nil { + var plan model.Plan + if err := h.db.WithContext(ctx).Where("id = ?", *user.PlanID).First(&plan).Error; err == nil { + planName = plan.Name + } + } + + var subscription *model.PlanSubscription + var sub model.PlanSubscription + if err := h.db.WithContext(ctx).Where("user_id = ?", id).Order("created_at DESC").First(&sub).Error; err == nil { + subscription = &sub + } + + response.Success(c, gin.H{ + "user": gin.H{ + "id": user.ID, + "email": user.Email, + "username": user.Username, + "avatar": user.Avatar, + "role": user.Role, + "plan_id": user.PlanID, + "plan_name": planName, + "storage_used": user.StorageUsed, + "created_at": user.CreatedAt, + "updated_at": user.UpdatedAt, + }, + "video_count": videoCount, + "wallet_balance": balance, + "subscription": subscription, + }) +} + +// @Summary Update User +// @Description Update a user from admin panel (admin only) +// @Tags admin +// @Accept json +// @Produce json +// @Param id path string true "User ID" +// @Param request body UpdateAdminUserRequest true "User payload" +// @Success 200 {object} response.Response +// @Router /admin/users/{id} [put] +// @Security BearerAuth +func (h *Handler) UpdateUser(c *gin.Context) { + id := c.Param("id") + currentUserID := c.GetString("userID") + + var req UpdateAdminUserRequest + if err := c.ShouldBindJSON(&req); err != nil { + response.Error(c, http.StatusBadRequest, err.Error()) + return + } + + updates := map[string]interface{}{} + if req.Email != nil { + email := strings.TrimSpace(*req.Email) + if email == "" { + response.Error(c, http.StatusBadRequest, "Email is required") + return + } + updates["email"] = email + } + if req.Username != nil { + updates["username"] = strings.TrimSpace(*req.Username) + } + if req.Role != nil { + role := normalizeAdminRole(*req.Role) + if !isValidAdminRole(role) { + response.Error(c, http.StatusBadRequest, "Invalid role. Must be USER, ADMIN, or BLOCK") + return + } + if id == currentUserID && role != "ADMIN" { + response.Error(c, http.StatusBadRequest, "Cannot change your own role") + return + } + updates["role"] = role + } + if req.PlanID != nil { + if err := h.ensurePlanExists(c, req.PlanID); err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + response.Error(c, http.StatusBadRequest, "Plan not found") + return + } + response.Error(c, http.StatusInternalServerError, "Failed to update user") + return + } + trimmed := strings.TrimSpace(*req.PlanID) + if trimmed == "" { + updates["plan_id"] = nil + } else { + updates["plan_id"] = trimmed + } + } + if req.Password != nil { + if strings.TrimSpace(*req.Password) == "" { + response.Error(c, http.StatusBadRequest, "Password must not be empty") + return + } + hashedPassword, err := bcrypt.GenerateFromPassword([]byte(*req.Password), bcrypt.DefaultCost) + if err != nil { + response.Error(c, http.StatusInternalServerError, "Failed to hash password") + return + } + updates["password"] = string(hashedPassword) + } + + if len(updates) == 0 { + response.Success(c, gin.H{"message": "No changes provided"}) + return + } + + result := h.db.WithContext(c.Request.Context()).Model(&model.User{}).Where("id = ?", id).Updates(updates) + if result.Error != nil { + if errors.Is(result.Error, gorm.ErrDuplicatedKey) { + response.Error(c, http.StatusBadRequest, "Email already registered") + return + } + h.logger.Error("Failed to update user", "error", result.Error) + response.Error(c, http.StatusInternalServerError, "Failed to update user") + return + } + if result.RowsAffected == 0 { + response.Error(c, http.StatusNotFound, "User not found") + return + } + + var user model.User + if err := h.db.WithContext(c.Request.Context()).Where("id = ?", id).First(&user).Error; err != nil { + response.Error(c, http.StatusInternalServerError, "Failed to reload user") + return + } + + response.Success(c, gin.H{"user": user}) +} + +// @Summary Update User Role +// @Description Change user role (admin only). Valid: USER, ADMIN, BLOCK +// @Tags admin +// @Accept json +// @Produce json +// @Param id path string true "User ID" +// @Param request body UpdateUserRoleRequest true "Role payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.Response +// @Failure 404 {object} response.Response +// @Router /admin/users/{id}/role [put] +// @Security BearerAuth +func (h *Handler) UpdateUserRole(c *gin.Context) { + id := c.Param("id") + currentUserID := c.GetString("userID") + if id == currentUserID { + response.Error(c, http.StatusBadRequest, "Cannot change your own role") + return + } + + var req UpdateUserRoleRequest + if err := c.ShouldBindJSON(&req); err != nil { + response.Error(c, http.StatusBadRequest, err.Error()) + return + } + + role := normalizeAdminRole(req.Role) + if !isValidAdminRole(role) { + response.Error(c, http.StatusBadRequest, "Invalid role. Must be USER, ADMIN, or BLOCK") + return + } + + result := h.db.WithContext(c.Request.Context()).Model(&model.User{}).Where("id = ?", id).Update("role", role) + if result.Error != nil { + h.logger.Error("Failed to update user role", "error", result.Error) + response.Error(c, http.StatusInternalServerError, "Failed to update role") + return + } + if result.RowsAffected == 0 { + response.Error(c, http.StatusNotFound, "User not found") + return + } + + response.Success(c, gin.H{"message": "Role updated", "role": role}) +} + +// @Summary Delete User +// @Description Delete a user and their data (admin only) +// @Tags admin +// @Produce json +// @Param id path string true "User ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.Response +// @Failure 404 {object} response.Response +// @Router /admin/users/{id} [delete] +// @Security BearerAuth +func (h *Handler) DeleteUser(c *gin.Context) { + id := c.Param("id") + currentUserID := c.GetString("userID") + if id == currentUserID { + response.Error(c, http.StatusBadRequest, "Cannot delete your own account") + return + } + + var user model.User + if err := h.db.WithContext(c.Request.Context()).Where("id = ?", id).First(&user).Error; err != nil { + if err == gorm.ErrRecordNotFound { + response.Error(c, http.StatusNotFound, "User not found") + return + } + response.Error(c, http.StatusInternalServerError, "Failed to find user") + return + } + + err := h.db.WithContext(c.Request.Context()).Transaction(func(tx *gorm.DB) error { + tables := []struct { + model interface{} + where string + }{ + {&model.VideoAdConfig{}, "user_id = ?"}, + {&model.AdTemplate{}, "user_id = ?"}, + {&model.Notification{}, "user_id = ?"}, + {&model.Domain{}, "user_id = ?"}, + {&model.WalletTransaction{}, "user_id = ?"}, + {&model.PlanSubscription{}, "user_id = ?"}, + {&model.UserPreference{}, "user_id = ?"}, + {&model.Video{}, "user_id = ?"}, + {&model.Payment{}, "user_id = ?"}, + } + for _, t := range tables { + if err := tx.Where(t.where, id).Delete(t.model).Error; err != nil { + return err + } + } + return tx.Where("id = ?", id).Delete(&model.User{}).Error + }) + if err != nil { + h.logger.Error("Failed to delete user", "error", err) + response.Error(c, http.StatusInternalServerError, "Failed to delete user") + return + } + + response.Success(c, gin.H{"message": "User deleted"}) +} diff --git a/internal/api/admin/videos.go b/internal/api/admin/videos.go new file mode 100644 index 0000000..868b823 --- /dev/null +++ b/internal/api/admin/videos.go @@ -0,0 +1,477 @@ +//go:build ignore +// +build ignore + +package admin + +import ( + "errors" + "net/http" + "strconv" + "strings" + + "github.com/gin-gonic/gin" + "github.com/google/uuid" + "gorm.io/gorm" + + "stream.api/internal/database/model" + "stream.api/pkg/response" +) + +type AdminVideoPayload struct { + ID string `json:"id"` + UserID string `json:"user_id"` + Title string `json:"title"` + Description string `json:"description,omitempty"` + URL string `json:"url"` + Status string `json:"status"` + Size int64 `json:"size"` + Duration int32 `json:"duration"` + Format string `json:"format"` + OwnerEmail string `json:"owner_email,omitempty"` + AdTemplateID *string `json:"ad_template_id,omitempty"` + AdTemplateName string `json:"ad_template_name,omitempty"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` +} + +type SaveAdminVideoRequest struct { + UserID string `json:"user_id" binding:"required"` + Title string `json:"title" binding:"required"` + Description string `json:"description"` + URL string `json:"url" binding:"required"` + Size int64 `json:"size" binding:"required"` + Duration int32 `json:"duration"` + Format string `json:"format"` + Status string `json:"status"` + AdTemplateID *string `json:"ad_template_id,omitempty"` +} + +func normalizeAdminVideoStatus(value string) string { + switch strings.ToLower(strings.TrimSpace(value)) { + case "processing", "pending": + return "processing" + case "failed", "error": + return "failed" + default: + return "ready" + } +} + +func (h *Handler) loadAdminVideoPayload(ctx *gin.Context, video model.Video) (AdminVideoPayload, error) { + payload := AdminVideoPayload{ + ID: video.ID, + UserID: video.UserID, + Title: video.Title, + Description: adminStringValue(video.Description), + URL: video.URL, + Status: adminStringValue(video.Status), + Size: video.Size, + Duration: video.Duration, + Format: video.Format, + CreatedAt: adminFormatTime(video.CreatedAt), + UpdatedAt: adminFormatTimeValue(video.UpdatedAt), + } + + var user model.User + if err := h.db.WithContext(ctx.Request.Context()).Select("id, email").Where("id = ?", video.UserID).First(&user).Error; err == nil { + payload.OwnerEmail = user.Email + } + + var adConfig model.VideoAdConfig + if err := h.db.WithContext(ctx.Request.Context()).Where("video_id = ?", video.ID).First(&adConfig).Error; err == nil { + payload.AdTemplateID = &adConfig.AdTemplateID + var template model.AdTemplate + if err := h.db.WithContext(ctx.Request.Context()).Where("id = ?", adConfig.AdTemplateID).First(&template).Error; err == nil { + payload.AdTemplateName = template.Name + } + } + + return payload, nil +} + +func (h *Handler) saveAdminVideoAdConfig(tx *gorm.DB, videoID, userID string, adTemplateID *string) error { + if adTemplateID == nil { + return nil + } + trimmed := strings.TrimSpace(*adTemplateID) + if trimmed == "" { + return tx.Where("video_id = ? AND user_id = ?", videoID, userID).Delete(&model.VideoAdConfig{}).Error + } + + var template model.AdTemplate + if err := tx.Where("id = ? AND user_id = ?", trimmed, userID).First(&template).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return &gin.Error{Err: errors.New("Ad template not found"), Type: gin.ErrorTypeBind} + } + return err + } + + var existing model.VideoAdConfig + if err := tx.Where("video_id = ? AND user_id = ?", videoID, userID).First(&existing).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return tx.Create(&model.VideoAdConfig{ + VideoID: videoID, + UserID: userID, + AdTemplateID: template.ID, + VastTagURL: template.VastTagURL, + AdFormat: template.AdFormat, + Duration: template.Duration, + }).Error + } + return err + } + + existing.AdTemplateID = template.ID + existing.VastTagURL = template.VastTagURL + existing.AdFormat = template.AdFormat + existing.Duration = template.Duration + return tx.Save(&existing).Error +} + +// @Summary List All Videos +// @Description Get paginated list of all videos across users (admin only) +// @Tags admin +// @Produce json +// @Param page query int false "Page" default(1) +// @Param limit query int false "Limit" default(20) +// @Param search query string false "Search by title" +// @Param user_id query string false "Filter by user ID" +// @Param status query string false "Filter by status" +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 403 {object} response.Response +// @Router /admin/videos [get] +// @Security BearerAuth +func (h *Handler) ListVideos(c *gin.Context) { + ctx := c.Request.Context() + page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) + if page < 1 { + page = 1 + } + limit, _ := strconv.Atoi(c.DefaultQuery("limit", "20")) + if limit <= 0 { + limit = 20 + } + if limit > 100 { + limit = 100 + } + offset := (page - 1) * limit + + search := strings.TrimSpace(c.Query("search")) + userID := strings.TrimSpace(c.Query("user_id")) + status := strings.TrimSpace(c.Query("status")) + + db := h.db.WithContext(ctx).Model(&model.Video{}) + if search != "" { + like := "%" + search + "%" + db = db.Where("title ILIKE ?", like) + } + if userID != "" { + db = db.Where("user_id = ?", userID) + } + if status != "" && !strings.EqualFold(status, "all") { + db = db.Where("status = ?", normalizeAdminVideoStatus(status)) + } + + var total int64 + if err := db.Count(&total).Error; err != nil { + response.Error(c, http.StatusInternalServerError, "Failed to list videos") + return + } + + var videos []model.Video + if err := db.Order("created_at DESC").Offset(offset).Limit(limit).Find(&videos).Error; err != nil { + h.logger.Error("Failed to list videos", "error", err) + response.Error(c, http.StatusInternalServerError, "Failed to list videos") + return + } + + result := make([]AdminVideoPayload, 0, len(videos)) + for _, v := range videos { + payload, err := h.loadAdminVideoPayload(c, v) + if err != nil { + response.Error(c, http.StatusInternalServerError, "Failed to list videos") + return + } + result = append(result, payload) + } + + response.Success(c, gin.H{ + "videos": result, + "total": total, + "page": page, + "limit": limit, + }) +} + +// @Summary Get Video Detail +// @Description Get video detail by ID (admin only) +// @Tags admin +// @Produce json +// @Param id path string true "Video ID" +// @Success 200 {object} response.Response +// @Router /admin/videos/{id} [get] +// @Security BearerAuth +func (h *Handler) GetVideo(c *gin.Context) { + id := strings.TrimSpace(c.Param("id")) + if id == "" { + response.Error(c, http.StatusNotFound, "Video not found") + return + } + + var video model.Video + if err := h.db.WithContext(c.Request.Context()).Where("id = ?", id).First(&video).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + response.Error(c, http.StatusNotFound, "Video not found") + return + } + response.Error(c, http.StatusInternalServerError, "Failed to get video") + return + } + + payload, err := h.loadAdminVideoPayload(c, video) + if err != nil { + response.Error(c, http.StatusInternalServerError, "Failed to get video") + return + } + + response.Success(c, gin.H{"video": payload}) +} + +// @Summary Create Video +// @Description Create a model video record for a user (admin only) +// @Tags admin +// @Accept json +// @Produce json +// @Param request body SaveAdminVideoRequest true "Video payload" +// @Success 201 {object} response.Response +// @Router /admin/videos [post] +// @Security BearerAuth +func (h *Handler) CreateVideo(c *gin.Context) { + var req SaveAdminVideoRequest + if err := c.ShouldBindJSON(&req); err != nil { + response.Error(c, http.StatusBadRequest, err.Error()) + return + } + + if strings.TrimSpace(req.UserID) == "" || strings.TrimSpace(req.Title) == "" || strings.TrimSpace(req.URL) == "" { + response.Error(c, http.StatusBadRequest, "User ID, title, and URL are required") + return + } + if req.Size < 0 { + response.Error(c, http.StatusBadRequest, "Size must be greater than or equal to 0") + return + } + + ctx := c.Request.Context() + var user model.User + if err := h.db.WithContext(ctx).Where("id = ?", strings.TrimSpace(req.UserID)).First(&user).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + response.Error(c, http.StatusBadRequest, "User not found") + return + } + response.Error(c, http.StatusInternalServerError, "Failed to create video") + return + } + + status := normalizeAdminVideoStatus(req.Status) + processingStatus := strings.ToUpper(status) + storageType := "WORKER" + video := &model.Video{ + ID: uuid.New().String(), + UserID: user.ID, + Name: strings.TrimSpace(req.Title), + Title: strings.TrimSpace(req.Title), + Description: adminStringPtr(req.Description), + URL: strings.TrimSpace(req.URL), + Size: req.Size, + Duration: req.Duration, + Format: strings.TrimSpace(req.Format), + Status: &status, + ProcessingStatus: &processingStatus, + StorageType: &storageType, + } + + if err := h.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + if err := tx.Create(video).Error; err != nil { + return err + } + if err := tx.Model(&model.User{}).Where("id = ?", user.ID).UpdateColumn("storage_used", gorm.Expr("storage_used + ?", video.Size)).Error; err != nil { + return err + } + if err := h.saveAdminVideoAdConfig(tx, video.ID, user.ID, req.AdTemplateID); err != nil { + return err + } + return nil + }); err != nil { + if strings.Contains(err.Error(), "Ad template not found") { + response.Error(c, http.StatusBadRequest, "Ad template not found") + return + } + h.logger.Error("Failed to create video", "error", err) + response.Error(c, http.StatusInternalServerError, "Failed to create video") + return + } + + payload, err := h.loadAdminVideoPayload(c, *video) + if err != nil { + response.Error(c, http.StatusInternalServerError, "Failed to create video") + return + } + + response.Created(c, gin.H{"video": payload}) +} + +// @Summary Update Video +// @Description Update video metadata and status (admin only) +// @Tags admin +// @Accept json +// @Produce json +// @Param id path string true "Video ID" +// @Param request body SaveAdminVideoRequest true "Video payload" +// @Success 200 {object} response.Response +// @Router /admin/videos/{id} [put] +// @Security BearerAuth +func (h *Handler) UpdateVideo(c *gin.Context) { + id := strings.TrimSpace(c.Param("id")) + if id == "" { + response.Error(c, http.StatusNotFound, "Video not found") + return + } + + var req SaveAdminVideoRequest + if err := c.ShouldBindJSON(&req); err != nil { + response.Error(c, http.StatusBadRequest, err.Error()) + return + } + if strings.TrimSpace(req.UserID) == "" || strings.TrimSpace(req.Title) == "" || strings.TrimSpace(req.URL) == "" { + response.Error(c, http.StatusBadRequest, "User ID, title, and URL are required") + return + } + if req.Size < 0 { + response.Error(c, http.StatusBadRequest, "Size must be greater than or equal to 0") + return + } + + ctx := c.Request.Context() + var video model.Video + if err := h.db.WithContext(ctx).Where("id = ?", id).First(&video).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + response.Error(c, http.StatusNotFound, "Video not found") + return + } + response.Error(c, http.StatusInternalServerError, "Failed to update video") + return + } + + var user model.User + if err := h.db.WithContext(ctx).Where("id = ?", strings.TrimSpace(req.UserID)).First(&user).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + response.Error(c, http.StatusBadRequest, "User not found") + return + } + response.Error(c, http.StatusInternalServerError, "Failed to update video") + return + } + + oldSize := video.Size + oldUserID := video.UserID + status := normalizeAdminVideoStatus(req.Status) + processingStatus := strings.ToUpper(status) + video.UserID = user.ID + video.Name = strings.TrimSpace(req.Title) + video.Title = strings.TrimSpace(req.Title) + video.Description = adminStringPtr(req.Description) + video.URL = strings.TrimSpace(req.URL) + video.Size = req.Size + video.Duration = req.Duration + video.Format = strings.TrimSpace(req.Format) + video.Status = &status + video.ProcessingStatus = &processingStatus + + if err := h.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + if err := tx.Save(&video).Error; err != nil { + return err + } + if oldUserID == user.ID { + delta := video.Size - oldSize + if delta != 0 { + if err := tx.Model(&model.User{}).Where("id = ?", user.ID).UpdateColumn("storage_used", gorm.Expr("GREATEST(storage_used + ?, 0)", delta)).Error; err != nil { + return err + } + } + } else { + if err := tx.Model(&model.User{}).Where("id = ?", oldUserID).UpdateColumn("storage_used", gorm.Expr("GREATEST(storage_used - ?, 0)", oldSize)).Error; err != nil { + return err + } + if err := tx.Model(&model.User{}).Where("id = ?", user.ID).UpdateColumn("storage_used", gorm.Expr("storage_used + ?", video.Size)).Error; err != nil { + return err + } + } + if oldUserID != user.ID { + if err := tx.Model(&model.VideoAdConfig{}).Where("video_id = ?", video.ID).Update("user_id", user.ID).Error; err != nil { + return err + } + } + if err := h.saveAdminVideoAdConfig(tx, video.ID, user.ID, req.AdTemplateID); err != nil { + return err + } + return nil + }); err != nil { + if strings.Contains(err.Error(), "Ad template not found") { + response.Error(c, http.StatusBadRequest, "Ad template not found") + return + } + h.logger.Error("Failed to update video", "error", err) + response.Error(c, http.StatusInternalServerError, "Failed to update video") + return + } + + payload, err := h.loadAdminVideoPayload(c, video) + if err != nil { + response.Error(c, http.StatusInternalServerError, "Failed to update video") + return + } + + response.Success(c, gin.H{"video": payload}) +} + +// @Summary Delete Video (Admin) +// @Description Delete any video by ID (admin only) +// @Tags admin +// @Produce json +// @Param id path string true "Video ID" +// @Success 200 {object} response.Response +// @Failure 404 {object} response.Response +// @Router /admin/videos/{id} [delete] +// @Security BearerAuth +func (h *Handler) DeleteVideo(c *gin.Context) { + id := c.Param("id") + + var video model.Video + if err := h.db.WithContext(c.Request.Context()).Where("id = ?", id).First(&video).Error; err != nil { + if err == gorm.ErrRecordNotFound { + response.Error(c, http.StatusNotFound, "Video not found") + return + } + response.Error(c, http.StatusInternalServerError, "Failed to find video") + return + } + + err := h.db.WithContext(c.Request.Context()).Transaction(func(tx *gorm.DB) error { + if err := tx.Where("video_id = ?", video.ID).Delete(&model.VideoAdConfig{}).Error; err != nil { + return err + } + if err := tx.Where("id = ?", video.ID).Delete(&model.Video{}).Error; err != nil { + return err + } + return tx.Model(&model.User{}).Where("id = ?", video.UserID).UpdateColumn("storage_used", gorm.Expr("GREATEST(storage_used - ?, 0)", video.Size)).Error + }) + if err != nil { + h.logger.Error("Failed to delete video", "error", err) + response.Error(c, http.StatusInternalServerError, "Failed to delete video") + return + } + + response.Success(c, gin.H{"message": "Video deleted"}) +} diff --git a/internal/api/adtemplates/handler.go b/internal/api/adtemplates/handler.go new file mode 100644 index 0000000..11112bc --- /dev/null +++ b/internal/api/adtemplates/handler.go @@ -0,0 +1,338 @@ +//go:build ignore +// +build ignore + +package adtemplates + +import ( + "net/http" + "strings" + + "github.com/gin-gonic/gin" + "github.com/google/uuid" + "gorm.io/gorm" + "stream.api/internal/database/model" + "stream.api/pkg/logger" + "stream.api/pkg/response" +) + +const upgradeRequiredMessage = "Upgrade required to manage Ads & VAST" + +type Handler struct { + logger logger.Logger + db *gorm.DB +} + +type SaveAdTemplateRequest struct { + Name string `json:"name" binding:"required"` + Description string `json:"description"` + VASTTagURL string `json:"vast_tag_url" binding:"required"` + AdFormat string `json:"ad_format"` + Duration *int `json:"duration"` + IsActive *bool `json:"is_active"` + IsDefault *bool `json:"is_default"` +} + +type TemplatePayload struct { + Template *model.AdTemplate `json:"template"` +} + +type TemplateListPayload struct { + Templates []model.AdTemplate `json:"templates"` +} + +func NewHandler(l logger.Logger, db *gorm.DB) *Handler { + return &Handler{logger: l, db: db} +} + +// @Summary List Ad Templates +// @Description Get all VAST ad templates for the current user +// @Tags ad-templates +// @Produce json +// @Success 200 {object} response.Response{data=TemplateListPayload} +// @Failure 401 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /ad-templates [get] +// @Security BearerAuth +func (h *Handler) ListTemplates(c *gin.Context) { + userID := c.GetString("userID") + if userID == "" { + response.Error(c, http.StatusUnauthorized, "Unauthorized") + return + } + + var items []model.AdTemplate + if err := h.db.WithContext(c.Request.Context()). + Where("user_id = ?", userID). + Order("is_default DESC"). + Order("created_at DESC"). + Find(&items).Error; err != nil { + h.logger.Error("Failed to list ad templates", "error", err) + response.Error(c, http.StatusInternalServerError, "Failed to load ad templates") + return + } + + response.Success(c, gin.H{"templates": items}) +} + +// @Summary Create Ad Template +// @Description Create a VAST ad template for the current user +// @Tags ad-templates +// @Accept json +// @Produce json +// @Param request body SaveAdTemplateRequest true "Ad template payload" +// @Success 201 {object} response.Response{data=TemplatePayload} +// @Failure 400 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 403 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /ad-templates [post] +// @Security BearerAuth +func (h *Handler) CreateTemplate(c *gin.Context) { + h.saveTemplate(c, true) +} + +// @Summary Update Ad Template +// @Description Update a VAST ad template for the current user +// @Tags ad-templates +// @Accept json +// @Produce json +// @Param id path string true "Ad Template ID" +// @Param request body SaveAdTemplateRequest true "Ad template payload" +// @Success 200 {object} response.Response{data=TemplatePayload} +// @Failure 400 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 403 {object} response.Response +// @Failure 404 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /ad-templates/{id} [put] +// @Security BearerAuth +func (h *Handler) UpdateTemplate(c *gin.Context) { + h.saveTemplate(c, false) +} + +// @Summary Delete Ad Template +// @Description Delete a VAST ad template for the current user +// @Tags ad-templates +// @Produce json +// @Param id path string true "Ad Template ID" +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 403 {object} response.Response +// @Failure 404 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /ad-templates/{id} [delete] +// @Security BearerAuth +func (h *Handler) DeleteTemplate(c *gin.Context) { + userID := c.GetString("userID") + if userID == "" { + response.Error(c, http.StatusUnauthorized, "Unauthorized") + return + } + if !requirePaidPlan(c) { + return + } + + id := strings.TrimSpace(c.Param("id")) + if id == "" { + response.Error(c, http.StatusNotFound, "Ad template not found") + return + } + + result := h.db.WithContext(c.Request.Context()).Transaction(func(tx *gorm.DB) error { + if err := tx.Where("ad_template_id = ? AND user_id = ?", id, userID). + Delete(&model.VideoAdConfig{}).Error; err != nil { + return err + } + + res := tx.Where("id = ? AND user_id = ?", id, userID).Delete(&model.AdTemplate{}) + if res.Error != nil { + return res.Error + } + if res.RowsAffected == 0 { + return gorm.ErrRecordNotFound + } + return nil + }) + if result != nil { + if result == gorm.ErrRecordNotFound { + response.Error(c, http.StatusNotFound, "Ad template not found") + return + } + h.logger.Error("Failed to delete ad template", "error", result) + response.Error(c, http.StatusInternalServerError, "Failed to delete ad template") + return + } + + response.Success(c, gin.H{"message": "Ad template deleted"}) +} + +func (h *Handler) saveTemplate(c *gin.Context, create bool) { + userID := c.GetString("userID") + if userID == "" { + response.Error(c, http.StatusUnauthorized, "Unauthorized") + return + } + if !requirePaidPlan(c) { + return + } + + var req SaveAdTemplateRequest + if err := c.ShouldBindJSON(&req); err != nil { + response.Error(c, http.StatusBadRequest, err.Error()) + return + } + + name := strings.TrimSpace(req.Name) + vastURL := strings.TrimSpace(req.VASTTagURL) + if name == "" || vastURL == "" { + response.Error(c, http.StatusBadRequest, "Name and VAST URL are required") + return + } + + format := normalizeAdFormat(req.AdFormat) + if format == "mid-roll" && (req.Duration == nil || *req.Duration <= 0) { + response.Error(c, http.StatusBadRequest, "Duration is required for mid-roll templates") + return + } + + ctx := c.Request.Context() + if create { + item := &model.AdTemplate{ + ID: uuid.New().String(), + UserID: userID, + Name: name, + Description: stringPointer(strings.TrimSpace(req.Description)), + VastTagURL: vastURL, + AdFormat: model.StringPtr(format), + Duration: intPtrToInt64Ptr(req.Duration), + IsActive: model.BoolPtr(req.IsActive == nil || *req.IsActive), + IsDefault: req.IsDefault != nil && *req.IsDefault, + } + if !adTemplateIsActive(item.IsActive) { + item.IsDefault = false + } + + if err := h.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + if item.IsDefault { + if err := unsetDefaultTemplates(tx, userID, ""); err != nil { + return err + } + } + + return tx.Create(item).Error + }); err != nil { + h.logger.Error("Failed to create ad template", "error", err) + response.Error(c, http.StatusInternalServerError, "Failed to save ad template") + return + } + + response.Created(c, gin.H{"template": item}) + return + } + + id := strings.TrimSpace(c.Param("id")) + if id == "" { + response.Error(c, http.StatusNotFound, "Ad template not found") + return + } + + var item model.AdTemplate + if err := h.db.WithContext(ctx).Where("id = ? AND user_id = ?", id, userID).First(&item).Error; err != nil { + if err == gorm.ErrRecordNotFound { + response.Error(c, http.StatusNotFound, "Ad template not found") + return + } + h.logger.Error("Failed to load ad template", "error", err) + response.Error(c, http.StatusInternalServerError, "Failed to save ad template") + return + } + + item.Name = name + item.Description = stringPointer(strings.TrimSpace(req.Description)) + item.VastTagURL = vastURL + item.AdFormat = model.StringPtr(format) + item.Duration = intPtrToInt64Ptr(req.Duration) + if req.IsActive != nil { + item.IsActive = model.BoolPtr(*req.IsActive) + } + if req.IsDefault != nil { + item.IsDefault = *req.IsDefault + } + if !adTemplateIsActive(item.IsActive) { + item.IsDefault = false + } + + if err := h.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + if item.IsDefault { + if err := unsetDefaultTemplates(tx, userID, item.ID); err != nil { + return err + } + } + + return tx.Save(&item).Error + }); err != nil { + h.logger.Error("Failed to update ad template", "error", err) + response.Error(c, http.StatusInternalServerError, "Failed to save ad template") + return + } + + response.Success(c, gin.H{"template": item}) +} + +func requirePaidPlan(c *gin.Context) bool { + userValue, exists := c.Get("user") + if !exists { + response.Error(c, http.StatusUnauthorized, "Unauthorized") + return false + } + + user, ok := userValue.(*model.User) + if !ok || user == nil { + response.Error(c, http.StatusUnauthorized, "Unauthorized") + return false + } + + if user.PlanID == nil || strings.TrimSpace(*user.PlanID) == "" { + response.Error(c, http.StatusForbidden, upgradeRequiredMessage) + return false + } + + return true +} + +func unsetDefaultTemplates(tx *gorm.DB, userID, excludeID string) error { + query := tx.Model(&model.AdTemplate{}).Where("user_id = ?", userID) + if excludeID != "" { + query = query.Where("id <> ?", excludeID) + } + + return query.Update("is_default", false).Error +} + +func normalizeAdFormat(value string) string { + switch strings.TrimSpace(strings.ToLower(value)) { + case "mid-roll", "post-roll": + return strings.TrimSpace(strings.ToLower(value)) + default: + return "pre-roll" + } +} + +func stringPointer(value string) *string { + if value == "" { + return nil + } + return &value +} + +func intPtrToInt64Ptr(value *int) *int64 { + if value == nil { + return nil + } + converted := int64(*value) + return &converted +} + +func adTemplateIsActive(value *bool) bool { + return value == nil || *value +} diff --git a/internal/api/auth/auth.go b/internal/api/auth/auth.go index 8d3d75b..7a3ebf2 100644 --- a/internal/api/auth/auth.go +++ b/internal/api/auth/auth.go @@ -1,8 +1,17 @@ +//go:build ignore +// +build ignore + package auth import ( + "crypto/rand" + "encoding/base64" "encoding/json" + "errors" + "fmt" "net/http" + "net/url" + "strings" "time" "github.com/gin-gonic/gin" @@ -10,6 +19,7 @@ import ( "golang.org/x/crypto/bcrypt" "golang.org/x/oauth2" "golang.org/x/oauth2/google" + "gorm.io/gorm" "stream.api/internal/config" "stream.api/internal/database/model" "stream.api/internal/database/query" @@ -20,18 +30,31 @@ import ( ) type handler struct { - cache cache.Cache - token token.Provider - logger logger.Logger - googleOauth *oauth2.Config + cache cache.Cache + token token.Provider + logger logger.Logger + db *gorm.DB + googleOauth *oauth2.Config + googleStateTTL time.Duration + frontendBaseURL string + googleFinalizePath string } // NewHandler creates a new instance of Handler -func NewHandler(c cache.Cache, t token.Provider, l logger.Logger, cfg *config.Config) AuthHandler { +func NewHandler(c cache.Cache, t token.Provider, l logger.Logger, cfg *config.Config, db *gorm.DB) AuthHandler { + stateTTL := time.Duration(cfg.Google.StateTTLMinute) * time.Minute + if stateTTL <= 0 { + stateTTL = 10 * time.Minute + } + return &handler{ - cache: c, - token: t, - logger: l, + cache: c, + token: t, + logger: l, + db: db, + googleStateTTL: stateTTL, + frontendBaseURL: strings.TrimRight(cfg.Frontend.BaseURL, "/"), + googleFinalizePath: cfg.Frontend.GoogleAuthFinalizePath, googleOauth: &oauth2.Config{ ClientID: cfg.Google.ClientID, ClientSecret: cfg.Google.ClientSecret, @@ -45,6 +68,16 @@ func NewHandler(c cache.Cache, t token.Provider, l logger.Logger, cfg *config.Co } } +// @Summary Login +// @Description Login with email and password +// @Tags auth +// @Accept json +// @Produce json +// @Param request body LoginRequest true "Login payload" +// @Success 200 {object} response.Response{data=UserPayload} +// @Failure 400 {object} response.Response +// @Failure 401 {object} response.Response +// @Router /auth/login [post] func (h *handler) Login(c *gin.Context) { var req LoginRequest if err := c.ShouldBindJSON(&req); err != nil { @@ -70,10 +103,20 @@ func (h *handler) Login(c *gin.Context) { return } - h.generateAndSetTokens(c, user.ID, user.Email, *user.Role) - response.Success(c, gin.H{"user": user}) + if err := h.generateAndSetTokens(c, user.ID, user.Email, safeRole(user.Role)); err != nil { + return + } + h.respondWithUserPayload(c, user) } +// @Summary Logout +// @Description Logout user and clear cookies +// @Tags auth +// @Produce json +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Router /auth/logout [post] +// @Security BearerAuth func (h *handler) Logout(c *gin.Context) { refreshToken, err := c.Cookie("refresh_token") if err == nil { @@ -90,6 +133,16 @@ func (h *handler) Logout(c *gin.Context) { response.Success(c, "Logged out") } +// @Summary Register +// @Description Register a new user +// @Tags auth +// @Accept json +// @Produce json +// @Param request body RegisterRequest true "Registration payload" +// @Success 201 {object} response.Response +// @Failure 400 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /auth/register [post] func (h *handler) Register(c *gin.Context) { var req RegisterRequest if err := c.ShouldBindJSON(&req); err != nil { @@ -129,6 +182,16 @@ func (h *handler) Register(c *gin.Context) { response.Created(c, "User registered") } +// @Summary Forgot Password +// @Description Request password reset link +// @Tags auth +// @Accept json +// @Produce json +// @Param request body ForgotPasswordRequest true "Forgot password payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /auth/forgot-password [post] func (h *handler) ForgotPassword(c *gin.Context) { var req ForgotPasswordRequest if err := c.ShouldBindJSON(&req); err != nil { @@ -157,6 +220,16 @@ func (h *handler) ForgotPassword(c *gin.Context) { response.Success(c, gin.H{"message": "If email exists, a reset link has been sent", "debug_token": tokenID}) } +// @Summary Reset Password +// @Description Reset password using token +// @Tags auth +// @Accept json +// @Produce json +// @Param request body ResetPasswordRequest true "Reset password payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /auth/reset-password [post] func (h *handler) ResetPassword(c *gin.Context) { var req ResetPasswordRequest if err := c.ShouldBindJSON(&req); err != nil { @@ -193,27 +266,82 @@ func (h *handler) ResetPassword(c *gin.Context) { response.Success(c, "Password reset successfully") } +// @Summary Google Login +// @Description Redirect to Google for Login +// @Tags auth +// @Router /auth/google/login [get] func (h *handler) LoginGoogle(c *gin.Context) { - url := h.googleOauth.AuthCodeURL("state", oauth2.AccessTypeOffline) + state, err := generateOAuthState() + if err != nil { + h.logger.Error("Failed to generate Google OAuth state", "error", err) + response.Fail(c, "Failed to start Google login") + return + } + + if err := h.cache.Set(c.Request.Context(), googleOAuthStateCacheKey(state), "1", h.googleStateTTL); err != nil { + h.logger.Error("Failed to persist Google OAuth state", "error", err) + response.Fail(c, "Failed to start Google login") + return + } + + url := h.googleOauth.AuthCodeURL(state, oauth2.AccessTypeOffline) c.Redirect(http.StatusTemporaryRedirect, url) } +// @Summary Google Callback +// @Description Callback for Google Login +// @Tags auth +// @Success 307 +// @Failure 400 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /auth/google/callback [get] func (h *handler) GoogleCallback(c *gin.Context) { - code := c.Query("code") + if oauthError := strings.TrimSpace(c.Query("error")); oauthError != "" { + h.redirectToGoogleFinalize(c, "error", oauthError) + return + } + + state := strings.TrimSpace(c.Query("state")) + if state == "" { + h.redirectToGoogleFinalize(c, "error", "missing_state") + return + } + + cachedState, err := h.cache.Get(c.Request.Context(), googleOAuthStateCacheKey(state)) + if err != nil || cachedState == "" { + h.redirectToGoogleFinalize(c, "error", "invalid_state") + return + } + _ = h.cache.Del(c.Request.Context(), googleOAuthStateCacheKey(state)) + + code := strings.TrimSpace(c.Query("code")) + if code == "" { + h.redirectToGoogleFinalize(c, "error", "missing_code") + return + } + tokenResp, err := h.googleOauth.Exchange(c.Request.Context(), code) if err != nil { - response.Error(c, http.StatusBadRequest, "Failed to exchange token") + h.logger.Error("Failed to exchange Google OAuth token", "error", err) + h.redirectToGoogleFinalize(c, "error", "exchange_failed") return } client := h.googleOauth.Client(c.Request.Context(), tokenResp) resp, err := client.Get("https://www.googleapis.com/oauth2/v2/userinfo") - if err != nil || resp.StatusCode != http.StatusOK { - response.Fail(c, "Failed to get user info") + if err != nil { + h.logger.Error("Failed to fetch Google user info", "error", err) + h.redirectToGoogleFinalize(c, "error", "userinfo_failed") return } defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + h.logger.Error("Google user info returned non-200", "status", resp.StatusCode) + h.redirectToGoogleFinalize(c, "error", "userinfo_failed") + return + } + var googleUser struct { ID string `json:"id"` Email string `json:"email"` @@ -221,7 +349,13 @@ func (h *handler) GoogleCallback(c *gin.Context) { Picture string `json:"picture"` } if err := json.NewDecoder(resp.Body).Decode(&googleUser); err != nil { - response.Fail(c, "Failed to parse user info") + h.logger.Error("Failed to decode Google user info", "error", err) + h.redirectToGoogleFinalize(c, "error", "userinfo_parse_failed") + return + } + + if strings.TrimSpace(googleUser.Email) == "" { + h.redirectToGoogleFinalize(c, "error", "missing_email") return } @@ -232,29 +366,300 @@ func (h *handler) GoogleCallback(c *gin.Context) { user = &model.User{ ID: uuid.New().String(), Email: googleUser.Email, - Username: &googleUser.Name, - GoogleID: &googleUser.ID, - Avatar: &googleUser.Picture, + Username: stringPointerOrNil(googleUser.Name), + GoogleID: stringPointerOrNil(googleUser.ID), + Avatar: stringPointerOrNil(googleUser.Picture), Role: &role, } if err := u.WithContext(c.Request.Context()).Create(user); err != nil { - response.Fail(c, "Failed to create user") + h.logger.Error("Failed to create Google user", "error", err) + h.redirectToGoogleFinalize(c, "error", "create_user_failed") return } - } else if user.GoogleID == nil || *user.GoogleID == "" { - u.WithContext(c.Request.Context()).Where(u.ID.Eq(user.ID)).Update(u.GoogleID, googleUser.ID) + } else { + updates := map[string]interface{}{} + if user.GoogleID == nil || strings.TrimSpace(*user.GoogleID) == "" { + updates["google_id"] = googleUser.ID + } + if user.Avatar == nil || strings.TrimSpace(*user.Avatar) == "" { + updates["avatar"] = googleUser.Picture + } + if user.Username == nil || strings.TrimSpace(*user.Username) == "" { + updates["username"] = googleUser.Name + } + if len(updates) > 0 { + if err := h.db.WithContext(c.Request.Context()).Model(&model.User{}).Where("id = ?", user.ID).Updates(updates).Error; err != nil { + h.logger.Error("Failed to update Google user", "error", err) + h.redirectToGoogleFinalize(c, "error", "update_user_failed") + return + } + user, err = u.WithContext(c.Request.Context()).Where(u.ID.Eq(user.ID)).First() + if err != nil { + h.logger.Error("Failed to reload Google user", "error", err) + h.redirectToGoogleFinalize(c, "error", "reload_user_failed") + return + } + } } - h.generateAndSetTokens(c, user.ID, user.Email, *user.Role) - response.Success(c, gin.H{"user": user}) + if err := h.generateAndSetTokens(c, user.ID, user.Email, safeRole(user.Role)); err != nil { + h.redirectToGoogleFinalize(c, "error", "session_failed") + return + } + + if h.frontendBaseURL == "" { + h.respondWithUserPayload(c, user) + return + } + + h.redirectToGoogleFinalize(c, "success", "") } -func (h *handler) generateAndSetTokens(c *gin.Context, userID, email, role string) { +// @Summary Get Current User +// @Description Get the authenticated user's profile payload +// @Tags auth +// @Produce json +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Router /me [get] +// @Security BearerAuth +func (h *handler) GetMe(c *gin.Context) { + userID := c.GetString("userID") + if userID == "" { + response.Error(c, http.StatusUnauthorized, "Unauthorized") + return + } + + u := query.User + user, err := u.WithContext(c.Request.Context()).Where(u.ID.Eq(userID)).First() + if err != nil { + response.Error(c, http.StatusUnauthorized, "Unauthorized") + return + } + + h.respondWithUserPayload(c, user) +} + +// @Summary Update Current User +// @Description Update the authenticated user's profile information +// @Tags auth +// @Accept json +// @Produce json +// @Param request body UpdateMeRequest true "Profile payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /me [put] +// @Security BearerAuth +func (h *handler) UpdateMe(c *gin.Context) { + userID := c.GetString("userID") + if userID == "" { + response.Error(c, http.StatusUnauthorized, "Unauthorized") + return + } + + var req UpdateMeRequest + if err := c.ShouldBindJSON(&req); err != nil { + response.Error(c, http.StatusBadRequest, err.Error()) + return + } + + user, err := UpdateUserProfile(c.Request.Context(), h.db, h.logger, userID, UpdateProfileInput{ + Username: req.Username, + Email: req.Email, + Language: req.Language, + Locale: req.Locale, + }) + if err != nil { + switch { + case errors.Is(err, ErrEmailRequired): + response.Error(c, http.StatusBadRequest, err.Error()) + case errors.Is(err, ErrEmailAlreadyRegistered): + response.Error(c, http.StatusBadRequest, err.Error()) + default: + response.Fail(c, "Failed to update profile") + } + return + } + + h.respondWithUserPayload(c, user) +} + +// @Summary Change Password +// @Description Change the authenticated user's local password +// @Tags auth +// @Accept json +// @Produce json +// @Param request body ChangePasswordRequest true "Password payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /auth/change-password [post] +// @Security BearerAuth +func (h *handler) ChangePassword(c *gin.Context) { + userID := c.GetString("userID") + if userID == "" { + response.Error(c, http.StatusUnauthorized, "Unauthorized") + return + } + + var req ChangePasswordRequest + if err := c.ShouldBindJSON(&req); err != nil { + response.Error(c, http.StatusBadRequest, err.Error()) + return + } + + u := query.User + user, err := u.WithContext(c.Request.Context()).Where(u.ID.Eq(userID)).First() + if err != nil { + response.Error(c, http.StatusUnauthorized, "Unauthorized") + return + } + + if user.Password == nil || strings.TrimSpace(*user.Password) == "" { + response.Error(c, http.StatusBadRequest, "This account does not have a local password") + return + } + + if err := bcrypt.CompareHashAndPassword([]byte(*user.Password), []byte(req.CurrentPassword)); err != nil { + response.Error(c, http.StatusBadRequest, "Current password is incorrect") + return + } + + if req.CurrentPassword == req.NewPassword { + response.Error(c, http.StatusBadRequest, "New password must be different") + return + } + + hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.NewPassword), bcrypt.DefaultCost) + if err != nil { + response.Fail(c, "Failed to hash password") + return + } + + if _, err := u.WithContext(c.Request.Context()).Where(u.ID.Eq(userID)).Update(u.Password, string(hashedPassword)); err != nil { + h.logger.Error("Failed to change password", "error", err) + response.Fail(c, "Failed to change password") + return + } + + response.Success(c, gin.H{"message": "Password changed successfully"}) +} + +// @Summary Clear My Data +// @Description Remove videos and settings-related resources for the authenticated user +// @Tags auth +// @Produce json +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /me/clear-data [post] +// @Security BearerAuth +func (h *handler) ClearMyData(c *gin.Context) { + userID := c.GetString("userID") + if userID == "" { + response.Error(c, http.StatusUnauthorized, "Unauthorized") + return + } + + ctx := c.Request.Context() + if err := h.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + if err := tx.Where("user_id = ?", userID).Delete(&model.Notification{}).Error; err != nil { + return err + } + if err := tx.Where("user_id = ?", userID).Delete(&model.Domain{}).Error; err != nil { + return err + } + if err := tx.Where("user_id = ?", userID).Delete(&model.AdTemplate{}).Error; err != nil { + return err + } + if err := tx.Where("user_id = ?", userID).Delete(&model.VideoAdConfig{}).Error; err != nil { + return err + } + if err := tx.Where("user_id = ?", userID).Delete(&model.Video{}).Error; err != nil { + return err + } + if err := tx.Model(&model.User{}).Where("id = ?", userID).Updates(map[string]interface{}{"storage_used": 0}).Error; err != nil { + return err + } + return nil + }); err != nil { + h.logger.Error("Failed to clear user data", "error", err) + response.Fail(c, "Failed to clear data") + return + } + + response.Success(c, gin.H{"message": "Data cleared successfully"}) +} + +// @Summary Delete My Account +// @Description Permanently delete the authenticated user's account and related data +// @Tags auth +// @Produce json +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /me [delete] +// @Security BearerAuth +func (h *handler) DeleteMe(c *gin.Context) { + userID := c.GetString("userID") + if userID == "" { + response.Error(c, http.StatusUnauthorized, "Unauthorized") + return + } + + ctx := c.Request.Context() + if err := h.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + if err := tx.Where("user_id = ?", userID).Delete(&model.Notification{}).Error; err != nil { + return err + } + if err := tx.Where("user_id = ?", userID).Delete(&model.Domain{}).Error; err != nil { + return err + } + if err := tx.Where("user_id = ?", userID).Delete(&model.AdTemplate{}).Error; err != nil { + return err + } + if err := tx.Where("user_id = ?", userID).Delete(&model.VideoAdConfig{}).Error; err != nil { + return err + } + if err := tx.Where("user_id = ?", userID).Delete(&model.WalletTransaction{}).Error; err != nil { + return err + } + if err := tx.Where("user_id = ?", userID).Delete(&model.PlanSubscription{}).Error; err != nil { + return err + } + if err := tx.Where("user_id = ?", userID).Delete(&model.UserPreference{}).Error; err != nil { + return err + } + if err := tx.Where("user_id = ?", userID).Delete(&model.Payment{}).Error; err != nil { + return err + } + if err := tx.Where("user_id = ?", userID).Delete(&model.Video{}).Error; err != nil { + return err + } + if err := tx.Where("id = ?", userID).Delete(&model.User{}).Error; err != nil { + return err + } + return nil + }); err != nil { + h.logger.Error("Failed to delete user", "error", err) + response.Fail(c, "Failed to delete account") + return + } + + c.SetCookie("access_token", "", -1, "/", "", false, true) + c.SetCookie("refresh_token", "", -1, "/", "", false, true) + response.Success(c, gin.H{"message": "Account deleted successfully"}) +} + +func (h *handler) generateAndSetTokens(c *gin.Context, userID, email, role string) error { td, err := h.token.GenerateTokenPair(userID, email, role) if err != nil { h.logger.Error("Token generation failed", "error", err) response.Fail(c, "Error generating tokens") - return + return err } // Store Refresh UUID in Redis @@ -262,9 +667,79 @@ func (h *handler) generateAndSetTokens(c *gin.Context, userID, email, role strin if err != nil { h.logger.Error("Session storage failed", "error", err) response.Fail(c, "Error storing session") - return + return err } c.SetCookie("access_token", td.AccessToken, int(td.AtExpires-time.Now().Unix()), "/", "", false, true) c.SetCookie("refresh_token", td.RefreshToken, int(td.RtExpires-time.Now().Unix()), "/", "", false, true) + return nil +} + +func (h *handler) respondWithUserPayload(c *gin.Context, user *model.User) { + payload, err := BuildUserPayload(c.Request.Context(), h.db, user) + if err != nil { + h.logger.Error("Failed to build user payload", "error", err) + response.Fail(c, "Failed to build user payload") + return + } + + response.Success(c, gin.H{"user": payload}) +} + +func safeRole(role *string) string { + if role == nil || strings.TrimSpace(*role) == "" { + return "USER" + } + return *role +} + +func generateOAuthState() (string, error) { + buffer := make([]byte, 32) + if _, err := rand.Read(buffer); err != nil { + return "", err + } + return base64.RawURLEncoding.EncodeToString(buffer), nil +} + +func googleOAuthStateCacheKey(state string) string { + return "google_oauth_state:" + state +} + +func stringPointerOrNil(value string) *string { + trimmed := strings.TrimSpace(value) + if trimmed == "" { + return nil + } + return &trimmed +} + +func (h *handler) redirectToGoogleFinalize(c *gin.Context, status, reason string) { + finalizeURL := h.googleFinalizeURL(status, reason) + if finalizeURL == "" { + response.Error(c, http.StatusBadRequest, reason) + return + } + c.Redirect(http.StatusTemporaryRedirect, finalizeURL) +} + +func (h *handler) googleFinalizeURL(status, reason string) string { + if h.frontendBaseURL == "" { + return "" + } + + finalizePath := h.googleFinalizePath + if strings.TrimSpace(finalizePath) == "" { + finalizePath = "/auth/google/finalize" + } + if !strings.HasPrefix(finalizePath, "/") { + finalizePath = "/" + finalizePath + } + + values := url.Values{} + values.Set("status", status) + if strings.TrimSpace(reason) != "" { + values.Set("reason", reason) + } + + return fmt.Sprintf("%s%s?%s", h.frontendBaseURL, finalizePath, values.Encode()) } diff --git a/internal/api/auth/interface.go b/internal/api/auth/interface.go index 82d9636..771f428 100644 --- a/internal/api/auth/interface.go +++ b/internal/api/auth/interface.go @@ -1,3 +1,6 @@ +//go:build ignore +// +build ignore + package auth import "github.com/gin-gonic/gin" @@ -11,6 +14,11 @@ type AuthHandler interface { ResetPassword(c *gin.Context) LoginGoogle(c *gin.Context) GoogleCallback(c *gin.Context) + GetMe(c *gin.Context) + UpdateMe(c *gin.Context) + ChangePassword(c *gin.Context) + DeleteMe(c *gin.Context) + ClearMyData(c *gin.Context) } // LoginRequest defines the payload for login @@ -36,3 +44,15 @@ type ResetPasswordRequest struct { Token string `json:"token" binding:"required"` NewPassword string `json:"new_password" binding:"required,min=6"` } + +type UpdateMeRequest struct { + Username *string `json:"username"` + Email *string `json:"email"` + Language *string `json:"language"` + Locale *string `json:"locale"` +} + +type ChangePasswordRequest struct { + CurrentPassword string `json:"current_password" binding:"required"` + NewPassword string `json:"new_password" binding:"required,min=6"` +} diff --git a/internal/api/auth/service.go b/internal/api/auth/service.go new file mode 100644 index 0000000..9e65db6 --- /dev/null +++ b/internal/api/auth/service.go @@ -0,0 +1,87 @@ +package auth + +import ( + "context" + "errors" + "strings" + + "gorm.io/gorm" + "stream.api/internal/database/model" + "stream.api/internal/database/query" + "stream.api/pkg/logger" +) + +var ( + ErrEmailRequired = errors.New("Email is required") + ErrEmailAlreadyRegistered = errors.New("Email already registered") +) + +type UpdateProfileInput struct { + Username *string + Email *string + Language *string + Locale *string +} + +func UpdateUserProfile(ctx context.Context, db *gorm.DB, l logger.Logger, userID string, req UpdateProfileInput) (*model.User, error) { + updates := map[string]any{} + if req.Username != nil { + username := strings.TrimSpace(*req.Username) + updates["username"] = username + } + if req.Email != nil { + email := strings.TrimSpace(*req.Email) + if email == "" { + return nil, ErrEmailRequired + } + updates["email"] = email + } + + if len(updates) > 0 { + if err := db.WithContext(ctx).Model(&model.User{}).Where("id = ?", userID).Updates(updates).Error; err != nil { + if errors.Is(err, gorm.ErrDuplicatedKey) { + return nil, ErrEmailAlreadyRegistered + } + l.Error("Failed to update user", "error", err) + return nil, err + } + } + + pref, err := model.FindOrCreateUserPreference(ctx, db, userID) + if err != nil { + l.Error("Failed to load user preference", "error", err) + return nil, err + } + + prefChanged := false + if req.Language != nil { + pref.Language = model.StringPtr(strings.TrimSpace(*req.Language)) + prefChanged = true + } + if req.Locale != nil { + pref.Locale = model.StringPtr(strings.TrimSpace(*req.Locale)) + prefChanged = true + } + if strings.TrimSpace(model.StringValue(pref.Language)) == "" { + pref.Language = model.StringPtr("en") + prefChanged = true + } + if strings.TrimSpace(model.StringValue(pref.Locale)) == "" { + pref.Locale = model.StringPtr(model.StringValue(pref.Language)) + prefChanged = true + } + if prefChanged { + if err := db.WithContext(ctx).Save(pref).Error; err != nil { + l.Error("Failed to save user preference", "error", err) + return nil, err + } + } + + u := query.User + user, err := u.WithContext(ctx).Where(u.ID.Eq(userID)).First() + if err != nil { + return nil, err + } + + return user, nil +} diff --git a/internal/api/auth/types.go b/internal/api/auth/types.go new file mode 100644 index 0000000..9379111 --- /dev/null +++ b/internal/api/auth/types.go @@ -0,0 +1,33 @@ +package auth + +type LoginRequest struct { + Email string `json:"email" binding:"required,email"` + Password string `json:"password" binding:"required"` +} + +type RegisterRequest struct { + Email string `json:"email" binding:"required,email"` + Password string `json:"password" binding:"required,min=6"` + Username string `json:"username" binding:"required"` +} + +type ForgotPasswordRequest struct { + Email string `json:"email" binding:"required,email"` +} + +type ResetPasswordRequest struct { + Token string `json:"token" binding:"required"` + NewPassword string `json:"new_password" binding:"required,min=6"` +} + +type UpdateMeRequest struct { + Username *string `json:"username"` + Email *string `json:"email"` + Language *string `json:"language"` + Locale *string `json:"locale"` +} + +type ChangePasswordRequest struct { + CurrentPassword string `json:"current_password" binding:"required"` + NewPassword string `json:"new_password" binding:"required,min=6"` +} diff --git a/internal/api/auth/user_payload.go b/internal/api/auth/user_payload.go new file mode 100644 index 0000000..4e1f480 --- /dev/null +++ b/internal/api/auth/user_payload.go @@ -0,0 +1,114 @@ +package auth + +import ( + "context" + "errors" + "strings" + "time" + + "gorm.io/gorm" + "stream.api/internal/database/model" +) + +type UserPayload struct { + ID string `json:"id"` + Email string `json:"email"` + Username *string `json:"username,omitempty"` + Avatar *string `json:"avatar,omitempty"` + Role *string `json:"role,omitempty"` + GoogleID *string `json:"google_id,omitempty"` + StorageUsed int64 `json:"storage_used"` + PlanID *string `json:"plan_id,omitempty"` + PlanStartedAt *time.Time `json:"plan_started_at,omitempty"` + PlanExpiresAt *time.Time `json:"plan_expires_at,omitempty"` + PlanTermMonths *int32 `json:"plan_term_months,omitempty"` + PlanPaymentMethod *string `json:"plan_payment_method,omitempty"` + PlanExpiringSoon bool `json:"plan_expiring_soon"` + WalletBalance float64 `json:"wallet_balance"` + Language string `json:"language"` + Locale string `json:"locale"` + CreatedAt *time.Time `json:"created_at,omitempty"` + UpdatedAt time.Time `json:"updated_at"` +} + +func BuildUserPayload(ctx context.Context, db *gorm.DB, user *model.User) (*UserPayload, error) { + pref, err := model.FindOrCreateUserPreference(ctx, db, user.ID) + if err != nil { + return nil, err + } + + walletBalance, err := model.GetWalletBalance(ctx, db, user.ID) + if err != nil { + return nil, err + } + + language := strings.TrimSpace(model.StringValue(pref.Language)) + if language == "" { + language = "en" + } + locale := strings.TrimSpace(model.StringValue(pref.Locale)) + if locale == "" { + locale = language + } + + effectivePlanID := user.PlanID + var planStartedAt *time.Time + var planExpiresAt *time.Time + var planTermMonths *int32 + var planPaymentMethod *string + planExpiringSoon := false + now := time.Now().UTC() + + subscription, err := model.GetLatestPlanSubscription(ctx, db, user.ID) + if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { + return nil, err + } + if err == nil { + startedAt := subscription.StartedAt.UTC() + expiresAt := subscription.ExpiresAt.UTC() + termMonths := subscription.TermMonths + paymentMethod := normalizePlanPaymentMethod(subscription.PaymentMethod) + + planStartedAt = &startedAt + planExpiresAt = &expiresAt + planTermMonths = &termMonths + planPaymentMethod = &paymentMethod + + if expiresAt.After(now) { + effectivePlanID = &subscription.PlanID + planExpiringSoon = model.IsSubscriptionExpiringSoon(expiresAt, now) + } else { + effectivePlanID = nil + } + } + + return &UserPayload{ + ID: user.ID, + Email: user.Email, + Username: user.Username, + Avatar: user.Avatar, + Role: user.Role, + GoogleID: user.GoogleID, + StorageUsed: user.StorageUsed, + PlanID: effectivePlanID, + PlanStartedAt: planStartedAt, + PlanExpiresAt: planExpiresAt, + PlanTermMonths: planTermMonths, + PlanPaymentMethod: planPaymentMethod, + PlanExpiringSoon: planExpiringSoon, + WalletBalance: walletBalance, + Language: language, + Locale: locale, + CreatedAt: user.CreatedAt, + UpdatedAt: user.UpdatedAt, + }, nil +} + +func normalizePlanPaymentMethod(value string) string { + switch strings.ToLower(strings.TrimSpace(value)) { + case "topup": + return "topup" + default: + return "wallet" + } +} diff --git a/internal/api/domains/handler.go b/internal/api/domains/handler.go new file mode 100644 index 0000000..48e97f4 --- /dev/null +++ b/internal/api/domains/handler.go @@ -0,0 +1,166 @@ +//go:build ignore +// +build ignore + +package domains + +import ( + "net/http" + "strings" + + "github.com/gin-gonic/gin" + "github.com/google/uuid" + "gorm.io/gorm" + "stream.api/internal/database/model" + "stream.api/pkg/logger" + "stream.api/pkg/response" +) + +type Handler struct { + logger logger.Logger + db *gorm.DB +} + +type CreateDomainRequest struct { + Name string `json:"name" binding:"required"` +} + +func NewHandler(l logger.Logger, db *gorm.DB) *Handler { + return &Handler{logger: l, db: db} +} + +// @Summary List Domains +// @Description Get all whitelisted domains for the current user +// @Tags domains +// @Produce json +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /domains [get] +// @Security BearerAuth +func (h *Handler) ListDomains(c *gin.Context) { + userID := c.GetString("userID") + if userID == "" { + response.Error(c, http.StatusUnauthorized, "Unauthorized") + return + } + + var items []model.Domain + if err := h.db.WithContext(c.Request.Context()). + Where("user_id = ?", userID). + Order("created_at DESC"). + Find(&items).Error; err != nil { + h.logger.Error("Failed to list domains", "error", err) + response.Error(c, http.StatusInternalServerError, "Failed to load domains") + return + } + + response.Success(c, gin.H{"domains": items}) +} + +// @Summary Create Domain +// @Description Add a domain to the current user's whitelist +// @Tags domains +// @Accept json +// @Produce json +// @Param request body CreateDomainRequest true "Domain payload" +// @Success 201 {object} response.Response +// @Failure 400 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /domains [post] +// @Security BearerAuth +func (h *Handler) CreateDomain(c *gin.Context) { + userID := c.GetString("userID") + if userID == "" { + response.Error(c, http.StatusUnauthorized, "Unauthorized") + return + } + + var req CreateDomainRequest + if err := c.ShouldBindJSON(&req); err != nil { + response.Error(c, http.StatusBadRequest, err.Error()) + return + } + + name := normalizeDomain(req.Name) + if name == "" || !strings.Contains(name, ".") || strings.ContainsAny(name, "/ ") { + response.Error(c, http.StatusBadRequest, "Invalid domain") + return + } + + var count int64 + if err := h.db.WithContext(c.Request.Context()). + Model(&model.Domain{}). + Where("user_id = ? AND name = ?", userID, name). + Count(&count).Error; err != nil { + h.logger.Error("Failed to validate domain", "error", err) + response.Error(c, http.StatusInternalServerError, "Failed to create domain") + return + } + if count > 0 { + response.Error(c, http.StatusBadRequest, "Domain already exists") + return + } + + item := &model.Domain{ + ID: uuid.New().String(), + UserID: userID, + Name: name, + } + if err := h.db.WithContext(c.Request.Context()).Create(item).Error; err != nil { + h.logger.Error("Failed to create domain", "error", err) + response.Error(c, http.StatusInternalServerError, "Failed to create domain") + return + } + + response.Created(c, gin.H{"domain": item}) +} + +// @Summary Delete Domain +// @Description Remove a domain from the current user's whitelist +// @Tags domains +// @Produce json +// @Param id path string true "Domain ID" +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 404 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /domains/{id} [delete] +// @Security BearerAuth +func (h *Handler) DeleteDomain(c *gin.Context) { + userID := c.GetString("userID") + if userID == "" { + response.Error(c, http.StatusUnauthorized, "Unauthorized") + return + } + + id := strings.TrimSpace(c.Param("id")) + if id == "" { + response.Error(c, http.StatusNotFound, "Domain not found") + return + } + + result := h.db.WithContext(c.Request.Context()). + Where("id = ? AND user_id = ?", id, userID). + Delete(&model.Domain{}) + if result.Error != nil { + h.logger.Error("Failed to delete domain", "error", result.Error) + response.Error(c, http.StatusInternalServerError, "Failed to delete domain") + return + } + if result.RowsAffected == 0 { + response.Error(c, http.StatusNotFound, "Domain not found") + return + } + + response.Success(c, gin.H{"message": "Domain deleted"}) +} + +func normalizeDomain(value string) string { + normalized := strings.TrimSpace(strings.ToLower(value)) + normalized = strings.TrimPrefix(normalized, "https://") + normalized = strings.TrimPrefix(normalized, "http://") + normalized = strings.TrimPrefix(normalized, "www.") + normalized = strings.TrimSuffix(normalized, "/") + return normalized +} diff --git a/internal/api/notifications/handler.go b/internal/api/notifications/handler.go new file mode 100644 index 0000000..6090616 --- /dev/null +++ b/internal/api/notifications/handler.go @@ -0,0 +1,246 @@ +//go:build ignore +// +build ignore + +package notifications + +import ( + "net/http" + "strings" + "time" + + "github.com/gin-gonic/gin" + "gorm.io/gorm" + "stream.api/internal/database/model" + "stream.api/pkg/logger" + "stream.api/pkg/response" +) + +type Handler struct { + logger logger.Logger + db *gorm.DB +} + +type NotificationItem struct { + ID string `json:"id"` + Type string `json:"type"` + Title string `json:"title"` + Message string `json:"message"` + Read bool `json:"read"` + ActionURL string `json:"actionUrl,omitempty"` + ActionLabel string `json:"actionLabel,omitempty"` + CreatedAt time.Time `json:"created_at"` +} + +func NewHandler(l logger.Logger, db *gorm.DB) *Handler { + return &Handler{logger: l, db: db} +} + +// @Summary List Notifications +// @Description Get notifications for the current user +// @Tags notifications +// @Produce json +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /notifications [get] +// @Security BearerAuth +func (h *Handler) ListNotifications(c *gin.Context) { + userID := c.GetString("userID") + if userID == "" { + response.Error(c, http.StatusUnauthorized, "Unauthorized") + return + } + + var rows []model.Notification + if err := h.db.WithContext(c.Request.Context()). + Where("user_id = ?", userID). + Order("created_at DESC"). + Find(&rows).Error; err != nil { + h.logger.Error("Failed to list notifications", "error", err) + response.Error(c, http.StatusInternalServerError, "Failed to load notifications") + return + } + + items := make([]NotificationItem, 0, len(rows)) + for _, row := range rows { + items = append(items, mapNotification(row)) + } + + response.Success(c, gin.H{"notifications": items}) +} + +// @Summary Mark Notification Read +// @Description Mark a single notification as read for the current user +// @Tags notifications +// @Produce json +// @Param id path string true "Notification ID" +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 404 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /notifications/{id}/read [post] +// @Security BearerAuth +func (h *Handler) MarkRead(c *gin.Context) { + h.updateReadState(c, true, false) +} + +// @Summary Mark All Notifications Read +// @Description Mark all notifications as read for the current user +// @Tags notifications +// @Produce json +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /notifications/read-all [post] +// @Security BearerAuth +func (h *Handler) MarkAllRead(c *gin.Context) { + userID := c.GetString("userID") + if userID == "" { + response.Error(c, http.StatusUnauthorized, "Unauthorized") + return + } + + if err := h.db.WithContext(c.Request.Context()). + Model(&model.Notification{}). + Where("user_id = ? AND is_read = ?", userID, false). + Update("is_read", true).Error; err != nil { + h.logger.Error("Failed to mark all notifications as read", "error", err) + response.Error(c, http.StatusInternalServerError, "Failed to update notifications") + return + } + + response.Success(c, gin.H{"message": "All notifications marked as read"}) +} + +// @Summary Delete Notification +// @Description Delete a single notification for the current user +// @Tags notifications +// @Produce json +// @Param id path string true "Notification ID" +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 404 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /notifications/{id} [delete] +// @Security BearerAuth +func (h *Handler) DeleteNotification(c *gin.Context) { + userID := c.GetString("userID") + if userID == "" { + response.Error(c, http.StatusUnauthorized, "Unauthorized") + return + } + + id := strings.TrimSpace(c.Param("id")) + if id == "" { + response.Error(c, http.StatusNotFound, "Notification not found") + return + } + + result := h.db.WithContext(c.Request.Context()). + Where("id = ? AND user_id = ?", id, userID). + Delete(&model.Notification{}) + if result.Error != nil { + h.logger.Error("Failed to delete notification", "error", result.Error) + response.Error(c, http.StatusInternalServerError, "Failed to delete notification") + return + } + if result.RowsAffected == 0 { + response.Error(c, http.StatusNotFound, "Notification not found") + return + } + + response.Success(c, gin.H{"message": "Notification deleted"}) +} + +// @Summary Clear Notifications +// @Description Delete all notifications for the current user +// @Tags notifications +// @Produce json +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /notifications [delete] +// @Security BearerAuth +func (h *Handler) ClearNotifications(c *gin.Context) { + userID := c.GetString("userID") + if userID == "" { + response.Error(c, http.StatusUnauthorized, "Unauthorized") + return + } + + if err := h.db.WithContext(c.Request.Context()).Where("user_id = ?", userID).Delete(&model.Notification{}).Error; err != nil { + h.logger.Error("Failed to clear notifications", "error", err) + response.Error(c, http.StatusInternalServerError, "Failed to clear notifications") + return + } + + response.Success(c, gin.H{"message": "All notifications deleted"}) +} + +func (h *Handler) updateReadState(c *gin.Context, value bool, silentNotFound bool) { + userID := c.GetString("userID") + if userID == "" { + response.Error(c, http.StatusUnauthorized, "Unauthorized") + return + } + + id := strings.TrimSpace(c.Param("id")) + if id == "" { + response.Error(c, http.StatusNotFound, "Notification not found") + return + } + + result := h.db.WithContext(c.Request.Context()). + Model(&model.Notification{}). + Where("id = ? AND user_id = ?", id, userID). + Update("is_read", value) + if result.Error != nil { + h.logger.Error("Failed to update notification", "error", result.Error) + response.Error(c, http.StatusInternalServerError, "Failed to update notification") + return + } + if result.RowsAffected == 0 && !silentNotFound { + response.Error(c, http.StatusNotFound, "Notification not found") + return + } + + response.Success(c, gin.H{"message": "Notification updated"}) +} + +func mapNotification(item model.Notification) NotificationItem { + createdAt := time.Time{} + if item.CreatedAt != nil { + createdAt = item.CreatedAt.UTC() + } + + return NotificationItem{ + ID: item.ID, + Type: normalizeType(item.Type), + Title: item.Title, + Message: item.Message, + Read: item.IsRead, + ActionURL: model.StringValue(item.ActionURL), + ActionLabel: model.StringValue(item.ActionLabel), + CreatedAt: createdAt, + } +} + +func normalizeType(value string) string { + lower := strings.ToLower(strings.TrimSpace(value)) + switch { + case strings.Contains(lower, "video"): + return "video" + case strings.Contains(lower, "payment"), strings.Contains(lower, "billing"): + return "payment" + case strings.Contains(lower, "warning"): + return "warning" + case strings.Contains(lower, "error"): + return "error" + case strings.Contains(lower, "success"): + return "success" + case strings.Contains(lower, "system"): + return "system" + default: + return "info" + } +} diff --git a/internal/api/payment/handler.go b/internal/api/payment/handler.go index 2440d6f..38fd1ae 100644 --- a/internal/api/payment/handler.go +++ b/internal/api/payment/handler.go @@ -1,10 +1,21 @@ +//go:build ignore +// +build ignore + package payment import ( + "context" + "encoding/json" + "errors" + "fmt" "net/http" + "strings" + "time" "github.com/gin-gonic/gin" "github.com/google/uuid" + "gorm.io/gorm" + "gorm.io/gorm/clause" "stream.api/internal/config" "stream.api/internal/database/model" "stream.api/internal/database/query" @@ -12,20 +23,70 @@ import ( "stream.api/pkg/response" ) +const ( + walletTransactionTypeTopup = "topup" + walletTransactionTypeSubscriptionDebit = "subscription_debit" + paymentMethodWallet = "wallet" + paymentMethodTopup = "topup" + paymentKindSubscription = "subscription" + paymentKindWalletTopup = "wallet_topup" +) + +var allowedTermMonths = map[int32]struct{}{ + 1: {}, + 3: {}, + 6: {}, + 12: {}, +} + type Handler struct { logger logger.Logger cfg *config.Config + db *gorm.DB } -func NewHandler(l logger.Logger, cfg *config.Config) PaymentHandler { +type paymentRow struct { + ID string `gorm:"column:id"` + Amount float64 `gorm:"column:amount"` + Currency *string `gorm:"column:currency"` + Status *string `gorm:"column:status"` + PlanID *string `gorm:"column:plan_id"` + PlanName *string `gorm:"column:plan_name"` + TermMonths *int32 `gorm:"column:term_months"` + PaymentMethod *string `gorm:"column:payment_method"` + ExpiresAt *time.Time `gorm:"column:expires_at"` + CreatedAt *time.Time `gorm:"column:created_at"` +} + +type paymentInvoiceDetails struct { + PlanName string + TermMonths *int32 + PaymentMethod string + ExpiresAt *time.Time + WalletAmount float64 + TopupAmount float64 +} + +type paymentError struct { + Code int + Message string + Data interface{} +} + +func (e *paymentError) Error() string { + return e.Message +} + +func NewHandler(l logger.Logger, cfg *config.Config, db *gorm.DB) PaymentHandler { return &Handler{ logger: l, cfg: cfg, + db: db, } } // @Summary Create Payment -// @Description Create a new payment +// @Description Create a new payment for buying or renewing a plan // @Tags payment // @Accept json // @Produce json @@ -33,6 +94,7 @@ func NewHandler(l logger.Logger, cfg *config.Config) PaymentHandler { // @Success 201 {object} response.Response // @Failure 400 {object} response.Response // @Failure 401 {object} response.Response +// @Failure 404 {object} response.Response // @Failure 500 {object} response.Response // @Router /payments [post] // @Security BearerAuth @@ -49,27 +111,696 @@ func (h *Handler) CreatePayment(c *gin.Context) { return } - // In a real scenario, we would contact Stripe/PayPal here to create a session - // For now, we just create a "PENDING" payment record. - - status := "PENDING" - provider := "STRIPE" - - payment := &model.Payment{ - ID: uuid.New().String(), - UserID: userID, - PlanID: &req.PlanID, - Amount: req.Amount, - Status: &status, - Provider: &provider, // Defaulting to Stripe for this example + planID := strings.TrimSpace(req.PlanID) + if planID == "" { + response.Error(c, http.StatusBadRequest, "Plan ID is required") + return + } + if !isAllowedTermMonths(req.TermMonths) { + response.Error(c, http.StatusBadRequest, "Term months must be one of 1, 3, 6, or 12") + return } - p := query.Payment - if err := p.WithContext(c.Request.Context()).Create(payment); err != nil { + paymentMethod := normalizePaymentMethod(req.PaymentMethod) + if paymentMethod == "" { + response.Error(c, http.StatusBadRequest, "Payment method must be wallet or topup") + return + } + + ctx := c.Request.Context() + var planRecord model.Plan + if err := h.db.WithContext(ctx).Where("id = ?", planID).First(&planRecord).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + response.Error(c, http.StatusNotFound, "Plan not found") + return + } + h.logger.Error("Failed to load plan", "error", err) + response.Error(c, http.StatusInternalServerError, "Failed to create payment") + return + } + + if planRecord.IsActive == nil || !*planRecord.IsActive { + response.Error(c, http.StatusBadRequest, "Plan is not active") + return + } + + totalAmount := planRecord.Price * float64(req.TermMonths) + if totalAmount < 0 { + response.Error(c, http.StatusBadRequest, "Amount must be greater than or equal to 0") + return + } + + status := "SUCCESS" + provider := "INTERNAL" + currency := normalizeCurrency(nil) + transactionID := buildTransactionID("sub") + now := time.Now().UTC() + + payment := &model.Payment{ + ID: uuid.New().String(), + UserID: userID, + PlanID: &planRecord.ID, + Amount: totalAmount, + Currency: ¤cy, + Status: &status, + Provider: &provider, + TransactionID: &transactionID, + } + + invoiceID := buildInvoiceID(payment.ID) + var subscription *model.PlanSubscription + var walletBalance float64 + + err := h.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + if _, err := lockUserForUpdate(ctx, tx, userID); err != nil { + return err + } + + currentSubscription, err := model.GetLatestPlanSubscription(ctx, tx, userID) + if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { + return err + } + + baseExpiry := now + if currentSubscription != nil && currentSubscription.ExpiresAt.After(baseExpiry) { + baseExpiry = currentSubscription.ExpiresAt.UTC() + } + newExpiry := baseExpiry.AddDate(0, int(req.TermMonths), 0) + + currentWalletBalance, err := model.GetWalletBalance(ctx, tx, userID) + if err != nil { + return err + } + shortfall := maxFloat(totalAmount-currentWalletBalance, 0) + + if paymentMethod == paymentMethodWallet && shortfall > 0 { + return &paymentError{ + Code: http.StatusBadRequest, + Message: "Insufficient wallet balance", + Data: gin.H{ + "payment_method": paymentMethod, + "wallet_balance": currentWalletBalance, + "total_amount": totalAmount, + "shortfall": shortfall, + }, + } + } + + topupAmount := 0.0 + if paymentMethod == paymentMethodTopup { + if req.TopupAmount == nil { + return &paymentError{ + Code: http.StatusBadRequest, + Message: "Top-up amount is required when payment method is topup", + Data: gin.H{ + "payment_method": paymentMethod, + "wallet_balance": currentWalletBalance, + "total_amount": totalAmount, + "shortfall": shortfall, + }, + } + } + + topupAmount = maxFloat(*req.TopupAmount, 0) + if topupAmount <= 0 { + return &paymentError{ + Code: http.StatusBadRequest, + Message: "Top-up amount must be greater than 0", + Data: gin.H{ + "payment_method": paymentMethod, + "wallet_balance": currentWalletBalance, + "total_amount": totalAmount, + "shortfall": shortfall, + }, + } + } + if topupAmount < shortfall { + return &paymentError{ + Code: http.StatusBadRequest, + Message: "Top-up amount must be greater than or equal to the required shortfall", + Data: gin.H{ + "payment_method": paymentMethod, + "wallet_balance": currentWalletBalance, + "total_amount": totalAmount, + "shortfall": shortfall, + "topup_amount": topupAmount, + }, + } + } + } + + if err := tx.Create(payment).Error; err != nil { + return err + } + + walletUsedAmount := totalAmount + + if paymentMethod == paymentMethodTopup { + topupTransaction := &model.WalletTransaction{ + ID: uuid.New().String(), + UserID: userID, + Type: walletTransactionTypeTopup, + Amount: topupAmount, + Currency: model.StringPtr(currency), + Note: model.StringPtr(fmt.Sprintf("Wallet top-up for %s (%d months)", planRecord.Name, req.TermMonths)), + PaymentID: &payment.ID, + PlanID: &planRecord.ID, + TermMonths: &req.TermMonths, + } + if err := tx.Create(topupTransaction).Error; err != nil { + return err + } + } + + debitTransaction := &model.WalletTransaction{ + ID: uuid.New().String(), + UserID: userID, + Type: walletTransactionTypeSubscriptionDebit, + Amount: -totalAmount, + Currency: model.StringPtr(currency), + Note: model.StringPtr(fmt.Sprintf("Subscription payment for %s (%d months)", planRecord.Name, req.TermMonths)), + PaymentID: &payment.ID, + PlanID: &planRecord.ID, + TermMonths: &req.TermMonths, + } + if err := tx.Create(debitTransaction).Error; err != nil { + return err + } + + subscription = &model.PlanSubscription{ + ID: uuid.New().String(), + UserID: userID, + PaymentID: payment.ID, + PlanID: planRecord.ID, + TermMonths: req.TermMonths, + PaymentMethod: paymentMethod, + WalletAmount: walletUsedAmount, + TopupAmount: topupAmount, + StartedAt: now, + ExpiresAt: newExpiry, + } + if err := tx.Create(subscription).Error; err != nil { + return err + } + + if err := tx.Model(&model.User{}). + Where("id = ?", userID). + Update("plan_id", planRecord.ID).Error; err != nil { + return err + } + + notification := buildSubscriptionNotification(userID, payment.ID, invoiceID, &planRecord, subscription) + if err := tx.Create(notification).Error; err != nil { + return err + } + + walletBalance, err = model.GetWalletBalance(ctx, tx, userID) + if err != nil { + return err + } + + return nil + }) + if err != nil { + var paymentErr *paymentError + if errors.As(err, &paymentErr) { + c.AbortWithStatusJSON(paymentErr.Code, response.Response{ + Code: paymentErr.Code, + Message: paymentErr.Message, + Data: paymentErr.Data, + }) + return + } + h.logger.Error("Failed to create payment", "error", err) response.Error(c, http.StatusInternalServerError, "Failed to create payment") return } - response.Created(c, gin.H{"payment": payment, "message": "Payment initiated"}) + response.Created(c, gin.H{ + "payment": payment, + "subscription": subscription, + "wallet_balance": walletBalance, + "invoice_id": invoiceID, + "message": "Payment completed successfully", + }) +} + +// @Summary List Payment History +// @Description Get payment history for the current user +// @Tags payment +// @Produce json +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /payments/history [get] +// @Security BearerAuth +func (h *Handler) ListPaymentHistory(c *gin.Context) { + userID := c.GetString("userID") + if userID == "" { + response.Error(c, http.StatusUnauthorized, "Unauthorized") + return + } + + var rows []paymentRow + if err := h.db.WithContext(c.Request.Context()). + Table("payment AS p"). + Select("p.id, p.amount, p.currency, p.status, p.plan_id, pl.name AS plan_name, ps.term_months, ps.payment_method, ps.expires_at, p.created_at"). + Joins("LEFT JOIN plan AS pl ON pl.id = p.plan_id"). + Joins("LEFT JOIN plan_subscriptions AS ps ON ps.payment_id = p.id"). + Where("p.user_id = ?", userID). + Order("p.created_at DESC"). + Scan(&rows).Error; err != nil { + h.logger.Error("Failed to fetch payment history", "error", err) + response.Error(c, http.StatusInternalServerError, "Failed to fetch payment history") + return + } + + items := make([]PaymentHistoryItem, 0, len(rows)) + for _, row := range rows { + items = append(items, PaymentHistoryItem{ + ID: row.ID, + Amount: row.Amount, + Currency: normalizeCurrency(row.Currency), + Status: normalizePaymentStatus(row.Status), + PlanID: row.PlanID, + PlanName: row.PlanName, + InvoiceID: buildInvoiceID(row.ID), + Kind: paymentKindSubscription, + TermMonths: row.TermMonths, + PaymentMethod: normalizeOptionalPaymentMethod(row.PaymentMethod), + ExpiresAt: row.ExpiresAt, + CreatedAt: row.CreatedAt, + }) + } + + var topups []model.WalletTransaction + if err := h.db.WithContext(c.Request.Context()). + Where("user_id = ? AND type = ? AND payment_id IS NULL", userID, walletTransactionTypeTopup). + Order("created_at DESC"). + Find(&topups).Error; err != nil { + h.logger.Error("Failed to fetch wallet topups", "error", err) + response.Error(c, http.StatusInternalServerError, "Failed to fetch payment history") + return + } + + for _, topup := range topups { + createdAt := topup.CreatedAt + items = append(items, PaymentHistoryItem{ + ID: topup.ID, + Amount: topup.Amount, + Currency: normalizeCurrency(topup.Currency), + Status: "success", + InvoiceID: buildInvoiceID(topup.ID), + Kind: paymentKindWalletTopup, + CreatedAt: createdAt, + }) + } + + sortPaymentHistory(items) + response.Success(c, gin.H{"payments": items}) +} + +// @Summary Top Up Wallet +// @Description Add funds to wallet balance for the current user +// @Tags payment +// @Accept json +// @Produce json +// @Param request body TopupWalletRequest true "Topup Info" +// @Success 201 {object} response.Response +// @Failure 400 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /wallet/topups [post] +// @Security BearerAuth +func (h *Handler) TopupWallet(c *gin.Context) { + var req TopupWalletRequest + if err := c.ShouldBindJSON(&req); err != nil { + response.Error(c, http.StatusBadRequest, err.Error()) + return + } + + userID := c.GetString("userID") + if userID == "" { + response.Error(c, http.StatusUnauthorized, "Unauthorized") + return + } + + amount := req.Amount + if amount < 1 { + response.Error(c, http.StatusBadRequest, "Amount must be at least 1") + return + } + + transaction := &model.WalletTransaction{ + ID: uuid.New().String(), + UserID: userID, + Type: walletTransactionTypeTopup, + Amount: amount, + Currency: model.StringPtr("USD"), + Note: model.StringPtr(fmt.Sprintf("Wallet top-up of %.2f USD", amount)), + } + + notification := &model.Notification{ + ID: uuid.New().String(), + UserID: userID, + Type: "billing.topup", + Title: "Wallet credited", + Message: fmt.Sprintf("Your wallet has been credited with %.2f USD.", amount), + Metadata: model.StringPtr(mustMarshalJSON(gin.H{ + "wallet_transaction_id": transaction.ID, + "invoice_id": buildInvoiceID(transaction.ID), + })), + } + + if err := h.db.WithContext(c.Request.Context()).Transaction(func(tx *gorm.DB) error { + if _, err := lockUserForUpdate(c.Request.Context(), tx, userID); err != nil { + return err + } + if err := tx.Create(transaction).Error; err != nil { + return err + } + if err := tx.Create(notification).Error; err != nil { + return err + } + return nil + }); err != nil { + h.logger.Error("Failed to top up wallet", "error", err) + response.Error(c, http.StatusInternalServerError, "Failed to top up wallet") + return + } + + balance, err := model.GetWalletBalance(c.Request.Context(), h.db, userID) + if err != nil { + h.logger.Error("Failed to calculate wallet balance", "error", err) + response.Error(c, http.StatusInternalServerError, "Failed to top up wallet") + return + } + + response.Created(c, gin.H{ + "wallet_transaction": transaction, + "wallet_balance": balance, + "invoice_id": buildInvoiceID(transaction.ID), + }) +} + +// @Summary Download Invoice +// @Description Download invoice text for a payment or wallet top-up +// @Tags payment +// @Produce plain +// @Param id path string true "Payment ID" +// @Success 200 {string} string +// @Failure 401 {object} response.Response +// @Failure 404 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /payments/{id}/invoice [get] +// @Security BearerAuth +func (h *Handler) DownloadInvoice(c *gin.Context) { + userID := c.GetString("userID") + if userID == "" { + response.Error(c, http.StatusUnauthorized, "Unauthorized") + return + } + + id := strings.TrimSpace(c.Param("id")) + if id == "" { + response.Error(c, http.StatusNotFound, "Invoice not found") + return + } + + ctx := c.Request.Context() + paymentRecord, err := query.Payment.WithContext(ctx). + Where(query.Payment.ID.Eq(id), query.Payment.UserID.Eq(userID)). + First() + if err == nil { + invoiceText, filename, buildErr := h.buildPaymentInvoice(ctx, paymentRecord) + if buildErr != nil { + h.logger.Error("Failed to build payment invoice", "error", buildErr) + response.Error(c, http.StatusInternalServerError, "Failed to download invoice") + return + } + serveInvoiceText(c, filename, invoiceText) + return + } + if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { + h.logger.Error("Failed to load payment invoice", "error", err) + response.Error(c, http.StatusInternalServerError, "Failed to download invoice") + return + } + + var topup model.WalletTransaction + if err := h.db.WithContext(ctx). + Where("id = ? AND user_id = ? AND type = ? AND payment_id IS NULL", id, userID, walletTransactionTypeTopup). + First(&topup).Error; err == nil { + invoiceText := buildTopupInvoice(&topup) + serveInvoiceText(c, buildInvoiceFilename(topup.ID), invoiceText) + return + } else if !errors.Is(err, gorm.ErrRecordNotFound) { + h.logger.Error("Failed to load topup invoice", "error", err) + response.Error(c, http.StatusInternalServerError, "Failed to download invoice") + return + } + + response.Error(c, http.StatusNotFound, "Invoice not found") +} + +func normalizePaymentStatus(status *string) string { + value := strings.ToLower(strings.TrimSpace(stringValue(status))) + switch value { + case "success", "succeeded", "paid": + return "success" + case "failed", "error", "canceled", "cancelled": + return "failed" + case "pending", "processing": + return "pending" + default: + if value == "" { + return "success" + } + return value + } +} + +func normalizeCurrency(currency *string) string { + value := strings.ToUpper(strings.TrimSpace(stringValue(currency))) + if value == "" { + return "USD" + } + return value +} + +func normalizePaymentMethod(value string) string { + switch strings.ToLower(strings.TrimSpace(value)) { + case paymentMethodWallet: + return paymentMethodWallet + case paymentMethodTopup: + return paymentMethodTopup + default: + return "" + } +} + +func normalizeOptionalPaymentMethod(value *string) *string { + normalized := normalizePaymentMethod(stringValue(value)) + if normalized == "" { + return nil + } + return &normalized +} + +func buildInvoiceID(id string) string { + trimmed := strings.ReplaceAll(strings.TrimSpace(id), "-", "") + if len(trimmed) > 12 { + trimmed = trimmed[:12] + } + return "INV-" + strings.ToUpper(trimmed) +} + +func buildTransactionID(prefix string) string { + return fmt.Sprintf("%s_%d", prefix, time.Now().UnixNano()) +} + +func buildInvoiceFilename(id string) string { + return fmt.Sprintf("invoice-%s.txt", id) +} + +func stringValue(value *string) string { + if value == nil { + return "" + } + return *value +} + +func sortPaymentHistory(items []PaymentHistoryItem) { + for i := 0; i < len(items); i++ { + for j := i + 1; j < len(items); j++ { + left := time.Time{} + right := time.Time{} + if items[i].CreatedAt != nil { + left = *items[i].CreatedAt + } + if items[j].CreatedAt != nil { + right = *items[j].CreatedAt + } + if right.After(left) { + items[i], items[j] = items[j], items[i] + } + } + } +} + +func serveInvoiceText(c *gin.Context, filename string, content string) { + c.Header("Content-Type", "text/plain; charset=utf-8") + c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%q", filename)) + c.String(http.StatusOK, content) +} + +func (h *Handler) buildPaymentInvoice(ctx context.Context, paymentRecord *model.Payment) (string, string, error) { + details, err := h.loadPaymentInvoiceDetails(ctx, paymentRecord) + if err != nil { + return "", "", err + } + + createdAt := formatOptionalTimestamp(paymentRecord.CreatedAt) + lines := []string{ + "Stream API Invoice", + fmt.Sprintf("Invoice ID: %s", buildInvoiceID(paymentRecord.ID)), + fmt.Sprintf("Payment ID: %s", paymentRecord.ID), + fmt.Sprintf("User ID: %s", paymentRecord.UserID), + fmt.Sprintf("Plan: %s", details.PlanName), + fmt.Sprintf("Amount: %.2f %s", paymentRecord.Amount, normalizeCurrency(paymentRecord.Currency)), + fmt.Sprintf("Status: %s", strings.ToUpper(normalizePaymentStatus(paymentRecord.Status))), + fmt.Sprintf("Provider: %s", strings.ToUpper(stringValue(paymentRecord.Provider))), + fmt.Sprintf("Payment Method: %s", strings.ToUpper(details.PaymentMethod)), + fmt.Sprintf("Transaction ID: %s", stringValue(paymentRecord.TransactionID)), + } + + if details.TermMonths != nil { + lines = append(lines, fmt.Sprintf("Term: %d month(s)", *details.TermMonths)) + } + if details.ExpiresAt != nil { + lines = append(lines, fmt.Sprintf("Valid Until: %s", details.ExpiresAt.UTC().Format(time.RFC3339))) + } + if details.WalletAmount > 0 { + lines = append(lines, fmt.Sprintf("Wallet Applied: %.2f %s", details.WalletAmount, normalizeCurrency(paymentRecord.Currency))) + } + if details.TopupAmount > 0 { + lines = append(lines, fmt.Sprintf("Top-up Added: %.2f %s", details.TopupAmount, normalizeCurrency(paymentRecord.Currency))) + } + lines = append(lines, fmt.Sprintf("Created At: %s", createdAt)) + + return strings.Join(lines, "\n"), buildInvoiceFilename(paymentRecord.ID), nil +} + +func buildTopupInvoice(transaction *model.WalletTransaction) string { + createdAt := formatOptionalTimestamp(transaction.CreatedAt) + return strings.Join([]string{ + "Stream API Wallet Top-up Invoice", + fmt.Sprintf("Invoice ID: %s", buildInvoiceID(transaction.ID)), + fmt.Sprintf("Wallet Transaction ID: %s", transaction.ID), + fmt.Sprintf("User ID: %s", transaction.UserID), + fmt.Sprintf("Amount: %.2f %s", transaction.Amount, normalizeCurrency(transaction.Currency)), + "Status: SUCCESS", + fmt.Sprintf("Type: %s", strings.ToUpper(transaction.Type)), + fmt.Sprintf("Note: %s", model.StringValue(transaction.Note)), + fmt.Sprintf("Created At: %s", createdAt), + }, "\n") +} + +func (h *Handler) loadPaymentInvoiceDetails(ctx context.Context, paymentRecord *model.Payment) (*paymentInvoiceDetails, error) { + details := &paymentInvoiceDetails{ + PlanName: "Unknown plan", + PaymentMethod: paymentMethodWallet, + } + + if paymentRecord.PlanID != nil && strings.TrimSpace(*paymentRecord.PlanID) != "" { + var planRecord model.Plan + if err := h.db.WithContext(ctx).Where("id = ?", *paymentRecord.PlanID).First(&planRecord).Error; err != nil { + if !errors.Is(err, gorm.ErrRecordNotFound) { + return nil, err + } + } else { + details.PlanName = planRecord.Name + } + } + + var subscription model.PlanSubscription + if err := h.db.WithContext(ctx). + Where("payment_id = ?", paymentRecord.ID). + Order("created_at DESC"). + First(&subscription).Error; err != nil { + if !errors.Is(err, gorm.ErrRecordNotFound) { + return nil, err + } + return details, nil + } + + details.TermMonths = &subscription.TermMonths + details.PaymentMethod = normalizePaymentMethod(subscription.PaymentMethod) + if details.PaymentMethod == "" { + details.PaymentMethod = paymentMethodWallet + } + details.ExpiresAt = &subscription.ExpiresAt + details.WalletAmount = subscription.WalletAmount + details.TopupAmount = subscription.TopupAmount + + return details, nil +} + +func buildSubscriptionNotification(userID string, paymentID string, invoiceID string, planRecord *model.Plan, subscription *model.PlanSubscription) *model.Notification { + return &model.Notification{ + ID: uuid.New().String(), + UserID: userID, + Type: "billing.subscription", + Title: "Subscription activated", + Message: fmt.Sprintf("Your subscription to %s is active until %s.", planRecord.Name, subscription.ExpiresAt.UTC().Format("2006-01-02")), + Metadata: model.StringPtr(mustMarshalJSON(gin.H{ + "payment_id": paymentID, + "invoice_id": invoiceID, + "plan_id": planRecord.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), + })), + } +} + +func isAllowedTermMonths(value int32) bool { + _, ok := allowedTermMonths[value] + return ok +} + +func lockUserForUpdate(ctx context.Context, tx *gorm.DB, userID string) (*model.User, error) { + var user model.User + if err := tx.WithContext(ctx). + Clauses(clause.Locking{Strength: "UPDATE"}). + Where("id = ?", userID). + First(&user).Error; err != nil { + return nil, err + } + return &user, nil +} + +func maxFloat(left float64, right float64) float64 { + if left > right { + return left + } + return right +} + +func formatOptionalTimestamp(value *time.Time) string { + if value == nil { + return "" + } + return value.UTC().Format(time.RFC3339) +} + +func mustMarshalJSON(value interface{}) string { + encoded, err := json.Marshal(value) + if err != nil { + return "{}" + } + return string(encoded) } diff --git a/internal/api/payment/http_types.go b/internal/api/payment/http_types.go new file mode 100644 index 0000000..3c49f9d --- /dev/null +++ b/internal/api/payment/http_types.go @@ -0,0 +1,12 @@ +package payment + +type CreatePaymentRequest struct { + PlanID string `json:"plan_id" binding:"required"` + TermMonths int32 `json:"term_months" binding:"required"` + PaymentMethod string `json:"payment_method" binding:"required"` + TopupAmount *float64 `json:"topup_amount,omitempty"` +} + +type TopupWalletRequest struct { + Amount float64 `json:"amount" binding:"required"` +} diff --git a/internal/api/payment/interface.go b/internal/api/payment/interface.go index 2032293..953f2d5 100644 --- a/internal/api/payment/interface.go +++ b/internal/api/payment/interface.go @@ -1,3 +1,6 @@ +//go:build ignore +// +build ignore + package payment import "github.com/gin-gonic/gin" @@ -5,10 +8,19 @@ import "github.com/gin-gonic/gin" // PaymentHandler defines the interface for payment operations type PaymentHandler interface { CreatePayment(c *gin.Context) + ListPaymentHistory(c *gin.Context) + TopupWallet(c *gin.Context) + DownloadInvoice(c *gin.Context) } // CreatePaymentRequest defines the payload for creating a payment type CreatePaymentRequest struct { - PlanID string `json:"plan_id" binding:"required"` + PlanID string `json:"plan_id" binding:"required"` + TermMonths int32 `json:"term_months" binding:"required"` + PaymentMethod string `json:"payment_method" binding:"required"` + TopupAmount *float64 `json:"topup_amount,omitempty"` +} + +type TopupWalletRequest struct { Amount float64 `json:"amount" binding:"required"` } diff --git a/internal/api/payment/types.go b/internal/api/payment/types.go new file mode 100644 index 0000000..5f719c9 --- /dev/null +++ b/internal/api/payment/types.go @@ -0,0 +1,18 @@ +package payment + +import "time" + +type PaymentHistoryItem struct { + ID string `json:"id"` + Amount float64 `json:"amount"` + Currency string `json:"currency"` + Status string `json:"status"` + PlanID *string `json:"plan_id,omitempty"` + PlanName *string `json:"plan_name,omitempty"` + InvoiceID string `json:"invoice_id"` + Kind string `json:"kind"` + TermMonths *int32 `json:"term_months,omitempty"` + PaymentMethod *string `json:"payment_method,omitempty"` + ExpiresAt *time.Time `json:"expires_at,omitempty"` + CreatedAt *time.Time `json:"created_at,omitempty"` +} diff --git a/internal/api/plan/handler.go b/internal/api/plan/handler.go index 3ec80b4..671974a 100644 --- a/internal/api/plan/handler.go +++ b/internal/api/plan/handler.go @@ -1,11 +1,15 @@ +//go:build ignore +// +build ignore + package plan import ( "net/http" "github.com/gin-gonic/gin" + "gorm.io/gorm" "stream.api/internal/config" - "stream.api/internal/database/query" + "stream.api/internal/database/model" "stream.api/pkg/logger" "stream.api/pkg/response" ) @@ -13,12 +17,14 @@ import ( type Handler struct { logger logger.Logger cfg *config.Config + db *gorm.DB } -func NewHandler(l logger.Logger, cfg *config.Config) PlanHandler { +func NewHandler(l logger.Logger, cfg *config.Config, db *gorm.DB) PlanHandler { return &Handler{ logger: l, cfg: cfg, + db: db, } } @@ -26,14 +32,13 @@ func NewHandler(l logger.Logger, cfg *config.Config) PlanHandler { // @Description Get all active plans // @Tags plan // @Produce json -// @Success 200 {object} response.Response{data=[]model.Plan} +// @Success 200 {object} response.Response // @Failure 500 {object} response.Response // @Router /plans [get] // @Security BearerAuth func (h *Handler) ListPlans(c *gin.Context) { - p := query.Plan - plans, err := p.WithContext(c.Request.Context()).Where(p.IsActive.Is(true)).Find() - if err != nil { + var plans []model.Plan + if err := h.db.WithContext(c.Request.Context()).Where("is_active = ?", true).Find(&plans).Error; err != nil { h.logger.Error("Failed to fetch plans", "error", err) response.Error(c, http.StatusInternalServerError, "Failed to fetch plans") return diff --git a/internal/api/plan/interface.go b/internal/api/plan/interface.go index 974df30..7e7fc4f 100644 --- a/internal/api/plan/interface.go +++ b/internal/api/plan/interface.go @@ -1,3 +1,6 @@ +//go:build ignore +// +build ignore + package plan import "github.com/gin-gonic/gin" diff --git a/internal/api/preferences/handler.go b/internal/api/preferences/handler.go new file mode 100644 index 0000000..b7edc5c --- /dev/null +++ b/internal/api/preferences/handler.go @@ -0,0 +1,112 @@ +//go:build ignore +// +build ignore + +package preferences + +import ( + "net/http" + + "github.com/gin-gonic/gin" + "gorm.io/gorm" + "stream.api/pkg/logger" + "stream.api/pkg/response" +) + +type Handler struct { + logger logger.Logger + db *gorm.DB +} + +type SettingsPreferencesRequest struct { + EmailNotifications *bool `json:"email_notifications"` + PushNotifications *bool `json:"push_notifications"` + MarketingNotifications *bool `json:"marketing_notifications"` + TelegramNotifications *bool `json:"telegram_notifications"` + Autoplay *bool `json:"autoplay"` + Loop *bool `json:"loop"` + Muted *bool `json:"muted"` + ShowControls *bool `json:"show_controls"` + Pip *bool `json:"pip"` + Airplay *bool `json:"airplay"` + Chromecast *bool `json:"chromecast"` + Language *string `json:"language"` + Locale *string `json:"locale"` +} + +func NewHandler(l logger.Logger, db *gorm.DB) *Handler { + return &Handler{logger: l, db: db} +} + +// @Summary Get Preferences +// @Description Get notification, player, and locale preferences for the current user +// @Tags settings +// @Produce json +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /settings/preferences [get] +// @Security BearerAuth +func (h *Handler) GetPreferences(c *gin.Context) { + userID := c.GetString("userID") + if userID == "" { + response.Error(c, http.StatusUnauthorized, "Unauthorized") + return + } + + pref, err := LoadUserPreferences(c.Request.Context(), h.db, userID) + if err != nil { + h.logger.Error("Failed to load preferences", "error", err) + response.Error(c, http.StatusInternalServerError, "Failed to load preferences") + return + } + + response.Success(c, gin.H{"preferences": pref}) +} + +// @Summary Update Preferences +// @Description Update notification, player, and locale preferences for the current user +// @Tags settings +// @Accept json +// @Produce json +// @Param request body SettingsPreferencesRequest true "Preferences payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /settings/preferences [put] +// @Security BearerAuth +func (h *Handler) UpdatePreferences(c *gin.Context) { + userID := c.GetString("userID") + if userID == "" { + response.Error(c, http.StatusUnauthorized, "Unauthorized") + return + } + + var req SettingsPreferencesRequest + if err := c.ShouldBindJSON(&req); err != nil { + response.Error(c, http.StatusBadRequest, err.Error()) + return + } + + pref, err := UpdateUserPreferences(c.Request.Context(), h.db, h.logger, userID, UpdateInput{ + EmailNotifications: req.EmailNotifications, + PushNotifications: req.PushNotifications, + MarketingNotifications: req.MarketingNotifications, + TelegramNotifications: req.TelegramNotifications, + Autoplay: req.Autoplay, + Loop: req.Loop, + Muted: req.Muted, + ShowControls: req.ShowControls, + Pip: req.Pip, + Airplay: req.Airplay, + Chromecast: req.Chromecast, + Language: req.Language, + Locale: req.Locale, + }) + if err != nil { + response.Error(c, http.StatusInternalServerError, "Failed to save preferences") + return + } + + response.Success(c, gin.H{"preferences": pref}) +} diff --git a/internal/api/preferences/service.go b/internal/api/preferences/service.go new file mode 100644 index 0000000..6c31dd8 --- /dev/null +++ b/internal/api/preferences/service.go @@ -0,0 +1,91 @@ +package preferences + +import ( + "context" + "strings" + + "gorm.io/gorm" + "stream.api/internal/database/model" + "stream.api/pkg/logger" +) + +type UpdateInput struct { + EmailNotifications *bool + PushNotifications *bool + MarketingNotifications *bool + TelegramNotifications *bool + Autoplay *bool + Loop *bool + Muted *bool + ShowControls *bool + Pip *bool + Airplay *bool + Chromecast *bool + Language *string + Locale *string +} + +func LoadUserPreferences(ctx context.Context, db *gorm.DB, userID string) (*model.UserPreference, error) { + return model.FindOrCreateUserPreference(ctx, db, userID) +} + +func UpdateUserPreferences(ctx context.Context, db *gorm.DB, l logger.Logger, userID string, req UpdateInput) (*model.UserPreference, error) { + pref, err := model.FindOrCreateUserPreference(ctx, db, userID) + if err != nil { + l.Error("Failed to load preferences", "error", err) + return nil, err + } + + if req.EmailNotifications != nil { + pref.EmailNotifications = model.BoolPtr(*req.EmailNotifications) + } + if req.PushNotifications != nil { + pref.PushNotifications = model.BoolPtr(*req.PushNotifications) + } + if req.MarketingNotifications != nil { + pref.MarketingNotifications = *req.MarketingNotifications + } + if req.TelegramNotifications != nil { + pref.TelegramNotifications = *req.TelegramNotifications + } + if req.Autoplay != nil { + pref.Autoplay = *req.Autoplay + } + if req.Loop != nil { + pref.Loop = *req.Loop + } + if req.Muted != nil { + pref.Muted = *req.Muted + } + if req.ShowControls != nil { + pref.ShowControls = model.BoolPtr(*req.ShowControls) + } + if req.Pip != nil { + pref.Pip = model.BoolPtr(*req.Pip) + } + if req.Airplay != nil { + pref.Airplay = model.BoolPtr(*req.Airplay) + } + if req.Chromecast != nil { + pref.Chromecast = model.BoolPtr(*req.Chromecast) + } + if req.Language != nil { + pref.Language = model.StringPtr(strings.TrimSpace(*req.Language)) + } + if req.Locale != nil { + pref.Locale = model.StringPtr(strings.TrimSpace(*req.Locale)) + } + if strings.TrimSpace(model.StringValue(pref.Language)) == "" { + pref.Language = model.StringPtr("en") + } + if strings.TrimSpace(model.StringValue(pref.Locale)) == "" { + pref.Locale = model.StringPtr(model.StringValue(pref.Language)) + } + + if err := db.WithContext(ctx).Save(pref).Error; err != nil { + l.Error("Failed to save preferences", "error", err) + return nil, err + } + + return pref, nil +} diff --git a/internal/api/usage/handler.go b/internal/api/usage/handler.go new file mode 100644 index 0000000..09186a4 --- /dev/null +++ b/internal/api/usage/handler.go @@ -0,0 +1,63 @@ +//go:build ignore +// +build ignore + +package usage + +import ( + "net/http" + + "github.com/gin-gonic/gin" + "gorm.io/gorm" + "stream.api/internal/database/model" + "stream.api/pkg/logger" + "stream.api/pkg/response" +) + +type Handler struct { + logger logger.Logger + db *gorm.DB +} + +func NewHandler(l logger.Logger, db *gorm.DB) UsageHandler { + return &Handler{ + logger: l, + db: db, + } +} + +// @Summary Get Usage +// @Description Get the authenticated user's total video count and total storage usage +// @Tags usage +// @Produce json +// @Success 200 {object} response.Response{data=UsagePayload} +// @Failure 401 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /usage [get] +// @Security BearerAuth +func (h *Handler) GetUsage(c *gin.Context) { + userID := c.GetString("userID") + if userID == "" { + response.Error(c, http.StatusUnauthorized, "Unauthorized") + return + } + + user, ok := c.Get("user") + if !ok { + response.Error(c, http.StatusUnauthorized, "Unauthorized") + return + } + + currentUser, ok := user.(*model.User) + if !ok || currentUser == nil || currentUser.ID != userID { + response.Error(c, http.StatusUnauthorized, "Unauthorized") + return + } + + payload, err := LoadUsage(c.Request.Context(), h.db, h.logger, currentUser) + if err != nil { + response.Error(c, http.StatusInternalServerError, "Failed to load usage") + return + } + + response.Success(c, payload) +} diff --git a/internal/api/usage/interface.go b/internal/api/usage/interface.go new file mode 100644 index 0000000..a060142 --- /dev/null +++ b/internal/api/usage/interface.go @@ -0,0 +1,11 @@ +//go:build ignore +// +build ignore + +package usage + +import "github.com/gin-gonic/gin" + +// UsageHandler defines the interface for usage operations +type UsageHandler interface { + GetUsage(c *gin.Context) +} diff --git a/internal/api/usage/service.go b/internal/api/usage/service.go new file mode 100644 index 0000000..3727454 --- /dev/null +++ b/internal/api/usage/service.go @@ -0,0 +1,23 @@ +package usage + +import ( + "context" + + "gorm.io/gorm" + "stream.api/internal/database/model" + "stream.api/pkg/logger" +) + +func LoadUsage(ctx context.Context, db *gorm.DB, l logger.Logger, user *model.User) (*UsagePayload, error) { + var totalVideos int64 + if err := db.WithContext(ctx).Model(&model.Video{}).Where("user_id = ?", user.ID).Count(&totalVideos).Error; err != nil { + l.Error("Failed to count user videos", "error", err, "user_id", user.ID) + return nil, err + } + + return &UsagePayload{ + UserID: user.ID, + TotalVideos: totalVideos, + TotalStorage: user.StorageUsed, + }, nil +} diff --git a/internal/api/usage/types.go b/internal/api/usage/types.go new file mode 100644 index 0000000..b5c2e2d --- /dev/null +++ b/internal/api/usage/types.go @@ -0,0 +1,7 @@ +package usage + +type UsagePayload struct { + UserID string `json:"user_id"` + TotalVideos int64 `json:"total_videos"` + TotalStorage int64 `json:"total_storage"` +} diff --git a/internal/api/video/handler.go b/internal/api/video/handler.go index c4109c7..c8e98db 100644 --- a/internal/api/video/handler.go +++ b/internal/api/video/handler.go @@ -1,16 +1,22 @@ +//go:build ignore +// +build ignore + package video import ( + "errors" "fmt" "net/http" + "net/url" "strconv" + "strings" "time" "github.com/gin-gonic/gin" "github.com/google/uuid" + "gorm.io/gorm" "stream.api/internal/config" "stream.api/internal/database/model" - "stream.api/internal/database/query" "stream.api/pkg/logger" "stream.api/pkg/response" "stream.api/pkg/storage" @@ -19,13 +25,22 @@ import ( type Handler struct { logger logger.Logger cfg *config.Config + db *gorm.DB storage storage.Provider } -func NewHandler(l logger.Logger, cfg *config.Config, s storage.Provider) VideoHandler { +type videoError struct { + Code int + Message string +} + +func (e *videoError) Error() string { return e.Message } + +func NewHandler(l logger.Logger, cfg *config.Config, db *gorm.DB, s storage.Provider) VideoHandler { return &Handler{ logger: l, cfg: cfg, + db: db, storage: s, } } @@ -62,7 +77,7 @@ func (h *Handler) GetUploadURL(c *gin.Context) { response.Success(c, gin.H{ "upload_url": url, "key": key, - "file_id": fileID, // Temporary ID, actual video record ID might differ or be same + "file_id": fileID, }) } @@ -85,38 +100,83 @@ func (h *Handler) CreateVideo(c *gin.Context) { } userID := c.GetString("userID") - - status := "PUBLIC" - storageType := "S3" - - video := &model.Video{ - ID: uuid.New().String(), - UserID: userID, - Name: req.Title, - Title: req.Title, - Description: &req.Description, - URL: req.URL, - Size: req.Size, - Duration: req.Duration, - Format: req.Format, - Status: &status, - StorageType: &storageType, + if userID == "" { + response.Error(c, http.StatusUnauthorized, "Unauthorized") + return } - q := query.Q - err := q.Transaction(func(tx *query.Query) error { - if err := tx.Video.WithContext(c.Request.Context()).Create(video); err != nil { + title := strings.TrimSpace(req.Title) + if title == "" { + response.Error(c, http.StatusBadRequest, "Title is required") + return + } + + videoURL := strings.TrimSpace(req.URL) + if videoURL == "" { + response.Error(c, http.StatusBadRequest, "URL is required") + return + } + + status := "ready" + processingStatus := "READY" + storageType := detectStorageType(videoURL) + description := strings.TrimSpace(req.Description) + format := strings.TrimSpace(req.Format) + + video := &model.Video{ + ID: uuid.New().String(), + UserID: userID, + Name: title, + Title: title, + Description: stringPointer(description), + URL: videoURL, + Size: req.Size, + Duration: req.Duration, + Format: format, + Status: &status, + ProcessingStatus: &processingStatus, + StorageType: &storageType, + } + + err := h.db.WithContext(c.Request.Context()).Transaction(func(tx *gorm.DB) error { + var defaultTemplate model.AdTemplate + hasDefaultTemplate := false + + if err := tx.Where("user_id = ? AND is_default = ? AND is_active = ?", userID, true, true). + Order("updated_at DESC"). + First(&defaultTemplate).Error; err != nil { + if !errors.Is(err, gorm.ErrRecordNotFound) { + return err + } + } else { + hasDefaultTemplate = true + } + + if err := tx.Create(video).Error; err != nil { return err } - // Atomic update: StorageUsed = StorageUsed + video.Size - // We use UpdateSimple with Add to ensure atomicity at database level: UPDATE users SET storage_used = storage_used + ? - if _, err := tx.User.WithContext(c.Request.Context()). - Where(tx.User.ID.Eq(userID)). - UpdateSimple(tx.User.StorageUsed.Add(video.Size)); err != nil { + if err := tx.Model(&model.User{}). + Where("id = ?", userID). + UpdateColumn("storage_used", gorm.Expr("storage_used + ?", video.Size)).Error; err != nil { return err } + if hasDefaultTemplate { + videoAdConfig := &model.VideoAdConfig{ + VideoID: video.ID, + UserID: userID, + AdTemplateID: defaultTemplate.ID, + VastTagURL: defaultTemplate.VastTagURL, + AdFormat: defaultTemplate.AdFormat, + Duration: defaultTemplate.Duration, + } + + if err := tx.Create(videoAdConfig).Error; err != nil { + return err + } + } + return nil }) @@ -140,17 +200,46 @@ func (h *Handler) CreateVideo(c *gin.Context) { // @Router /videos [get] // @Security BearerAuth func (h *Handler) ListVideos(c *gin.Context) { + userID := c.GetString("userID") + if userID == "" { + response.Error(c, http.StatusUnauthorized, "Unauthorized") + return + } + page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) + if page < 1 { + page = 1 + } limit, _ := strconv.Atoi(c.DefaultQuery("limit", "10")) + if limit <= 0 { + limit = 10 + } + if limit > 100 { + limit = 100 + } offset := (page - 1) * limit - v := query.Video - videos, count, err := v.WithContext(c.Request.Context()). - Where(v.Status.Eq("PUBLIC")). - Order(v.CreatedAt.Desc()). - FindByPage(offset, limit) + search := strings.TrimSpace(c.Query("search")) + status := strings.TrimSpace(c.Query("status")) - if err != nil { + db := h.db.WithContext(c.Request.Context()).Model(&model.Video{}).Where("user_id = ?", userID) + if search != "" { + like := "%" + search + "%" + db = db.Where("title ILIKE ? OR description ILIKE ?", like, like) + } + if status != "" && !strings.EqualFold(status, "all") { + db = db.Where("status = ?", normalizeVideoStatus(status)) + } + + var total int64 + if err := db.Count(&total).Error; err != nil { + h.logger.Error("Failed to count videos", "error", err) + response.Error(c, http.StatusInternalServerError, "Failed to fetch videos") + return + } + + var videos []*model.Video + if err := db.Order("created_at DESC").Offset(offset).Limit(limit).Find(&videos).Error; err != nil { h.logger.Error("Failed to fetch videos", "error", err) response.Error(c, http.StatusInternalServerError, "Failed to fetch videos") return @@ -158,7 +247,7 @@ func (h *Handler) ListVideos(c *gin.Context) { response.Success(c, gin.H{ "videos": videos, - "total": count, + "total": total, "page": page, "limit": limit, }) @@ -174,19 +263,321 @@ func (h *Handler) ListVideos(c *gin.Context) { // @Router /videos/{id} [get] // @Security BearerAuth func (h *Handler) GetVideo(c *gin.Context) { - id := c.Param("id") - v := query.Video - - // Atomically increment views: UPDATE videos SET views = views + 1 WHERE id = ? - // We intentionally ignore errors here (like record not found) because the subsequent fetch will handle 404s, - // and we don't want to fail the read if writing the view count fails for some transient reason. - v.WithContext(c.Request.Context()).Where(v.ID.Eq(id)).UpdateSimple(v.Views.Add(1)) - - video, err := v.WithContext(c.Request.Context()).Where(v.ID.Eq(id)).First() - if err != nil { - response.Error(c, http.StatusNotFound, "Video not found") + userID := c.GetString("userID") + if userID == "" { + response.Error(c, http.StatusUnauthorized, "Unauthorized") return } - response.Success(c, gin.H{"video": video}) + id := c.Param("id") + + h.db.WithContext(c.Request.Context()).Model(&model.Video{}). + Where("id = ? AND user_id = ?", id, userID). + UpdateColumn("views", gorm.Expr("views + ?", 1)) + + var video model.Video + if err := h.db.WithContext(c.Request.Context()).Where("id = ? AND user_id = ?", id, userID).First(&video).Error; err != nil { + if err == gorm.ErrRecordNotFound { + response.Error(c, http.StatusNotFound, "Video not found") + return + } + h.logger.Error("Failed to fetch video", "error", err) + response.Error(c, http.StatusInternalServerError, "Failed to fetch video") + return + } + + result := gin.H{"video": &video} + + var adConfig model.VideoAdConfig + if err := h.db.WithContext(c.Request.Context()). + Where("video_id = ? AND user_id = ?", id, userID). + First(&adConfig).Error; err == nil { + adPayload := VideoAdConfigPayload{ + AdTemplateID: adConfig.AdTemplateID, + VASTTagURL: adConfig.VastTagURL, + AdFormat: model.StringValue(adConfig.AdFormat), + Duration: int64PtrToIntPtr(adConfig.Duration), + } + + var template model.AdTemplate + if err := h.db.WithContext(c.Request.Context()). + Where("id = ? AND user_id = ?", adConfig.AdTemplateID, userID). + First(&template).Error; err == nil { + adPayload.TemplateName = template.Name + } + + result["ad_config"] = adPayload + } + + response.Success(c, result) +} + +// @Summary Update Video +// @Description Update title and description for a video owned by the current user +// @Tags video +// @Accept json +// @Produce json +// @Param id path string true "Video ID" +// @Param request body UpdateVideoRequest true "Video payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 404 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /videos/{id} [put] +// @Security BearerAuth +func (h *Handler) UpdateVideo(c *gin.Context) { + userID := c.GetString("userID") + if userID == "" { + response.Error(c, http.StatusUnauthorized, "Unauthorized") + return + } + + id := c.Param("id") + var req UpdateVideoRequest + if err := c.ShouldBindJSON(&req); err != nil { + response.Error(c, http.StatusBadRequest, err.Error()) + return + } + + title := strings.TrimSpace(req.Title) + if title == "" { + response.Error(c, http.StatusBadRequest, "Title is required") + return + } + description := strings.TrimSpace(req.Description) + ctx := c.Request.Context() + + err := h.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + result := tx.Model(&model.Video{}). + Where("id = ? AND user_id = ?", id, userID). + Updates(map[string]interface{}{ + "name": title, + "title": title, + "description": stringPointer(description), + }) + if result.Error != nil { + return result.Error + } + if result.RowsAffected == 0 { + return gorm.ErrRecordNotFound + } + + if req.AdTemplateID != nil { + templateID := strings.TrimSpace(*req.AdTemplateID) + + if templateID == "" { + tx.Where("video_id = ? AND user_id = ?", id, userID).Delete(&model.VideoAdConfig{}) + } else { + var template model.AdTemplate + if err := tx.Where("id = ? AND user_id = ?", templateID, userID). + First(&template).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return &videoError{Code: http.StatusBadRequest, Message: "Ad template not found"} + } + return err + } + + var existing model.VideoAdConfig + if err := tx.Where("video_id = ? AND user_id = ?", id, userID). + First(&existing).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + newConfig := &model.VideoAdConfig{ + VideoID: id, + UserID: userID, + AdTemplateID: template.ID, + VastTagURL: template.VastTagURL, + AdFormat: template.AdFormat, + Duration: template.Duration, + } + return tx.Create(newConfig).Error + } + return err + } + + existing.AdTemplateID = template.ID + existing.VastTagURL = template.VastTagURL + existing.AdFormat = template.AdFormat + existing.Duration = template.Duration + return tx.Save(&existing).Error + } + } + + return nil + }) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + response.Error(c, http.StatusNotFound, "Video not found") + return + } + var ve *videoError + if errors.As(err, &ve) { + response.Error(c, ve.Code, ve.Message) + return + } + h.logger.Error("Failed to update video", "error", err) + response.Error(c, http.StatusInternalServerError, "Failed to update video") + return + } + + var video model.Video + if err := h.db.WithContext(ctx).Where("id = ? AND user_id = ?", id, userID).First(&video).Error; err != nil { + h.logger.Error("Failed to reload video", "error", err) + response.Error(c, http.StatusInternalServerError, "Failed to update video") + return + } + + resp := gin.H{"video": &video} + + var adConfig model.VideoAdConfig + if err := h.db.WithContext(ctx). + Where("video_id = ? AND user_id = ?", id, userID). + First(&adConfig).Error; err == nil { + adPayload := VideoAdConfigPayload{ + AdTemplateID: adConfig.AdTemplateID, + VASTTagURL: adConfig.VastTagURL, + AdFormat: model.StringValue(adConfig.AdFormat), + Duration: int64PtrToIntPtr(adConfig.Duration), + } + + var template model.AdTemplate + if err := h.db.WithContext(ctx). + Where("id = ? AND user_id = ?", adConfig.AdTemplateID, userID). + First(&template).Error; err == nil { + adPayload.TemplateName = template.Name + } + + resp["ad_config"] = adPayload + } + + response.Success(c, resp) +} + +// @Summary Delete Video +// @Description Delete a video owned by the current user +// @Tags video +// @Produce json +// @Param id path string true "Video ID" +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 404 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /videos/{id} [delete] +// @Security BearerAuth +func (h *Handler) DeleteVideo(c *gin.Context) { + userID := c.GetString("userID") + if userID == "" { + response.Error(c, http.StatusUnauthorized, "Unauthorized") + return + } + + id := c.Param("id") + var video model.Video + if err := h.db.WithContext(c.Request.Context()).Where("id = ? AND user_id = ?", id, userID).First(&video).Error; err != nil { + if err == gorm.ErrRecordNotFound { + response.Error(c, http.StatusNotFound, "Video not found") + return + } + h.logger.Error("Failed to load video for deletion", "error", err) + response.Error(c, http.StatusInternalServerError, "Failed to delete video") + return + } + + if h.storage != nil && shouldDeleteStoredObject(video.URL) { + if err := h.storage.Delete(video.URL); err != nil { + parsedKey := extractObjectKey(video.URL) + if parsedKey != "" && parsedKey != video.URL { + if deleteErr := h.storage.Delete(parsedKey); deleteErr != nil { + h.logger.Error("Failed to delete video object", "error", deleteErr, "video_id", video.ID) + response.Error(c, http.StatusInternalServerError, "Failed to delete video") + return + } + } else { + h.logger.Error("Failed to delete video object", "error", err, "video_id", video.ID) + response.Error(c, http.StatusInternalServerError, "Failed to delete video") + return + } + } + } + + if err := h.db.WithContext(c.Request.Context()).Transaction(func(tx *gorm.DB) error { + if err := tx.Where("video_id = ? AND user_id = ?", video.ID, userID).Delete(&model.VideoAdConfig{}).Error; err != nil { + return err + } + if err := tx.Where("id = ? AND user_id = ?", video.ID, userID).Delete(&model.Video{}).Error; err != nil { + return err + } + if err := tx.Model(&model.User{}). + Where("id = ?", userID). + UpdateColumn("storage_used", gorm.Expr("storage_used - ?", video.Size)).Error; err != nil { + return err + } + return nil + }); err != nil { + h.logger.Error("Failed to delete video record", "error", err) + response.Error(c, http.StatusInternalServerError, "Failed to delete video") + return + } + + response.Success(c, gin.H{"message": "Video deleted successfully"}) +} + +func normalizeVideoStatus(value string) string { + switch strings.ToLower(strings.TrimSpace(value)) { + case "processing", "pending": + return "processing" + case "failed", "error": + return "failed" + default: + return "ready" + } +} + +func detectStorageType(rawURL string) string { + if shouldDeleteStoredObject(rawURL) { + return "S3" + } + return "WORKER" +} + +func shouldDeleteStoredObject(rawURL string) bool { + trimmed := strings.TrimSpace(rawURL) + if trimmed == "" { + return false + } + parsed, err := url.Parse(trimmed) + if err != nil { + return !strings.HasPrefix(trimmed, "/") + } + return parsed.Scheme == "" && parsed.Host == "" && !strings.HasPrefix(trimmed, "/") +} + +func extractObjectKey(rawURL string) string { + trimmed := strings.TrimSpace(rawURL) + if trimmed == "" { + return "" + } + parsed, err := url.Parse(trimmed) + if err != nil { + return trimmed + } + if parsed.Scheme == "" && parsed.Host == "" { + return strings.TrimPrefix(parsed.Path, "/") + } + return strings.TrimPrefix(parsed.Path, "/") +} + +func stringPointer(value string) *string { + if value == "" { + return nil + } + return &value +} + +func int64PtrToIntPtr(value *int64) *int { + if value == nil { + return nil + } + converted := int(*value) + return &converted } diff --git a/internal/api/video/interface.go b/internal/api/video/interface.go index 9d16db6..4de5f16 100644 --- a/internal/api/video/interface.go +++ b/internal/api/video/interface.go @@ -1,3 +1,6 @@ +//go:build ignore +// +build ignore + package video import "github.com/gin-gonic/gin" @@ -8,6 +11,8 @@ type VideoHandler interface { CreateVideo(c *gin.Context) ListVideos(c *gin.Context) GetVideo(c *gin.Context) + UpdateVideo(c *gin.Context) + DeleteVideo(c *gin.Context) } // UploadURLRequest defines the payload for requesting an upload URL @@ -26,3 +31,17 @@ type CreateVideoRequest struct { Duration int32 `json:"duration"` // Maybe client knows, or we process later Format string `json:"format"` } + +type UpdateVideoRequest struct { + Title string `json:"title" binding:"required"` + Description string `json:"description"` + AdTemplateID *string `json:"ad_template_id,omitempty"` +} + +type VideoAdConfigPayload struct { + AdTemplateID string `json:"ad_template_id"` + TemplateName string `json:"template_name,omitempty"` + VASTTagURL string `json:"vast_tag_url,omitempty"` + AdFormat string `json:"ad_format,omitempty"` + Duration *int `json:"duration,omitempty"` +} diff --git a/internal/app/app.go b/internal/app/app.go index 3907ce9..3bb22ab 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -1,29 +1,38 @@ +//go:build ignore +// +build ignore + package app import ( + "context" "net/http" "github.com/gin-contrib/cors" "github.com/gin-gonic/gin" "gorm.io/gorm" + "stream.api/internal/api/admin" + "stream.api/internal/api/adtemplates" "stream.api/internal/api/auth" + "stream.api/internal/api/domains" + "stream.api/internal/api/notifications" "stream.api/internal/api/payment" "stream.api/internal/api/plan" + "stream.api/internal/api/preferences" + "stream.api/internal/api/usage" "stream.api/internal/api/video" "stream.api/internal/config" "stream.api/internal/middleware" + videoruntime "stream.api/internal/video/runtime" "stream.api/pkg/cache" "stream.api/pkg/logger" - "stream.api/pkg/response" "stream.api/pkg/storage" "stream.api/pkg/token" swaggerFiles "github.com/swaggo/files" ginSwagger "github.com/swaggo/gin-swagger" - _ "stream.api/docs" ) -func SetupRouter(cfg *config.Config, db *gorm.DB, c cache.Cache, t token.Provider, l logger.Logger) *gin.Engine { +func SetupRouter(cfg *config.Config, db *gorm.DB, c cache.Cache, t token.Provider, l logger.Logger) (*gin.Engine, *videoruntime.Module, error) { if cfg.Server.Mode == "release" { gin.SetMode(gin.ReleaseMode) } @@ -36,7 +45,7 @@ func SetupRouter(cfg *config.Config, db *gorm.DB, c cache.Cache, t token.Provide r.Use(middleware.ErrorHandler()) // Handle c.Errors // CORS Middleware r.Use(cors.New(cors.Config{ - AllowOrigins: []string{"http://localhost:5173", "http://localhost:8080"}, + AllowOrigins: cfg.CORS.AllowOrigins, AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}, AllowHeaders: []string{"Origin", "Authorization", "Content-Type"}, ExposeHeaders: []string{"Content-Length"}, @@ -57,7 +66,7 @@ func SetupRouter(cfg *config.Config, db *gorm.DB, c cache.Cache, t token.Provide }) // Auth Handler - authHandler := auth.NewHandler(c, t, l, cfg) + authHandler := auth.NewHandler(c, t, l, cfg, db) // api := r.Group("/v") authGroup := r.Group("/auth") { @@ -70,7 +79,7 @@ func SetupRouter(cfg *config.Config, db *gorm.DB, c cache.Cache, t token.Provide } // Auth Middleware - authMiddleware := middleware.NewAuthMiddleware(c, t, cfg) + authMiddleware := middleware.NewAuthMiddleware(c, t, cfg, db, l) // Init Storage Provider (S3) s3Provider, err := storage.NewS3Provider(cfg) @@ -81,22 +90,47 @@ func SetupRouter(cfg *config.Config, db *gorm.DB, c cache.Cache, t token.Provide } // Handlers - planHandler := plan.NewHandler(l, cfg) - paymentHandler := payment.NewHandler(l, cfg) - videoHandler := video.NewHandler(l, cfg, s3Provider) + planHandler := plan.NewHandler(l, cfg, db) + paymentHandler := payment.NewHandler(l, cfg, db) + usageHandler := usage.NewHandler(l, db) + videoHandler := video.NewHandler(l, cfg, db, s3Provider) + preferencesHandler := preferences.NewHandler(l, db) + notificationHandler := notifications.NewHandler(l, db) + domainHandler := domains.NewHandler(l, db) + adTemplateHandler := adtemplates.NewHandler(l, db) // Example protected group protected := r.Group("") protected.Use(authMiddleware.Handle()) { - protected.GET("/me", func(c *gin.Context) { - user, _ := c.Get("user") - response.Success(c, gin.H{"user": user}) - // c.JSON(http.StatusOK, gin.H{ - // "user": user, - // }) - }) + protected.GET("/me", authHandler.GetMe) + protected.PUT("/me", authHandler.UpdateMe) + protected.DELETE("/me", authHandler.DeleteMe) + protected.POST("/me/clear-data", authHandler.ClearMyData) protected.POST("/auth/logout", authHandler.Logout) + protected.POST("/auth/change-password", authHandler.ChangePassword) + + preferences := protected.Group("/settings/preferences") + preferences.GET("", preferencesHandler.GetPreferences) + preferences.PUT("", preferencesHandler.UpdatePreferences) + + notifications := protected.Group("/notifications") + notifications.GET("", notificationHandler.ListNotifications) + notifications.POST("/:id/read", notificationHandler.MarkRead) + notifications.POST("/read-all", notificationHandler.MarkAllRead) + notifications.DELETE("/:id", notificationHandler.DeleteNotification) + notifications.DELETE("", notificationHandler.ClearNotifications) + + domains := protected.Group("/domains") + domains.GET("", domainHandler.ListDomains) + domains.POST("", domainHandler.CreateDomain) + domains.DELETE("/:id", domainHandler.DeleteDomain) + + adTemplates := protected.Group("/ad-templates") + adTemplates.GET("", adTemplateHandler.ListTemplates) + adTemplates.POST("", adTemplateHandler.CreateTemplate) + adTemplates.PUT("/:id", adTemplateHandler.UpdateTemplate) + adTemplates.DELETE("/:id", adTemplateHandler.DeleteTemplate) // Plans plans := protected.Group("/plans") @@ -105,6 +139,12 @@ func SetupRouter(cfg *config.Config, db *gorm.DB, c cache.Cache, t token.Provide // Payments payments := protected.Group("/payments") payments.POST("", paymentHandler.CreatePayment) + payments.GET("/history", paymentHandler.ListPaymentHistory) + payments.GET("/:id/invoice", paymentHandler.DownloadInvoice) + wallet := protected.Group("/wallet") + wallet.POST("/topups", paymentHandler.TopupWallet) + + protected.GET("/usage", usageHandler.GetUsage) // Videos video := protected.Group("/videos") @@ -112,7 +152,68 @@ func SetupRouter(cfg *config.Config, db *gorm.DB, c cache.Cache, t token.Provide video.POST("", videoHandler.CreateVideo) video.GET("", videoHandler.ListVideos) video.GET("/:id", videoHandler.GetVideo) + video.PUT("/:id", videoHandler.UpdateVideo) + video.DELETE("/:id", videoHandler.DeleteVideo) } - return r + renderModule, err := videoruntime.NewModule(context.Background(), cfg, db, c, t, l) + if err != nil { + return nil, nil, err + } + + r.Use(videoruntime.MetricsMiddleware()) + r.GET("/health/live", renderModule.HandleLive) + r.GET("/health/ready", renderModule.HandleReady) + r.GET("/health/detailed", renderModule.HandleDetailed) + r.GET("/metrics", renderModule.MetricsHandler()) + + // Admin routes — require auth + admin role + adminHandler := admin.NewHandler(l, db, renderModule) + adminGroup := r.Group("/admin") + adminGroup.Use(authMiddleware.Handle()) + adminGroup.Use(middleware.RequireAdmin()) + { + adminGroup.GET("/dashboard", adminHandler.Dashboard) + + adminGroup.GET("/users", adminHandler.ListUsers) + adminGroup.POST("/users", adminHandler.CreateUser) + adminGroup.GET("/users/:id", adminHandler.GetUser) + adminGroup.PUT("/users/:id", adminHandler.UpdateUser) + adminGroup.PUT("/users/:id/role", adminHandler.UpdateUserRole) + adminGroup.DELETE("/users/:id", adminHandler.DeleteUser) + + adminGroup.GET("/videos", adminHandler.ListVideos) + adminGroup.POST("/videos", adminHandler.CreateVideo) + adminGroup.GET("/videos/:id", adminHandler.GetVideo) + adminGroup.PUT("/videos/:id", adminHandler.UpdateVideo) + adminGroup.DELETE("/videos/:id", adminHandler.DeleteVideo) + + adminGroup.GET("/payments", adminHandler.ListPayments) + adminGroup.POST("/payments", adminHandler.CreatePayment) + adminGroup.GET("/payments/:id", adminHandler.GetPayment) + adminGroup.PUT("/payments/:id", adminHandler.UpdatePayment) + + adminGroup.GET("/plans", adminHandler.ListPlans) + adminGroup.POST("/plans", adminHandler.CreatePlan) + adminGroup.PUT("/plans/:id", adminHandler.UpdatePlan) + adminGroup.DELETE("/plans/:id", adminHandler.DeletePlan) + + adminGroup.GET("/ad-templates", adminHandler.ListAdTemplates) + adminGroup.POST("/ad-templates", adminHandler.CreateAdTemplate) + adminGroup.GET("/ad-templates/:id", adminHandler.GetAdTemplate) + adminGroup.PUT("/ad-templates/:id", adminHandler.UpdateAdTemplate) + adminGroup.DELETE("/ad-templates/:id", adminHandler.DeleteAdTemplate) + + adminGroup.GET("/jobs", adminHandler.ListJobs) + adminGroup.POST("/jobs", adminHandler.CreateJob) + adminGroup.GET("/jobs/:id", adminHandler.GetJob) + adminGroup.GET("/jobs/:id/logs", adminHandler.GetJobLogs) + adminGroup.POST("/jobs/:id/cancel", adminHandler.CancelJob) + adminGroup.POST("/jobs/:id/retry", adminHandler.RetryJob) + adminGroup.GET("/agents", adminHandler.ListAgents) + adminGroup.POST("/agents/:id/restart", adminHandler.RestartAgent) + adminGroup.POST("/agents/:id/update", adminHandler.UpdateAgent) + } + + return r, renderModule, nil } diff --git a/internal/config/config.go b/internal/config/config.go index 3411682..2e3304a 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -12,13 +12,30 @@ type Config struct { Redis RedisConfig JWT JWTConfig Google GoogleConfig + Frontend FrontendConfig + CORS CORSConfig Email EmailConfig AWS AWSConfig + Render RenderConfig + Internal InternalAuthConfig } type ServerConfig struct { - Port string `mapstructure:"port"` - Mode string `mapstructure:"mode"` // e.g., "debug", "release" + Port string `mapstructure:"port"` + GRPCPort string `mapstructure:"grpc_port"` + Mode string `mapstructure:"mode"` // e.g., "debug", "release" +} + +type RenderConfig struct { + AgentSecret string `mapstructure:"agent_secret"` + EnableMetrics bool `mapstructure:"enable_metrics"` + EnableTracing bool `mapstructure:"enable_tracing"` + OTLPEndpoint string `mapstructure:"otlp_endpoint"` + ServiceName string `mapstructure:"service_name"` +} + +type InternalAuthConfig struct { + Marker string `mapstructure:"marker"` } type DatabaseConfig struct { @@ -36,9 +53,19 @@ type JWTConfig struct { } type GoogleConfig struct { - ClientID string `mapstructure:"client_id"` - ClientSecret string `mapstructure:"client_secret"` - RedirectURL string `mapstructure:"redirect_url"` + ClientID string `mapstructure:"client_id"` + ClientSecret string `mapstructure:"client_secret"` + RedirectURL string `mapstructure:"redirect_url"` + StateTTLMinute int `mapstructure:"state_ttl_minutes"` +} + +type FrontendConfig struct { + BaseURL string `mapstructure:"base_url"` + GoogleAuthFinalizePath string `mapstructure:"google_auth_finalize_path"` +} + +type CORSConfig struct { + AllowOrigins []string `mapstructure:"allow_origins"` } type EmailConfig struct { @@ -60,8 +87,16 @@ func LoadConfig() (*Config, error) { // Set defaults v.SetDefault("server.port", "8080") + v.SetDefault("server.grpc_port", "9000") v.SetDefault("server.mode", "debug") v.SetDefault("redis.db", 0) + v.SetDefault("render.enable_metrics", true) + v.SetDefault("render.enable_tracing", false) + v.SetDefault("render.service_name", "stream-api-render") + v.SetDefault("google.state_ttl_minutes", 10) + v.SetDefault("frontend.google_auth_finalize_path", "/auth/google/finalize") + v.SetDefault("internal.marker", "") + v.SetDefault("cors.allow_origins", []string{"http://localhost:5173", "http://localhost:8080", "http://localhost:8081"}) // Environment variable settings v.SetEnvPrefix("APP") diff --git a/internal/database/model/ad_templates.gen.go b/internal/database/model/ad_templates.gen.go new file mode 100644 index 0000000..69c28a0 --- /dev/null +++ b/internal/database/model/ad_templates.gen.go @@ -0,0 +1,32 @@ +// 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 TableNameAdTemplate = "ad_templates" + +// AdTemplate mapped from table +type AdTemplate struct { + ID string `gorm:"column:id;type:uuid;primaryKey" json:"id"` + UserID string `gorm:"column:user_id;type:uuid;not null;index:idx_ad_templates_user_id,priority:1" json:"user_id"` + Name string `gorm:"column:name;type:text;not null" json:"name"` + Description *string `gorm:"column:description;type:text" json:"description"` + VastTagURL string `gorm:"column:vast_tag_url;type:text;not null" json:"vast_tag_url"` + AdFormat *string `gorm:"column:ad_format;type:character varying(50);not null;default:pre-roll" json:"ad_format"` + Duration *int64 `gorm:"column:duration;type:bigint" json:"duration"` + IsActive *bool `gorm:"column:is_active;type:boolean;not null;default:true" json:"is_active"` + CreatedAt *time.Time `gorm:"column:created_at;type:timestamp with time zone" json:"created_at"` + UpdatedAt *time.Time `gorm:"column:updated_at;type:timestamp with time zone" json:"updated_at"` + IsDefault bool `gorm:"column:is_default;type:boolean;not null" json:"is_default"` + Version *int64 `gorm:"column:version;type:bigint;not null;default:1;version" json:"-"` +} + +// TableName AdTemplate's table name +func (*AdTemplate) TableName() string { + return TableNameAdTemplate +} diff --git a/internal/database/model/domains.gen.go b/internal/database/model/domains.gen.go new file mode 100644 index 0000000..b4e84f5 --- /dev/null +++ b/internal/database/model/domains.gen.go @@ -0,0 +1,26 @@ +// 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 TableNameDomain = "domains" + +// Domain mapped from table +type Domain struct { + ID string `gorm:"column:id;type:uuid;primaryKey" json:"id"` + UserID string `gorm:"column:user_id;type:uuid;not null;index:idx_domains_user_id,priority:1" json:"user_id"` + Name string `gorm:"column:name;type:text;not null" json:"name"` + CreatedAt *time.Time `gorm:"column:created_at;type:timestamp with time zone" 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 Domain's table name +func (*Domain) TableName() string { + return TableNameDomain +} diff --git a/internal/database/model/helpers.go b/internal/database/model/helpers.go new file mode 100644 index 0000000..8815fa6 --- /dev/null +++ b/internal/database/model/helpers.go @@ -0,0 +1,138 @@ +package model + +import ( + "context" + "errors" + "strings" + "time" + + "gorm.io/gorm" +) + +const ( + defaultPreferenceLanguage = "en" + defaultPreferenceLocale = "en" +) + +func DefaultUserPreference(userID string) *UserPreference { + return &UserPreference{ + UserID: userID, + Language: StringPtr(defaultPreferenceLanguage), + Locale: StringPtr(defaultPreferenceLocale), + EmailNotifications: BoolPtr(true), + PushNotifications: BoolPtr(true), + MarketingNotifications: false, + TelegramNotifications: false, + Autoplay: false, + Loop: false, + Muted: false, + ShowControls: BoolPtr(true), + Pip: BoolPtr(true), + Airplay: BoolPtr(true), + Chromecast: BoolPtr(true), + } +} + +func FindOrCreateUserPreference(ctx context.Context, db *gorm.DB, userID string) (*UserPreference, error) { + var pref UserPreference + if err := db.WithContext(ctx).Where("user_id = ?", userID).First(&pref).Error; err == nil { + normalizeUserPreferenceDefaults(&pref) + return &pref, nil + } else if !errors.Is(err, gorm.ErrRecordNotFound) { + return nil, err + } + + pref = *DefaultUserPreference(userID) + if err := db.WithContext(ctx).Create(&pref).Error; err != nil { + return nil, err + } + return &pref, nil +} + +func GetWalletBalance(ctx context.Context, db *gorm.DB, userID string) (float64, error) { + var balance float64 + if err := db.WithContext(ctx). + Model(&WalletTransaction{}). + Where("user_id = ?", userID). + Select("COALESCE(SUM(amount), 0)"). + Scan(&balance).Error; err != nil { + return 0, err + } + return balance, nil +} + +func GetLatestPlanSubscription(ctx context.Context, db *gorm.DB, userID string) (*PlanSubscription, error) { + userID = strings.TrimSpace(userID) + if userID == "" { + return nil, gorm.ErrRecordNotFound + } + + var subscription PlanSubscription + if err := db.WithContext(ctx). + Where("user_id = ?", userID). + Order("created_at DESC"). + Order("id DESC"). + First(&subscription).Error; err != nil { + return nil, err + } + + return &subscription, nil +} + +func IsSubscriptionExpiringSoon(expiresAt time.Time, now time.Time) bool { + if expiresAt.IsZero() || !expiresAt.After(now) { + return false + } + return expiresAt.Sub(now) <= 7*24*time.Hour +} + +func normalizeUserPreferenceDefaults(pref *UserPreference) { + if pref == nil { + return + } + if strings.TrimSpace(StringValue(pref.Language)) == "" { + pref.Language = StringPtr(defaultPreferenceLanguage) + } + if strings.TrimSpace(StringValue(pref.Locale)) == "" { + locale := StringValue(pref.Language) + if strings.TrimSpace(locale) == "" { + locale = defaultPreferenceLocale + } + pref.Locale = StringPtr(locale) + } + if pref.EmailNotifications == nil { + pref.EmailNotifications = BoolPtr(true) + } + if pref.PushNotifications == nil { + pref.PushNotifications = BoolPtr(true) + } + if pref.ShowControls == nil { + pref.ShowControls = BoolPtr(true) + } + if pref.Pip == nil { + pref.Pip = BoolPtr(true) + } + if pref.Airplay == nil { + pref.Airplay = BoolPtr(true) + } + if pref.Chromecast == nil { + pref.Chromecast = BoolPtr(true) + } +} + +func StringPtr(value string) *string { + v := value + return &v +} + +func BoolPtr(value bool) *bool { + v := value + return &v +} + +func StringValue(value *string) string { + if value == nil { + return "" + } + return *value +} diff --git a/internal/database/model/jobs.gen.go b/internal/database/model/jobs.gen.go new file mode 100644 index 0000000..6c1b29f --- /dev/null +++ b/internal/database/model/jobs.gen.go @@ -0,0 +1,37 @@ +// 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 TableNameJob = "jobs" + +// Job mapped from table +type Job struct { + ID string `gorm:"column:id;type:text;primaryKey" json:"id"` + Status *string `gorm:"column:status;type:text" json:"status"` + Priority *int64 `gorm:"column:priority;type:bigint;index:idx_jobs_priority,priority:1" json:"priority"` + InputURL *string `gorm:"column:input_url;type:text" json:"input_url"` + OutputURL *string `gorm:"column:output_url;type:text" json:"output_url"` + TotalDuration *int64 `gorm:"column:total_duration;type:bigint" json:"total_duration"` + CurrentTime *int64 `gorm:"column:current_time;type:bigint" json:"current_time"` + Progress *float64 `gorm:"column:progress;type:numeric" json:"progress"` + AgentID *int64 `gorm:"column:agent_id;type:bigint" json:"agent_id"` + Logs *string `gorm:"column:logs;type:text" json:"logs"` + Config *string `gorm:"column:config;type:text" json:"config"` + Cancelled *bool `gorm:"column:cancelled;type:boolean" json:"cancelled"` + RetryCount *int64 `gorm:"column:retry_count;type:bigint" json:"retry_count"` + MaxRetries *int64 `gorm:"column:max_retries;type:bigint;default:3" json:"max_retries"` + CreatedAt *time.Time `gorm:"column:created_at;type:timestamp with time zone" 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;version" json:"-"` +} + +// TableName Job's table name +func (*Job) TableName() string { + return TableNameJob +} diff --git a/internal/database/model/notifications.gen.go b/internal/database/model/notifications.gen.go new file mode 100644 index 0000000..64169d3 --- /dev/null +++ b/internal/database/model/notifications.gen.go @@ -0,0 +1,32 @@ +// 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 TableNameNotification = "notifications" + +// Notification mapped from table +type Notification struct { + ID string `gorm:"column:id;type:uuid;primaryKey" json:"id"` + UserID string `gorm:"column:user_id;type:uuid;not null;index:idx_notifications_user_id,priority:1" json:"user_id"` + Type string `gorm:"column:type;type:character varying(50);not null" json:"type"` + Title string `gorm:"column:title;type:text;not null" json:"title"` + Message string `gorm:"column:message;type:text;not null" json:"message"` + Metadata *string `gorm:"column:metadata;type:text" json:"metadata"` + ActionURL *string `gorm:"column:action_url;type:text" json:"action_url"` + ActionLabel *string `gorm:"column:action_label;type:text" json:"action_label"` + IsRead bool `gorm:"column:is_read;type:boolean;not null" json:"is_read"` + CreatedAt *time.Time `gorm:"column:created_at;type:timestamp with time zone" 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 Notification's table name +func (*Notification) TableName() string { + return TableNameNotification +} diff --git a/internal/database/model/plan.gen.go b/internal/database/model/plan.gen.go index 56f6fb7..0d246c8 100644 --- a/internal/database/model/plan.gen.go +++ b/internal/database/model/plan.gen.go @@ -4,22 +4,26 @@ package model +import ( + "github.com/lib/pq" +) + const TableNamePlan = "plan" // Plan mapped from table type Plan struct { - ID string `gorm:"column:id;type:uuid;primaryKey;default:gen_random_uuid()" json:"id"` - Name string `gorm:"column:name;type:text;not null" json:"name"` - Description *string `gorm:"column:description;type:text" json:"description"` - Price float64 `gorm:"column:price;type:numeric(65,30);not null" json:"price"` - Cycle string `gorm:"column:cycle;type:character varying(20);not null" json:"cycle"` - StorageLimit int64 `gorm:"column:storage_limit;type:bigint;not null" json:"storage_limit"` - UploadLimit int32 `gorm:"column:upload_limit;type:integer;not null" json:"upload_limit"` - DurationLimit int32 `gorm:"column:duration_limit;type:integer;not null" json:"duration_limit"` - QualityLimit string `gorm:"column:quality_limit;type:text;not null" json:"quality_limit"` - Features *string `gorm:"column:features;type:text[]" json:"features"` - IsActive *bool `gorm:"column:is_active;type:boolean;not null;default:true" json:"is_active"` - Version *int64 `gorm:"column:version;type:bigint;not null;default:1;version" json:"-"` + ID string `gorm:"column:id;type:uuid;primaryKey;default:gen_random_uuid()" json:"id"` + Name string `gorm:"column:name;type:text;not null" json:"name"` + Description *string `gorm:"column:description;type:text" json:"description"` + Price float64 `gorm:"column:price;type:numeric(65,30);not null" json:"price"` + Cycle string `gorm:"column:cycle;type:character varying(20);not null" json:"cycle"` + StorageLimit int64 `gorm:"column:storage_limit;type:bigint;not null" json:"storage_limit"` + UploadLimit int32 `gorm:"column:upload_limit;type:integer;not null" json:"upload_limit"` + DurationLimit int32 `gorm:"column:duration_limit;type:integer;not null" json:"duration_limit"` + QualityLimit string `gorm:"column:quality_limit;type:text;not null" json:"quality_limit"` + Features pq.StringArray `gorm:"column:features;type:text[]" json:"features"` + IsActive *bool `gorm:"column:is_active;type:boolean;not null;default:true" json:"is_active"` + Version *int64 `gorm:"column:version;type:bigint;not null;default:1;version" json:"-"` } // TableName Plan's table name diff --git a/internal/database/model/plan_subscriptions.gen.go b/internal/database/model/plan_subscriptions.gen.go new file mode 100644 index 0000000..d861a5a --- /dev/null +++ b/internal/database/model/plan_subscriptions.gen.go @@ -0,0 +1,36 @@ +// 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 TableNamePlanSubscription = "plan_subscriptions" + +// PlanSubscription mapped from table +type PlanSubscription struct { + ID string `gorm:"column:id;type:uuid;primaryKey" json:"id"` + UserID string `gorm:"column:user_id;type:uuid;not null;index:idx_plan_subscriptions_user_id,priority:1" json:"user_id"` + PaymentID string `gorm:"column:payment_id;type:uuid;not null;index:idx_plan_subscriptions_payment_id,priority:1" json:"payment_id"` + PlanID string `gorm:"column:plan_id;type:uuid;not null;index:idx_plan_subscriptions_plan_id,priority:1" json:"plan_id"` + TermMonths int32 `gorm:"column:term_months;type:integer;not null" json:"term_months"` + PaymentMethod string `gorm:"column:payment_method;type:character varying(20);not null" json:"payment_method"` + WalletAmount float64 `gorm:"column:wallet_amount;type:numeric(65,30);not null" json:"wallet_amount"` + TopupAmount float64 `gorm:"column:topup_amount;type:numeric(65,30);not null" json:"topup_amount"` + StartedAt time.Time `gorm:"column:started_at;type:timestamp with time zone;not null" json:"started_at"` + ExpiresAt time.Time `gorm:"column:expires_at;type:timestamp with time zone;not null;index:idx_plan_subscriptions_expires_at,priority:1" json:"expires_at"` + Reminder7DSentAt *time.Time `gorm:"column:reminder_7d_sent_at;type:timestamp with time zone" json:"reminder_7d_sent_at"` + Reminder3DSentAt *time.Time `gorm:"column:reminder_3d_sent_at;type:timestamp with time zone" json:"reminder_3d_sent_at"` + Reminder1DSentAt *time.Time `gorm:"column:reminder_1d_sent_at;type:timestamp with time zone" json:"reminder_1d_sent_at"` + CreatedAt *time.Time `gorm:"column:created_at;type:timestamp with time zone" 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 PlanSubscription's table name +func (*PlanSubscription) TableName() string { + return TableNamePlanSubscription +} diff --git a/internal/database/model/user_preferences.gen.go b/internal/database/model/user_preferences.gen.go new file mode 100644 index 0000000..c2b5e2a --- /dev/null +++ b/internal/database/model/user_preferences.gen.go @@ -0,0 +1,38 @@ +// 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 TableNameUserPreference = "user_preferences" + +// UserPreference mapped from table +type UserPreference struct { + UserID string `gorm:"column:user_id;type:uuid;primaryKey" json:"user_id"` + Language *string `gorm:"column:language;type:text;not null;default:en" json:"language"` + Locale *string `gorm:"column:locale;type:text;not null;default:en" json:"locale"` + EmailNotifications *bool `gorm:"column:email_notifications;type:boolean;not null;default:true" json:"email_notifications"` + PushNotifications *bool `gorm:"column:push_notifications;type:boolean;not null;default:true" json:"push_notifications"` + MarketingNotifications bool `gorm:"column:marketing_notifications;type:boolean;not null" json:"marketing_notifications"` + TelegramNotifications bool `gorm:"column:telegram_notifications;type:boolean;not null" json:"telegram_notifications"` + Autoplay bool `gorm:"column:autoplay;type:boolean;not null" json:"autoplay"` + Loop bool `gorm:"column:loop;type:boolean;not null" json:"loop"` + Muted bool `gorm:"column:muted;type:boolean;not null" json:"muted"` + ShowControls *bool `gorm:"column:show_controls;type:boolean;not null;default:true" json:"show_controls"` + Pip *bool `gorm:"column:pip;type:boolean;not null;default:true" json:"pip"` + 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"` + CreatedAt *time.Time `gorm:"column:created_at;type:timestamp with time zone" json:"created_at"` + UpdatedAt *time.Time `gorm:"column:updated_at;type:timestamp with time zone" json:"updated_at"` + EncrytionM3u8 bool `gorm:"column:encrytion_m3u8;type:boolean;not null" json:"encrytion_m3u8"` + Version *int64 `gorm:"column:version;type:bigint;not null;default:1;version" json:"-"` +} + +// TableName UserPreference's table name +func (*UserPreference) TableName() string { + return TableNameUserPreference +} diff --git a/internal/database/model/video_ad_configs.gen.go b/internal/database/model/video_ad_configs.gen.go new file mode 100644 index 0000000..4d28341 --- /dev/null +++ b/internal/database/model/video_ad_configs.gen.go @@ -0,0 +1,29 @@ +// 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 TableNameVideoAdConfig = "video_ad_configs" + +// VideoAdConfig mapped from table +type VideoAdConfig struct { + VideoID string `gorm:"column:video_id;type:uuid;primaryKey" json:"video_id"` + UserID string `gorm:"column:user_id;type:uuid;not null;index:idx_video_ad_configs_user_id,priority:1" json:"user_id"` + AdTemplateID string `gorm:"column:ad_template_id;type:uuid;not null" json:"ad_template_id"` + VastTagURL string `gorm:"column:vast_tag_url;type:text;not null" json:"vast_tag_url"` + AdFormat *string `gorm:"column:ad_format;type:character varying(50);not null;default:pre-roll" json:"ad_format"` + Duration *int64 `gorm:"column:duration;type:bigint" json:"duration"` + CreatedAt *time.Time `gorm:"column:created_at;type:timestamp with time zone" 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 VideoAdConfig's table name +func (*VideoAdConfig) TableName() string { + return TableNameVideoAdConfig +} diff --git a/internal/database/model/wallet_transactions.gen.go b/internal/database/model/wallet_transactions.gen.go new file mode 100644 index 0000000..4e058b3 --- /dev/null +++ b/internal/database/model/wallet_transactions.gen.go @@ -0,0 +1,32 @@ +// 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 TableNameWalletTransaction = "wallet_transactions" + +// WalletTransaction mapped from table +type WalletTransaction struct { + ID string `gorm:"column:id;type:uuid;primaryKey" json:"id"` + UserID string `gorm:"column:user_id;type:uuid;not null;index:idx_wallet_transactions_user_id,priority:1" json:"user_id"` + Type string `gorm:"column:type;type:character varying(50);not null" json:"type"` + Amount float64 `gorm:"column:amount;type:numeric(65,30);not null" json:"amount"` + Currency *string `gorm:"column:currency;type:text;not null;default:USD" json:"currency"` + Note *string `gorm:"column:note;type:text" json:"note"` + CreatedAt *time.Time `gorm:"column:created_at;type:timestamp with time zone" json:"created_at"` + UpdatedAt *time.Time `gorm:"column:updated_at;type:timestamp with time zone" json:"updated_at"` + PaymentID *string `gorm:"column:payment_id;type:uuid;index:idx_wallet_transactions_payment_id,priority:1" json:"payment_id"` + PlanID *string `gorm:"column:plan_id;type:uuid;index:idx_wallet_transactions_plan_id,priority:1" json:"plan_id"` + TermMonths *int32 `gorm:"column:term_months;type:integer" json:"term_months"` + Version *int64 `gorm:"column:version;type:bigint;not null;default:1;version" json:"-"` +} + +// TableName WalletTransaction's table name +func (*WalletTransaction) TableName() string { + return TableNameWalletTransaction +} diff --git a/internal/database/query/ad_templates.gen.go b/internal/database/query/ad_templates.gen.go new file mode 100644 index 0000000..bad7b06 --- /dev/null +++ b/internal/database/query/ad_templates.gen.go @@ -0,0 +1,437 @@ +// 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 newAdTemplate(db *gorm.DB, opts ...gen.DOOption) adTemplate { + _adTemplate := adTemplate{} + + _adTemplate.adTemplateDo.UseDB(db, opts...) + _adTemplate.adTemplateDo.UseModel(&model.AdTemplate{}) + + tableName := _adTemplate.adTemplateDo.TableName() + _adTemplate.ALL = field.NewAsterisk(tableName) + _adTemplate.ID = field.NewString(tableName, "id") + _adTemplate.UserID = field.NewString(tableName, "user_id") + _adTemplate.Name = field.NewString(tableName, "name") + _adTemplate.Description = field.NewString(tableName, "description") + _adTemplate.VastTagURL = field.NewString(tableName, "vast_tag_url") + _adTemplate.AdFormat = field.NewString(tableName, "ad_format") + _adTemplate.Duration = field.NewInt64(tableName, "duration") + _adTemplate.IsActive = field.NewBool(tableName, "is_active") + _adTemplate.CreatedAt = field.NewTime(tableName, "created_at") + _adTemplate.UpdatedAt = field.NewTime(tableName, "updated_at") + _adTemplate.IsDefault = field.NewBool(tableName, "is_default") + _adTemplate.Version = field.NewInt64(tableName, "version") + + _adTemplate.fillFieldMap() + + return _adTemplate +} + +type adTemplate struct { + adTemplateDo adTemplateDo + + ALL field.Asterisk + ID field.String + UserID field.String + Name field.String + Description field.String + VastTagURL field.String + AdFormat field.String + Duration field.Int64 + IsActive field.Bool + CreatedAt field.Time + UpdatedAt field.Time + IsDefault field.Bool + Version field.Int64 + + fieldMap map[string]field.Expr +} + +func (a adTemplate) Table(newTableName string) *adTemplate { + a.adTemplateDo.UseTable(newTableName) + return a.updateTableName(newTableName) +} + +func (a adTemplate) As(alias string) *adTemplate { + a.adTemplateDo.DO = *(a.adTemplateDo.As(alias).(*gen.DO)) + return a.updateTableName(alias) +} + +func (a *adTemplate) updateTableName(table string) *adTemplate { + a.ALL = field.NewAsterisk(table) + a.ID = field.NewString(table, "id") + a.UserID = field.NewString(table, "user_id") + a.Name = field.NewString(table, "name") + a.Description = field.NewString(table, "description") + a.VastTagURL = field.NewString(table, "vast_tag_url") + a.AdFormat = field.NewString(table, "ad_format") + a.Duration = field.NewInt64(table, "duration") + a.IsActive = field.NewBool(table, "is_active") + a.CreatedAt = field.NewTime(table, "created_at") + a.UpdatedAt = field.NewTime(table, "updated_at") + a.IsDefault = field.NewBool(table, "is_default") + a.Version = field.NewInt64(table, "version") + + a.fillFieldMap() + + return a +} + +func (a *adTemplate) WithContext(ctx context.Context) IAdTemplateDo { + return a.adTemplateDo.WithContext(ctx) +} + +func (a adTemplate) TableName() string { return a.adTemplateDo.TableName() } + +func (a adTemplate) Alias() string { return a.adTemplateDo.Alias() } + +func (a adTemplate) Columns(cols ...field.Expr) gen.Columns { return a.adTemplateDo.Columns(cols...) } + +func (a *adTemplate) GetFieldByName(fieldName string) (field.OrderExpr, bool) { + _f, ok := a.fieldMap[fieldName] + if !ok || _f == nil { + return nil, false + } + _oe, ok := _f.(field.OrderExpr) + return _oe, ok +} + +func (a *adTemplate) fillFieldMap() { + a.fieldMap = make(map[string]field.Expr, 12) + a.fieldMap["id"] = a.ID + a.fieldMap["user_id"] = a.UserID + a.fieldMap["name"] = a.Name + a.fieldMap["description"] = a.Description + a.fieldMap["vast_tag_url"] = a.VastTagURL + a.fieldMap["ad_format"] = a.AdFormat + a.fieldMap["duration"] = a.Duration + a.fieldMap["is_active"] = a.IsActive + a.fieldMap["created_at"] = a.CreatedAt + a.fieldMap["updated_at"] = a.UpdatedAt + a.fieldMap["is_default"] = a.IsDefault + a.fieldMap["version"] = a.Version +} + +func (a adTemplate) clone(db *gorm.DB) adTemplate { + a.adTemplateDo.ReplaceConnPool(db.Statement.ConnPool) + return a +} + +func (a adTemplate) replaceDB(db *gorm.DB) adTemplate { + a.adTemplateDo.ReplaceDB(db) + return a +} + +type adTemplateDo struct{ gen.DO } + +type IAdTemplateDo interface { + gen.SubQuery + Debug() IAdTemplateDo + WithContext(ctx context.Context) IAdTemplateDo + WithResult(fc func(tx gen.Dao)) gen.ResultInfo + ReplaceDB(db *gorm.DB) + ReadDB() IAdTemplateDo + WriteDB() IAdTemplateDo + As(alias string) gen.Dao + Session(config *gorm.Session) IAdTemplateDo + Columns(cols ...field.Expr) gen.Columns + Clauses(conds ...clause.Expression) IAdTemplateDo + Not(conds ...gen.Condition) IAdTemplateDo + Or(conds ...gen.Condition) IAdTemplateDo + Select(conds ...field.Expr) IAdTemplateDo + Where(conds ...gen.Condition) IAdTemplateDo + Order(conds ...field.Expr) IAdTemplateDo + Distinct(cols ...field.Expr) IAdTemplateDo + Omit(cols ...field.Expr) IAdTemplateDo + Join(table schema.Tabler, on ...field.Expr) IAdTemplateDo + LeftJoin(table schema.Tabler, on ...field.Expr) IAdTemplateDo + RightJoin(table schema.Tabler, on ...field.Expr) IAdTemplateDo + Group(cols ...field.Expr) IAdTemplateDo + Having(conds ...gen.Condition) IAdTemplateDo + Limit(limit int) IAdTemplateDo + Offset(offset int) IAdTemplateDo + Count() (count int64, err error) + Scopes(funcs ...func(gen.Dao) gen.Dao) IAdTemplateDo + Unscoped() IAdTemplateDo + Create(values ...*model.AdTemplate) error + CreateInBatches(values []*model.AdTemplate, batchSize int) error + Save(values ...*model.AdTemplate) error + First() (*model.AdTemplate, error) + Take() (*model.AdTemplate, error) + Last() (*model.AdTemplate, error) + Find() ([]*model.AdTemplate, error) + FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.AdTemplate, err error) + FindInBatches(result *[]*model.AdTemplate, batchSize int, fc func(tx gen.Dao, batch int) error) error + Pluck(column field.Expr, dest interface{}) error + Delete(...*model.AdTemplate) (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) IAdTemplateDo + Assign(attrs ...field.AssignExpr) IAdTemplateDo + Joins(fields ...field.RelationField) IAdTemplateDo + Preload(fields ...field.RelationField) IAdTemplateDo + FirstOrInit() (*model.AdTemplate, error) + FirstOrCreate() (*model.AdTemplate, error) + FindByPage(offset int, limit int) (result []*model.AdTemplate, 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) IAdTemplateDo + UnderlyingDB() *gorm.DB + schema.Tabler +} + +func (a adTemplateDo) Debug() IAdTemplateDo { + return a.withDO(a.DO.Debug()) +} + +func (a adTemplateDo) WithContext(ctx context.Context) IAdTemplateDo { + return a.withDO(a.DO.WithContext(ctx)) +} + +func (a adTemplateDo) ReadDB() IAdTemplateDo { + return a.Clauses(dbresolver.Read) +} + +func (a adTemplateDo) WriteDB() IAdTemplateDo { + return a.Clauses(dbresolver.Write) +} + +func (a adTemplateDo) Session(config *gorm.Session) IAdTemplateDo { + return a.withDO(a.DO.Session(config)) +} + +func (a adTemplateDo) Clauses(conds ...clause.Expression) IAdTemplateDo { + return a.withDO(a.DO.Clauses(conds...)) +} + +func (a adTemplateDo) Returning(value interface{}, columns ...string) IAdTemplateDo { + return a.withDO(a.DO.Returning(value, columns...)) +} + +func (a adTemplateDo) Not(conds ...gen.Condition) IAdTemplateDo { + return a.withDO(a.DO.Not(conds...)) +} + +func (a adTemplateDo) Or(conds ...gen.Condition) IAdTemplateDo { + return a.withDO(a.DO.Or(conds...)) +} + +func (a adTemplateDo) Select(conds ...field.Expr) IAdTemplateDo { + return a.withDO(a.DO.Select(conds...)) +} + +func (a adTemplateDo) Where(conds ...gen.Condition) IAdTemplateDo { + return a.withDO(a.DO.Where(conds...)) +} + +func (a adTemplateDo) Order(conds ...field.Expr) IAdTemplateDo { + return a.withDO(a.DO.Order(conds...)) +} + +func (a adTemplateDo) Distinct(cols ...field.Expr) IAdTemplateDo { + return a.withDO(a.DO.Distinct(cols...)) +} + +func (a adTemplateDo) Omit(cols ...field.Expr) IAdTemplateDo { + return a.withDO(a.DO.Omit(cols...)) +} + +func (a adTemplateDo) Join(table schema.Tabler, on ...field.Expr) IAdTemplateDo { + return a.withDO(a.DO.Join(table, on...)) +} + +func (a adTemplateDo) LeftJoin(table schema.Tabler, on ...field.Expr) IAdTemplateDo { + return a.withDO(a.DO.LeftJoin(table, on...)) +} + +func (a adTemplateDo) RightJoin(table schema.Tabler, on ...field.Expr) IAdTemplateDo { + return a.withDO(a.DO.RightJoin(table, on...)) +} + +func (a adTemplateDo) Group(cols ...field.Expr) IAdTemplateDo { + return a.withDO(a.DO.Group(cols...)) +} + +func (a adTemplateDo) Having(conds ...gen.Condition) IAdTemplateDo { + return a.withDO(a.DO.Having(conds...)) +} + +func (a adTemplateDo) Limit(limit int) IAdTemplateDo { + return a.withDO(a.DO.Limit(limit)) +} + +func (a adTemplateDo) Offset(offset int) IAdTemplateDo { + return a.withDO(a.DO.Offset(offset)) +} + +func (a adTemplateDo) Scopes(funcs ...func(gen.Dao) gen.Dao) IAdTemplateDo { + return a.withDO(a.DO.Scopes(funcs...)) +} + +func (a adTemplateDo) Unscoped() IAdTemplateDo { + return a.withDO(a.DO.Unscoped()) +} + +func (a adTemplateDo) Create(values ...*model.AdTemplate) error { + if len(values) == 0 { + return nil + } + return a.DO.Create(values) +} + +func (a adTemplateDo) CreateInBatches(values []*model.AdTemplate, batchSize int) error { + return a.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 (a adTemplateDo) Save(values ...*model.AdTemplate) error { + if len(values) == 0 { + return nil + } + return a.DO.Save(values) +} + +func (a adTemplateDo) First() (*model.AdTemplate, error) { + if result, err := a.DO.First(); err != nil { + return nil, err + } else { + return result.(*model.AdTemplate), nil + } +} + +func (a adTemplateDo) Take() (*model.AdTemplate, error) { + if result, err := a.DO.Take(); err != nil { + return nil, err + } else { + return result.(*model.AdTemplate), nil + } +} + +func (a adTemplateDo) Last() (*model.AdTemplate, error) { + if result, err := a.DO.Last(); err != nil { + return nil, err + } else { + return result.(*model.AdTemplate), nil + } +} + +func (a adTemplateDo) Find() ([]*model.AdTemplate, error) { + result, err := a.DO.Find() + return result.([]*model.AdTemplate), err +} + +func (a adTemplateDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.AdTemplate, err error) { + buf := make([]*model.AdTemplate, 0, batchSize) + err = a.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 (a adTemplateDo) FindInBatches(result *[]*model.AdTemplate, batchSize int, fc func(tx gen.Dao, batch int) error) error { + return a.DO.FindInBatches(result, batchSize, fc) +} + +func (a adTemplateDo) Attrs(attrs ...field.AssignExpr) IAdTemplateDo { + return a.withDO(a.DO.Attrs(attrs...)) +} + +func (a adTemplateDo) Assign(attrs ...field.AssignExpr) IAdTemplateDo { + return a.withDO(a.DO.Assign(attrs...)) +} + +func (a adTemplateDo) Joins(fields ...field.RelationField) IAdTemplateDo { + for _, _f := range fields { + a = *a.withDO(a.DO.Joins(_f)) + } + return &a +} + +func (a adTemplateDo) Preload(fields ...field.RelationField) IAdTemplateDo { + for _, _f := range fields { + a = *a.withDO(a.DO.Preload(_f)) + } + return &a +} + +func (a adTemplateDo) FirstOrInit() (*model.AdTemplate, error) { + if result, err := a.DO.FirstOrInit(); err != nil { + return nil, err + } else { + return result.(*model.AdTemplate), nil + } +} + +func (a adTemplateDo) FirstOrCreate() (*model.AdTemplate, error) { + if result, err := a.DO.FirstOrCreate(); err != nil { + return nil, err + } else { + return result.(*model.AdTemplate), nil + } +} + +func (a adTemplateDo) FindByPage(offset int, limit int) (result []*model.AdTemplate, count int64, err error) { + result, err = a.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 = a.Offset(-1).Limit(-1).Count() + return +} + +func (a adTemplateDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) { + count, err = a.Count() + if err != nil { + return + } + + err = a.Offset(offset).Limit(limit).Scan(result) + return +} + +func (a adTemplateDo) Scan(result interface{}) (err error) { + return a.DO.Scan(result) +} + +func (a adTemplateDo) Delete(models ...*model.AdTemplate) (result gen.ResultInfo, err error) { + return a.DO.Delete(models) +} + +func (a *adTemplateDo) withDO(do gen.Dao) *adTemplateDo { + a.DO = *do.(*gen.DO) + return a +} diff --git a/internal/database/query/domains.gen.go b/internal/database/query/domains.gen.go new file mode 100644 index 0000000..fc0b33f --- /dev/null +++ b/internal/database/query/domains.gen.go @@ -0,0 +1,411 @@ +// 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 newDomain(db *gorm.DB, opts ...gen.DOOption) domain { + _domain := domain{} + + _domain.domainDo.UseDB(db, opts...) + _domain.domainDo.UseModel(&model.Domain{}) + + tableName := _domain.domainDo.TableName() + _domain.ALL = field.NewAsterisk(tableName) + _domain.ID = field.NewString(tableName, "id") + _domain.UserID = field.NewString(tableName, "user_id") + _domain.Name = field.NewString(tableName, "name") + _domain.CreatedAt = field.NewTime(tableName, "created_at") + _domain.UpdatedAt = field.NewTime(tableName, "updated_at") + _domain.Version = field.NewInt64(tableName, "version") + + _domain.fillFieldMap() + + return _domain +} + +type domain struct { + domainDo domainDo + + ALL field.Asterisk + ID field.String + UserID field.String + Name field.String + CreatedAt field.Time + UpdatedAt field.Time + Version field.Int64 + + fieldMap map[string]field.Expr +} + +func (d domain) Table(newTableName string) *domain { + d.domainDo.UseTable(newTableName) + return d.updateTableName(newTableName) +} + +func (d domain) As(alias string) *domain { + d.domainDo.DO = *(d.domainDo.As(alias).(*gen.DO)) + return d.updateTableName(alias) +} + +func (d *domain) updateTableName(table string) *domain { + d.ALL = field.NewAsterisk(table) + d.ID = field.NewString(table, "id") + d.UserID = field.NewString(table, "user_id") + d.Name = field.NewString(table, "name") + d.CreatedAt = field.NewTime(table, "created_at") + d.UpdatedAt = field.NewTime(table, "updated_at") + d.Version = field.NewInt64(table, "version") + + d.fillFieldMap() + + return d +} + +func (d *domain) WithContext(ctx context.Context) IDomainDo { return d.domainDo.WithContext(ctx) } + +func (d domain) TableName() string { return d.domainDo.TableName() } + +func (d domain) Alias() string { return d.domainDo.Alias() } + +func (d domain) Columns(cols ...field.Expr) gen.Columns { return d.domainDo.Columns(cols...) } + +func (d *domain) GetFieldByName(fieldName string) (field.OrderExpr, bool) { + _f, ok := d.fieldMap[fieldName] + if !ok || _f == nil { + return nil, false + } + _oe, ok := _f.(field.OrderExpr) + return _oe, ok +} + +func (d *domain) fillFieldMap() { + d.fieldMap = make(map[string]field.Expr, 6) + d.fieldMap["id"] = d.ID + d.fieldMap["user_id"] = d.UserID + d.fieldMap["name"] = d.Name + d.fieldMap["created_at"] = d.CreatedAt + d.fieldMap["updated_at"] = d.UpdatedAt + d.fieldMap["version"] = d.Version +} + +func (d domain) clone(db *gorm.DB) domain { + d.domainDo.ReplaceConnPool(db.Statement.ConnPool) + return d +} + +func (d domain) replaceDB(db *gorm.DB) domain { + d.domainDo.ReplaceDB(db) + return d +} + +type domainDo struct{ gen.DO } + +type IDomainDo interface { + gen.SubQuery + Debug() IDomainDo + WithContext(ctx context.Context) IDomainDo + WithResult(fc func(tx gen.Dao)) gen.ResultInfo + ReplaceDB(db *gorm.DB) + ReadDB() IDomainDo + WriteDB() IDomainDo + As(alias string) gen.Dao + Session(config *gorm.Session) IDomainDo + Columns(cols ...field.Expr) gen.Columns + Clauses(conds ...clause.Expression) IDomainDo + Not(conds ...gen.Condition) IDomainDo + Or(conds ...gen.Condition) IDomainDo + Select(conds ...field.Expr) IDomainDo + Where(conds ...gen.Condition) IDomainDo + Order(conds ...field.Expr) IDomainDo + Distinct(cols ...field.Expr) IDomainDo + Omit(cols ...field.Expr) IDomainDo + Join(table schema.Tabler, on ...field.Expr) IDomainDo + LeftJoin(table schema.Tabler, on ...field.Expr) IDomainDo + RightJoin(table schema.Tabler, on ...field.Expr) IDomainDo + Group(cols ...field.Expr) IDomainDo + Having(conds ...gen.Condition) IDomainDo + Limit(limit int) IDomainDo + Offset(offset int) IDomainDo + Count() (count int64, err error) + Scopes(funcs ...func(gen.Dao) gen.Dao) IDomainDo + Unscoped() IDomainDo + Create(values ...*model.Domain) error + CreateInBatches(values []*model.Domain, batchSize int) error + Save(values ...*model.Domain) error + First() (*model.Domain, error) + Take() (*model.Domain, error) + Last() (*model.Domain, error) + Find() ([]*model.Domain, error) + FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.Domain, err error) + FindInBatches(result *[]*model.Domain, batchSize int, fc func(tx gen.Dao, batch int) error) error + Pluck(column field.Expr, dest interface{}) error + Delete(...*model.Domain) (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) IDomainDo + Assign(attrs ...field.AssignExpr) IDomainDo + Joins(fields ...field.RelationField) IDomainDo + Preload(fields ...field.RelationField) IDomainDo + FirstOrInit() (*model.Domain, error) + FirstOrCreate() (*model.Domain, error) + FindByPage(offset int, limit int) (result []*model.Domain, 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) IDomainDo + UnderlyingDB() *gorm.DB + schema.Tabler +} + +func (d domainDo) Debug() IDomainDo { + return d.withDO(d.DO.Debug()) +} + +func (d domainDo) WithContext(ctx context.Context) IDomainDo { + return d.withDO(d.DO.WithContext(ctx)) +} + +func (d domainDo) ReadDB() IDomainDo { + return d.Clauses(dbresolver.Read) +} + +func (d domainDo) WriteDB() IDomainDo { + return d.Clauses(dbresolver.Write) +} + +func (d domainDo) Session(config *gorm.Session) IDomainDo { + return d.withDO(d.DO.Session(config)) +} + +func (d domainDo) Clauses(conds ...clause.Expression) IDomainDo { + return d.withDO(d.DO.Clauses(conds...)) +} + +func (d domainDo) Returning(value interface{}, columns ...string) IDomainDo { + return d.withDO(d.DO.Returning(value, columns...)) +} + +func (d domainDo) Not(conds ...gen.Condition) IDomainDo { + return d.withDO(d.DO.Not(conds...)) +} + +func (d domainDo) Or(conds ...gen.Condition) IDomainDo { + return d.withDO(d.DO.Or(conds...)) +} + +func (d domainDo) Select(conds ...field.Expr) IDomainDo { + return d.withDO(d.DO.Select(conds...)) +} + +func (d domainDo) Where(conds ...gen.Condition) IDomainDo { + return d.withDO(d.DO.Where(conds...)) +} + +func (d domainDo) Order(conds ...field.Expr) IDomainDo { + return d.withDO(d.DO.Order(conds...)) +} + +func (d domainDo) Distinct(cols ...field.Expr) IDomainDo { + return d.withDO(d.DO.Distinct(cols...)) +} + +func (d domainDo) Omit(cols ...field.Expr) IDomainDo { + return d.withDO(d.DO.Omit(cols...)) +} + +func (d domainDo) Join(table schema.Tabler, on ...field.Expr) IDomainDo { + return d.withDO(d.DO.Join(table, on...)) +} + +func (d domainDo) LeftJoin(table schema.Tabler, on ...field.Expr) IDomainDo { + return d.withDO(d.DO.LeftJoin(table, on...)) +} + +func (d domainDo) RightJoin(table schema.Tabler, on ...field.Expr) IDomainDo { + return d.withDO(d.DO.RightJoin(table, on...)) +} + +func (d domainDo) Group(cols ...field.Expr) IDomainDo { + return d.withDO(d.DO.Group(cols...)) +} + +func (d domainDo) Having(conds ...gen.Condition) IDomainDo { + return d.withDO(d.DO.Having(conds...)) +} + +func (d domainDo) Limit(limit int) IDomainDo { + return d.withDO(d.DO.Limit(limit)) +} + +func (d domainDo) Offset(offset int) IDomainDo { + return d.withDO(d.DO.Offset(offset)) +} + +func (d domainDo) Scopes(funcs ...func(gen.Dao) gen.Dao) IDomainDo { + return d.withDO(d.DO.Scopes(funcs...)) +} + +func (d domainDo) Unscoped() IDomainDo { + return d.withDO(d.DO.Unscoped()) +} + +func (d domainDo) Create(values ...*model.Domain) error { + if len(values) == 0 { + return nil + } + return d.DO.Create(values) +} + +func (d domainDo) CreateInBatches(values []*model.Domain, batchSize int) error { + return d.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 (d domainDo) Save(values ...*model.Domain) error { + if len(values) == 0 { + return nil + } + return d.DO.Save(values) +} + +func (d domainDo) First() (*model.Domain, error) { + if result, err := d.DO.First(); err != nil { + return nil, err + } else { + return result.(*model.Domain), nil + } +} + +func (d domainDo) Take() (*model.Domain, error) { + if result, err := d.DO.Take(); err != nil { + return nil, err + } else { + return result.(*model.Domain), nil + } +} + +func (d domainDo) Last() (*model.Domain, error) { + if result, err := d.DO.Last(); err != nil { + return nil, err + } else { + return result.(*model.Domain), nil + } +} + +func (d domainDo) Find() ([]*model.Domain, error) { + result, err := d.DO.Find() + return result.([]*model.Domain), err +} + +func (d domainDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.Domain, err error) { + buf := make([]*model.Domain, 0, batchSize) + err = d.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 (d domainDo) FindInBatches(result *[]*model.Domain, batchSize int, fc func(tx gen.Dao, batch int) error) error { + return d.DO.FindInBatches(result, batchSize, fc) +} + +func (d domainDo) Attrs(attrs ...field.AssignExpr) IDomainDo { + return d.withDO(d.DO.Attrs(attrs...)) +} + +func (d domainDo) Assign(attrs ...field.AssignExpr) IDomainDo { + return d.withDO(d.DO.Assign(attrs...)) +} + +func (d domainDo) Joins(fields ...field.RelationField) IDomainDo { + for _, _f := range fields { + d = *d.withDO(d.DO.Joins(_f)) + } + return &d +} + +func (d domainDo) Preload(fields ...field.RelationField) IDomainDo { + for _, _f := range fields { + d = *d.withDO(d.DO.Preload(_f)) + } + return &d +} + +func (d domainDo) FirstOrInit() (*model.Domain, error) { + if result, err := d.DO.FirstOrInit(); err != nil { + return nil, err + } else { + return result.(*model.Domain), nil + } +} + +func (d domainDo) FirstOrCreate() (*model.Domain, error) { + if result, err := d.DO.FirstOrCreate(); err != nil { + return nil, err + } else { + return result.(*model.Domain), nil + } +} + +func (d domainDo) FindByPage(offset int, limit int) (result []*model.Domain, count int64, err error) { + result, err = d.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 = d.Offset(-1).Limit(-1).Count() + return +} + +func (d domainDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) { + count, err = d.Count() + if err != nil { + return + } + + err = d.Offset(offset).Limit(limit).Scan(result) + return +} + +func (d domainDo) Scan(result interface{}) (err error) { + return d.DO.Scan(result) +} + +func (d domainDo) Delete(models ...*model.Domain) (result gen.ResultInfo, err error) { + return d.DO.Delete(models) +} + +func (d *domainDo) withDO(do gen.Dao) *domainDo { + d.DO = *do.(*gen.DO) + return d +} diff --git a/internal/database/query/gen.go b/internal/database/query/gen.go index c665aed..b80b196 100644 --- a/internal/database/query/gen.go +++ b/internal/database/query/gen.go @@ -16,49 +16,89 @@ import ( ) var ( - Q = new(Query) - Payment *payment - Plan *plan - User *user - Video *video + Q = new(Query) + AdTemplate *adTemplate + Domain *domain + Job *job + Notification *notification + Payment *payment + Plan *plan + PlanSubscription *planSubscription + User *user + UserPreference *userPreference + Video *video + VideoAdConfig *videoAdConfig + WalletTransaction *walletTransaction ) func SetDefault(db *gorm.DB, opts ...gen.DOOption) { *Q = *Use(db, opts...) + AdTemplate = &Q.AdTemplate + Domain = &Q.Domain + Job = &Q.Job + Notification = &Q.Notification Payment = &Q.Payment Plan = &Q.Plan + PlanSubscription = &Q.PlanSubscription User = &Q.User + UserPreference = &Q.UserPreference Video = &Q.Video + VideoAdConfig = &Q.VideoAdConfig + WalletTransaction = &Q.WalletTransaction } func Use(db *gorm.DB, opts ...gen.DOOption) *Query { return &Query{ - db: db, - Payment: newPayment(db, opts...), - Plan: newPlan(db, opts...), - User: newUser(db, opts...), - Video: newVideo(db, opts...), + db: db, + AdTemplate: newAdTemplate(db, opts...), + Domain: newDomain(db, opts...), + Job: newJob(db, opts...), + Notification: newNotification(db, opts...), + Payment: newPayment(db, opts...), + Plan: newPlan(db, opts...), + PlanSubscription: newPlanSubscription(db, opts...), + User: newUser(db, opts...), + UserPreference: newUserPreference(db, opts...), + Video: newVideo(db, opts...), + VideoAdConfig: newVideoAdConfig(db, opts...), + WalletTransaction: newWalletTransaction(db, opts...), } } type Query struct { db *gorm.DB - Payment payment - Plan plan - User user - Video video + AdTemplate adTemplate + Domain domain + Job job + Notification notification + Payment payment + Plan plan + PlanSubscription planSubscription + User user + UserPreference userPreference + Video video + VideoAdConfig videoAdConfig + WalletTransaction walletTransaction } func (q *Query) Available() bool { return q.db != nil } func (q *Query) clone(db *gorm.DB) *Query { return &Query{ - db: db, - Payment: q.Payment.clone(db), - Plan: q.Plan.clone(db), - User: q.User.clone(db), - Video: q.Video.clone(db), + db: db, + AdTemplate: q.AdTemplate.clone(db), + Domain: q.Domain.clone(db), + Job: q.Job.clone(db), + Notification: q.Notification.clone(db), + Payment: q.Payment.clone(db), + Plan: q.Plan.clone(db), + PlanSubscription: q.PlanSubscription.clone(db), + User: q.User.clone(db), + UserPreference: q.UserPreference.clone(db), + Video: q.Video.clone(db), + VideoAdConfig: q.VideoAdConfig.clone(db), + WalletTransaction: q.WalletTransaction.clone(db), } } @@ -72,27 +112,51 @@ func (q *Query) WriteDB() *Query { func (q *Query) ReplaceDB(db *gorm.DB) *Query { return &Query{ - db: db, - Payment: q.Payment.replaceDB(db), - Plan: q.Plan.replaceDB(db), - User: q.User.replaceDB(db), - Video: q.Video.replaceDB(db), + db: db, + AdTemplate: q.AdTemplate.replaceDB(db), + Domain: q.Domain.replaceDB(db), + Job: q.Job.replaceDB(db), + Notification: q.Notification.replaceDB(db), + Payment: q.Payment.replaceDB(db), + Plan: q.Plan.replaceDB(db), + PlanSubscription: q.PlanSubscription.replaceDB(db), + User: q.User.replaceDB(db), + UserPreference: q.UserPreference.replaceDB(db), + Video: q.Video.replaceDB(db), + VideoAdConfig: q.VideoAdConfig.replaceDB(db), + WalletTransaction: q.WalletTransaction.replaceDB(db), } } type queryCtx struct { - Payment IPaymentDo - Plan IPlanDo - User IUserDo - Video IVideoDo + AdTemplate IAdTemplateDo + Domain IDomainDo + Job IJobDo + Notification INotificationDo + Payment IPaymentDo + Plan IPlanDo + PlanSubscription IPlanSubscriptionDo + User IUserDo + UserPreference IUserPreferenceDo + Video IVideoDo + VideoAdConfig IVideoAdConfigDo + WalletTransaction IWalletTransactionDo } func (q *Query) WithContext(ctx context.Context) *queryCtx { return &queryCtx{ - Payment: q.Payment.WithContext(ctx), - Plan: q.Plan.WithContext(ctx), - User: q.User.WithContext(ctx), - Video: q.Video.WithContext(ctx), + AdTemplate: q.AdTemplate.WithContext(ctx), + Domain: q.Domain.WithContext(ctx), + Job: q.Job.WithContext(ctx), + Notification: q.Notification.WithContext(ctx), + Payment: q.Payment.WithContext(ctx), + Plan: q.Plan.WithContext(ctx), + PlanSubscription: q.PlanSubscription.WithContext(ctx), + User: q.User.WithContext(ctx), + UserPreference: q.UserPreference.WithContext(ctx), + Video: q.Video.WithContext(ctx), + VideoAdConfig: q.VideoAdConfig.WithContext(ctx), + WalletTransaction: q.WalletTransaction.WithContext(ctx), } } diff --git a/internal/database/query/jobs.gen.go b/internal/database/query/jobs.gen.go new file mode 100644 index 0000000..a720580 --- /dev/null +++ b/internal/database/query/jobs.gen.go @@ -0,0 +1,455 @@ +// 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 newJob(db *gorm.DB, opts ...gen.DOOption) job { + _job := job{} + + _job.jobDo.UseDB(db, opts...) + _job.jobDo.UseModel(&model.Job{}) + + tableName := _job.jobDo.TableName() + _job.ALL = field.NewAsterisk(tableName) + _job.ID = field.NewString(tableName, "id") + _job.Status = field.NewString(tableName, "status") + _job.Priority = field.NewInt64(tableName, "priority") + _job.InputURL = field.NewString(tableName, "input_url") + _job.OutputURL = field.NewString(tableName, "output_url") + _job.TotalDuration = field.NewInt64(tableName, "total_duration") + _job.CurrentTime = field.NewInt64(tableName, "current_time") + _job.Progress = field.NewFloat64(tableName, "progress") + _job.AgentID = field.NewInt64(tableName, "agent_id") + _job.Logs = field.NewString(tableName, "logs") + _job.Config = field.NewString(tableName, "config") + _job.Cancelled = field.NewBool(tableName, "cancelled") + _job.RetryCount = field.NewInt64(tableName, "retry_count") + _job.MaxRetries = field.NewInt64(tableName, "max_retries") + _job.CreatedAt = field.NewTime(tableName, "created_at") + _job.UpdatedAt = field.NewTime(tableName, "updated_at") + _job.Version = field.NewInt64(tableName, "version") + + _job.fillFieldMap() + + return _job +} + +type job struct { + jobDo jobDo + + ALL field.Asterisk + ID field.String + Status field.String + Priority field.Int64 + InputURL field.String + OutputURL field.String + TotalDuration field.Int64 + CurrentTime field.Int64 + Progress field.Float64 + AgentID field.Int64 + Logs field.String + Config field.String + Cancelled field.Bool + RetryCount field.Int64 + MaxRetries field.Int64 + CreatedAt field.Time + UpdatedAt field.Time + Version field.Int64 + + fieldMap map[string]field.Expr +} + +func (j job) Table(newTableName string) *job { + j.jobDo.UseTable(newTableName) + return j.updateTableName(newTableName) +} + +func (j job) As(alias string) *job { + j.jobDo.DO = *(j.jobDo.As(alias).(*gen.DO)) + return j.updateTableName(alias) +} + +func (j *job) updateTableName(table string) *job { + j.ALL = field.NewAsterisk(table) + j.ID = field.NewString(table, "id") + j.Status = field.NewString(table, "status") + j.Priority = field.NewInt64(table, "priority") + j.InputURL = field.NewString(table, "input_url") + j.OutputURL = field.NewString(table, "output_url") + j.TotalDuration = field.NewInt64(table, "total_duration") + j.CurrentTime = field.NewInt64(table, "current_time") + j.Progress = field.NewFloat64(table, "progress") + j.AgentID = field.NewInt64(table, "agent_id") + j.Logs = field.NewString(table, "logs") + j.Config = field.NewString(table, "config") + j.Cancelled = field.NewBool(table, "cancelled") + j.RetryCount = field.NewInt64(table, "retry_count") + j.MaxRetries = field.NewInt64(table, "max_retries") + j.CreatedAt = field.NewTime(table, "created_at") + j.UpdatedAt = field.NewTime(table, "updated_at") + j.Version = field.NewInt64(table, "version") + + j.fillFieldMap() + + return j +} + +func (j *job) WithContext(ctx context.Context) IJobDo { return j.jobDo.WithContext(ctx) } + +func (j job) TableName() string { return j.jobDo.TableName() } + +func (j job) Alias() string { return j.jobDo.Alias() } + +func (j job) Columns(cols ...field.Expr) gen.Columns { return j.jobDo.Columns(cols...) } + +func (j *job) GetFieldByName(fieldName string) (field.OrderExpr, bool) { + _f, ok := j.fieldMap[fieldName] + if !ok || _f == nil { + return nil, false + } + _oe, ok := _f.(field.OrderExpr) + return _oe, ok +} + +func (j *job) fillFieldMap() { + j.fieldMap = make(map[string]field.Expr, 17) + j.fieldMap["id"] = j.ID + j.fieldMap["status"] = j.Status + j.fieldMap["priority"] = j.Priority + j.fieldMap["input_url"] = j.InputURL + j.fieldMap["output_url"] = j.OutputURL + j.fieldMap["total_duration"] = j.TotalDuration + j.fieldMap["current_time"] = j.CurrentTime + j.fieldMap["progress"] = j.Progress + j.fieldMap["agent_id"] = j.AgentID + j.fieldMap["logs"] = j.Logs + j.fieldMap["config"] = j.Config + j.fieldMap["cancelled"] = j.Cancelled + j.fieldMap["retry_count"] = j.RetryCount + j.fieldMap["max_retries"] = j.MaxRetries + j.fieldMap["created_at"] = j.CreatedAt + j.fieldMap["updated_at"] = j.UpdatedAt + j.fieldMap["version"] = j.Version +} + +func (j job) clone(db *gorm.DB) job { + j.jobDo.ReplaceConnPool(db.Statement.ConnPool) + return j +} + +func (j job) replaceDB(db *gorm.DB) job { + j.jobDo.ReplaceDB(db) + return j +} + +type jobDo struct{ gen.DO } + +type IJobDo interface { + gen.SubQuery + Debug() IJobDo + WithContext(ctx context.Context) IJobDo + WithResult(fc func(tx gen.Dao)) gen.ResultInfo + ReplaceDB(db *gorm.DB) + ReadDB() IJobDo + WriteDB() IJobDo + As(alias string) gen.Dao + Session(config *gorm.Session) IJobDo + Columns(cols ...field.Expr) gen.Columns + Clauses(conds ...clause.Expression) IJobDo + Not(conds ...gen.Condition) IJobDo + Or(conds ...gen.Condition) IJobDo + Select(conds ...field.Expr) IJobDo + Where(conds ...gen.Condition) IJobDo + Order(conds ...field.Expr) IJobDo + Distinct(cols ...field.Expr) IJobDo + Omit(cols ...field.Expr) IJobDo + Join(table schema.Tabler, on ...field.Expr) IJobDo + LeftJoin(table schema.Tabler, on ...field.Expr) IJobDo + RightJoin(table schema.Tabler, on ...field.Expr) IJobDo + Group(cols ...field.Expr) IJobDo + Having(conds ...gen.Condition) IJobDo + Limit(limit int) IJobDo + Offset(offset int) IJobDo + Count() (count int64, err error) + Scopes(funcs ...func(gen.Dao) gen.Dao) IJobDo + Unscoped() IJobDo + Create(values ...*model.Job) error + CreateInBatches(values []*model.Job, batchSize int) error + Save(values ...*model.Job) error + First() (*model.Job, error) + Take() (*model.Job, error) + Last() (*model.Job, error) + Find() ([]*model.Job, error) + FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.Job, err error) + FindInBatches(result *[]*model.Job, batchSize int, fc func(tx gen.Dao, batch int) error) error + Pluck(column field.Expr, dest interface{}) error + Delete(...*model.Job) (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) IJobDo + Assign(attrs ...field.AssignExpr) IJobDo + Joins(fields ...field.RelationField) IJobDo + Preload(fields ...field.RelationField) IJobDo + FirstOrInit() (*model.Job, error) + FirstOrCreate() (*model.Job, error) + FindByPage(offset int, limit int) (result []*model.Job, 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) IJobDo + UnderlyingDB() *gorm.DB + schema.Tabler +} + +func (j jobDo) Debug() IJobDo { + return j.withDO(j.DO.Debug()) +} + +func (j jobDo) WithContext(ctx context.Context) IJobDo { + return j.withDO(j.DO.WithContext(ctx)) +} + +func (j jobDo) ReadDB() IJobDo { + return j.Clauses(dbresolver.Read) +} + +func (j jobDo) WriteDB() IJobDo { + return j.Clauses(dbresolver.Write) +} + +func (j jobDo) Session(config *gorm.Session) IJobDo { + return j.withDO(j.DO.Session(config)) +} + +func (j jobDo) Clauses(conds ...clause.Expression) IJobDo { + return j.withDO(j.DO.Clauses(conds...)) +} + +func (j jobDo) Returning(value interface{}, columns ...string) IJobDo { + return j.withDO(j.DO.Returning(value, columns...)) +} + +func (j jobDo) Not(conds ...gen.Condition) IJobDo { + return j.withDO(j.DO.Not(conds...)) +} + +func (j jobDo) Or(conds ...gen.Condition) IJobDo { + return j.withDO(j.DO.Or(conds...)) +} + +func (j jobDo) Select(conds ...field.Expr) IJobDo { + return j.withDO(j.DO.Select(conds...)) +} + +func (j jobDo) Where(conds ...gen.Condition) IJobDo { + return j.withDO(j.DO.Where(conds...)) +} + +func (j jobDo) Order(conds ...field.Expr) IJobDo { + return j.withDO(j.DO.Order(conds...)) +} + +func (j jobDo) Distinct(cols ...field.Expr) IJobDo { + return j.withDO(j.DO.Distinct(cols...)) +} + +func (j jobDo) Omit(cols ...field.Expr) IJobDo { + return j.withDO(j.DO.Omit(cols...)) +} + +func (j jobDo) Join(table schema.Tabler, on ...field.Expr) IJobDo { + return j.withDO(j.DO.Join(table, on...)) +} + +func (j jobDo) LeftJoin(table schema.Tabler, on ...field.Expr) IJobDo { + return j.withDO(j.DO.LeftJoin(table, on...)) +} + +func (j jobDo) RightJoin(table schema.Tabler, on ...field.Expr) IJobDo { + return j.withDO(j.DO.RightJoin(table, on...)) +} + +func (j jobDo) Group(cols ...field.Expr) IJobDo { + return j.withDO(j.DO.Group(cols...)) +} + +func (j jobDo) Having(conds ...gen.Condition) IJobDo { + return j.withDO(j.DO.Having(conds...)) +} + +func (j jobDo) Limit(limit int) IJobDo { + return j.withDO(j.DO.Limit(limit)) +} + +func (j jobDo) Offset(offset int) IJobDo { + return j.withDO(j.DO.Offset(offset)) +} + +func (j jobDo) Scopes(funcs ...func(gen.Dao) gen.Dao) IJobDo { + return j.withDO(j.DO.Scopes(funcs...)) +} + +func (j jobDo) Unscoped() IJobDo { + return j.withDO(j.DO.Unscoped()) +} + +func (j jobDo) Create(values ...*model.Job) error { + if len(values) == 0 { + return nil + } + return j.DO.Create(values) +} + +func (j jobDo) CreateInBatches(values []*model.Job, batchSize int) error { + return j.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 (j jobDo) Save(values ...*model.Job) error { + if len(values) == 0 { + return nil + } + return j.DO.Save(values) +} + +func (j jobDo) First() (*model.Job, error) { + if result, err := j.DO.First(); err != nil { + return nil, err + } else { + return result.(*model.Job), nil + } +} + +func (j jobDo) Take() (*model.Job, error) { + if result, err := j.DO.Take(); err != nil { + return nil, err + } else { + return result.(*model.Job), nil + } +} + +func (j jobDo) Last() (*model.Job, error) { + if result, err := j.DO.Last(); err != nil { + return nil, err + } else { + return result.(*model.Job), nil + } +} + +func (j jobDo) Find() ([]*model.Job, error) { + result, err := j.DO.Find() + return result.([]*model.Job), err +} + +func (j jobDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.Job, err error) { + buf := make([]*model.Job, 0, batchSize) + err = j.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 (j jobDo) FindInBatches(result *[]*model.Job, batchSize int, fc func(tx gen.Dao, batch int) error) error { + return j.DO.FindInBatches(result, batchSize, fc) +} + +func (j jobDo) Attrs(attrs ...field.AssignExpr) IJobDo { + return j.withDO(j.DO.Attrs(attrs...)) +} + +func (j jobDo) Assign(attrs ...field.AssignExpr) IJobDo { + return j.withDO(j.DO.Assign(attrs...)) +} + +func (j jobDo) Joins(fields ...field.RelationField) IJobDo { + for _, _f := range fields { + j = *j.withDO(j.DO.Joins(_f)) + } + return &j +} + +func (j jobDo) Preload(fields ...field.RelationField) IJobDo { + for _, _f := range fields { + j = *j.withDO(j.DO.Preload(_f)) + } + return &j +} + +func (j jobDo) FirstOrInit() (*model.Job, error) { + if result, err := j.DO.FirstOrInit(); err != nil { + return nil, err + } else { + return result.(*model.Job), nil + } +} + +func (j jobDo) FirstOrCreate() (*model.Job, error) { + if result, err := j.DO.FirstOrCreate(); err != nil { + return nil, err + } else { + return result.(*model.Job), nil + } +} + +func (j jobDo) FindByPage(offset int, limit int) (result []*model.Job, count int64, err error) { + result, err = j.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 = j.Offset(-1).Limit(-1).Count() + return +} + +func (j jobDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) { + count, err = j.Count() + if err != nil { + return + } + + err = j.Offset(offset).Limit(limit).Scan(result) + return +} + +func (j jobDo) Scan(result interface{}) (err error) { + return j.DO.Scan(result) +} + +func (j jobDo) Delete(models ...*model.Job) (result gen.ResultInfo, err error) { + return j.DO.Delete(models) +} + +func (j *jobDo) withDO(do gen.Dao) *jobDo { + j.DO = *do.(*gen.DO) + return j +} diff --git a/internal/database/query/notifications.gen.go b/internal/database/query/notifications.gen.go new file mode 100644 index 0000000..0d74f8e --- /dev/null +++ b/internal/database/query/notifications.gen.go @@ -0,0 +1,439 @@ +// 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 newNotification(db *gorm.DB, opts ...gen.DOOption) notification { + _notification := notification{} + + _notification.notificationDo.UseDB(db, opts...) + _notification.notificationDo.UseModel(&model.Notification{}) + + tableName := _notification.notificationDo.TableName() + _notification.ALL = field.NewAsterisk(tableName) + _notification.ID = field.NewString(tableName, "id") + _notification.UserID = field.NewString(tableName, "user_id") + _notification.Type = field.NewString(tableName, "type") + _notification.Title = field.NewString(tableName, "title") + _notification.Message = field.NewString(tableName, "message") + _notification.Metadata = field.NewString(tableName, "metadata") + _notification.ActionURL = field.NewString(tableName, "action_url") + _notification.ActionLabel = field.NewString(tableName, "action_label") + _notification.IsRead = field.NewBool(tableName, "is_read") + _notification.CreatedAt = field.NewTime(tableName, "created_at") + _notification.UpdatedAt = field.NewTime(tableName, "updated_at") + _notification.Version = field.NewInt64(tableName, "version") + + _notification.fillFieldMap() + + return _notification +} + +type notification struct { + notificationDo notificationDo + + ALL field.Asterisk + ID field.String + UserID field.String + Type field.String + Title field.String + Message field.String + Metadata field.String + ActionURL field.String + ActionLabel field.String + IsRead field.Bool + CreatedAt field.Time + UpdatedAt field.Time + Version field.Int64 + + fieldMap map[string]field.Expr +} + +func (n notification) Table(newTableName string) *notification { + n.notificationDo.UseTable(newTableName) + return n.updateTableName(newTableName) +} + +func (n notification) As(alias string) *notification { + n.notificationDo.DO = *(n.notificationDo.As(alias).(*gen.DO)) + return n.updateTableName(alias) +} + +func (n *notification) updateTableName(table string) *notification { + n.ALL = field.NewAsterisk(table) + n.ID = field.NewString(table, "id") + n.UserID = field.NewString(table, "user_id") + n.Type = field.NewString(table, "type") + n.Title = field.NewString(table, "title") + n.Message = field.NewString(table, "message") + n.Metadata = field.NewString(table, "metadata") + n.ActionURL = field.NewString(table, "action_url") + n.ActionLabel = field.NewString(table, "action_label") + n.IsRead = field.NewBool(table, "is_read") + n.CreatedAt = field.NewTime(table, "created_at") + n.UpdatedAt = field.NewTime(table, "updated_at") + n.Version = field.NewInt64(table, "version") + + n.fillFieldMap() + + return n +} + +func (n *notification) WithContext(ctx context.Context) INotificationDo { + return n.notificationDo.WithContext(ctx) +} + +func (n notification) TableName() string { return n.notificationDo.TableName() } + +func (n notification) Alias() string { return n.notificationDo.Alias() } + +func (n notification) Columns(cols ...field.Expr) gen.Columns { + return n.notificationDo.Columns(cols...) +} + +func (n *notification) GetFieldByName(fieldName string) (field.OrderExpr, bool) { + _f, ok := n.fieldMap[fieldName] + if !ok || _f == nil { + return nil, false + } + _oe, ok := _f.(field.OrderExpr) + return _oe, ok +} + +func (n *notification) fillFieldMap() { + n.fieldMap = make(map[string]field.Expr, 12) + n.fieldMap["id"] = n.ID + n.fieldMap["user_id"] = n.UserID + n.fieldMap["type"] = n.Type + n.fieldMap["title"] = n.Title + n.fieldMap["message"] = n.Message + n.fieldMap["metadata"] = n.Metadata + n.fieldMap["action_url"] = n.ActionURL + n.fieldMap["action_label"] = n.ActionLabel + n.fieldMap["is_read"] = n.IsRead + n.fieldMap["created_at"] = n.CreatedAt + n.fieldMap["updated_at"] = n.UpdatedAt + n.fieldMap["version"] = n.Version +} + +func (n notification) clone(db *gorm.DB) notification { + n.notificationDo.ReplaceConnPool(db.Statement.ConnPool) + return n +} + +func (n notification) replaceDB(db *gorm.DB) notification { + n.notificationDo.ReplaceDB(db) + return n +} + +type notificationDo struct{ gen.DO } + +type INotificationDo interface { + gen.SubQuery + Debug() INotificationDo + WithContext(ctx context.Context) INotificationDo + WithResult(fc func(tx gen.Dao)) gen.ResultInfo + ReplaceDB(db *gorm.DB) + ReadDB() INotificationDo + WriteDB() INotificationDo + As(alias string) gen.Dao + Session(config *gorm.Session) INotificationDo + Columns(cols ...field.Expr) gen.Columns + Clauses(conds ...clause.Expression) INotificationDo + Not(conds ...gen.Condition) INotificationDo + Or(conds ...gen.Condition) INotificationDo + Select(conds ...field.Expr) INotificationDo + Where(conds ...gen.Condition) INotificationDo + Order(conds ...field.Expr) INotificationDo + Distinct(cols ...field.Expr) INotificationDo + Omit(cols ...field.Expr) INotificationDo + Join(table schema.Tabler, on ...field.Expr) INotificationDo + LeftJoin(table schema.Tabler, on ...field.Expr) INotificationDo + RightJoin(table schema.Tabler, on ...field.Expr) INotificationDo + Group(cols ...field.Expr) INotificationDo + Having(conds ...gen.Condition) INotificationDo + Limit(limit int) INotificationDo + Offset(offset int) INotificationDo + Count() (count int64, err error) + Scopes(funcs ...func(gen.Dao) gen.Dao) INotificationDo + Unscoped() INotificationDo + Create(values ...*model.Notification) error + CreateInBatches(values []*model.Notification, batchSize int) error + Save(values ...*model.Notification) error + First() (*model.Notification, error) + Take() (*model.Notification, error) + Last() (*model.Notification, error) + Find() ([]*model.Notification, error) + FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.Notification, err error) + FindInBatches(result *[]*model.Notification, batchSize int, fc func(tx gen.Dao, batch int) error) error + Pluck(column field.Expr, dest interface{}) error + Delete(...*model.Notification) (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) INotificationDo + Assign(attrs ...field.AssignExpr) INotificationDo + Joins(fields ...field.RelationField) INotificationDo + Preload(fields ...field.RelationField) INotificationDo + FirstOrInit() (*model.Notification, error) + FirstOrCreate() (*model.Notification, error) + FindByPage(offset int, limit int) (result []*model.Notification, 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) INotificationDo + UnderlyingDB() *gorm.DB + schema.Tabler +} + +func (n notificationDo) Debug() INotificationDo { + return n.withDO(n.DO.Debug()) +} + +func (n notificationDo) WithContext(ctx context.Context) INotificationDo { + return n.withDO(n.DO.WithContext(ctx)) +} + +func (n notificationDo) ReadDB() INotificationDo { + return n.Clauses(dbresolver.Read) +} + +func (n notificationDo) WriteDB() INotificationDo { + return n.Clauses(dbresolver.Write) +} + +func (n notificationDo) Session(config *gorm.Session) INotificationDo { + return n.withDO(n.DO.Session(config)) +} + +func (n notificationDo) Clauses(conds ...clause.Expression) INotificationDo { + return n.withDO(n.DO.Clauses(conds...)) +} + +func (n notificationDo) Returning(value interface{}, columns ...string) INotificationDo { + return n.withDO(n.DO.Returning(value, columns...)) +} + +func (n notificationDo) Not(conds ...gen.Condition) INotificationDo { + return n.withDO(n.DO.Not(conds...)) +} + +func (n notificationDo) Or(conds ...gen.Condition) INotificationDo { + return n.withDO(n.DO.Or(conds...)) +} + +func (n notificationDo) Select(conds ...field.Expr) INotificationDo { + return n.withDO(n.DO.Select(conds...)) +} + +func (n notificationDo) Where(conds ...gen.Condition) INotificationDo { + return n.withDO(n.DO.Where(conds...)) +} + +func (n notificationDo) Order(conds ...field.Expr) INotificationDo { + return n.withDO(n.DO.Order(conds...)) +} + +func (n notificationDo) Distinct(cols ...field.Expr) INotificationDo { + return n.withDO(n.DO.Distinct(cols...)) +} + +func (n notificationDo) Omit(cols ...field.Expr) INotificationDo { + return n.withDO(n.DO.Omit(cols...)) +} + +func (n notificationDo) Join(table schema.Tabler, on ...field.Expr) INotificationDo { + return n.withDO(n.DO.Join(table, on...)) +} + +func (n notificationDo) LeftJoin(table schema.Tabler, on ...field.Expr) INotificationDo { + return n.withDO(n.DO.LeftJoin(table, on...)) +} + +func (n notificationDo) RightJoin(table schema.Tabler, on ...field.Expr) INotificationDo { + return n.withDO(n.DO.RightJoin(table, on...)) +} + +func (n notificationDo) Group(cols ...field.Expr) INotificationDo { + return n.withDO(n.DO.Group(cols...)) +} + +func (n notificationDo) Having(conds ...gen.Condition) INotificationDo { + return n.withDO(n.DO.Having(conds...)) +} + +func (n notificationDo) Limit(limit int) INotificationDo { + return n.withDO(n.DO.Limit(limit)) +} + +func (n notificationDo) Offset(offset int) INotificationDo { + return n.withDO(n.DO.Offset(offset)) +} + +func (n notificationDo) Scopes(funcs ...func(gen.Dao) gen.Dao) INotificationDo { + return n.withDO(n.DO.Scopes(funcs...)) +} + +func (n notificationDo) Unscoped() INotificationDo { + return n.withDO(n.DO.Unscoped()) +} + +func (n notificationDo) Create(values ...*model.Notification) error { + if len(values) == 0 { + return nil + } + return n.DO.Create(values) +} + +func (n notificationDo) CreateInBatches(values []*model.Notification, batchSize int) error { + return n.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 (n notificationDo) Save(values ...*model.Notification) error { + if len(values) == 0 { + return nil + } + return n.DO.Save(values) +} + +func (n notificationDo) First() (*model.Notification, error) { + if result, err := n.DO.First(); err != nil { + return nil, err + } else { + return result.(*model.Notification), nil + } +} + +func (n notificationDo) Take() (*model.Notification, error) { + if result, err := n.DO.Take(); err != nil { + return nil, err + } else { + return result.(*model.Notification), nil + } +} + +func (n notificationDo) Last() (*model.Notification, error) { + if result, err := n.DO.Last(); err != nil { + return nil, err + } else { + return result.(*model.Notification), nil + } +} + +func (n notificationDo) Find() ([]*model.Notification, error) { + result, err := n.DO.Find() + return result.([]*model.Notification), err +} + +func (n notificationDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.Notification, err error) { + buf := make([]*model.Notification, 0, batchSize) + err = n.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 (n notificationDo) FindInBatches(result *[]*model.Notification, batchSize int, fc func(tx gen.Dao, batch int) error) error { + return n.DO.FindInBatches(result, batchSize, fc) +} + +func (n notificationDo) Attrs(attrs ...field.AssignExpr) INotificationDo { + return n.withDO(n.DO.Attrs(attrs...)) +} + +func (n notificationDo) Assign(attrs ...field.AssignExpr) INotificationDo { + return n.withDO(n.DO.Assign(attrs...)) +} + +func (n notificationDo) Joins(fields ...field.RelationField) INotificationDo { + for _, _f := range fields { + n = *n.withDO(n.DO.Joins(_f)) + } + return &n +} + +func (n notificationDo) Preload(fields ...field.RelationField) INotificationDo { + for _, _f := range fields { + n = *n.withDO(n.DO.Preload(_f)) + } + return &n +} + +func (n notificationDo) FirstOrInit() (*model.Notification, error) { + if result, err := n.DO.FirstOrInit(); err != nil { + return nil, err + } else { + return result.(*model.Notification), nil + } +} + +func (n notificationDo) FirstOrCreate() (*model.Notification, error) { + if result, err := n.DO.FirstOrCreate(); err != nil { + return nil, err + } else { + return result.(*model.Notification), nil + } +} + +func (n notificationDo) FindByPage(offset int, limit int) (result []*model.Notification, count int64, err error) { + result, err = n.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 = n.Offset(-1).Limit(-1).Count() + return +} + +func (n notificationDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) { + count, err = n.Count() + if err != nil { + return + } + + err = n.Offset(offset).Limit(limit).Scan(result) + return +} + +func (n notificationDo) Scan(result interface{}) (err error) { + return n.DO.Scan(result) +} + +func (n notificationDo) Delete(models ...*model.Notification) (result gen.ResultInfo, err error) { + return n.DO.Delete(models) +} + +func (n *notificationDo) withDO(do gen.Dao) *notificationDo { + n.DO = *do.(*gen.DO) + return n +} diff --git a/internal/database/query/plan.gen.go b/internal/database/query/plan.gen.go index 2b5bc4e..4de7028 100644 --- a/internal/database/query/plan.gen.go +++ b/internal/database/query/plan.gen.go @@ -37,7 +37,7 @@ func newPlan(db *gorm.DB, opts ...gen.DOOption) plan { _plan.UploadLimit = field.NewInt32(tableName, "upload_limit") _plan.DurationLimit = field.NewInt32(tableName, "duration_limit") _plan.QualityLimit = field.NewString(tableName, "quality_limit") - _plan.Features = field.NewString(tableName, "features") + _plan.Features = field.NewField(tableName, "features") _plan.IsActive = field.NewBool(tableName, "is_active") _plan.Version = field.NewInt64(tableName, "version") @@ -59,7 +59,7 @@ type plan struct { UploadLimit field.Int32 DurationLimit field.Int32 QualityLimit field.String - Features field.String + Features field.Field IsActive field.Bool Version field.Int64 @@ -87,7 +87,7 @@ func (p *plan) updateTableName(table string) *plan { p.UploadLimit = field.NewInt32(table, "upload_limit") p.DurationLimit = field.NewInt32(table, "duration_limit") p.QualityLimit = field.NewString(table, "quality_limit") - p.Features = field.NewString(table, "features") + p.Features = field.NewField(table, "features") p.IsActive = field.NewBool(table, "is_active") p.Version = field.NewInt64(table, "version") diff --git a/internal/database/query/plan_subscriptions.gen.go b/internal/database/query/plan_subscriptions.gen.go new file mode 100644 index 0000000..472c60d --- /dev/null +++ b/internal/database/query/plan_subscriptions.gen.go @@ -0,0 +1,455 @@ +// 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 newPlanSubscription(db *gorm.DB, opts ...gen.DOOption) planSubscription { + _planSubscription := planSubscription{} + + _planSubscription.planSubscriptionDo.UseDB(db, opts...) + _planSubscription.planSubscriptionDo.UseModel(&model.PlanSubscription{}) + + tableName := _planSubscription.planSubscriptionDo.TableName() + _planSubscription.ALL = field.NewAsterisk(tableName) + _planSubscription.ID = field.NewString(tableName, "id") + _planSubscription.UserID = field.NewString(tableName, "user_id") + _planSubscription.PaymentID = field.NewString(tableName, "payment_id") + _planSubscription.PlanID = field.NewString(tableName, "plan_id") + _planSubscription.TermMonths = field.NewInt32(tableName, "term_months") + _planSubscription.PaymentMethod = field.NewString(tableName, "payment_method") + _planSubscription.WalletAmount = field.NewFloat64(tableName, "wallet_amount") + _planSubscription.TopupAmount = field.NewFloat64(tableName, "topup_amount") + _planSubscription.StartedAt = field.NewTime(tableName, "started_at") + _planSubscription.ExpiresAt = field.NewTime(tableName, "expires_at") + _planSubscription.Reminder7DSentAt = field.NewTime(tableName, "reminder_7d_sent_at") + _planSubscription.Reminder3DSentAt = field.NewTime(tableName, "reminder_3d_sent_at") + _planSubscription.Reminder1DSentAt = field.NewTime(tableName, "reminder_1d_sent_at") + _planSubscription.CreatedAt = field.NewTime(tableName, "created_at") + _planSubscription.UpdatedAt = field.NewTime(tableName, "updated_at") + _planSubscription.Version = field.NewInt64(tableName, "version") + + _planSubscription.fillFieldMap() + + return _planSubscription +} + +type planSubscription struct { + planSubscriptionDo planSubscriptionDo + + ALL field.Asterisk + ID field.String + UserID field.String + PaymentID field.String + PlanID field.String + TermMonths field.Int32 + PaymentMethod field.String + WalletAmount field.Float64 + TopupAmount field.Float64 + StartedAt field.Time + ExpiresAt field.Time + Reminder7DSentAt field.Time + Reminder3DSentAt field.Time + Reminder1DSentAt field.Time + CreatedAt field.Time + UpdatedAt field.Time + Version field.Int64 + + fieldMap map[string]field.Expr +} + +func (p planSubscription) Table(newTableName string) *planSubscription { + p.planSubscriptionDo.UseTable(newTableName) + return p.updateTableName(newTableName) +} + +func (p planSubscription) As(alias string) *planSubscription { + p.planSubscriptionDo.DO = *(p.planSubscriptionDo.As(alias).(*gen.DO)) + return p.updateTableName(alias) +} + +func (p *planSubscription) updateTableName(table string) *planSubscription { + p.ALL = field.NewAsterisk(table) + p.ID = field.NewString(table, "id") + p.UserID = field.NewString(table, "user_id") + p.PaymentID = field.NewString(table, "payment_id") + p.PlanID = field.NewString(table, "plan_id") + p.TermMonths = field.NewInt32(table, "term_months") + p.PaymentMethod = field.NewString(table, "payment_method") + p.WalletAmount = field.NewFloat64(table, "wallet_amount") + p.TopupAmount = field.NewFloat64(table, "topup_amount") + p.StartedAt = field.NewTime(table, "started_at") + p.ExpiresAt = field.NewTime(table, "expires_at") + p.Reminder7DSentAt = field.NewTime(table, "reminder_7d_sent_at") + p.Reminder3DSentAt = field.NewTime(table, "reminder_3d_sent_at") + p.Reminder1DSentAt = field.NewTime(table, "reminder_1d_sent_at") + 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 *planSubscription) WithContext(ctx context.Context) IPlanSubscriptionDo { + return p.planSubscriptionDo.WithContext(ctx) +} + +func (p planSubscription) TableName() string { return p.planSubscriptionDo.TableName() } + +func (p planSubscription) Alias() string { return p.planSubscriptionDo.Alias() } + +func (p planSubscription) Columns(cols ...field.Expr) gen.Columns { + return p.planSubscriptionDo.Columns(cols...) +} + +func (p *planSubscription) 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 *planSubscription) fillFieldMap() { + p.fieldMap = make(map[string]field.Expr, 16) + p.fieldMap["id"] = p.ID + p.fieldMap["user_id"] = p.UserID + p.fieldMap["payment_id"] = p.PaymentID + p.fieldMap["plan_id"] = p.PlanID + p.fieldMap["term_months"] = p.TermMonths + p.fieldMap["payment_method"] = p.PaymentMethod + p.fieldMap["wallet_amount"] = p.WalletAmount + p.fieldMap["topup_amount"] = p.TopupAmount + p.fieldMap["started_at"] = p.StartedAt + p.fieldMap["expires_at"] = p.ExpiresAt + p.fieldMap["reminder_7d_sent_at"] = p.Reminder7DSentAt + p.fieldMap["reminder_3d_sent_at"] = p.Reminder3DSentAt + p.fieldMap["reminder_1d_sent_at"] = p.Reminder1DSentAt + p.fieldMap["created_at"] = p.CreatedAt + p.fieldMap["updated_at"] = p.UpdatedAt + p.fieldMap["version"] = p.Version +} + +func (p planSubscription) clone(db *gorm.DB) planSubscription { + p.planSubscriptionDo.ReplaceConnPool(db.Statement.ConnPool) + return p +} + +func (p planSubscription) replaceDB(db *gorm.DB) planSubscription { + p.planSubscriptionDo.ReplaceDB(db) + return p +} + +type planSubscriptionDo struct{ gen.DO } + +type IPlanSubscriptionDo interface { + gen.SubQuery + Debug() IPlanSubscriptionDo + WithContext(ctx context.Context) IPlanSubscriptionDo + WithResult(fc func(tx gen.Dao)) gen.ResultInfo + ReplaceDB(db *gorm.DB) + ReadDB() IPlanSubscriptionDo + WriteDB() IPlanSubscriptionDo + As(alias string) gen.Dao + Session(config *gorm.Session) IPlanSubscriptionDo + Columns(cols ...field.Expr) gen.Columns + Clauses(conds ...clause.Expression) IPlanSubscriptionDo + Not(conds ...gen.Condition) IPlanSubscriptionDo + Or(conds ...gen.Condition) IPlanSubscriptionDo + Select(conds ...field.Expr) IPlanSubscriptionDo + Where(conds ...gen.Condition) IPlanSubscriptionDo + Order(conds ...field.Expr) IPlanSubscriptionDo + Distinct(cols ...field.Expr) IPlanSubscriptionDo + Omit(cols ...field.Expr) IPlanSubscriptionDo + Join(table schema.Tabler, on ...field.Expr) IPlanSubscriptionDo + LeftJoin(table schema.Tabler, on ...field.Expr) IPlanSubscriptionDo + RightJoin(table schema.Tabler, on ...field.Expr) IPlanSubscriptionDo + Group(cols ...field.Expr) IPlanSubscriptionDo + Having(conds ...gen.Condition) IPlanSubscriptionDo + Limit(limit int) IPlanSubscriptionDo + Offset(offset int) IPlanSubscriptionDo + Count() (count int64, err error) + Scopes(funcs ...func(gen.Dao) gen.Dao) IPlanSubscriptionDo + Unscoped() IPlanSubscriptionDo + Create(values ...*model.PlanSubscription) error + CreateInBatches(values []*model.PlanSubscription, batchSize int) error + Save(values ...*model.PlanSubscription) error + First() (*model.PlanSubscription, error) + Take() (*model.PlanSubscription, error) + Last() (*model.PlanSubscription, error) + Find() ([]*model.PlanSubscription, error) + FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.PlanSubscription, err error) + FindInBatches(result *[]*model.PlanSubscription, batchSize int, fc func(tx gen.Dao, batch int) error) error + Pluck(column field.Expr, dest interface{}) error + Delete(...*model.PlanSubscription) (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) IPlanSubscriptionDo + Assign(attrs ...field.AssignExpr) IPlanSubscriptionDo + Joins(fields ...field.RelationField) IPlanSubscriptionDo + Preload(fields ...field.RelationField) IPlanSubscriptionDo + FirstOrInit() (*model.PlanSubscription, error) + FirstOrCreate() (*model.PlanSubscription, error) + FindByPage(offset int, limit int) (result []*model.PlanSubscription, 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) IPlanSubscriptionDo + UnderlyingDB() *gorm.DB + schema.Tabler +} + +func (p planSubscriptionDo) Debug() IPlanSubscriptionDo { + return p.withDO(p.DO.Debug()) +} + +func (p planSubscriptionDo) WithContext(ctx context.Context) IPlanSubscriptionDo { + return p.withDO(p.DO.WithContext(ctx)) +} + +func (p planSubscriptionDo) ReadDB() IPlanSubscriptionDo { + return p.Clauses(dbresolver.Read) +} + +func (p planSubscriptionDo) WriteDB() IPlanSubscriptionDo { + return p.Clauses(dbresolver.Write) +} + +func (p planSubscriptionDo) Session(config *gorm.Session) IPlanSubscriptionDo { + return p.withDO(p.DO.Session(config)) +} + +func (p planSubscriptionDo) Clauses(conds ...clause.Expression) IPlanSubscriptionDo { + return p.withDO(p.DO.Clauses(conds...)) +} + +func (p planSubscriptionDo) Returning(value interface{}, columns ...string) IPlanSubscriptionDo { + return p.withDO(p.DO.Returning(value, columns...)) +} + +func (p planSubscriptionDo) Not(conds ...gen.Condition) IPlanSubscriptionDo { + return p.withDO(p.DO.Not(conds...)) +} + +func (p planSubscriptionDo) Or(conds ...gen.Condition) IPlanSubscriptionDo { + return p.withDO(p.DO.Or(conds...)) +} + +func (p planSubscriptionDo) Select(conds ...field.Expr) IPlanSubscriptionDo { + return p.withDO(p.DO.Select(conds...)) +} + +func (p planSubscriptionDo) Where(conds ...gen.Condition) IPlanSubscriptionDo { + return p.withDO(p.DO.Where(conds...)) +} + +func (p planSubscriptionDo) Order(conds ...field.Expr) IPlanSubscriptionDo { + return p.withDO(p.DO.Order(conds...)) +} + +func (p planSubscriptionDo) Distinct(cols ...field.Expr) IPlanSubscriptionDo { + return p.withDO(p.DO.Distinct(cols...)) +} + +func (p planSubscriptionDo) Omit(cols ...field.Expr) IPlanSubscriptionDo { + return p.withDO(p.DO.Omit(cols...)) +} + +func (p planSubscriptionDo) Join(table schema.Tabler, on ...field.Expr) IPlanSubscriptionDo { + return p.withDO(p.DO.Join(table, on...)) +} + +func (p planSubscriptionDo) LeftJoin(table schema.Tabler, on ...field.Expr) IPlanSubscriptionDo { + return p.withDO(p.DO.LeftJoin(table, on...)) +} + +func (p planSubscriptionDo) RightJoin(table schema.Tabler, on ...field.Expr) IPlanSubscriptionDo { + return p.withDO(p.DO.RightJoin(table, on...)) +} + +func (p planSubscriptionDo) Group(cols ...field.Expr) IPlanSubscriptionDo { + return p.withDO(p.DO.Group(cols...)) +} + +func (p planSubscriptionDo) Having(conds ...gen.Condition) IPlanSubscriptionDo { + return p.withDO(p.DO.Having(conds...)) +} + +func (p planSubscriptionDo) Limit(limit int) IPlanSubscriptionDo { + return p.withDO(p.DO.Limit(limit)) +} + +func (p planSubscriptionDo) Offset(offset int) IPlanSubscriptionDo { + return p.withDO(p.DO.Offset(offset)) +} + +func (p planSubscriptionDo) Scopes(funcs ...func(gen.Dao) gen.Dao) IPlanSubscriptionDo { + return p.withDO(p.DO.Scopes(funcs...)) +} + +func (p planSubscriptionDo) Unscoped() IPlanSubscriptionDo { + return p.withDO(p.DO.Unscoped()) +} + +func (p planSubscriptionDo) Create(values ...*model.PlanSubscription) error { + if len(values) == 0 { + return nil + } + return p.DO.Create(values) +} + +func (p planSubscriptionDo) CreateInBatches(values []*model.PlanSubscription, 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 planSubscriptionDo) Save(values ...*model.PlanSubscription) error { + if len(values) == 0 { + return nil + } + return p.DO.Save(values) +} + +func (p planSubscriptionDo) First() (*model.PlanSubscription, error) { + if result, err := p.DO.First(); err != nil { + return nil, err + } else { + return result.(*model.PlanSubscription), nil + } +} + +func (p planSubscriptionDo) Take() (*model.PlanSubscription, error) { + if result, err := p.DO.Take(); err != nil { + return nil, err + } else { + return result.(*model.PlanSubscription), nil + } +} + +func (p planSubscriptionDo) Last() (*model.PlanSubscription, error) { + if result, err := p.DO.Last(); err != nil { + return nil, err + } else { + return result.(*model.PlanSubscription), nil + } +} + +func (p planSubscriptionDo) Find() ([]*model.PlanSubscription, error) { + result, err := p.DO.Find() + return result.([]*model.PlanSubscription), err +} + +func (p planSubscriptionDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.PlanSubscription, err error) { + buf := make([]*model.PlanSubscription, 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 planSubscriptionDo) FindInBatches(result *[]*model.PlanSubscription, batchSize int, fc func(tx gen.Dao, batch int) error) error { + return p.DO.FindInBatches(result, batchSize, fc) +} + +func (p planSubscriptionDo) Attrs(attrs ...field.AssignExpr) IPlanSubscriptionDo { + return p.withDO(p.DO.Attrs(attrs...)) +} + +func (p planSubscriptionDo) Assign(attrs ...field.AssignExpr) IPlanSubscriptionDo { + return p.withDO(p.DO.Assign(attrs...)) +} + +func (p planSubscriptionDo) Joins(fields ...field.RelationField) IPlanSubscriptionDo { + for _, _f := range fields { + p = *p.withDO(p.DO.Joins(_f)) + } + return &p +} + +func (p planSubscriptionDo) Preload(fields ...field.RelationField) IPlanSubscriptionDo { + for _, _f := range fields { + p = *p.withDO(p.DO.Preload(_f)) + } + return &p +} + +func (p planSubscriptionDo) FirstOrInit() (*model.PlanSubscription, error) { + if result, err := p.DO.FirstOrInit(); err != nil { + return nil, err + } else { + return result.(*model.PlanSubscription), nil + } +} + +func (p planSubscriptionDo) FirstOrCreate() (*model.PlanSubscription, error) { + if result, err := p.DO.FirstOrCreate(); err != nil { + return nil, err + } else { + return result.(*model.PlanSubscription), nil + } +} + +func (p planSubscriptionDo) FindByPage(offset int, limit int) (result []*model.PlanSubscription, 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 planSubscriptionDo) 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 planSubscriptionDo) Scan(result interface{}) (err error) { + return p.DO.Scan(result) +} + +func (p planSubscriptionDo) Delete(models ...*model.PlanSubscription) (result gen.ResultInfo, err error) { + return p.DO.Delete(models) +} + +func (p *planSubscriptionDo) withDO(do gen.Dao) *planSubscriptionDo { + p.DO = *do.(*gen.DO) + return p +} diff --git a/internal/database/query/user_preferences.gen.go b/internal/database/query/user_preferences.gen.go new file mode 100644 index 0000000..8e94618 --- /dev/null +++ b/internal/database/query/user_preferences.gen.go @@ -0,0 +1,463 @@ +// 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 newUserPreference(db *gorm.DB, opts ...gen.DOOption) userPreference { + _userPreference := userPreference{} + + _userPreference.userPreferenceDo.UseDB(db, opts...) + _userPreference.userPreferenceDo.UseModel(&model.UserPreference{}) + + tableName := _userPreference.userPreferenceDo.TableName() + _userPreference.ALL = field.NewAsterisk(tableName) + _userPreference.UserID = field.NewString(tableName, "user_id") + _userPreference.Language = field.NewString(tableName, "language") + _userPreference.Locale = field.NewString(tableName, "locale") + _userPreference.EmailNotifications = field.NewBool(tableName, "email_notifications") + _userPreference.PushNotifications = field.NewBool(tableName, "push_notifications") + _userPreference.MarketingNotifications = field.NewBool(tableName, "marketing_notifications") + _userPreference.TelegramNotifications = field.NewBool(tableName, "telegram_notifications") + _userPreference.Autoplay = field.NewBool(tableName, "autoplay") + _userPreference.Loop = field.NewBool(tableName, "loop") + _userPreference.Muted = field.NewBool(tableName, "muted") + _userPreference.ShowControls = field.NewBool(tableName, "show_controls") + _userPreference.Pip = field.NewBool(tableName, "pip") + _userPreference.Airplay = field.NewBool(tableName, "airplay") + _userPreference.Chromecast = field.NewBool(tableName, "chromecast") + _userPreference.CreatedAt = field.NewTime(tableName, "created_at") + _userPreference.UpdatedAt = field.NewTime(tableName, "updated_at") + _userPreference.EncrytionM3u8 = field.NewBool(tableName, "encrytion_m3u8") + _userPreference.Version = field.NewInt64(tableName, "version") + + _userPreference.fillFieldMap() + + return _userPreference +} + +type userPreference struct { + userPreferenceDo userPreferenceDo + + ALL field.Asterisk + UserID field.String + Language field.String + Locale field.String + EmailNotifications field.Bool + PushNotifications field.Bool + MarketingNotifications field.Bool + TelegramNotifications field.Bool + Autoplay field.Bool + Loop field.Bool + Muted field.Bool + ShowControls field.Bool + Pip field.Bool + Airplay field.Bool + Chromecast field.Bool + CreatedAt field.Time + UpdatedAt field.Time + EncrytionM3u8 field.Bool + Version field.Int64 + + fieldMap map[string]field.Expr +} + +func (u userPreference) Table(newTableName string) *userPreference { + u.userPreferenceDo.UseTable(newTableName) + return u.updateTableName(newTableName) +} + +func (u userPreference) As(alias string) *userPreference { + u.userPreferenceDo.DO = *(u.userPreferenceDo.As(alias).(*gen.DO)) + return u.updateTableName(alias) +} + +func (u *userPreference) updateTableName(table string) *userPreference { + u.ALL = field.NewAsterisk(table) + u.UserID = field.NewString(table, "user_id") + u.Language = field.NewString(table, "language") + u.Locale = field.NewString(table, "locale") + u.EmailNotifications = field.NewBool(table, "email_notifications") + u.PushNotifications = field.NewBool(table, "push_notifications") + u.MarketingNotifications = field.NewBool(table, "marketing_notifications") + u.TelegramNotifications = field.NewBool(table, "telegram_notifications") + u.Autoplay = field.NewBool(table, "autoplay") + u.Loop = field.NewBool(table, "loop") + u.Muted = field.NewBool(table, "muted") + u.ShowControls = field.NewBool(table, "show_controls") + u.Pip = field.NewBool(table, "pip") + u.Airplay = field.NewBool(table, "airplay") + u.Chromecast = field.NewBool(table, "chromecast") + u.CreatedAt = field.NewTime(table, "created_at") + u.UpdatedAt = field.NewTime(table, "updated_at") + u.EncrytionM3u8 = field.NewBool(table, "encrytion_m3u8") + u.Version = field.NewInt64(table, "version") + + u.fillFieldMap() + + return u +} + +func (u *userPreference) WithContext(ctx context.Context) IUserPreferenceDo { + return u.userPreferenceDo.WithContext(ctx) +} + +func (u userPreference) TableName() string { return u.userPreferenceDo.TableName() } + +func (u userPreference) Alias() string { return u.userPreferenceDo.Alias() } + +func (u userPreference) Columns(cols ...field.Expr) gen.Columns { + return u.userPreferenceDo.Columns(cols...) +} + +func (u *userPreference) GetFieldByName(fieldName string) (field.OrderExpr, bool) { + _f, ok := u.fieldMap[fieldName] + if !ok || _f == nil { + return nil, false + } + _oe, ok := _f.(field.OrderExpr) + return _oe, ok +} + +func (u *userPreference) fillFieldMap() { + u.fieldMap = make(map[string]field.Expr, 18) + u.fieldMap["user_id"] = u.UserID + u.fieldMap["language"] = u.Language + u.fieldMap["locale"] = u.Locale + u.fieldMap["email_notifications"] = u.EmailNotifications + u.fieldMap["push_notifications"] = u.PushNotifications + u.fieldMap["marketing_notifications"] = u.MarketingNotifications + u.fieldMap["telegram_notifications"] = u.TelegramNotifications + u.fieldMap["autoplay"] = u.Autoplay + u.fieldMap["loop"] = u.Loop + u.fieldMap["muted"] = u.Muted + u.fieldMap["show_controls"] = u.ShowControls + u.fieldMap["pip"] = u.Pip + u.fieldMap["airplay"] = u.Airplay + u.fieldMap["chromecast"] = u.Chromecast + u.fieldMap["created_at"] = u.CreatedAt + u.fieldMap["updated_at"] = u.UpdatedAt + u.fieldMap["encrytion_m3u8"] = u.EncrytionM3u8 + u.fieldMap["version"] = u.Version +} + +func (u userPreference) clone(db *gorm.DB) userPreference { + u.userPreferenceDo.ReplaceConnPool(db.Statement.ConnPool) + return u +} + +func (u userPreference) replaceDB(db *gorm.DB) userPreference { + u.userPreferenceDo.ReplaceDB(db) + return u +} + +type userPreferenceDo struct{ gen.DO } + +type IUserPreferenceDo interface { + gen.SubQuery + Debug() IUserPreferenceDo + WithContext(ctx context.Context) IUserPreferenceDo + WithResult(fc func(tx gen.Dao)) gen.ResultInfo + ReplaceDB(db *gorm.DB) + ReadDB() IUserPreferenceDo + WriteDB() IUserPreferenceDo + As(alias string) gen.Dao + Session(config *gorm.Session) IUserPreferenceDo + Columns(cols ...field.Expr) gen.Columns + Clauses(conds ...clause.Expression) IUserPreferenceDo + Not(conds ...gen.Condition) IUserPreferenceDo + Or(conds ...gen.Condition) IUserPreferenceDo + Select(conds ...field.Expr) IUserPreferenceDo + Where(conds ...gen.Condition) IUserPreferenceDo + Order(conds ...field.Expr) IUserPreferenceDo + Distinct(cols ...field.Expr) IUserPreferenceDo + Omit(cols ...field.Expr) IUserPreferenceDo + Join(table schema.Tabler, on ...field.Expr) IUserPreferenceDo + LeftJoin(table schema.Tabler, on ...field.Expr) IUserPreferenceDo + RightJoin(table schema.Tabler, on ...field.Expr) IUserPreferenceDo + Group(cols ...field.Expr) IUserPreferenceDo + Having(conds ...gen.Condition) IUserPreferenceDo + Limit(limit int) IUserPreferenceDo + Offset(offset int) IUserPreferenceDo + Count() (count int64, err error) + Scopes(funcs ...func(gen.Dao) gen.Dao) IUserPreferenceDo + Unscoped() IUserPreferenceDo + Create(values ...*model.UserPreference) error + CreateInBatches(values []*model.UserPreference, batchSize int) error + Save(values ...*model.UserPreference) error + First() (*model.UserPreference, error) + Take() (*model.UserPreference, error) + Last() (*model.UserPreference, error) + Find() ([]*model.UserPreference, error) + FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.UserPreference, err error) + FindInBatches(result *[]*model.UserPreference, batchSize int, fc func(tx gen.Dao, batch int) error) error + Pluck(column field.Expr, dest interface{}) error + Delete(...*model.UserPreference) (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) IUserPreferenceDo + Assign(attrs ...field.AssignExpr) IUserPreferenceDo + Joins(fields ...field.RelationField) IUserPreferenceDo + Preload(fields ...field.RelationField) IUserPreferenceDo + FirstOrInit() (*model.UserPreference, error) + FirstOrCreate() (*model.UserPreference, error) + FindByPage(offset int, limit int) (result []*model.UserPreference, 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) IUserPreferenceDo + UnderlyingDB() *gorm.DB + schema.Tabler +} + +func (u userPreferenceDo) Debug() IUserPreferenceDo { + return u.withDO(u.DO.Debug()) +} + +func (u userPreferenceDo) WithContext(ctx context.Context) IUserPreferenceDo { + return u.withDO(u.DO.WithContext(ctx)) +} + +func (u userPreferenceDo) ReadDB() IUserPreferenceDo { + return u.Clauses(dbresolver.Read) +} + +func (u userPreferenceDo) WriteDB() IUserPreferenceDo { + return u.Clauses(dbresolver.Write) +} + +func (u userPreferenceDo) Session(config *gorm.Session) IUserPreferenceDo { + return u.withDO(u.DO.Session(config)) +} + +func (u userPreferenceDo) Clauses(conds ...clause.Expression) IUserPreferenceDo { + return u.withDO(u.DO.Clauses(conds...)) +} + +func (u userPreferenceDo) Returning(value interface{}, columns ...string) IUserPreferenceDo { + return u.withDO(u.DO.Returning(value, columns...)) +} + +func (u userPreferenceDo) Not(conds ...gen.Condition) IUserPreferenceDo { + return u.withDO(u.DO.Not(conds...)) +} + +func (u userPreferenceDo) Or(conds ...gen.Condition) IUserPreferenceDo { + return u.withDO(u.DO.Or(conds...)) +} + +func (u userPreferenceDo) Select(conds ...field.Expr) IUserPreferenceDo { + return u.withDO(u.DO.Select(conds...)) +} + +func (u userPreferenceDo) Where(conds ...gen.Condition) IUserPreferenceDo { + return u.withDO(u.DO.Where(conds...)) +} + +func (u userPreferenceDo) Order(conds ...field.Expr) IUserPreferenceDo { + return u.withDO(u.DO.Order(conds...)) +} + +func (u userPreferenceDo) Distinct(cols ...field.Expr) IUserPreferenceDo { + return u.withDO(u.DO.Distinct(cols...)) +} + +func (u userPreferenceDo) Omit(cols ...field.Expr) IUserPreferenceDo { + return u.withDO(u.DO.Omit(cols...)) +} + +func (u userPreferenceDo) Join(table schema.Tabler, on ...field.Expr) IUserPreferenceDo { + return u.withDO(u.DO.Join(table, on...)) +} + +func (u userPreferenceDo) LeftJoin(table schema.Tabler, on ...field.Expr) IUserPreferenceDo { + return u.withDO(u.DO.LeftJoin(table, on...)) +} + +func (u userPreferenceDo) RightJoin(table schema.Tabler, on ...field.Expr) IUserPreferenceDo { + return u.withDO(u.DO.RightJoin(table, on...)) +} + +func (u userPreferenceDo) Group(cols ...field.Expr) IUserPreferenceDo { + return u.withDO(u.DO.Group(cols...)) +} + +func (u userPreferenceDo) Having(conds ...gen.Condition) IUserPreferenceDo { + return u.withDO(u.DO.Having(conds...)) +} + +func (u userPreferenceDo) Limit(limit int) IUserPreferenceDo { + return u.withDO(u.DO.Limit(limit)) +} + +func (u userPreferenceDo) Offset(offset int) IUserPreferenceDo { + return u.withDO(u.DO.Offset(offset)) +} + +func (u userPreferenceDo) Scopes(funcs ...func(gen.Dao) gen.Dao) IUserPreferenceDo { + return u.withDO(u.DO.Scopes(funcs...)) +} + +func (u userPreferenceDo) Unscoped() IUserPreferenceDo { + return u.withDO(u.DO.Unscoped()) +} + +func (u userPreferenceDo) Create(values ...*model.UserPreference) error { + if len(values) == 0 { + return nil + } + return u.DO.Create(values) +} + +func (u userPreferenceDo) CreateInBatches(values []*model.UserPreference, batchSize int) error { + return u.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 (u userPreferenceDo) Save(values ...*model.UserPreference) error { + if len(values) == 0 { + return nil + } + return u.DO.Save(values) +} + +func (u userPreferenceDo) First() (*model.UserPreference, error) { + if result, err := u.DO.First(); err != nil { + return nil, err + } else { + return result.(*model.UserPreference), nil + } +} + +func (u userPreferenceDo) Take() (*model.UserPreference, error) { + if result, err := u.DO.Take(); err != nil { + return nil, err + } else { + return result.(*model.UserPreference), nil + } +} + +func (u userPreferenceDo) Last() (*model.UserPreference, error) { + if result, err := u.DO.Last(); err != nil { + return nil, err + } else { + return result.(*model.UserPreference), nil + } +} + +func (u userPreferenceDo) Find() ([]*model.UserPreference, error) { + result, err := u.DO.Find() + return result.([]*model.UserPreference), err +} + +func (u userPreferenceDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.UserPreference, err error) { + buf := make([]*model.UserPreference, 0, batchSize) + err = u.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 (u userPreferenceDo) FindInBatches(result *[]*model.UserPreference, batchSize int, fc func(tx gen.Dao, batch int) error) error { + return u.DO.FindInBatches(result, batchSize, fc) +} + +func (u userPreferenceDo) Attrs(attrs ...field.AssignExpr) IUserPreferenceDo { + return u.withDO(u.DO.Attrs(attrs...)) +} + +func (u userPreferenceDo) Assign(attrs ...field.AssignExpr) IUserPreferenceDo { + return u.withDO(u.DO.Assign(attrs...)) +} + +func (u userPreferenceDo) Joins(fields ...field.RelationField) IUserPreferenceDo { + for _, _f := range fields { + u = *u.withDO(u.DO.Joins(_f)) + } + return &u +} + +func (u userPreferenceDo) Preload(fields ...field.RelationField) IUserPreferenceDo { + for _, _f := range fields { + u = *u.withDO(u.DO.Preload(_f)) + } + return &u +} + +func (u userPreferenceDo) FirstOrInit() (*model.UserPreference, error) { + if result, err := u.DO.FirstOrInit(); err != nil { + return nil, err + } else { + return result.(*model.UserPreference), nil + } +} + +func (u userPreferenceDo) FirstOrCreate() (*model.UserPreference, error) { + if result, err := u.DO.FirstOrCreate(); err != nil { + return nil, err + } else { + return result.(*model.UserPreference), nil + } +} + +func (u userPreferenceDo) FindByPage(offset int, limit int) (result []*model.UserPreference, count int64, err error) { + result, err = u.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 = u.Offset(-1).Limit(-1).Count() + return +} + +func (u userPreferenceDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) { + count, err = u.Count() + if err != nil { + return + } + + err = u.Offset(offset).Limit(limit).Scan(result) + return +} + +func (u userPreferenceDo) Scan(result interface{}) (err error) { + return u.DO.Scan(result) +} + +func (u userPreferenceDo) Delete(models ...*model.UserPreference) (result gen.ResultInfo, err error) { + return u.DO.Delete(models) +} + +func (u *userPreferenceDo) withDO(do gen.Dao) *userPreferenceDo { + u.DO = *do.(*gen.DO) + return u +} diff --git a/internal/database/query/video_ad_configs.gen.go b/internal/database/query/video_ad_configs.gen.go new file mode 100644 index 0000000..5eb3c9a --- /dev/null +++ b/internal/database/query/video_ad_configs.gen.go @@ -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 newVideoAdConfig(db *gorm.DB, opts ...gen.DOOption) videoAdConfig { + _videoAdConfig := videoAdConfig{} + + _videoAdConfig.videoAdConfigDo.UseDB(db, opts...) + _videoAdConfig.videoAdConfigDo.UseModel(&model.VideoAdConfig{}) + + tableName := _videoAdConfig.videoAdConfigDo.TableName() + _videoAdConfig.ALL = field.NewAsterisk(tableName) + _videoAdConfig.VideoID = field.NewString(tableName, "video_id") + _videoAdConfig.UserID = field.NewString(tableName, "user_id") + _videoAdConfig.AdTemplateID = field.NewString(tableName, "ad_template_id") + _videoAdConfig.VastTagURL = field.NewString(tableName, "vast_tag_url") + _videoAdConfig.AdFormat = field.NewString(tableName, "ad_format") + _videoAdConfig.Duration = field.NewInt64(tableName, "duration") + _videoAdConfig.CreatedAt = field.NewTime(tableName, "created_at") + _videoAdConfig.UpdatedAt = field.NewTime(tableName, "updated_at") + _videoAdConfig.Version = field.NewInt64(tableName, "version") + + _videoAdConfig.fillFieldMap() + + return _videoAdConfig +} + +type videoAdConfig struct { + videoAdConfigDo videoAdConfigDo + + ALL field.Asterisk + VideoID field.String + UserID field.String + AdTemplateID field.String + VastTagURL field.String + AdFormat field.String + Duration field.Int64 + CreatedAt field.Time + UpdatedAt field.Time + Version field.Int64 + + fieldMap map[string]field.Expr +} + +func (v videoAdConfig) Table(newTableName string) *videoAdConfig { + v.videoAdConfigDo.UseTable(newTableName) + return v.updateTableName(newTableName) +} + +func (v videoAdConfig) As(alias string) *videoAdConfig { + v.videoAdConfigDo.DO = *(v.videoAdConfigDo.As(alias).(*gen.DO)) + return v.updateTableName(alias) +} + +func (v *videoAdConfig) updateTableName(table string) *videoAdConfig { + v.ALL = field.NewAsterisk(table) + v.VideoID = field.NewString(table, "video_id") + v.UserID = field.NewString(table, "user_id") + v.AdTemplateID = field.NewString(table, "ad_template_id") + v.VastTagURL = field.NewString(table, "vast_tag_url") + v.AdFormat = field.NewString(table, "ad_format") + v.Duration = field.NewInt64(table, "duration") + v.CreatedAt = field.NewTime(table, "created_at") + v.UpdatedAt = field.NewTime(table, "updated_at") + v.Version = field.NewInt64(table, "version") + + v.fillFieldMap() + + return v +} + +func (v *videoAdConfig) WithContext(ctx context.Context) IVideoAdConfigDo { + return v.videoAdConfigDo.WithContext(ctx) +} + +func (v videoAdConfig) TableName() string { return v.videoAdConfigDo.TableName() } + +func (v videoAdConfig) Alias() string { return v.videoAdConfigDo.Alias() } + +func (v videoAdConfig) Columns(cols ...field.Expr) gen.Columns { + return v.videoAdConfigDo.Columns(cols...) +} + +func (v *videoAdConfig) GetFieldByName(fieldName string) (field.OrderExpr, bool) { + _f, ok := v.fieldMap[fieldName] + if !ok || _f == nil { + return nil, false + } + _oe, ok := _f.(field.OrderExpr) + return _oe, ok +} + +func (v *videoAdConfig) fillFieldMap() { + v.fieldMap = make(map[string]field.Expr, 9) + v.fieldMap["video_id"] = v.VideoID + v.fieldMap["user_id"] = v.UserID + v.fieldMap["ad_template_id"] = v.AdTemplateID + v.fieldMap["vast_tag_url"] = v.VastTagURL + v.fieldMap["ad_format"] = v.AdFormat + v.fieldMap["duration"] = v.Duration + v.fieldMap["created_at"] = v.CreatedAt + v.fieldMap["updated_at"] = v.UpdatedAt + v.fieldMap["version"] = v.Version +} + +func (v videoAdConfig) clone(db *gorm.DB) videoAdConfig { + v.videoAdConfigDo.ReplaceConnPool(db.Statement.ConnPool) + return v +} + +func (v videoAdConfig) replaceDB(db *gorm.DB) videoAdConfig { + v.videoAdConfigDo.ReplaceDB(db) + return v +} + +type videoAdConfigDo struct{ gen.DO } + +type IVideoAdConfigDo interface { + gen.SubQuery + Debug() IVideoAdConfigDo + WithContext(ctx context.Context) IVideoAdConfigDo + WithResult(fc func(tx gen.Dao)) gen.ResultInfo + ReplaceDB(db *gorm.DB) + ReadDB() IVideoAdConfigDo + WriteDB() IVideoAdConfigDo + As(alias string) gen.Dao + Session(config *gorm.Session) IVideoAdConfigDo + Columns(cols ...field.Expr) gen.Columns + Clauses(conds ...clause.Expression) IVideoAdConfigDo + Not(conds ...gen.Condition) IVideoAdConfigDo + Or(conds ...gen.Condition) IVideoAdConfigDo + Select(conds ...field.Expr) IVideoAdConfigDo + Where(conds ...gen.Condition) IVideoAdConfigDo + Order(conds ...field.Expr) IVideoAdConfigDo + Distinct(cols ...field.Expr) IVideoAdConfigDo + Omit(cols ...field.Expr) IVideoAdConfigDo + Join(table schema.Tabler, on ...field.Expr) IVideoAdConfigDo + LeftJoin(table schema.Tabler, on ...field.Expr) IVideoAdConfigDo + RightJoin(table schema.Tabler, on ...field.Expr) IVideoAdConfigDo + Group(cols ...field.Expr) IVideoAdConfigDo + Having(conds ...gen.Condition) IVideoAdConfigDo + Limit(limit int) IVideoAdConfigDo + Offset(offset int) IVideoAdConfigDo + Count() (count int64, err error) + Scopes(funcs ...func(gen.Dao) gen.Dao) IVideoAdConfigDo + Unscoped() IVideoAdConfigDo + Create(values ...*model.VideoAdConfig) error + CreateInBatches(values []*model.VideoAdConfig, batchSize int) error + Save(values ...*model.VideoAdConfig) error + First() (*model.VideoAdConfig, error) + Take() (*model.VideoAdConfig, error) + Last() (*model.VideoAdConfig, error) + Find() ([]*model.VideoAdConfig, error) + FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.VideoAdConfig, err error) + FindInBatches(result *[]*model.VideoAdConfig, batchSize int, fc func(tx gen.Dao, batch int) error) error + Pluck(column field.Expr, dest interface{}) error + Delete(...*model.VideoAdConfig) (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) IVideoAdConfigDo + Assign(attrs ...field.AssignExpr) IVideoAdConfigDo + Joins(fields ...field.RelationField) IVideoAdConfigDo + Preload(fields ...field.RelationField) IVideoAdConfigDo + FirstOrInit() (*model.VideoAdConfig, error) + FirstOrCreate() (*model.VideoAdConfig, error) + FindByPage(offset int, limit int) (result []*model.VideoAdConfig, 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) IVideoAdConfigDo + UnderlyingDB() *gorm.DB + schema.Tabler +} + +func (v videoAdConfigDo) Debug() IVideoAdConfigDo { + return v.withDO(v.DO.Debug()) +} + +func (v videoAdConfigDo) WithContext(ctx context.Context) IVideoAdConfigDo { + return v.withDO(v.DO.WithContext(ctx)) +} + +func (v videoAdConfigDo) ReadDB() IVideoAdConfigDo { + return v.Clauses(dbresolver.Read) +} + +func (v videoAdConfigDo) WriteDB() IVideoAdConfigDo { + return v.Clauses(dbresolver.Write) +} + +func (v videoAdConfigDo) Session(config *gorm.Session) IVideoAdConfigDo { + return v.withDO(v.DO.Session(config)) +} + +func (v videoAdConfigDo) Clauses(conds ...clause.Expression) IVideoAdConfigDo { + return v.withDO(v.DO.Clauses(conds...)) +} + +func (v videoAdConfigDo) Returning(value interface{}, columns ...string) IVideoAdConfigDo { + return v.withDO(v.DO.Returning(value, columns...)) +} + +func (v videoAdConfigDo) Not(conds ...gen.Condition) IVideoAdConfigDo { + return v.withDO(v.DO.Not(conds...)) +} + +func (v videoAdConfigDo) Or(conds ...gen.Condition) IVideoAdConfigDo { + return v.withDO(v.DO.Or(conds...)) +} + +func (v videoAdConfigDo) Select(conds ...field.Expr) IVideoAdConfigDo { + return v.withDO(v.DO.Select(conds...)) +} + +func (v videoAdConfigDo) Where(conds ...gen.Condition) IVideoAdConfigDo { + return v.withDO(v.DO.Where(conds...)) +} + +func (v videoAdConfigDo) Order(conds ...field.Expr) IVideoAdConfigDo { + return v.withDO(v.DO.Order(conds...)) +} + +func (v videoAdConfigDo) Distinct(cols ...field.Expr) IVideoAdConfigDo { + return v.withDO(v.DO.Distinct(cols...)) +} + +func (v videoAdConfigDo) Omit(cols ...field.Expr) IVideoAdConfigDo { + return v.withDO(v.DO.Omit(cols...)) +} + +func (v videoAdConfigDo) Join(table schema.Tabler, on ...field.Expr) IVideoAdConfigDo { + return v.withDO(v.DO.Join(table, on...)) +} + +func (v videoAdConfigDo) LeftJoin(table schema.Tabler, on ...field.Expr) IVideoAdConfigDo { + return v.withDO(v.DO.LeftJoin(table, on...)) +} + +func (v videoAdConfigDo) RightJoin(table schema.Tabler, on ...field.Expr) IVideoAdConfigDo { + return v.withDO(v.DO.RightJoin(table, on...)) +} + +func (v videoAdConfigDo) Group(cols ...field.Expr) IVideoAdConfigDo { + return v.withDO(v.DO.Group(cols...)) +} + +func (v videoAdConfigDo) Having(conds ...gen.Condition) IVideoAdConfigDo { + return v.withDO(v.DO.Having(conds...)) +} + +func (v videoAdConfigDo) Limit(limit int) IVideoAdConfigDo { + return v.withDO(v.DO.Limit(limit)) +} + +func (v videoAdConfigDo) Offset(offset int) IVideoAdConfigDo { + return v.withDO(v.DO.Offset(offset)) +} + +func (v videoAdConfigDo) Scopes(funcs ...func(gen.Dao) gen.Dao) IVideoAdConfigDo { + return v.withDO(v.DO.Scopes(funcs...)) +} + +func (v videoAdConfigDo) Unscoped() IVideoAdConfigDo { + return v.withDO(v.DO.Unscoped()) +} + +func (v videoAdConfigDo) Create(values ...*model.VideoAdConfig) error { + if len(values) == 0 { + return nil + } + return v.DO.Create(values) +} + +func (v videoAdConfigDo) CreateInBatches(values []*model.VideoAdConfig, batchSize int) error { + return v.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 (v videoAdConfigDo) Save(values ...*model.VideoAdConfig) error { + if len(values) == 0 { + return nil + } + return v.DO.Save(values) +} + +func (v videoAdConfigDo) First() (*model.VideoAdConfig, error) { + if result, err := v.DO.First(); err != nil { + return nil, err + } else { + return result.(*model.VideoAdConfig), nil + } +} + +func (v videoAdConfigDo) Take() (*model.VideoAdConfig, error) { + if result, err := v.DO.Take(); err != nil { + return nil, err + } else { + return result.(*model.VideoAdConfig), nil + } +} + +func (v videoAdConfigDo) Last() (*model.VideoAdConfig, error) { + if result, err := v.DO.Last(); err != nil { + return nil, err + } else { + return result.(*model.VideoAdConfig), nil + } +} + +func (v videoAdConfigDo) Find() ([]*model.VideoAdConfig, error) { + result, err := v.DO.Find() + return result.([]*model.VideoAdConfig), err +} + +func (v videoAdConfigDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.VideoAdConfig, err error) { + buf := make([]*model.VideoAdConfig, 0, batchSize) + err = v.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 (v videoAdConfigDo) FindInBatches(result *[]*model.VideoAdConfig, batchSize int, fc func(tx gen.Dao, batch int) error) error { + return v.DO.FindInBatches(result, batchSize, fc) +} + +func (v videoAdConfigDo) Attrs(attrs ...field.AssignExpr) IVideoAdConfigDo { + return v.withDO(v.DO.Attrs(attrs...)) +} + +func (v videoAdConfigDo) Assign(attrs ...field.AssignExpr) IVideoAdConfigDo { + return v.withDO(v.DO.Assign(attrs...)) +} + +func (v videoAdConfigDo) Joins(fields ...field.RelationField) IVideoAdConfigDo { + for _, _f := range fields { + v = *v.withDO(v.DO.Joins(_f)) + } + return &v +} + +func (v videoAdConfigDo) Preload(fields ...field.RelationField) IVideoAdConfigDo { + for _, _f := range fields { + v = *v.withDO(v.DO.Preload(_f)) + } + return &v +} + +func (v videoAdConfigDo) FirstOrInit() (*model.VideoAdConfig, error) { + if result, err := v.DO.FirstOrInit(); err != nil { + return nil, err + } else { + return result.(*model.VideoAdConfig), nil + } +} + +func (v videoAdConfigDo) FirstOrCreate() (*model.VideoAdConfig, error) { + if result, err := v.DO.FirstOrCreate(); err != nil { + return nil, err + } else { + return result.(*model.VideoAdConfig), nil + } +} + +func (v videoAdConfigDo) FindByPage(offset int, limit int) (result []*model.VideoAdConfig, count int64, err error) { + result, err = v.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 = v.Offset(-1).Limit(-1).Count() + return +} + +func (v videoAdConfigDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) { + count, err = v.Count() + if err != nil { + return + } + + err = v.Offset(offset).Limit(limit).Scan(result) + return +} + +func (v videoAdConfigDo) Scan(result interface{}) (err error) { + return v.DO.Scan(result) +} + +func (v videoAdConfigDo) Delete(models ...*model.VideoAdConfig) (result gen.ResultInfo, err error) { + return v.DO.Delete(models) +} + +func (v *videoAdConfigDo) withDO(do gen.Dao) *videoAdConfigDo { + v.DO = *do.(*gen.DO) + return v +} diff --git a/internal/database/query/wallet_transactions.gen.go b/internal/database/query/wallet_transactions.gen.go new file mode 100644 index 0000000..e6c02ad --- /dev/null +++ b/internal/database/query/wallet_transactions.gen.go @@ -0,0 +1,439 @@ +// 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 newWalletTransaction(db *gorm.DB, opts ...gen.DOOption) walletTransaction { + _walletTransaction := walletTransaction{} + + _walletTransaction.walletTransactionDo.UseDB(db, opts...) + _walletTransaction.walletTransactionDo.UseModel(&model.WalletTransaction{}) + + tableName := _walletTransaction.walletTransactionDo.TableName() + _walletTransaction.ALL = field.NewAsterisk(tableName) + _walletTransaction.ID = field.NewString(tableName, "id") + _walletTransaction.UserID = field.NewString(tableName, "user_id") + _walletTransaction.Type = field.NewString(tableName, "type") + _walletTransaction.Amount = field.NewFloat64(tableName, "amount") + _walletTransaction.Currency = field.NewString(tableName, "currency") + _walletTransaction.Note = field.NewString(tableName, "note") + _walletTransaction.CreatedAt = field.NewTime(tableName, "created_at") + _walletTransaction.UpdatedAt = field.NewTime(tableName, "updated_at") + _walletTransaction.PaymentID = field.NewString(tableName, "payment_id") + _walletTransaction.PlanID = field.NewString(tableName, "plan_id") + _walletTransaction.TermMonths = field.NewInt32(tableName, "term_months") + _walletTransaction.Version = field.NewInt64(tableName, "version") + + _walletTransaction.fillFieldMap() + + return _walletTransaction +} + +type walletTransaction struct { + walletTransactionDo walletTransactionDo + + ALL field.Asterisk + ID field.String + UserID field.String + Type field.String + Amount field.Float64 + Currency field.String + Note field.String + CreatedAt field.Time + UpdatedAt field.Time + PaymentID field.String + PlanID field.String + TermMonths field.Int32 + Version field.Int64 + + fieldMap map[string]field.Expr +} + +func (w walletTransaction) Table(newTableName string) *walletTransaction { + w.walletTransactionDo.UseTable(newTableName) + return w.updateTableName(newTableName) +} + +func (w walletTransaction) As(alias string) *walletTransaction { + w.walletTransactionDo.DO = *(w.walletTransactionDo.As(alias).(*gen.DO)) + return w.updateTableName(alias) +} + +func (w *walletTransaction) updateTableName(table string) *walletTransaction { + w.ALL = field.NewAsterisk(table) + w.ID = field.NewString(table, "id") + w.UserID = field.NewString(table, "user_id") + w.Type = field.NewString(table, "type") + w.Amount = field.NewFloat64(table, "amount") + w.Currency = field.NewString(table, "currency") + w.Note = field.NewString(table, "note") + w.CreatedAt = field.NewTime(table, "created_at") + w.UpdatedAt = field.NewTime(table, "updated_at") + w.PaymentID = field.NewString(table, "payment_id") + w.PlanID = field.NewString(table, "plan_id") + w.TermMonths = field.NewInt32(table, "term_months") + w.Version = field.NewInt64(table, "version") + + w.fillFieldMap() + + return w +} + +func (w *walletTransaction) WithContext(ctx context.Context) IWalletTransactionDo { + return w.walletTransactionDo.WithContext(ctx) +} + +func (w walletTransaction) TableName() string { return w.walletTransactionDo.TableName() } + +func (w walletTransaction) Alias() string { return w.walletTransactionDo.Alias() } + +func (w walletTransaction) Columns(cols ...field.Expr) gen.Columns { + return w.walletTransactionDo.Columns(cols...) +} + +func (w *walletTransaction) GetFieldByName(fieldName string) (field.OrderExpr, bool) { + _f, ok := w.fieldMap[fieldName] + if !ok || _f == nil { + return nil, false + } + _oe, ok := _f.(field.OrderExpr) + return _oe, ok +} + +func (w *walletTransaction) fillFieldMap() { + w.fieldMap = make(map[string]field.Expr, 12) + w.fieldMap["id"] = w.ID + w.fieldMap["user_id"] = w.UserID + w.fieldMap["type"] = w.Type + w.fieldMap["amount"] = w.Amount + w.fieldMap["currency"] = w.Currency + w.fieldMap["note"] = w.Note + w.fieldMap["created_at"] = w.CreatedAt + w.fieldMap["updated_at"] = w.UpdatedAt + w.fieldMap["payment_id"] = w.PaymentID + w.fieldMap["plan_id"] = w.PlanID + w.fieldMap["term_months"] = w.TermMonths + w.fieldMap["version"] = w.Version +} + +func (w walletTransaction) clone(db *gorm.DB) walletTransaction { + w.walletTransactionDo.ReplaceConnPool(db.Statement.ConnPool) + return w +} + +func (w walletTransaction) replaceDB(db *gorm.DB) walletTransaction { + w.walletTransactionDo.ReplaceDB(db) + return w +} + +type walletTransactionDo struct{ gen.DO } + +type IWalletTransactionDo interface { + gen.SubQuery + Debug() IWalletTransactionDo + WithContext(ctx context.Context) IWalletTransactionDo + WithResult(fc func(tx gen.Dao)) gen.ResultInfo + ReplaceDB(db *gorm.DB) + ReadDB() IWalletTransactionDo + WriteDB() IWalletTransactionDo + As(alias string) gen.Dao + Session(config *gorm.Session) IWalletTransactionDo + Columns(cols ...field.Expr) gen.Columns + Clauses(conds ...clause.Expression) IWalletTransactionDo + Not(conds ...gen.Condition) IWalletTransactionDo + Or(conds ...gen.Condition) IWalletTransactionDo + Select(conds ...field.Expr) IWalletTransactionDo + Where(conds ...gen.Condition) IWalletTransactionDo + Order(conds ...field.Expr) IWalletTransactionDo + Distinct(cols ...field.Expr) IWalletTransactionDo + Omit(cols ...field.Expr) IWalletTransactionDo + Join(table schema.Tabler, on ...field.Expr) IWalletTransactionDo + LeftJoin(table schema.Tabler, on ...field.Expr) IWalletTransactionDo + RightJoin(table schema.Tabler, on ...field.Expr) IWalletTransactionDo + Group(cols ...field.Expr) IWalletTransactionDo + Having(conds ...gen.Condition) IWalletTransactionDo + Limit(limit int) IWalletTransactionDo + Offset(offset int) IWalletTransactionDo + Count() (count int64, err error) + Scopes(funcs ...func(gen.Dao) gen.Dao) IWalletTransactionDo + Unscoped() IWalletTransactionDo + Create(values ...*model.WalletTransaction) error + CreateInBatches(values []*model.WalletTransaction, batchSize int) error + Save(values ...*model.WalletTransaction) error + First() (*model.WalletTransaction, error) + Take() (*model.WalletTransaction, error) + Last() (*model.WalletTransaction, error) + Find() ([]*model.WalletTransaction, error) + FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.WalletTransaction, err error) + FindInBatches(result *[]*model.WalletTransaction, batchSize int, fc func(tx gen.Dao, batch int) error) error + Pluck(column field.Expr, dest interface{}) error + Delete(...*model.WalletTransaction) (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) IWalletTransactionDo + Assign(attrs ...field.AssignExpr) IWalletTransactionDo + Joins(fields ...field.RelationField) IWalletTransactionDo + Preload(fields ...field.RelationField) IWalletTransactionDo + FirstOrInit() (*model.WalletTransaction, error) + FirstOrCreate() (*model.WalletTransaction, error) + FindByPage(offset int, limit int) (result []*model.WalletTransaction, 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) IWalletTransactionDo + UnderlyingDB() *gorm.DB + schema.Tabler +} + +func (w walletTransactionDo) Debug() IWalletTransactionDo { + return w.withDO(w.DO.Debug()) +} + +func (w walletTransactionDo) WithContext(ctx context.Context) IWalletTransactionDo { + return w.withDO(w.DO.WithContext(ctx)) +} + +func (w walletTransactionDo) ReadDB() IWalletTransactionDo { + return w.Clauses(dbresolver.Read) +} + +func (w walletTransactionDo) WriteDB() IWalletTransactionDo { + return w.Clauses(dbresolver.Write) +} + +func (w walletTransactionDo) Session(config *gorm.Session) IWalletTransactionDo { + return w.withDO(w.DO.Session(config)) +} + +func (w walletTransactionDo) Clauses(conds ...clause.Expression) IWalletTransactionDo { + return w.withDO(w.DO.Clauses(conds...)) +} + +func (w walletTransactionDo) Returning(value interface{}, columns ...string) IWalletTransactionDo { + return w.withDO(w.DO.Returning(value, columns...)) +} + +func (w walletTransactionDo) Not(conds ...gen.Condition) IWalletTransactionDo { + return w.withDO(w.DO.Not(conds...)) +} + +func (w walletTransactionDo) Or(conds ...gen.Condition) IWalletTransactionDo { + return w.withDO(w.DO.Or(conds...)) +} + +func (w walletTransactionDo) Select(conds ...field.Expr) IWalletTransactionDo { + return w.withDO(w.DO.Select(conds...)) +} + +func (w walletTransactionDo) Where(conds ...gen.Condition) IWalletTransactionDo { + return w.withDO(w.DO.Where(conds...)) +} + +func (w walletTransactionDo) Order(conds ...field.Expr) IWalletTransactionDo { + return w.withDO(w.DO.Order(conds...)) +} + +func (w walletTransactionDo) Distinct(cols ...field.Expr) IWalletTransactionDo { + return w.withDO(w.DO.Distinct(cols...)) +} + +func (w walletTransactionDo) Omit(cols ...field.Expr) IWalletTransactionDo { + return w.withDO(w.DO.Omit(cols...)) +} + +func (w walletTransactionDo) Join(table schema.Tabler, on ...field.Expr) IWalletTransactionDo { + return w.withDO(w.DO.Join(table, on...)) +} + +func (w walletTransactionDo) LeftJoin(table schema.Tabler, on ...field.Expr) IWalletTransactionDo { + return w.withDO(w.DO.LeftJoin(table, on...)) +} + +func (w walletTransactionDo) RightJoin(table schema.Tabler, on ...field.Expr) IWalletTransactionDo { + return w.withDO(w.DO.RightJoin(table, on...)) +} + +func (w walletTransactionDo) Group(cols ...field.Expr) IWalletTransactionDo { + return w.withDO(w.DO.Group(cols...)) +} + +func (w walletTransactionDo) Having(conds ...gen.Condition) IWalletTransactionDo { + return w.withDO(w.DO.Having(conds...)) +} + +func (w walletTransactionDo) Limit(limit int) IWalletTransactionDo { + return w.withDO(w.DO.Limit(limit)) +} + +func (w walletTransactionDo) Offset(offset int) IWalletTransactionDo { + return w.withDO(w.DO.Offset(offset)) +} + +func (w walletTransactionDo) Scopes(funcs ...func(gen.Dao) gen.Dao) IWalletTransactionDo { + return w.withDO(w.DO.Scopes(funcs...)) +} + +func (w walletTransactionDo) Unscoped() IWalletTransactionDo { + return w.withDO(w.DO.Unscoped()) +} + +func (w walletTransactionDo) Create(values ...*model.WalletTransaction) error { + if len(values) == 0 { + return nil + } + return w.DO.Create(values) +} + +func (w walletTransactionDo) CreateInBatches(values []*model.WalletTransaction, batchSize int) error { + return w.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 (w walletTransactionDo) Save(values ...*model.WalletTransaction) error { + if len(values) == 0 { + return nil + } + return w.DO.Save(values) +} + +func (w walletTransactionDo) First() (*model.WalletTransaction, error) { + if result, err := w.DO.First(); err != nil { + return nil, err + } else { + return result.(*model.WalletTransaction), nil + } +} + +func (w walletTransactionDo) Take() (*model.WalletTransaction, error) { + if result, err := w.DO.Take(); err != nil { + return nil, err + } else { + return result.(*model.WalletTransaction), nil + } +} + +func (w walletTransactionDo) Last() (*model.WalletTransaction, error) { + if result, err := w.DO.Last(); err != nil { + return nil, err + } else { + return result.(*model.WalletTransaction), nil + } +} + +func (w walletTransactionDo) Find() ([]*model.WalletTransaction, error) { + result, err := w.DO.Find() + return result.([]*model.WalletTransaction), err +} + +func (w walletTransactionDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.WalletTransaction, err error) { + buf := make([]*model.WalletTransaction, 0, batchSize) + err = w.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 (w walletTransactionDo) FindInBatches(result *[]*model.WalletTransaction, batchSize int, fc func(tx gen.Dao, batch int) error) error { + return w.DO.FindInBatches(result, batchSize, fc) +} + +func (w walletTransactionDo) Attrs(attrs ...field.AssignExpr) IWalletTransactionDo { + return w.withDO(w.DO.Attrs(attrs...)) +} + +func (w walletTransactionDo) Assign(attrs ...field.AssignExpr) IWalletTransactionDo { + return w.withDO(w.DO.Assign(attrs...)) +} + +func (w walletTransactionDo) Joins(fields ...field.RelationField) IWalletTransactionDo { + for _, _f := range fields { + w = *w.withDO(w.DO.Joins(_f)) + } + return &w +} + +func (w walletTransactionDo) Preload(fields ...field.RelationField) IWalletTransactionDo { + for _, _f := range fields { + w = *w.withDO(w.DO.Preload(_f)) + } + return &w +} + +func (w walletTransactionDo) FirstOrInit() (*model.WalletTransaction, error) { + if result, err := w.DO.FirstOrInit(); err != nil { + return nil, err + } else { + return result.(*model.WalletTransaction), nil + } +} + +func (w walletTransactionDo) FirstOrCreate() (*model.WalletTransaction, error) { + if result, err := w.DO.FirstOrCreate(); err != nil { + return nil, err + } else { + return result.(*model.WalletTransaction), nil + } +} + +func (w walletTransactionDo) FindByPage(offset int, limit int) (result []*model.WalletTransaction, count int64, err error) { + result, err = w.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 = w.Offset(-1).Limit(-1).Count() + return +} + +func (w walletTransactionDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) { + count, err = w.Count() + if err != nil { + return + } + + err = w.Offset(offset).Limit(limit).Scan(result) + return +} + +func (w walletTransactionDo) Scan(result interface{}) (err error) { + return w.DO.Scan(result) +} + +func (w walletTransactionDo) Delete(models ...*model.WalletTransaction) (result gen.ResultInfo, err error) { + return w.DO.Delete(models) +} + +func (w *walletTransactionDo) withDO(do gen.Dao) *walletTransactionDo { + w.DO = *do.(*gen.DO) + return w +} diff --git a/internal/gen/proto/app/v1/account.pb.go b/internal/gen/proto/app/v1/account.pb.go new file mode 100644 index 0000000..8db30ab --- /dev/null +++ b/internal/gen/proto/app/v1/account.pb.go @@ -0,0 +1,1076 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc (unknown) +// source: app/v1/account.proto + +package appv1 + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type GetMeRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetMeRequest) Reset() { + *x = GetMeRequest{} + mi := &file_app_v1_account_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetMeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetMeRequest) ProtoMessage() {} + +func (x *GetMeRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_account_proto_msgTypes[0] + 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 GetMeRequest.ProtoReflect.Descriptor instead. +func (*GetMeRequest) Descriptor() ([]byte, []int) { + return file_app_v1_account_proto_rawDescGZIP(), []int{0} +} + +type GetMeResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + User *User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetMeResponse) Reset() { + *x = GetMeResponse{} + mi := &file_app_v1_account_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetMeResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetMeResponse) ProtoMessage() {} + +func (x *GetMeResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_account_proto_msgTypes[1] + 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 GetMeResponse.ProtoReflect.Descriptor instead. +func (*GetMeResponse) Descriptor() ([]byte, []int) { + return file_app_v1_account_proto_rawDescGZIP(), []int{1} +} + +func (x *GetMeResponse) GetUser() *User { + if x != nil { + return x.User + } + return nil +} + +type UpdateMeRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Username *string `protobuf:"bytes,1,opt,name=username,proto3,oneof" json:"username,omitempty"` + Email *string `protobuf:"bytes,2,opt,name=email,proto3,oneof" json:"email,omitempty"` + Language *string `protobuf:"bytes,3,opt,name=language,proto3,oneof" json:"language,omitempty"` + Locale *string `protobuf:"bytes,4,opt,name=locale,proto3,oneof" json:"locale,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UpdateMeRequest) Reset() { + *x = UpdateMeRequest{} + mi := &file_app_v1_account_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdateMeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateMeRequest) ProtoMessage() {} + +func (x *UpdateMeRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_account_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateMeRequest.ProtoReflect.Descriptor instead. +func (*UpdateMeRequest) Descriptor() ([]byte, []int) { + return file_app_v1_account_proto_rawDescGZIP(), []int{2} +} + +func (x *UpdateMeRequest) GetUsername() string { + if x != nil && x.Username != nil { + return *x.Username + } + return "" +} + +func (x *UpdateMeRequest) GetEmail() string { + if x != nil && x.Email != nil { + return *x.Email + } + return "" +} + +func (x *UpdateMeRequest) GetLanguage() string { + if x != nil && x.Language != nil { + return *x.Language + } + return "" +} + +func (x *UpdateMeRequest) GetLocale() string { + if x != nil && x.Locale != nil { + return *x.Locale + } + return "" +} + +type UpdateMeResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + User *User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UpdateMeResponse) Reset() { + *x = UpdateMeResponse{} + mi := &file_app_v1_account_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdateMeResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateMeResponse) ProtoMessage() {} + +func (x *UpdateMeResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_account_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateMeResponse.ProtoReflect.Descriptor instead. +func (*UpdateMeResponse) Descriptor() ([]byte, []int) { + return file_app_v1_account_proto_rawDescGZIP(), []int{3} +} + +func (x *UpdateMeResponse) GetUser() *User { + if x != nil { + return x.User + } + return nil +} + +type DeleteMeRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DeleteMeRequest) Reset() { + *x = DeleteMeRequest{} + mi := &file_app_v1_account_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeleteMeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteMeRequest) ProtoMessage() {} + +func (x *DeleteMeRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_account_proto_msgTypes[4] + 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 DeleteMeRequest.ProtoReflect.Descriptor instead. +func (*DeleteMeRequest) Descriptor() ([]byte, []int) { + return file_app_v1_account_proto_rawDescGZIP(), []int{4} +} + +type ClearMyDataRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ClearMyDataRequest) Reset() { + *x = ClearMyDataRequest{} + mi := &file_app_v1_account_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ClearMyDataRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ClearMyDataRequest) ProtoMessage() {} + +func (x *ClearMyDataRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_account_proto_msgTypes[5] + 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 ClearMyDataRequest.ProtoReflect.Descriptor instead. +func (*ClearMyDataRequest) Descriptor() ([]byte, []int) { + return file_app_v1_account_proto_rawDescGZIP(), []int{5} +} + +type GetPreferencesRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetPreferencesRequest) Reset() { + *x = GetPreferencesRequest{} + mi := &file_app_v1_account_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetPreferencesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetPreferencesRequest) ProtoMessage() {} + +func (x *GetPreferencesRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_account_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 GetPreferencesRequest.ProtoReflect.Descriptor instead. +func (*GetPreferencesRequest) Descriptor() ([]byte, []int) { + return file_app_v1_account_proto_rawDescGZIP(), []int{6} +} + +type GetPreferencesResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Preferences *Preferences `protobuf:"bytes,1,opt,name=preferences,proto3" json:"preferences,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetPreferencesResponse) Reset() { + *x = GetPreferencesResponse{} + mi := &file_app_v1_account_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetPreferencesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetPreferencesResponse) ProtoMessage() {} + +func (x *GetPreferencesResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_account_proto_msgTypes[7] + 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 GetPreferencesResponse.ProtoReflect.Descriptor instead. +func (*GetPreferencesResponse) Descriptor() ([]byte, []int) { + return file_app_v1_account_proto_rawDescGZIP(), []int{7} +} + +func (x *GetPreferencesResponse) GetPreferences() *Preferences { + if x != nil { + return x.Preferences + } + return nil +} + +type UpdatePreferencesRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + EmailNotifications *bool `protobuf:"varint,1,opt,name=email_notifications,json=emailNotifications,proto3,oneof" json:"email_notifications,omitempty"` + PushNotifications *bool `protobuf:"varint,2,opt,name=push_notifications,json=pushNotifications,proto3,oneof" json:"push_notifications,omitempty"` + MarketingNotifications *bool `protobuf:"varint,3,opt,name=marketing_notifications,json=marketingNotifications,proto3,oneof" json:"marketing_notifications,omitempty"` + TelegramNotifications *bool `protobuf:"varint,4,opt,name=telegram_notifications,json=telegramNotifications,proto3,oneof" json:"telegram_notifications,omitempty"` + Autoplay *bool `protobuf:"varint,5,opt,name=autoplay,proto3,oneof" json:"autoplay,omitempty"` + Loop *bool `protobuf:"varint,6,opt,name=loop,proto3,oneof" json:"loop,omitempty"` + Muted *bool `protobuf:"varint,7,opt,name=muted,proto3,oneof" json:"muted,omitempty"` + ShowControls *bool `protobuf:"varint,8,opt,name=show_controls,json=showControls,proto3,oneof" json:"show_controls,omitempty"` + Pip *bool `protobuf:"varint,9,opt,name=pip,proto3,oneof" json:"pip,omitempty"` + Airplay *bool `protobuf:"varint,10,opt,name=airplay,proto3,oneof" json:"airplay,omitempty"` + Chromecast *bool `protobuf:"varint,11,opt,name=chromecast,proto3,oneof" json:"chromecast,omitempty"` + Language *string `protobuf:"bytes,12,opt,name=language,proto3,oneof" json:"language,omitempty"` + Locale *string `protobuf:"bytes,13,opt,name=locale,proto3,oneof" json:"locale,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UpdatePreferencesRequest) Reset() { + *x = UpdatePreferencesRequest{} + mi := &file_app_v1_account_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdatePreferencesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdatePreferencesRequest) ProtoMessage() {} + +func (x *UpdatePreferencesRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_account_proto_msgTypes[8] + 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 UpdatePreferencesRequest.ProtoReflect.Descriptor instead. +func (*UpdatePreferencesRequest) Descriptor() ([]byte, []int) { + return file_app_v1_account_proto_rawDescGZIP(), []int{8} +} + +func (x *UpdatePreferencesRequest) GetEmailNotifications() bool { + if x != nil && x.EmailNotifications != nil { + return *x.EmailNotifications + } + return false +} + +func (x *UpdatePreferencesRequest) GetPushNotifications() bool { + if x != nil && x.PushNotifications != nil { + return *x.PushNotifications + } + return false +} + +func (x *UpdatePreferencesRequest) GetMarketingNotifications() bool { + if x != nil && x.MarketingNotifications != nil { + return *x.MarketingNotifications + } + return false +} + +func (x *UpdatePreferencesRequest) GetTelegramNotifications() bool { + if x != nil && x.TelegramNotifications != nil { + return *x.TelegramNotifications + } + return false +} + +func (x *UpdatePreferencesRequest) GetAutoplay() bool { + if x != nil && x.Autoplay != nil { + return *x.Autoplay + } + return false +} + +func (x *UpdatePreferencesRequest) GetLoop() bool { + if x != nil && x.Loop != nil { + return *x.Loop + } + return false +} + +func (x *UpdatePreferencesRequest) GetMuted() bool { + if x != nil && x.Muted != nil { + return *x.Muted + } + return false +} + +func (x *UpdatePreferencesRequest) GetShowControls() bool { + if x != nil && x.ShowControls != nil { + return *x.ShowControls + } + return false +} + +func (x *UpdatePreferencesRequest) GetPip() bool { + if x != nil && x.Pip != nil { + return *x.Pip + } + return false +} + +func (x *UpdatePreferencesRequest) GetAirplay() bool { + if x != nil && x.Airplay != nil { + return *x.Airplay + } + return false +} + +func (x *UpdatePreferencesRequest) GetChromecast() bool { + if x != nil && x.Chromecast != nil { + return *x.Chromecast + } + return false +} + +func (x *UpdatePreferencesRequest) GetLanguage() string { + if x != nil && x.Language != nil { + return *x.Language + } + return "" +} + +func (x *UpdatePreferencesRequest) GetLocale() string { + if x != nil && x.Locale != nil { + return *x.Locale + } + return "" +} + +type UpdatePreferencesResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Preferences *Preferences `protobuf:"bytes,1,opt,name=preferences,proto3" json:"preferences,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UpdatePreferencesResponse) Reset() { + *x = UpdatePreferencesResponse{} + mi := &file_app_v1_account_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdatePreferencesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdatePreferencesResponse) ProtoMessage() {} + +func (x *UpdatePreferencesResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_account_proto_msgTypes[9] + 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 UpdatePreferencesResponse.ProtoReflect.Descriptor instead. +func (*UpdatePreferencesResponse) Descriptor() ([]byte, []int) { + return file_app_v1_account_proto_rawDescGZIP(), []int{9} +} + +func (x *UpdatePreferencesResponse) GetPreferences() *Preferences { + if x != nil { + return x.Preferences + } + return nil +} + +type GetUsageRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetUsageRequest) Reset() { + *x = GetUsageRequest{} + mi := &file_app_v1_account_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetUsageRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetUsageRequest) ProtoMessage() {} + +func (x *GetUsageRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_account_proto_msgTypes[10] + 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 GetUsageRequest.ProtoReflect.Descriptor instead. +func (*GetUsageRequest) Descriptor() ([]byte, []int) { + return file_app_v1_account_proto_rawDescGZIP(), []int{10} +} + +type GetUsageResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + UserId string `protobuf:"bytes,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` + TotalVideos int64 `protobuf:"varint,2,opt,name=total_videos,json=totalVideos,proto3" json:"total_videos,omitempty"` + TotalStorage int64 `protobuf:"varint,3,opt,name=total_storage,json=totalStorage,proto3" json:"total_storage,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetUsageResponse) Reset() { + *x = GetUsageResponse{} + mi := &file_app_v1_account_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetUsageResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetUsageResponse) ProtoMessage() {} + +func (x *GetUsageResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_account_proto_msgTypes[11] + 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 GetUsageResponse.ProtoReflect.Descriptor instead. +func (*GetUsageResponse) Descriptor() ([]byte, []int) { + return file_app_v1_account_proto_rawDescGZIP(), []int{11} +} + +func (x *GetUsageResponse) GetUserId() string { + if x != nil { + return x.UserId + } + return "" +} + +func (x *GetUsageResponse) GetTotalVideos() int64 { + if x != nil { + return x.TotalVideos + } + return 0 +} + +func (x *GetUsageResponse) GetTotalStorage() int64 { + if x != nil { + return x.TotalStorage + } + return 0 +} + +type ListNotificationsRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListNotificationsRequest) Reset() { + *x = ListNotificationsRequest{} + mi := &file_app_v1_account_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListNotificationsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListNotificationsRequest) ProtoMessage() {} + +func (x *ListNotificationsRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_account_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 ListNotificationsRequest.ProtoReflect.Descriptor instead. +func (*ListNotificationsRequest) Descriptor() ([]byte, []int) { + return file_app_v1_account_proto_rawDescGZIP(), []int{12} +} + +type ListNotificationsResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Notifications []*Notification `protobuf:"bytes,1,rep,name=notifications,proto3" json:"notifications,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListNotificationsResponse) Reset() { + *x = ListNotificationsResponse{} + mi := &file_app_v1_account_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListNotificationsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListNotificationsResponse) ProtoMessage() {} + +func (x *ListNotificationsResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_account_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 ListNotificationsResponse.ProtoReflect.Descriptor instead. +func (*ListNotificationsResponse) Descriptor() ([]byte, []int) { + return file_app_v1_account_proto_rawDescGZIP(), []int{13} +} + +func (x *ListNotificationsResponse) GetNotifications() []*Notification { + if x != nil { + return x.Notifications + } + return nil +} + +type MarkNotificationReadRequest 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 *MarkNotificationReadRequest) Reset() { + *x = MarkNotificationReadRequest{} + mi := &file_app_v1_account_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *MarkNotificationReadRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MarkNotificationReadRequest) ProtoMessage() {} + +func (x *MarkNotificationReadRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_account_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 MarkNotificationReadRequest.ProtoReflect.Descriptor instead. +func (*MarkNotificationReadRequest) Descriptor() ([]byte, []int) { + return file_app_v1_account_proto_rawDescGZIP(), []int{14} +} + +func (x *MarkNotificationReadRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type MarkAllNotificationsReadRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *MarkAllNotificationsReadRequest) Reset() { + *x = MarkAllNotificationsReadRequest{} + mi := &file_app_v1_account_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *MarkAllNotificationsReadRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MarkAllNotificationsReadRequest) ProtoMessage() {} + +func (x *MarkAllNotificationsReadRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_account_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 MarkAllNotificationsReadRequest.ProtoReflect.Descriptor instead. +func (*MarkAllNotificationsReadRequest) Descriptor() ([]byte, []int) { + return file_app_v1_account_proto_rawDescGZIP(), []int{15} +} + +type DeleteNotificationRequest 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 *DeleteNotificationRequest) Reset() { + *x = DeleteNotificationRequest{} + mi := &file_app_v1_account_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeleteNotificationRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteNotificationRequest) ProtoMessage() {} + +func (x *DeleteNotificationRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_account_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 DeleteNotificationRequest.ProtoReflect.Descriptor instead. +func (*DeleteNotificationRequest) Descriptor() ([]byte, []int) { + return file_app_v1_account_proto_rawDescGZIP(), []int{16} +} + +func (x *DeleteNotificationRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type ClearNotificationsRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ClearNotificationsRequest) Reset() { + *x = ClearNotificationsRequest{} + mi := &file_app_v1_account_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ClearNotificationsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ClearNotificationsRequest) ProtoMessage() {} + +func (x *ClearNotificationsRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_account_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 ClearNotificationsRequest.ProtoReflect.Descriptor instead. +func (*ClearNotificationsRequest) Descriptor() ([]byte, []int) { + return file_app_v1_account_proto_rawDescGZIP(), []int{17} +} + +var File_app_v1_account_proto protoreflect.FileDescriptor + +const file_app_v1_account_proto_rawDesc = "" + + "\n" + + "\x14app/v1/account.proto\x12\rstream.app.v1\x1a\x13app/v1/common.proto\"\x0e\n" + + "\fGetMeRequest\"8\n" + + "\rGetMeResponse\x12'\n" + + "\x04user\x18\x01 \x01(\v2\x13.stream.app.v1.UserR\x04user\"\xba\x01\n" + + "\x0fUpdateMeRequest\x12\x1f\n" + + "\busername\x18\x01 \x01(\tH\x00R\busername\x88\x01\x01\x12\x19\n" + + "\x05email\x18\x02 \x01(\tH\x01R\x05email\x88\x01\x01\x12\x1f\n" + + "\blanguage\x18\x03 \x01(\tH\x02R\blanguage\x88\x01\x01\x12\x1b\n" + + "\x06locale\x18\x04 \x01(\tH\x03R\x06locale\x88\x01\x01B\v\n" + + "\t_usernameB\b\n" + + "\x06_emailB\v\n" + + "\t_languageB\t\n" + + "\a_locale\";\n" + + "\x10UpdateMeResponse\x12'\n" + + "\x04user\x18\x01 \x01(\v2\x13.stream.app.v1.UserR\x04user\"\x11\n" + + "\x0fDeleteMeRequest\"\x14\n" + + "\x12ClearMyDataRequest\"\x17\n" + + "\x15GetPreferencesRequest\"V\n" + + "\x16GetPreferencesResponse\x12<\n" + + "\vpreferences\x18\x01 \x01(\v2\x1a.stream.app.v1.PreferencesR\vpreferences\"\xe9\x05\n" + + "\x18UpdatePreferencesRequest\x124\n" + + "\x13email_notifications\x18\x01 \x01(\bH\x00R\x12emailNotifications\x88\x01\x01\x122\n" + + "\x12push_notifications\x18\x02 \x01(\bH\x01R\x11pushNotifications\x88\x01\x01\x12<\n" + + "\x17marketing_notifications\x18\x03 \x01(\bH\x02R\x16marketingNotifications\x88\x01\x01\x12:\n" + + "\x16telegram_notifications\x18\x04 \x01(\bH\x03R\x15telegramNotifications\x88\x01\x01\x12\x1f\n" + + "\bautoplay\x18\x05 \x01(\bH\x04R\bautoplay\x88\x01\x01\x12\x17\n" + + "\x04loop\x18\x06 \x01(\bH\x05R\x04loop\x88\x01\x01\x12\x19\n" + + "\x05muted\x18\a \x01(\bH\x06R\x05muted\x88\x01\x01\x12(\n" + + "\rshow_controls\x18\b \x01(\bH\aR\fshowControls\x88\x01\x01\x12\x15\n" + + "\x03pip\x18\t \x01(\bH\bR\x03pip\x88\x01\x01\x12\x1d\n" + + "\aairplay\x18\n" + + " \x01(\bH\tR\aairplay\x88\x01\x01\x12#\n" + + "\n" + + "chromecast\x18\v \x01(\bH\n" + + "R\n" + + "chromecast\x88\x01\x01\x12\x1f\n" + + "\blanguage\x18\f \x01(\tH\vR\blanguage\x88\x01\x01\x12\x1b\n" + + "\x06locale\x18\r \x01(\tH\fR\x06locale\x88\x01\x01B\x16\n" + + "\x14_email_notificationsB\x15\n" + + "\x13_push_notificationsB\x1a\n" + + "\x18_marketing_notificationsB\x19\n" + + "\x17_telegram_notificationsB\v\n" + + "\t_autoplayB\a\n" + + "\x05_loopB\b\n" + + "\x06_mutedB\x10\n" + + "\x0e_show_controlsB\x06\n" + + "\x04_pipB\n" + + "\n" + + "\b_airplayB\r\n" + + "\v_chromecastB\v\n" + + "\t_languageB\t\n" + + "\a_locale\"Y\n" + + "\x19UpdatePreferencesResponse\x12<\n" + + "\vpreferences\x18\x01 \x01(\v2\x1a.stream.app.v1.PreferencesR\vpreferences\"\x11\n" + + "\x0fGetUsageRequest\"s\n" + + "\x10GetUsageResponse\x12\x17\n" + + "\auser_id\x18\x01 \x01(\tR\x06userId\x12!\n" + + "\ftotal_videos\x18\x02 \x01(\x03R\vtotalVideos\x12#\n" + + "\rtotal_storage\x18\x03 \x01(\x03R\ftotalStorage\"\x1a\n" + + "\x18ListNotificationsRequest\"^\n" + + "\x19ListNotificationsResponse\x12A\n" + + "\rnotifications\x18\x01 \x03(\v2\x1b.stream.app.v1.NotificationR\rnotifications\"-\n" + + "\x1bMarkNotificationReadRequest\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\"!\n" + + "\x1fMarkAllNotificationsReadRequest\"+\n" + + "\x19DeleteNotificationRequest\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\"\x1b\n" + + "\x19ClearNotificationsRequest2\xbf\x02\n" + + "\x0eAccountService\x12B\n" + + "\x05GetMe\x12\x1b.stream.app.v1.GetMeRequest\x1a\x1c.stream.app.v1.GetMeResponse\x12K\n" + + "\bUpdateMe\x12\x1e.stream.app.v1.UpdateMeRequest\x1a\x1f.stream.app.v1.UpdateMeResponse\x12J\n" + + "\bDeleteMe\x12\x1e.stream.app.v1.DeleteMeRequest\x1a\x1e.stream.app.v1.MessageResponse\x12P\n" + + "\vClearMyData\x12!.stream.app.v1.ClearMyDataRequest\x1a\x1e.stream.app.v1.MessageResponse2\xdb\x01\n" + + "\x12PreferencesService\x12]\n" + + "\x0eGetPreferences\x12$.stream.app.v1.GetPreferencesRequest\x1a%.stream.app.v1.GetPreferencesResponse\x12f\n" + + "\x11UpdatePreferences\x12'.stream.app.v1.UpdatePreferencesRequest\x1a(.stream.app.v1.UpdatePreferencesResponse2[\n" + + "\fUsageService\x12K\n" + + "\bGetUsage\x12\x1e.stream.app.v1.GetUsageRequest\x1a\x1f.stream.app.v1.GetUsageResponse2\x8e\x04\n" + + "\x14NotificationsService\x12f\n" + + "\x11ListNotifications\x12'.stream.app.v1.ListNotificationsRequest\x1a(.stream.app.v1.ListNotificationsResponse\x12b\n" + + "\x14MarkNotificationRead\x12*.stream.app.v1.MarkNotificationReadRequest\x1a\x1e.stream.app.v1.MessageResponse\x12j\n" + + "\x18MarkAllNotificationsRead\x12..stream.app.v1.MarkAllNotificationsReadRequest\x1a\x1e.stream.app.v1.MessageResponse\x12^\n" + + "\x12DeleteNotification\x12(.stream.app.v1.DeleteNotificationRequest\x1a\x1e.stream.app.v1.MessageResponse\x12^\n" + + "\x12ClearNotifications\x12(.stream.app.v1.ClearNotificationsRequest\x1a\x1e.stream.app.v1.MessageResponseB,Z*stream.api/internal/gen/proto/app/v1;appv1b\x06proto3" + +var ( + file_app_v1_account_proto_rawDescOnce sync.Once + file_app_v1_account_proto_rawDescData []byte +) + +func file_app_v1_account_proto_rawDescGZIP() []byte { + file_app_v1_account_proto_rawDescOnce.Do(func() { + file_app_v1_account_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_v1_account_proto_rawDesc), len(file_app_v1_account_proto_rawDesc))) + }) + return file_app_v1_account_proto_rawDescData +} + +var file_app_v1_account_proto_msgTypes = make([]protoimpl.MessageInfo, 18) +var file_app_v1_account_proto_goTypes = []any{ + (*GetMeRequest)(nil), // 0: stream.app.v1.GetMeRequest + (*GetMeResponse)(nil), // 1: stream.app.v1.GetMeResponse + (*UpdateMeRequest)(nil), // 2: stream.app.v1.UpdateMeRequest + (*UpdateMeResponse)(nil), // 3: stream.app.v1.UpdateMeResponse + (*DeleteMeRequest)(nil), // 4: stream.app.v1.DeleteMeRequest + (*ClearMyDataRequest)(nil), // 5: stream.app.v1.ClearMyDataRequest + (*GetPreferencesRequest)(nil), // 6: stream.app.v1.GetPreferencesRequest + (*GetPreferencesResponse)(nil), // 7: stream.app.v1.GetPreferencesResponse + (*UpdatePreferencesRequest)(nil), // 8: stream.app.v1.UpdatePreferencesRequest + (*UpdatePreferencesResponse)(nil), // 9: stream.app.v1.UpdatePreferencesResponse + (*GetUsageRequest)(nil), // 10: stream.app.v1.GetUsageRequest + (*GetUsageResponse)(nil), // 11: stream.app.v1.GetUsageResponse + (*ListNotificationsRequest)(nil), // 12: stream.app.v1.ListNotificationsRequest + (*ListNotificationsResponse)(nil), // 13: stream.app.v1.ListNotificationsResponse + (*MarkNotificationReadRequest)(nil), // 14: stream.app.v1.MarkNotificationReadRequest + (*MarkAllNotificationsReadRequest)(nil), // 15: stream.app.v1.MarkAllNotificationsReadRequest + (*DeleteNotificationRequest)(nil), // 16: stream.app.v1.DeleteNotificationRequest + (*ClearNotificationsRequest)(nil), // 17: stream.app.v1.ClearNotificationsRequest + (*User)(nil), // 18: stream.app.v1.User + (*Preferences)(nil), // 19: stream.app.v1.Preferences + (*Notification)(nil), // 20: stream.app.v1.Notification + (*MessageResponse)(nil), // 21: stream.app.v1.MessageResponse +} +var file_app_v1_account_proto_depIdxs = []int32{ + 18, // 0: stream.app.v1.GetMeResponse.user:type_name -> stream.app.v1.User + 18, // 1: stream.app.v1.UpdateMeResponse.user:type_name -> stream.app.v1.User + 19, // 2: stream.app.v1.GetPreferencesResponse.preferences:type_name -> stream.app.v1.Preferences + 19, // 3: stream.app.v1.UpdatePreferencesResponse.preferences:type_name -> stream.app.v1.Preferences + 20, // 4: stream.app.v1.ListNotificationsResponse.notifications:type_name -> stream.app.v1.Notification + 0, // 5: stream.app.v1.AccountService.GetMe:input_type -> stream.app.v1.GetMeRequest + 2, // 6: stream.app.v1.AccountService.UpdateMe:input_type -> stream.app.v1.UpdateMeRequest + 4, // 7: stream.app.v1.AccountService.DeleteMe:input_type -> stream.app.v1.DeleteMeRequest + 5, // 8: stream.app.v1.AccountService.ClearMyData:input_type -> stream.app.v1.ClearMyDataRequest + 6, // 9: stream.app.v1.PreferencesService.GetPreferences:input_type -> stream.app.v1.GetPreferencesRequest + 8, // 10: stream.app.v1.PreferencesService.UpdatePreferences:input_type -> stream.app.v1.UpdatePreferencesRequest + 10, // 11: stream.app.v1.UsageService.GetUsage:input_type -> stream.app.v1.GetUsageRequest + 12, // 12: stream.app.v1.NotificationsService.ListNotifications:input_type -> stream.app.v1.ListNotificationsRequest + 14, // 13: stream.app.v1.NotificationsService.MarkNotificationRead:input_type -> stream.app.v1.MarkNotificationReadRequest + 15, // 14: stream.app.v1.NotificationsService.MarkAllNotificationsRead:input_type -> stream.app.v1.MarkAllNotificationsReadRequest + 16, // 15: stream.app.v1.NotificationsService.DeleteNotification:input_type -> stream.app.v1.DeleteNotificationRequest + 17, // 16: stream.app.v1.NotificationsService.ClearNotifications:input_type -> stream.app.v1.ClearNotificationsRequest + 1, // 17: stream.app.v1.AccountService.GetMe:output_type -> stream.app.v1.GetMeResponse + 3, // 18: stream.app.v1.AccountService.UpdateMe:output_type -> stream.app.v1.UpdateMeResponse + 21, // 19: stream.app.v1.AccountService.DeleteMe:output_type -> stream.app.v1.MessageResponse + 21, // 20: stream.app.v1.AccountService.ClearMyData:output_type -> stream.app.v1.MessageResponse + 7, // 21: stream.app.v1.PreferencesService.GetPreferences:output_type -> stream.app.v1.GetPreferencesResponse + 9, // 22: stream.app.v1.PreferencesService.UpdatePreferences:output_type -> stream.app.v1.UpdatePreferencesResponse + 11, // 23: stream.app.v1.UsageService.GetUsage:output_type -> stream.app.v1.GetUsageResponse + 13, // 24: stream.app.v1.NotificationsService.ListNotifications:output_type -> stream.app.v1.ListNotificationsResponse + 21, // 25: stream.app.v1.NotificationsService.MarkNotificationRead:output_type -> stream.app.v1.MessageResponse + 21, // 26: stream.app.v1.NotificationsService.MarkAllNotificationsRead:output_type -> stream.app.v1.MessageResponse + 21, // 27: stream.app.v1.NotificationsService.DeleteNotification:output_type -> stream.app.v1.MessageResponse + 21, // 28: stream.app.v1.NotificationsService.ClearNotifications:output_type -> stream.app.v1.MessageResponse + 17, // [17:29] is the sub-list for method output_type + 5, // [5:17] is the sub-list for method input_type + 5, // [5:5] is the sub-list for extension type_name + 5, // [5:5] is the sub-list for extension extendee + 0, // [0:5] is the sub-list for field type_name +} + +func init() { file_app_v1_account_proto_init() } +func file_app_v1_account_proto_init() { + if File_app_v1_account_proto != nil { + return + } + file_app_v1_common_proto_init() + file_app_v1_account_proto_msgTypes[2].OneofWrappers = []any{} + file_app_v1_account_proto_msgTypes[8].OneofWrappers = []any{} + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_v1_account_proto_rawDesc), len(file_app_v1_account_proto_rawDesc)), + NumEnums: 0, + NumMessages: 18, + NumExtensions: 0, + NumServices: 4, + }, + GoTypes: file_app_v1_account_proto_goTypes, + DependencyIndexes: file_app_v1_account_proto_depIdxs, + MessageInfos: file_app_v1_account_proto_msgTypes, + }.Build() + File_app_v1_account_proto = out.File + file_app_v1_account_proto_goTypes = nil + file_app_v1_account_proto_depIdxs = nil +} diff --git a/internal/gen/proto/app/v1/account_grpc.pb.go b/internal/gen/proto/app/v1/account_grpc.pb.go new file mode 100644 index 0000000..9f02b3f --- /dev/null +++ b/internal/gen/proto/app/v1/account_grpc.pb.go @@ -0,0 +1,731 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.6.1 +// - protoc (unknown) +// source: app/v1/account.proto + +package appv1 + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + AccountService_GetMe_FullMethodName = "/stream.app.v1.AccountService/GetMe" + AccountService_UpdateMe_FullMethodName = "/stream.app.v1.AccountService/UpdateMe" + AccountService_DeleteMe_FullMethodName = "/stream.app.v1.AccountService/DeleteMe" + AccountService_ClearMyData_FullMethodName = "/stream.app.v1.AccountService/ClearMyData" +) + +// AccountServiceClient is the client API for AccountService 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 AccountServiceClient interface { + GetMe(ctx context.Context, in *GetMeRequest, opts ...grpc.CallOption) (*GetMeResponse, error) + UpdateMe(ctx context.Context, in *UpdateMeRequest, opts ...grpc.CallOption) (*UpdateMeResponse, error) + DeleteMe(ctx context.Context, in *DeleteMeRequest, opts ...grpc.CallOption) (*MessageResponse, error) + ClearMyData(ctx context.Context, in *ClearMyDataRequest, opts ...grpc.CallOption) (*MessageResponse, error) +} + +type accountServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewAccountServiceClient(cc grpc.ClientConnInterface) AccountServiceClient { + return &accountServiceClient{cc} +} + +func (c *accountServiceClient) GetMe(ctx context.Context, in *GetMeRequest, opts ...grpc.CallOption) (*GetMeResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetMeResponse) + err := c.cc.Invoke(ctx, AccountService_GetMe_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *accountServiceClient) UpdateMe(ctx context.Context, in *UpdateMeRequest, opts ...grpc.CallOption) (*UpdateMeResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(UpdateMeResponse) + err := c.cc.Invoke(ctx, AccountService_UpdateMe_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *accountServiceClient) DeleteMe(ctx context.Context, in *DeleteMeRequest, opts ...grpc.CallOption) (*MessageResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(MessageResponse) + err := c.cc.Invoke(ctx, AccountService_DeleteMe_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *accountServiceClient) ClearMyData(ctx context.Context, in *ClearMyDataRequest, opts ...grpc.CallOption) (*MessageResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(MessageResponse) + err := c.cc.Invoke(ctx, AccountService_ClearMyData_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// AccountServiceServer is the server API for AccountService service. +// All implementations must embed UnimplementedAccountServiceServer +// for forward compatibility. +type AccountServiceServer interface { + GetMe(context.Context, *GetMeRequest) (*GetMeResponse, error) + UpdateMe(context.Context, *UpdateMeRequest) (*UpdateMeResponse, error) + DeleteMe(context.Context, *DeleteMeRequest) (*MessageResponse, error) + ClearMyData(context.Context, *ClearMyDataRequest) (*MessageResponse, error) + mustEmbedUnimplementedAccountServiceServer() +} + +// UnimplementedAccountServiceServer 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 UnimplementedAccountServiceServer struct{} + +func (UnimplementedAccountServiceServer) GetMe(context.Context, *GetMeRequest) (*GetMeResponse, error) { + return nil, status.Error(codes.Unimplemented, "method GetMe not implemented") +} +func (UnimplementedAccountServiceServer) UpdateMe(context.Context, *UpdateMeRequest) (*UpdateMeResponse, error) { + return nil, status.Error(codes.Unimplemented, "method UpdateMe not implemented") +} +func (UnimplementedAccountServiceServer) DeleteMe(context.Context, *DeleteMeRequest) (*MessageResponse, error) { + return nil, status.Error(codes.Unimplemented, "method DeleteMe not implemented") +} +func (UnimplementedAccountServiceServer) ClearMyData(context.Context, *ClearMyDataRequest) (*MessageResponse, error) { + return nil, status.Error(codes.Unimplemented, "method ClearMyData not implemented") +} +func (UnimplementedAccountServiceServer) mustEmbedUnimplementedAccountServiceServer() {} +func (UnimplementedAccountServiceServer) testEmbeddedByValue() {} + +// UnsafeAccountServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to AccountServiceServer will +// result in compilation errors. +type UnsafeAccountServiceServer interface { + mustEmbedUnimplementedAccountServiceServer() +} + +func RegisterAccountServiceServer(s grpc.ServiceRegistrar, srv AccountServiceServer) { + // If the following call panics, it indicates UnimplementedAccountServiceServer 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(&AccountService_ServiceDesc, srv) +} + +func _AccountService_GetMe_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetMeRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AccountServiceServer).GetMe(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AccountService_GetMe_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AccountServiceServer).GetMe(ctx, req.(*GetMeRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AccountService_UpdateMe_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateMeRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AccountServiceServer).UpdateMe(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AccountService_UpdateMe_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AccountServiceServer).UpdateMe(ctx, req.(*UpdateMeRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AccountService_DeleteMe_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteMeRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AccountServiceServer).DeleteMe(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AccountService_DeleteMe_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AccountServiceServer).DeleteMe(ctx, req.(*DeleteMeRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AccountService_ClearMyData_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ClearMyDataRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AccountServiceServer).ClearMyData(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AccountService_ClearMyData_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AccountServiceServer).ClearMyData(ctx, req.(*ClearMyDataRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// AccountService_ServiceDesc is the grpc.ServiceDesc for AccountService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var AccountService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "stream.app.v1.AccountService", + HandlerType: (*AccountServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetMe", + Handler: _AccountService_GetMe_Handler, + }, + { + MethodName: "UpdateMe", + Handler: _AccountService_UpdateMe_Handler, + }, + { + MethodName: "DeleteMe", + Handler: _AccountService_DeleteMe_Handler, + }, + { + MethodName: "ClearMyData", + Handler: _AccountService_ClearMyData_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "app/v1/account.proto", +} + +const ( + PreferencesService_GetPreferences_FullMethodName = "/stream.app.v1.PreferencesService/GetPreferences" + PreferencesService_UpdatePreferences_FullMethodName = "/stream.app.v1.PreferencesService/UpdatePreferences" +) + +// PreferencesServiceClient is the client API for PreferencesService 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 PreferencesServiceClient interface { + GetPreferences(ctx context.Context, in *GetPreferencesRequest, opts ...grpc.CallOption) (*GetPreferencesResponse, error) + UpdatePreferences(ctx context.Context, in *UpdatePreferencesRequest, opts ...grpc.CallOption) (*UpdatePreferencesResponse, error) +} + +type preferencesServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewPreferencesServiceClient(cc grpc.ClientConnInterface) PreferencesServiceClient { + return &preferencesServiceClient{cc} +} + +func (c *preferencesServiceClient) GetPreferences(ctx context.Context, in *GetPreferencesRequest, opts ...grpc.CallOption) (*GetPreferencesResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetPreferencesResponse) + err := c.cc.Invoke(ctx, PreferencesService_GetPreferences_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *preferencesServiceClient) UpdatePreferences(ctx context.Context, in *UpdatePreferencesRequest, opts ...grpc.CallOption) (*UpdatePreferencesResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(UpdatePreferencesResponse) + err := c.cc.Invoke(ctx, PreferencesService_UpdatePreferences_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// PreferencesServiceServer is the server API for PreferencesService service. +// All implementations must embed UnimplementedPreferencesServiceServer +// for forward compatibility. +type PreferencesServiceServer interface { + GetPreferences(context.Context, *GetPreferencesRequest) (*GetPreferencesResponse, error) + UpdatePreferences(context.Context, *UpdatePreferencesRequest) (*UpdatePreferencesResponse, error) + mustEmbedUnimplementedPreferencesServiceServer() +} + +// UnimplementedPreferencesServiceServer 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 UnimplementedPreferencesServiceServer struct{} + +func (UnimplementedPreferencesServiceServer) GetPreferences(context.Context, *GetPreferencesRequest) (*GetPreferencesResponse, error) { + return nil, status.Error(codes.Unimplemented, "method GetPreferences not implemented") +} +func (UnimplementedPreferencesServiceServer) UpdatePreferences(context.Context, *UpdatePreferencesRequest) (*UpdatePreferencesResponse, error) { + return nil, status.Error(codes.Unimplemented, "method UpdatePreferences not implemented") +} +func (UnimplementedPreferencesServiceServer) mustEmbedUnimplementedPreferencesServiceServer() {} +func (UnimplementedPreferencesServiceServer) testEmbeddedByValue() {} + +// UnsafePreferencesServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to PreferencesServiceServer will +// result in compilation errors. +type UnsafePreferencesServiceServer interface { + mustEmbedUnimplementedPreferencesServiceServer() +} + +func RegisterPreferencesServiceServer(s grpc.ServiceRegistrar, srv PreferencesServiceServer) { + // If the following call panics, it indicates UnimplementedPreferencesServiceServer 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(&PreferencesService_ServiceDesc, srv) +} + +func _PreferencesService_GetPreferences_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetPreferencesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(PreferencesServiceServer).GetPreferences(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: PreferencesService_GetPreferences_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(PreferencesServiceServer).GetPreferences(ctx, req.(*GetPreferencesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _PreferencesService_UpdatePreferences_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdatePreferencesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(PreferencesServiceServer).UpdatePreferences(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: PreferencesService_UpdatePreferences_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(PreferencesServiceServer).UpdatePreferences(ctx, req.(*UpdatePreferencesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// PreferencesService_ServiceDesc is the grpc.ServiceDesc for PreferencesService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var PreferencesService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "stream.app.v1.PreferencesService", + HandlerType: (*PreferencesServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetPreferences", + Handler: _PreferencesService_GetPreferences_Handler, + }, + { + MethodName: "UpdatePreferences", + Handler: _PreferencesService_UpdatePreferences_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "app/v1/account.proto", +} + +const ( + UsageService_GetUsage_FullMethodName = "/stream.app.v1.UsageService/GetUsage" +) + +// UsageServiceClient is the client API for UsageService 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 UsageServiceClient interface { + GetUsage(ctx context.Context, in *GetUsageRequest, opts ...grpc.CallOption) (*GetUsageResponse, error) +} + +type usageServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewUsageServiceClient(cc grpc.ClientConnInterface) UsageServiceClient { + return &usageServiceClient{cc} +} + +func (c *usageServiceClient) GetUsage(ctx context.Context, in *GetUsageRequest, opts ...grpc.CallOption) (*GetUsageResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetUsageResponse) + err := c.cc.Invoke(ctx, UsageService_GetUsage_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// UsageServiceServer is the server API for UsageService service. +// All implementations must embed UnimplementedUsageServiceServer +// for forward compatibility. +type UsageServiceServer interface { + GetUsage(context.Context, *GetUsageRequest) (*GetUsageResponse, error) + mustEmbedUnimplementedUsageServiceServer() +} + +// UnimplementedUsageServiceServer 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 UnimplementedUsageServiceServer struct{} + +func (UnimplementedUsageServiceServer) GetUsage(context.Context, *GetUsageRequest) (*GetUsageResponse, error) { + return nil, status.Error(codes.Unimplemented, "method GetUsage not implemented") +} +func (UnimplementedUsageServiceServer) mustEmbedUnimplementedUsageServiceServer() {} +func (UnimplementedUsageServiceServer) testEmbeddedByValue() {} + +// UnsafeUsageServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to UsageServiceServer will +// result in compilation errors. +type UnsafeUsageServiceServer interface { + mustEmbedUnimplementedUsageServiceServer() +} + +func RegisterUsageServiceServer(s grpc.ServiceRegistrar, srv UsageServiceServer) { + // If the following call panics, it indicates UnimplementedUsageServiceServer 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(&UsageService_ServiceDesc, srv) +} + +func _UsageService_GetUsage_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetUsageRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(UsageServiceServer).GetUsage(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: UsageService_GetUsage_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(UsageServiceServer).GetUsage(ctx, req.(*GetUsageRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// UsageService_ServiceDesc is the grpc.ServiceDesc for UsageService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var UsageService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "stream.app.v1.UsageService", + HandlerType: (*UsageServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetUsage", + Handler: _UsageService_GetUsage_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "app/v1/account.proto", +} + +const ( + NotificationsService_ListNotifications_FullMethodName = "/stream.app.v1.NotificationsService/ListNotifications" + NotificationsService_MarkNotificationRead_FullMethodName = "/stream.app.v1.NotificationsService/MarkNotificationRead" + NotificationsService_MarkAllNotificationsRead_FullMethodName = "/stream.app.v1.NotificationsService/MarkAllNotificationsRead" + NotificationsService_DeleteNotification_FullMethodName = "/stream.app.v1.NotificationsService/DeleteNotification" + NotificationsService_ClearNotifications_FullMethodName = "/stream.app.v1.NotificationsService/ClearNotifications" +) + +// NotificationsServiceClient is the client API for NotificationsService 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 NotificationsServiceClient interface { + ListNotifications(ctx context.Context, in *ListNotificationsRequest, opts ...grpc.CallOption) (*ListNotificationsResponse, error) + MarkNotificationRead(ctx context.Context, in *MarkNotificationReadRequest, opts ...grpc.CallOption) (*MessageResponse, error) + MarkAllNotificationsRead(ctx context.Context, in *MarkAllNotificationsReadRequest, opts ...grpc.CallOption) (*MessageResponse, error) + DeleteNotification(ctx context.Context, in *DeleteNotificationRequest, opts ...grpc.CallOption) (*MessageResponse, error) + ClearNotifications(ctx context.Context, in *ClearNotificationsRequest, opts ...grpc.CallOption) (*MessageResponse, error) +} + +type notificationsServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewNotificationsServiceClient(cc grpc.ClientConnInterface) NotificationsServiceClient { + return ¬ificationsServiceClient{cc} +} + +func (c *notificationsServiceClient) ListNotifications(ctx context.Context, in *ListNotificationsRequest, opts ...grpc.CallOption) (*ListNotificationsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(ListNotificationsResponse) + err := c.cc.Invoke(ctx, NotificationsService_ListNotifications_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *notificationsServiceClient) MarkNotificationRead(ctx context.Context, in *MarkNotificationReadRequest, opts ...grpc.CallOption) (*MessageResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(MessageResponse) + err := c.cc.Invoke(ctx, NotificationsService_MarkNotificationRead_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *notificationsServiceClient) MarkAllNotificationsRead(ctx context.Context, in *MarkAllNotificationsReadRequest, opts ...grpc.CallOption) (*MessageResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(MessageResponse) + err := c.cc.Invoke(ctx, NotificationsService_MarkAllNotificationsRead_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *notificationsServiceClient) DeleteNotification(ctx context.Context, in *DeleteNotificationRequest, opts ...grpc.CallOption) (*MessageResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(MessageResponse) + err := c.cc.Invoke(ctx, NotificationsService_DeleteNotification_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *notificationsServiceClient) ClearNotifications(ctx context.Context, in *ClearNotificationsRequest, opts ...grpc.CallOption) (*MessageResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(MessageResponse) + err := c.cc.Invoke(ctx, NotificationsService_ClearNotifications_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// NotificationsServiceServer is the server API for NotificationsService service. +// All implementations must embed UnimplementedNotificationsServiceServer +// for forward compatibility. +type NotificationsServiceServer interface { + ListNotifications(context.Context, *ListNotificationsRequest) (*ListNotificationsResponse, error) + MarkNotificationRead(context.Context, *MarkNotificationReadRequest) (*MessageResponse, error) + MarkAllNotificationsRead(context.Context, *MarkAllNotificationsReadRequest) (*MessageResponse, error) + DeleteNotification(context.Context, *DeleteNotificationRequest) (*MessageResponse, error) + ClearNotifications(context.Context, *ClearNotificationsRequest) (*MessageResponse, error) + mustEmbedUnimplementedNotificationsServiceServer() +} + +// UnimplementedNotificationsServiceServer 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 UnimplementedNotificationsServiceServer struct{} + +func (UnimplementedNotificationsServiceServer) ListNotifications(context.Context, *ListNotificationsRequest) (*ListNotificationsResponse, error) { + return nil, status.Error(codes.Unimplemented, "method ListNotifications not implemented") +} +func (UnimplementedNotificationsServiceServer) MarkNotificationRead(context.Context, *MarkNotificationReadRequest) (*MessageResponse, error) { + return nil, status.Error(codes.Unimplemented, "method MarkNotificationRead not implemented") +} +func (UnimplementedNotificationsServiceServer) MarkAllNotificationsRead(context.Context, *MarkAllNotificationsReadRequest) (*MessageResponse, error) { + return nil, status.Error(codes.Unimplemented, "method MarkAllNotificationsRead not implemented") +} +func (UnimplementedNotificationsServiceServer) DeleteNotification(context.Context, *DeleteNotificationRequest) (*MessageResponse, error) { + return nil, status.Error(codes.Unimplemented, "method DeleteNotification not implemented") +} +func (UnimplementedNotificationsServiceServer) ClearNotifications(context.Context, *ClearNotificationsRequest) (*MessageResponse, error) { + return nil, status.Error(codes.Unimplemented, "method ClearNotifications not implemented") +} +func (UnimplementedNotificationsServiceServer) mustEmbedUnimplementedNotificationsServiceServer() {} +func (UnimplementedNotificationsServiceServer) testEmbeddedByValue() {} + +// UnsafeNotificationsServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to NotificationsServiceServer will +// result in compilation errors. +type UnsafeNotificationsServiceServer interface { + mustEmbedUnimplementedNotificationsServiceServer() +} + +func RegisterNotificationsServiceServer(s grpc.ServiceRegistrar, srv NotificationsServiceServer) { + // If the following call panics, it indicates UnimplementedNotificationsServiceServer 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(&NotificationsService_ServiceDesc, srv) +} + +func _NotificationsService_ListNotifications_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListNotificationsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NotificationsServiceServer).ListNotifications(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: NotificationsService_ListNotifications_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NotificationsServiceServer).ListNotifications(ctx, req.(*ListNotificationsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _NotificationsService_MarkNotificationRead_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MarkNotificationReadRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NotificationsServiceServer).MarkNotificationRead(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: NotificationsService_MarkNotificationRead_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NotificationsServiceServer).MarkNotificationRead(ctx, req.(*MarkNotificationReadRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _NotificationsService_MarkAllNotificationsRead_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MarkAllNotificationsReadRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NotificationsServiceServer).MarkAllNotificationsRead(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: NotificationsService_MarkAllNotificationsRead_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NotificationsServiceServer).MarkAllNotificationsRead(ctx, req.(*MarkAllNotificationsReadRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _NotificationsService_DeleteNotification_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteNotificationRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NotificationsServiceServer).DeleteNotification(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: NotificationsService_DeleteNotification_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NotificationsServiceServer).DeleteNotification(ctx, req.(*DeleteNotificationRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _NotificationsService_ClearNotifications_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ClearNotificationsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NotificationsServiceServer).ClearNotifications(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: NotificationsService_ClearNotifications_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NotificationsServiceServer).ClearNotifications(ctx, req.(*ClearNotificationsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// NotificationsService_ServiceDesc is the grpc.ServiceDesc for NotificationsService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var NotificationsService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "stream.app.v1.NotificationsService", + HandlerType: (*NotificationsServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "ListNotifications", + Handler: _NotificationsService_ListNotifications_Handler, + }, + { + MethodName: "MarkNotificationRead", + Handler: _NotificationsService_MarkNotificationRead_Handler, + }, + { + MethodName: "MarkAllNotificationsRead", + Handler: _NotificationsService_MarkAllNotificationsRead_Handler, + }, + { + MethodName: "DeleteNotification", + Handler: _NotificationsService_DeleteNotification_Handler, + }, + { + MethodName: "ClearNotifications", + Handler: _NotificationsService_ClearNotifications_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "app/v1/account.proto", +} diff --git a/internal/gen/proto/app/v1/admin.pb.go b/internal/gen/proto/app/v1/admin.pb.go new file mode 100644 index 0000000..ab975a5 --- /dev/null +++ b/internal/gen/proto/app/v1/admin.pb.go @@ -0,0 +1,4226 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc (unknown) +// source: app/v1/admin.proto + +package appv1 + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type GetAdminDashboardRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetAdminDashboardRequest) Reset() { + *x = GetAdminDashboardRequest{} + mi := &file_app_v1_admin_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetAdminDashboardRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetAdminDashboardRequest) ProtoMessage() {} + +func (x *GetAdminDashboardRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[0] + 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 GetAdminDashboardRequest.ProtoReflect.Descriptor instead. +func (*GetAdminDashboardRequest) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{0} +} + +type GetAdminDashboardResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Dashboard *AdminDashboard `protobuf:"bytes,1,opt,name=dashboard,proto3" json:"dashboard,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetAdminDashboardResponse) Reset() { + *x = GetAdminDashboardResponse{} + mi := &file_app_v1_admin_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetAdminDashboardResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetAdminDashboardResponse) ProtoMessage() {} + +func (x *GetAdminDashboardResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[1] + 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 GetAdminDashboardResponse.ProtoReflect.Descriptor instead. +func (*GetAdminDashboardResponse) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{1} +} + +func (x *GetAdminDashboardResponse) GetDashboard() *AdminDashboard { + if x != nil { + return x.Dashboard + } + return nil +} + +type ListAdminUsersRequest 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"` + Search *string `protobuf:"bytes,3,opt,name=search,proto3,oneof" json:"search,omitempty"` + Role *string `protobuf:"bytes,4,opt,name=role,proto3,oneof" json:"role,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListAdminUsersRequest) Reset() { + *x = ListAdminUsersRequest{} + mi := &file_app_v1_admin_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListAdminUsersRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListAdminUsersRequest) ProtoMessage() {} + +func (x *ListAdminUsersRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListAdminUsersRequest.ProtoReflect.Descriptor instead. +func (*ListAdminUsersRequest) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{2} +} + +func (x *ListAdminUsersRequest) GetPage() int32 { + if x != nil { + return x.Page + } + return 0 +} + +func (x *ListAdminUsersRequest) GetLimit() int32 { + if x != nil { + return x.Limit + } + return 0 +} + +func (x *ListAdminUsersRequest) GetSearch() string { + if x != nil && x.Search != nil { + return *x.Search + } + return "" +} + +func (x *ListAdminUsersRequest) GetRole() string { + if x != nil && x.Role != nil { + return *x.Role + } + return "" +} + +type ListAdminUsersResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Users []*AdminUser `protobuf:"bytes,1,rep,name=users,proto3" json:"users,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 *ListAdminUsersResponse) Reset() { + *x = ListAdminUsersResponse{} + mi := &file_app_v1_admin_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListAdminUsersResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListAdminUsersResponse) ProtoMessage() {} + +func (x *ListAdminUsersResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListAdminUsersResponse.ProtoReflect.Descriptor instead. +func (*ListAdminUsersResponse) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{3} +} + +func (x *ListAdminUsersResponse) GetUsers() []*AdminUser { + if x != nil { + return x.Users + } + return nil +} + +func (x *ListAdminUsersResponse) GetTotal() int64 { + if x != nil { + return x.Total + } + return 0 +} + +func (x *ListAdminUsersResponse) GetPage() int32 { + if x != nil { + return x.Page + } + return 0 +} + +func (x *ListAdminUsersResponse) GetLimit() int32 { + if x != nil { + return x.Limit + } + return 0 +} + +type GetAdminUserRequest 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 *GetAdminUserRequest) Reset() { + *x = GetAdminUserRequest{} + mi := &file_app_v1_admin_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetAdminUserRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetAdminUserRequest) ProtoMessage() {} + +func (x *GetAdminUserRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[4] + 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 GetAdminUserRequest.ProtoReflect.Descriptor instead. +func (*GetAdminUserRequest) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{4} +} + +func (x *GetAdminUserRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type GetAdminUserResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + User *AdminUserDetail `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetAdminUserResponse) Reset() { + *x = GetAdminUserResponse{} + mi := &file_app_v1_admin_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetAdminUserResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetAdminUserResponse) ProtoMessage() {} + +func (x *GetAdminUserResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[5] + 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 GetAdminUserResponse.ProtoReflect.Descriptor instead. +func (*GetAdminUserResponse) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{5} +} + +func (x *GetAdminUserResponse) GetUser() *AdminUserDetail { + if x != nil { + return x.User + } + return nil +} + +type CreateAdminUserRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Email string `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"` + Username *string `protobuf:"bytes,2,opt,name=username,proto3,oneof" json:"username,omitempty"` + Password string `protobuf:"bytes,3,opt,name=password,proto3" json:"password,omitempty"` + Role *string `protobuf:"bytes,4,opt,name=role,proto3,oneof" json:"role,omitempty"` + PlanId *string `protobuf:"bytes,5,opt,name=plan_id,json=planId,proto3,oneof" json:"plan_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateAdminUserRequest) Reset() { + *x = CreateAdminUserRequest{} + mi := &file_app_v1_admin_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateAdminUserRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateAdminUserRequest) ProtoMessage() {} + +func (x *CreateAdminUserRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_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 CreateAdminUserRequest.ProtoReflect.Descriptor instead. +func (*CreateAdminUserRequest) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{6} +} + +func (x *CreateAdminUserRequest) GetEmail() string { + if x != nil { + return x.Email + } + return "" +} + +func (x *CreateAdminUserRequest) GetUsername() string { + if x != nil && x.Username != nil { + return *x.Username + } + return "" +} + +func (x *CreateAdminUserRequest) GetPassword() string { + if x != nil { + return x.Password + } + return "" +} + +func (x *CreateAdminUserRequest) GetRole() string { + if x != nil && x.Role != nil { + return *x.Role + } + return "" +} + +func (x *CreateAdminUserRequest) GetPlanId() string { + if x != nil && x.PlanId != nil { + return *x.PlanId + } + return "" +} + +type CreateAdminUserResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + User *AdminUser `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateAdminUserResponse) Reset() { + *x = CreateAdminUserResponse{} + mi := &file_app_v1_admin_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateAdminUserResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateAdminUserResponse) ProtoMessage() {} + +func (x *CreateAdminUserResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[7] + 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 CreateAdminUserResponse.ProtoReflect.Descriptor instead. +func (*CreateAdminUserResponse) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{7} +} + +func (x *CreateAdminUserResponse) GetUser() *AdminUser { + if x != nil { + return x.User + } + return nil +} + +type UpdateAdminUserRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Email *string `protobuf:"bytes,2,opt,name=email,proto3,oneof" json:"email,omitempty"` + Username *string `protobuf:"bytes,3,opt,name=username,proto3,oneof" json:"username,omitempty"` + Password *string `protobuf:"bytes,4,opt,name=password,proto3,oneof" json:"password,omitempty"` + Role *string `protobuf:"bytes,5,opt,name=role,proto3,oneof" json:"role,omitempty"` + PlanId *string `protobuf:"bytes,6,opt,name=plan_id,json=planId,proto3,oneof" json:"plan_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UpdateAdminUserRequest) Reset() { + *x = UpdateAdminUserRequest{} + mi := &file_app_v1_admin_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdateAdminUserRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateAdminUserRequest) ProtoMessage() {} + +func (x *UpdateAdminUserRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[8] + 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 UpdateAdminUserRequest.ProtoReflect.Descriptor instead. +func (*UpdateAdminUserRequest) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{8} +} + +func (x *UpdateAdminUserRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *UpdateAdminUserRequest) GetEmail() string { + if x != nil && x.Email != nil { + return *x.Email + } + return "" +} + +func (x *UpdateAdminUserRequest) GetUsername() string { + if x != nil && x.Username != nil { + return *x.Username + } + return "" +} + +func (x *UpdateAdminUserRequest) GetPassword() string { + if x != nil && x.Password != nil { + return *x.Password + } + return "" +} + +func (x *UpdateAdminUserRequest) GetRole() string { + if x != nil && x.Role != nil { + return *x.Role + } + return "" +} + +func (x *UpdateAdminUserRequest) GetPlanId() string { + if x != nil && x.PlanId != nil { + return *x.PlanId + } + return "" +} + +type UpdateAdminUserResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + User *AdminUser `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UpdateAdminUserResponse) Reset() { + *x = UpdateAdminUserResponse{} + mi := &file_app_v1_admin_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdateAdminUserResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateAdminUserResponse) ProtoMessage() {} + +func (x *UpdateAdminUserResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[9] + 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 UpdateAdminUserResponse.ProtoReflect.Descriptor instead. +func (*UpdateAdminUserResponse) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{9} +} + +func (x *UpdateAdminUserResponse) GetUser() *AdminUser { + if x != nil { + return x.User + } + return nil +} + +type UpdateAdminUserRoleRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Role string `protobuf:"bytes,2,opt,name=role,proto3" json:"role,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UpdateAdminUserRoleRequest) Reset() { + *x = UpdateAdminUserRoleRequest{} + mi := &file_app_v1_admin_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdateAdminUserRoleRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateAdminUserRoleRequest) ProtoMessage() {} + +func (x *UpdateAdminUserRoleRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[10] + 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 UpdateAdminUserRoleRequest.ProtoReflect.Descriptor instead. +func (*UpdateAdminUserRoleRequest) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{10} +} + +func (x *UpdateAdminUserRoleRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *UpdateAdminUserRoleRequest) GetRole() string { + if x != nil { + return x.Role + } + return "" +} + +type UpdateAdminUserRoleResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` + Role string `protobuf:"bytes,2,opt,name=role,proto3" json:"role,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UpdateAdminUserRoleResponse) Reset() { + *x = UpdateAdminUserRoleResponse{} + mi := &file_app_v1_admin_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdateAdminUserRoleResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateAdminUserRoleResponse) ProtoMessage() {} + +func (x *UpdateAdminUserRoleResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[11] + 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 UpdateAdminUserRoleResponse.ProtoReflect.Descriptor instead. +func (*UpdateAdminUserRoleResponse) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{11} +} + +func (x *UpdateAdminUserRoleResponse) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +func (x *UpdateAdminUserRoleResponse) GetRole() string { + if x != nil { + return x.Role + } + return "" +} + +type DeleteAdminUserRequest 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 *DeleteAdminUserRequest) Reset() { + *x = DeleteAdminUserRequest{} + mi := &file_app_v1_admin_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeleteAdminUserRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteAdminUserRequest) ProtoMessage() {} + +func (x *DeleteAdminUserRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_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 DeleteAdminUserRequest.ProtoReflect.Descriptor instead. +func (*DeleteAdminUserRequest) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{12} +} + +func (x *DeleteAdminUserRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type ListAdminVideosRequest 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"` + Search *string `protobuf:"bytes,3,opt,name=search,proto3,oneof" json:"search,omitempty"` + UserId *string `protobuf:"bytes,4,opt,name=user_id,json=userId,proto3,oneof" json:"user_id,omitempty"` + Status *string `protobuf:"bytes,5,opt,name=status,proto3,oneof" json:"status,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListAdminVideosRequest) Reset() { + *x = ListAdminVideosRequest{} + mi := &file_app_v1_admin_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListAdminVideosRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListAdminVideosRequest) ProtoMessage() {} + +func (x *ListAdminVideosRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_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 ListAdminVideosRequest.ProtoReflect.Descriptor instead. +func (*ListAdminVideosRequest) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{13} +} + +func (x *ListAdminVideosRequest) GetPage() int32 { + if x != nil { + return x.Page + } + return 0 +} + +func (x *ListAdminVideosRequest) GetLimit() int32 { + if x != nil { + return x.Limit + } + return 0 +} + +func (x *ListAdminVideosRequest) GetSearch() string { + if x != nil && x.Search != nil { + return *x.Search + } + return "" +} + +func (x *ListAdminVideosRequest) GetUserId() string { + if x != nil && x.UserId != nil { + return *x.UserId + } + return "" +} + +func (x *ListAdminVideosRequest) GetStatus() string { + if x != nil && x.Status != nil { + return *x.Status + } + return "" +} + +type ListAdminVideosResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Videos []*AdminVideo `protobuf:"bytes,1,rep,name=videos,proto3" json:"videos,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 *ListAdminVideosResponse) Reset() { + *x = ListAdminVideosResponse{} + mi := &file_app_v1_admin_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListAdminVideosResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListAdminVideosResponse) ProtoMessage() {} + +func (x *ListAdminVideosResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_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 ListAdminVideosResponse.ProtoReflect.Descriptor instead. +func (*ListAdminVideosResponse) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{14} +} + +func (x *ListAdminVideosResponse) GetVideos() []*AdminVideo { + if x != nil { + return x.Videos + } + return nil +} + +func (x *ListAdminVideosResponse) GetTotal() int64 { + if x != nil { + return x.Total + } + return 0 +} + +func (x *ListAdminVideosResponse) GetPage() int32 { + if x != nil { + return x.Page + } + return 0 +} + +func (x *ListAdminVideosResponse) GetLimit() int32 { + if x != nil { + return x.Limit + } + return 0 +} + +type GetAdminVideoRequest 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 *GetAdminVideoRequest) Reset() { + *x = GetAdminVideoRequest{} + mi := &file_app_v1_admin_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetAdminVideoRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetAdminVideoRequest) ProtoMessage() {} + +func (x *GetAdminVideoRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_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 GetAdminVideoRequest.ProtoReflect.Descriptor instead. +func (*GetAdminVideoRequest) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{15} +} + +func (x *GetAdminVideoRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type GetAdminVideoResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Video *AdminVideo `protobuf:"bytes,1,opt,name=video,proto3" json:"video,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetAdminVideoResponse) Reset() { + *x = GetAdminVideoResponse{} + mi := &file_app_v1_admin_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetAdminVideoResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetAdminVideoResponse) ProtoMessage() {} + +func (x *GetAdminVideoResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_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 GetAdminVideoResponse.ProtoReflect.Descriptor instead. +func (*GetAdminVideoResponse) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{16} +} + +func (x *GetAdminVideoResponse) GetVideo() *AdminVideo { + if x != nil { + return x.Video + } + return nil +} + +type CreateAdminVideoRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + UserId string `protobuf:"bytes,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` + Title string `protobuf:"bytes,2,opt,name=title,proto3" json:"title,omitempty"` + Description *string `protobuf:"bytes,3,opt,name=description,proto3,oneof" json:"description,omitempty"` + Url string `protobuf:"bytes,4,opt,name=url,proto3" json:"url,omitempty"` + Size int64 `protobuf:"varint,5,opt,name=size,proto3" json:"size,omitempty"` + Duration int32 `protobuf:"varint,6,opt,name=duration,proto3" json:"duration,omitempty"` + Format *string `protobuf:"bytes,7,opt,name=format,proto3,oneof" json:"format,omitempty"` + Status *string `protobuf:"bytes,8,opt,name=status,proto3,oneof" json:"status,omitempty"` + AdTemplateId *string `protobuf:"bytes,9,opt,name=ad_template_id,json=adTemplateId,proto3,oneof" json:"ad_template_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateAdminVideoRequest) Reset() { + *x = CreateAdminVideoRequest{} + mi := &file_app_v1_admin_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateAdminVideoRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateAdminVideoRequest) ProtoMessage() {} + +func (x *CreateAdminVideoRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_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 CreateAdminVideoRequest.ProtoReflect.Descriptor instead. +func (*CreateAdminVideoRequest) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{17} +} + +func (x *CreateAdminVideoRequest) GetUserId() string { + if x != nil { + return x.UserId + } + return "" +} + +func (x *CreateAdminVideoRequest) GetTitle() string { + if x != nil { + return x.Title + } + return "" +} + +func (x *CreateAdminVideoRequest) GetDescription() string { + if x != nil && x.Description != nil { + return *x.Description + } + return "" +} + +func (x *CreateAdminVideoRequest) GetUrl() string { + if x != nil { + return x.Url + } + return "" +} + +func (x *CreateAdminVideoRequest) GetSize() int64 { + if x != nil { + return x.Size + } + return 0 +} + +func (x *CreateAdminVideoRequest) GetDuration() int32 { + if x != nil { + return x.Duration + } + return 0 +} + +func (x *CreateAdminVideoRequest) GetFormat() string { + if x != nil && x.Format != nil { + return *x.Format + } + return "" +} + +func (x *CreateAdminVideoRequest) GetStatus() string { + if x != nil && x.Status != nil { + return *x.Status + } + return "" +} + +func (x *CreateAdminVideoRequest) GetAdTemplateId() string { + if x != nil && x.AdTemplateId != nil { + return *x.AdTemplateId + } + return "" +} + +type CreateAdminVideoResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Video *AdminVideo `protobuf:"bytes,1,opt,name=video,proto3" json:"video,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateAdminVideoResponse) Reset() { + *x = CreateAdminVideoResponse{} + mi := &file_app_v1_admin_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateAdminVideoResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateAdminVideoResponse) ProtoMessage() {} + +func (x *CreateAdminVideoResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_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 CreateAdminVideoResponse.ProtoReflect.Descriptor instead. +func (*CreateAdminVideoResponse) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{18} +} + +func (x *CreateAdminVideoResponse) GetVideo() *AdminVideo { + if x != nil { + return x.Video + } + return nil +} + +type UpdateAdminVideoRequest 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"` + Title string `protobuf:"bytes,3,opt,name=title,proto3" json:"title,omitempty"` + Description *string `protobuf:"bytes,4,opt,name=description,proto3,oneof" json:"description,omitempty"` + Url string `protobuf:"bytes,5,opt,name=url,proto3" json:"url,omitempty"` + Size int64 `protobuf:"varint,6,opt,name=size,proto3" json:"size,omitempty"` + Duration int32 `protobuf:"varint,7,opt,name=duration,proto3" json:"duration,omitempty"` + Format *string `protobuf:"bytes,8,opt,name=format,proto3,oneof" json:"format,omitempty"` + Status *string `protobuf:"bytes,9,opt,name=status,proto3,oneof" json:"status,omitempty"` + AdTemplateId *string `protobuf:"bytes,10,opt,name=ad_template_id,json=adTemplateId,proto3,oneof" json:"ad_template_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UpdateAdminVideoRequest) Reset() { + *x = UpdateAdminVideoRequest{} + mi := &file_app_v1_admin_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdateAdminVideoRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateAdminVideoRequest) ProtoMessage() {} + +func (x *UpdateAdminVideoRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_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 UpdateAdminVideoRequest.ProtoReflect.Descriptor instead. +func (*UpdateAdminVideoRequest) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{19} +} + +func (x *UpdateAdminVideoRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *UpdateAdminVideoRequest) GetUserId() string { + if x != nil { + return x.UserId + } + return "" +} + +func (x *UpdateAdminVideoRequest) GetTitle() string { + if x != nil { + return x.Title + } + return "" +} + +func (x *UpdateAdminVideoRequest) GetDescription() string { + if x != nil && x.Description != nil { + return *x.Description + } + return "" +} + +func (x *UpdateAdminVideoRequest) GetUrl() string { + if x != nil { + return x.Url + } + return "" +} + +func (x *UpdateAdminVideoRequest) GetSize() int64 { + if x != nil { + return x.Size + } + return 0 +} + +func (x *UpdateAdminVideoRequest) GetDuration() int32 { + if x != nil { + return x.Duration + } + return 0 +} + +func (x *UpdateAdminVideoRequest) GetFormat() string { + if x != nil && x.Format != nil { + return *x.Format + } + return "" +} + +func (x *UpdateAdminVideoRequest) GetStatus() string { + if x != nil && x.Status != nil { + return *x.Status + } + return "" +} + +func (x *UpdateAdminVideoRequest) GetAdTemplateId() string { + if x != nil && x.AdTemplateId != nil { + return *x.AdTemplateId + } + return "" +} + +type UpdateAdminVideoResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Video *AdminVideo `protobuf:"bytes,1,opt,name=video,proto3" json:"video,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UpdateAdminVideoResponse) Reset() { + *x = UpdateAdminVideoResponse{} + mi := &file_app_v1_admin_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdateAdminVideoResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateAdminVideoResponse) ProtoMessage() {} + +func (x *UpdateAdminVideoResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_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 UpdateAdminVideoResponse.ProtoReflect.Descriptor instead. +func (*UpdateAdminVideoResponse) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{20} +} + +func (x *UpdateAdminVideoResponse) GetVideo() *AdminVideo { + if x != nil { + return x.Video + } + return nil +} + +type DeleteAdminVideoRequest 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 *DeleteAdminVideoRequest) Reset() { + *x = DeleteAdminVideoRequest{} + mi := &file_app_v1_admin_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeleteAdminVideoRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteAdminVideoRequest) ProtoMessage() {} + +func (x *DeleteAdminVideoRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[21] + 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 DeleteAdminVideoRequest.ProtoReflect.Descriptor instead. +func (*DeleteAdminVideoRequest) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{21} +} + +func (x *DeleteAdminVideoRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type ListAdminPaymentsRequest 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"` + UserId *string `protobuf:"bytes,3,opt,name=user_id,json=userId,proto3,oneof" json:"user_id,omitempty"` + Status *string `protobuf:"bytes,4,opt,name=status,proto3,oneof" json:"status,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListAdminPaymentsRequest) Reset() { + *x = ListAdminPaymentsRequest{} + mi := &file_app_v1_admin_proto_msgTypes[22] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListAdminPaymentsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListAdminPaymentsRequest) ProtoMessage() {} + +func (x *ListAdminPaymentsRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[22] + 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 ListAdminPaymentsRequest.ProtoReflect.Descriptor instead. +func (*ListAdminPaymentsRequest) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{22} +} + +func (x *ListAdminPaymentsRequest) GetPage() int32 { + if x != nil { + return x.Page + } + return 0 +} + +func (x *ListAdminPaymentsRequest) GetLimit() int32 { + if x != nil { + return x.Limit + } + return 0 +} + +func (x *ListAdminPaymentsRequest) GetUserId() string { + if x != nil && x.UserId != nil { + return *x.UserId + } + return "" +} + +func (x *ListAdminPaymentsRequest) GetStatus() string { + if x != nil && x.Status != nil { + return *x.Status + } + return "" +} + +type ListAdminPaymentsResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Payments []*AdminPayment `protobuf:"bytes,1,rep,name=payments,proto3" json:"payments,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 *ListAdminPaymentsResponse) Reset() { + *x = ListAdminPaymentsResponse{} + mi := &file_app_v1_admin_proto_msgTypes[23] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListAdminPaymentsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListAdminPaymentsResponse) ProtoMessage() {} + +func (x *ListAdminPaymentsResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[23] + 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 ListAdminPaymentsResponse.ProtoReflect.Descriptor instead. +func (*ListAdminPaymentsResponse) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{23} +} + +func (x *ListAdminPaymentsResponse) GetPayments() []*AdminPayment { + if x != nil { + return x.Payments + } + return nil +} + +func (x *ListAdminPaymentsResponse) GetTotal() int64 { + if x != nil { + return x.Total + } + return 0 +} + +func (x *ListAdminPaymentsResponse) GetPage() int32 { + if x != nil { + return x.Page + } + return 0 +} + +func (x *ListAdminPaymentsResponse) GetLimit() int32 { + if x != nil { + return x.Limit + } + return 0 +} + +type GetAdminPaymentRequest 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 *GetAdminPaymentRequest) Reset() { + *x = GetAdminPaymentRequest{} + mi := &file_app_v1_admin_proto_msgTypes[24] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetAdminPaymentRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetAdminPaymentRequest) ProtoMessage() {} + +func (x *GetAdminPaymentRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_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 GetAdminPaymentRequest.ProtoReflect.Descriptor instead. +func (*GetAdminPaymentRequest) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{24} +} + +func (x *GetAdminPaymentRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type GetAdminPaymentResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Payment *AdminPayment `protobuf:"bytes,1,opt,name=payment,proto3" json:"payment,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetAdminPaymentResponse) Reset() { + *x = GetAdminPaymentResponse{} + mi := &file_app_v1_admin_proto_msgTypes[25] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetAdminPaymentResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetAdminPaymentResponse) ProtoMessage() {} + +func (x *GetAdminPaymentResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[25] + 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 GetAdminPaymentResponse.ProtoReflect.Descriptor instead. +func (*GetAdminPaymentResponse) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{25} +} + +func (x *GetAdminPaymentResponse) GetPayment() *AdminPayment { + if x != nil { + return x.Payment + } + return nil +} + +type CreateAdminPaymentRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + UserId string `protobuf:"bytes,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` + PlanId string `protobuf:"bytes,2,opt,name=plan_id,json=planId,proto3" json:"plan_id,omitempty"` + TermMonths int32 `protobuf:"varint,3,opt,name=term_months,json=termMonths,proto3" json:"term_months,omitempty"` + PaymentMethod string `protobuf:"bytes,4,opt,name=payment_method,json=paymentMethod,proto3" json:"payment_method,omitempty"` + TopupAmount *float64 `protobuf:"fixed64,5,opt,name=topup_amount,json=topupAmount,proto3,oneof" json:"topup_amount,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateAdminPaymentRequest) Reset() { + *x = CreateAdminPaymentRequest{} + mi := &file_app_v1_admin_proto_msgTypes[26] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateAdminPaymentRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateAdminPaymentRequest) ProtoMessage() {} + +func (x *CreateAdminPaymentRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[26] + 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 CreateAdminPaymentRequest.ProtoReflect.Descriptor instead. +func (*CreateAdminPaymentRequest) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{26} +} + +func (x *CreateAdminPaymentRequest) GetUserId() string { + if x != nil { + return x.UserId + } + return "" +} + +func (x *CreateAdminPaymentRequest) GetPlanId() string { + if x != nil { + return x.PlanId + } + return "" +} + +func (x *CreateAdminPaymentRequest) GetTermMonths() int32 { + if x != nil { + return x.TermMonths + } + return 0 +} + +func (x *CreateAdminPaymentRequest) GetPaymentMethod() string { + if x != nil { + return x.PaymentMethod + } + return "" +} + +func (x *CreateAdminPaymentRequest) GetTopupAmount() float64 { + if x != nil && x.TopupAmount != nil { + return *x.TopupAmount + } + return 0 +} + +type CreateAdminPaymentResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Payment *AdminPayment `protobuf:"bytes,1,opt,name=payment,proto3" json:"payment,omitempty"` + Subscription *PlanSubscription `protobuf:"bytes,2,opt,name=subscription,proto3" json:"subscription,omitempty"` + WalletBalance float64 `protobuf:"fixed64,3,opt,name=wallet_balance,json=walletBalance,proto3" json:"wallet_balance,omitempty"` + InvoiceId string `protobuf:"bytes,4,opt,name=invoice_id,json=invoiceId,proto3" json:"invoice_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateAdminPaymentResponse) Reset() { + *x = CreateAdminPaymentResponse{} + mi := &file_app_v1_admin_proto_msgTypes[27] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateAdminPaymentResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateAdminPaymentResponse) ProtoMessage() {} + +func (x *CreateAdminPaymentResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[27] + 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 CreateAdminPaymentResponse.ProtoReflect.Descriptor instead. +func (*CreateAdminPaymentResponse) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{27} +} + +func (x *CreateAdminPaymentResponse) GetPayment() *AdminPayment { + if x != nil { + return x.Payment + } + return nil +} + +func (x *CreateAdminPaymentResponse) GetSubscription() *PlanSubscription { + if x != nil { + return x.Subscription + } + return nil +} + +func (x *CreateAdminPaymentResponse) GetWalletBalance() float64 { + if x != nil { + return x.WalletBalance + } + return 0 +} + +func (x *CreateAdminPaymentResponse) GetInvoiceId() string { + if x != nil { + return x.InvoiceId + } + return "" +} + +type UpdateAdminPaymentRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Status string `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UpdateAdminPaymentRequest) Reset() { + *x = UpdateAdminPaymentRequest{} + mi := &file_app_v1_admin_proto_msgTypes[28] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdateAdminPaymentRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateAdminPaymentRequest) ProtoMessage() {} + +func (x *UpdateAdminPaymentRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[28] + 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 UpdateAdminPaymentRequest.ProtoReflect.Descriptor instead. +func (*UpdateAdminPaymentRequest) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{28} +} + +func (x *UpdateAdminPaymentRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *UpdateAdminPaymentRequest) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +type UpdateAdminPaymentResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Payment *AdminPayment `protobuf:"bytes,1,opt,name=payment,proto3" json:"payment,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UpdateAdminPaymentResponse) Reset() { + *x = UpdateAdminPaymentResponse{} + mi := &file_app_v1_admin_proto_msgTypes[29] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdateAdminPaymentResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateAdminPaymentResponse) ProtoMessage() {} + +func (x *UpdateAdminPaymentResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[29] + 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 UpdateAdminPaymentResponse.ProtoReflect.Descriptor instead. +func (*UpdateAdminPaymentResponse) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{29} +} + +func (x *UpdateAdminPaymentResponse) GetPayment() *AdminPayment { + if x != nil { + return x.Payment + } + return nil +} + +type ListAdminPlansRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListAdminPlansRequest) Reset() { + *x = ListAdminPlansRequest{} + mi := &file_app_v1_admin_proto_msgTypes[30] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListAdminPlansRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListAdminPlansRequest) ProtoMessage() {} + +func (x *ListAdminPlansRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[30] + 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 ListAdminPlansRequest.ProtoReflect.Descriptor instead. +func (*ListAdminPlansRequest) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{30} +} + +type ListAdminPlansResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Plans []*AdminPlan `protobuf:"bytes,1,rep,name=plans,proto3" json:"plans,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListAdminPlansResponse) Reset() { + *x = ListAdminPlansResponse{} + mi := &file_app_v1_admin_proto_msgTypes[31] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListAdminPlansResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListAdminPlansResponse) ProtoMessage() {} + +func (x *ListAdminPlansResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[31] + 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 ListAdminPlansResponse.ProtoReflect.Descriptor instead. +func (*ListAdminPlansResponse) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{31} +} + +func (x *ListAdminPlansResponse) GetPlans() []*AdminPlan { + if x != nil { + return x.Plans + } + return nil +} + +type CreateAdminPlanRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Description *string `protobuf:"bytes,2,opt,name=description,proto3,oneof" json:"description,omitempty"` + Features []string `protobuf:"bytes,3,rep,name=features,proto3" json:"features,omitempty"` + Price float64 `protobuf:"fixed64,4,opt,name=price,proto3" json:"price,omitempty"` + Cycle string `protobuf:"bytes,5,opt,name=cycle,proto3" json:"cycle,omitempty"` + StorageLimit int64 `protobuf:"varint,6,opt,name=storage_limit,json=storageLimit,proto3" json:"storage_limit,omitempty"` + UploadLimit int32 `protobuf:"varint,7,opt,name=upload_limit,json=uploadLimit,proto3" json:"upload_limit,omitempty"` + IsActive *bool `protobuf:"varint,8,opt,name=is_active,json=isActive,proto3,oneof" json:"is_active,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateAdminPlanRequest) Reset() { + *x = CreateAdminPlanRequest{} + mi := &file_app_v1_admin_proto_msgTypes[32] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateAdminPlanRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateAdminPlanRequest) ProtoMessage() {} + +func (x *CreateAdminPlanRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[32] + 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 CreateAdminPlanRequest.ProtoReflect.Descriptor instead. +func (*CreateAdminPlanRequest) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{32} +} + +func (x *CreateAdminPlanRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *CreateAdminPlanRequest) GetDescription() string { + if x != nil && x.Description != nil { + return *x.Description + } + return "" +} + +func (x *CreateAdminPlanRequest) GetFeatures() []string { + if x != nil { + return x.Features + } + return nil +} + +func (x *CreateAdminPlanRequest) GetPrice() float64 { + if x != nil { + return x.Price + } + return 0 +} + +func (x *CreateAdminPlanRequest) GetCycle() string { + if x != nil { + return x.Cycle + } + return "" +} + +func (x *CreateAdminPlanRequest) GetStorageLimit() int64 { + if x != nil { + return x.StorageLimit + } + return 0 +} + +func (x *CreateAdminPlanRequest) GetUploadLimit() int32 { + if x != nil { + return x.UploadLimit + } + return 0 +} + +func (x *CreateAdminPlanRequest) GetIsActive() bool { + if x != nil && x.IsActive != nil { + return *x.IsActive + } + return false +} + +type CreateAdminPlanResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Plan *AdminPlan `protobuf:"bytes,1,opt,name=plan,proto3" json:"plan,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateAdminPlanResponse) Reset() { + *x = CreateAdminPlanResponse{} + mi := &file_app_v1_admin_proto_msgTypes[33] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateAdminPlanResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateAdminPlanResponse) ProtoMessage() {} + +func (x *CreateAdminPlanResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[33] + 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 CreateAdminPlanResponse.ProtoReflect.Descriptor instead. +func (*CreateAdminPlanResponse) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{33} +} + +func (x *CreateAdminPlanResponse) GetPlan() *AdminPlan { + if x != nil { + return x.Plan + } + return nil +} + +type UpdateAdminPlanRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Description *string `protobuf:"bytes,3,opt,name=description,proto3,oneof" json:"description,omitempty"` + Features []string `protobuf:"bytes,4,rep,name=features,proto3" json:"features,omitempty"` + Price float64 `protobuf:"fixed64,5,opt,name=price,proto3" json:"price,omitempty"` + Cycle string `protobuf:"bytes,6,opt,name=cycle,proto3" json:"cycle,omitempty"` + StorageLimit int64 `protobuf:"varint,7,opt,name=storage_limit,json=storageLimit,proto3" json:"storage_limit,omitempty"` + UploadLimit int32 `protobuf:"varint,8,opt,name=upload_limit,json=uploadLimit,proto3" json:"upload_limit,omitempty"` + IsActive *bool `protobuf:"varint,9,opt,name=is_active,json=isActive,proto3,oneof" json:"is_active,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UpdateAdminPlanRequest) Reset() { + *x = UpdateAdminPlanRequest{} + mi := &file_app_v1_admin_proto_msgTypes[34] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdateAdminPlanRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateAdminPlanRequest) ProtoMessage() {} + +func (x *UpdateAdminPlanRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[34] + 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 UpdateAdminPlanRequest.ProtoReflect.Descriptor instead. +func (*UpdateAdminPlanRequest) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{34} +} + +func (x *UpdateAdminPlanRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *UpdateAdminPlanRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *UpdateAdminPlanRequest) GetDescription() string { + if x != nil && x.Description != nil { + return *x.Description + } + return "" +} + +func (x *UpdateAdminPlanRequest) GetFeatures() []string { + if x != nil { + return x.Features + } + return nil +} + +func (x *UpdateAdminPlanRequest) GetPrice() float64 { + if x != nil { + return x.Price + } + return 0 +} + +func (x *UpdateAdminPlanRequest) GetCycle() string { + if x != nil { + return x.Cycle + } + return "" +} + +func (x *UpdateAdminPlanRequest) GetStorageLimit() int64 { + if x != nil { + return x.StorageLimit + } + return 0 +} + +func (x *UpdateAdminPlanRequest) GetUploadLimit() int32 { + if x != nil { + return x.UploadLimit + } + return 0 +} + +func (x *UpdateAdminPlanRequest) GetIsActive() bool { + if x != nil && x.IsActive != nil { + return *x.IsActive + } + return false +} + +type UpdateAdminPlanResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Plan *AdminPlan `protobuf:"bytes,1,opt,name=plan,proto3" json:"plan,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UpdateAdminPlanResponse) Reset() { + *x = UpdateAdminPlanResponse{} + mi := &file_app_v1_admin_proto_msgTypes[35] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdateAdminPlanResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateAdminPlanResponse) ProtoMessage() {} + +func (x *UpdateAdminPlanResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[35] + 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 UpdateAdminPlanResponse.ProtoReflect.Descriptor instead. +func (*UpdateAdminPlanResponse) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{35} +} + +func (x *UpdateAdminPlanResponse) GetPlan() *AdminPlan { + if x != nil { + return x.Plan + } + return nil +} + +type DeleteAdminPlanRequest 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 *DeleteAdminPlanRequest) Reset() { + *x = DeleteAdminPlanRequest{} + mi := &file_app_v1_admin_proto_msgTypes[36] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeleteAdminPlanRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteAdminPlanRequest) ProtoMessage() {} + +func (x *DeleteAdminPlanRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[36] + 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 DeleteAdminPlanRequest.ProtoReflect.Descriptor instead. +func (*DeleteAdminPlanRequest) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{36} +} + +func (x *DeleteAdminPlanRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type DeleteAdminPlanResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` + Mode string `protobuf:"bytes,2,opt,name=mode,proto3" json:"mode,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DeleteAdminPlanResponse) Reset() { + *x = DeleteAdminPlanResponse{} + mi := &file_app_v1_admin_proto_msgTypes[37] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeleteAdminPlanResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteAdminPlanResponse) ProtoMessage() {} + +func (x *DeleteAdminPlanResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[37] + 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 DeleteAdminPlanResponse.ProtoReflect.Descriptor instead. +func (*DeleteAdminPlanResponse) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{37} +} + +func (x *DeleteAdminPlanResponse) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +func (x *DeleteAdminPlanResponse) GetMode() string { + if x != nil { + return x.Mode + } + return "" +} + +type ListAdminAdTemplatesRequest 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"` + UserId *string `protobuf:"bytes,3,opt,name=user_id,json=userId,proto3,oneof" json:"user_id,omitempty"` + Search *string `protobuf:"bytes,4,opt,name=search,proto3,oneof" json:"search,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListAdminAdTemplatesRequest) Reset() { + *x = ListAdminAdTemplatesRequest{} + mi := &file_app_v1_admin_proto_msgTypes[38] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListAdminAdTemplatesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListAdminAdTemplatesRequest) ProtoMessage() {} + +func (x *ListAdminAdTemplatesRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[38] + 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 ListAdminAdTemplatesRequest.ProtoReflect.Descriptor instead. +func (*ListAdminAdTemplatesRequest) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{38} +} + +func (x *ListAdminAdTemplatesRequest) GetPage() int32 { + if x != nil { + return x.Page + } + return 0 +} + +func (x *ListAdminAdTemplatesRequest) GetLimit() int32 { + if x != nil { + return x.Limit + } + return 0 +} + +func (x *ListAdminAdTemplatesRequest) GetUserId() string { + if x != nil && x.UserId != nil { + return *x.UserId + } + return "" +} + +func (x *ListAdminAdTemplatesRequest) GetSearch() string { + if x != nil && x.Search != nil { + return *x.Search + } + return "" +} + +type ListAdminAdTemplatesResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Templates []*AdminAdTemplate `protobuf:"bytes,1,rep,name=templates,proto3" json:"templates,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 *ListAdminAdTemplatesResponse) Reset() { + *x = ListAdminAdTemplatesResponse{} + mi := &file_app_v1_admin_proto_msgTypes[39] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListAdminAdTemplatesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListAdminAdTemplatesResponse) ProtoMessage() {} + +func (x *ListAdminAdTemplatesResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[39] + 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 ListAdminAdTemplatesResponse.ProtoReflect.Descriptor instead. +func (*ListAdminAdTemplatesResponse) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{39} +} + +func (x *ListAdminAdTemplatesResponse) GetTemplates() []*AdminAdTemplate { + if x != nil { + return x.Templates + } + return nil +} + +func (x *ListAdminAdTemplatesResponse) GetTotal() int64 { + if x != nil { + return x.Total + } + return 0 +} + +func (x *ListAdminAdTemplatesResponse) GetPage() int32 { + if x != nil { + return x.Page + } + return 0 +} + +func (x *ListAdminAdTemplatesResponse) GetLimit() int32 { + if x != nil { + return x.Limit + } + return 0 +} + +type GetAdminAdTemplateRequest 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 *GetAdminAdTemplateRequest) Reset() { + *x = GetAdminAdTemplateRequest{} + mi := &file_app_v1_admin_proto_msgTypes[40] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetAdminAdTemplateRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetAdminAdTemplateRequest) ProtoMessage() {} + +func (x *GetAdminAdTemplateRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[40] + 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 GetAdminAdTemplateRequest.ProtoReflect.Descriptor instead. +func (*GetAdminAdTemplateRequest) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{40} +} + +func (x *GetAdminAdTemplateRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type GetAdminAdTemplateResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Template *AdminAdTemplate `protobuf:"bytes,1,opt,name=template,proto3" json:"template,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetAdminAdTemplateResponse) Reset() { + *x = GetAdminAdTemplateResponse{} + mi := &file_app_v1_admin_proto_msgTypes[41] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetAdminAdTemplateResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetAdminAdTemplateResponse) ProtoMessage() {} + +func (x *GetAdminAdTemplateResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[41] + 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 GetAdminAdTemplateResponse.ProtoReflect.Descriptor instead. +func (*GetAdminAdTemplateResponse) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{41} +} + +func (x *GetAdminAdTemplateResponse) GetTemplate() *AdminAdTemplate { + if x != nil { + return x.Template + } + return nil +} + +type CreateAdminAdTemplateRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + UserId string `protobuf:"bytes,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Description *string `protobuf:"bytes,3,opt,name=description,proto3,oneof" json:"description,omitempty"` + VastTagUrl string `protobuf:"bytes,4,opt,name=vast_tag_url,json=vastTagUrl,proto3" json:"vast_tag_url,omitempty"` + AdFormat *string `protobuf:"bytes,5,opt,name=ad_format,json=adFormat,proto3,oneof" json:"ad_format,omitempty"` + Duration *int64 `protobuf:"varint,6,opt,name=duration,proto3,oneof" json:"duration,omitempty"` + IsActive *bool `protobuf:"varint,7,opt,name=is_active,json=isActive,proto3,oneof" json:"is_active,omitempty"` + IsDefault *bool `protobuf:"varint,8,opt,name=is_default,json=isDefault,proto3,oneof" json:"is_default,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateAdminAdTemplateRequest) Reset() { + *x = CreateAdminAdTemplateRequest{} + mi := &file_app_v1_admin_proto_msgTypes[42] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateAdminAdTemplateRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateAdminAdTemplateRequest) ProtoMessage() {} + +func (x *CreateAdminAdTemplateRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[42] + 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 CreateAdminAdTemplateRequest.ProtoReflect.Descriptor instead. +func (*CreateAdminAdTemplateRequest) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{42} +} + +func (x *CreateAdminAdTemplateRequest) GetUserId() string { + if x != nil { + return x.UserId + } + return "" +} + +func (x *CreateAdminAdTemplateRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *CreateAdminAdTemplateRequest) GetDescription() string { + if x != nil && x.Description != nil { + return *x.Description + } + return "" +} + +func (x *CreateAdminAdTemplateRequest) GetVastTagUrl() string { + if x != nil { + return x.VastTagUrl + } + return "" +} + +func (x *CreateAdminAdTemplateRequest) GetAdFormat() string { + if x != nil && x.AdFormat != nil { + return *x.AdFormat + } + return "" +} + +func (x *CreateAdminAdTemplateRequest) GetDuration() int64 { + if x != nil && x.Duration != nil { + return *x.Duration + } + return 0 +} + +func (x *CreateAdminAdTemplateRequest) GetIsActive() bool { + if x != nil && x.IsActive != nil { + return *x.IsActive + } + return false +} + +func (x *CreateAdminAdTemplateRequest) GetIsDefault() bool { + if x != nil && x.IsDefault != nil { + return *x.IsDefault + } + return false +} + +type CreateAdminAdTemplateResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Template *AdminAdTemplate `protobuf:"bytes,1,opt,name=template,proto3" json:"template,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateAdminAdTemplateResponse) Reset() { + *x = CreateAdminAdTemplateResponse{} + mi := &file_app_v1_admin_proto_msgTypes[43] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateAdminAdTemplateResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateAdminAdTemplateResponse) ProtoMessage() {} + +func (x *CreateAdminAdTemplateResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[43] + 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 CreateAdminAdTemplateResponse.ProtoReflect.Descriptor instead. +func (*CreateAdminAdTemplateResponse) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{43} +} + +func (x *CreateAdminAdTemplateResponse) GetTemplate() *AdminAdTemplate { + if x != nil { + return x.Template + } + return nil +} + +type UpdateAdminAdTemplateRequest 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"` + Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` + Description *string `protobuf:"bytes,4,opt,name=description,proto3,oneof" json:"description,omitempty"` + VastTagUrl string `protobuf:"bytes,5,opt,name=vast_tag_url,json=vastTagUrl,proto3" json:"vast_tag_url,omitempty"` + AdFormat *string `protobuf:"bytes,6,opt,name=ad_format,json=adFormat,proto3,oneof" json:"ad_format,omitempty"` + Duration *int64 `protobuf:"varint,7,opt,name=duration,proto3,oneof" json:"duration,omitempty"` + IsActive *bool `protobuf:"varint,8,opt,name=is_active,json=isActive,proto3,oneof" json:"is_active,omitempty"` + IsDefault *bool `protobuf:"varint,9,opt,name=is_default,json=isDefault,proto3,oneof" json:"is_default,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UpdateAdminAdTemplateRequest) Reset() { + *x = UpdateAdminAdTemplateRequest{} + mi := &file_app_v1_admin_proto_msgTypes[44] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdateAdminAdTemplateRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateAdminAdTemplateRequest) ProtoMessage() {} + +func (x *UpdateAdminAdTemplateRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[44] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateAdminAdTemplateRequest.ProtoReflect.Descriptor instead. +func (*UpdateAdminAdTemplateRequest) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{44} +} + +func (x *UpdateAdminAdTemplateRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *UpdateAdminAdTemplateRequest) GetUserId() string { + if x != nil { + return x.UserId + } + return "" +} + +func (x *UpdateAdminAdTemplateRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *UpdateAdminAdTemplateRequest) GetDescription() string { + if x != nil && x.Description != nil { + return *x.Description + } + return "" +} + +func (x *UpdateAdminAdTemplateRequest) GetVastTagUrl() string { + if x != nil { + return x.VastTagUrl + } + return "" +} + +func (x *UpdateAdminAdTemplateRequest) GetAdFormat() string { + if x != nil && x.AdFormat != nil { + return *x.AdFormat + } + return "" +} + +func (x *UpdateAdminAdTemplateRequest) GetDuration() int64 { + if x != nil && x.Duration != nil { + return *x.Duration + } + return 0 +} + +func (x *UpdateAdminAdTemplateRequest) GetIsActive() bool { + if x != nil && x.IsActive != nil { + return *x.IsActive + } + return false +} + +func (x *UpdateAdminAdTemplateRequest) GetIsDefault() bool { + if x != nil && x.IsDefault != nil { + return *x.IsDefault + } + return false +} + +type UpdateAdminAdTemplateResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Template *AdminAdTemplate `protobuf:"bytes,1,opt,name=template,proto3" json:"template,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UpdateAdminAdTemplateResponse) Reset() { + *x = UpdateAdminAdTemplateResponse{} + mi := &file_app_v1_admin_proto_msgTypes[45] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdateAdminAdTemplateResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateAdminAdTemplateResponse) ProtoMessage() {} + +func (x *UpdateAdminAdTemplateResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[45] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateAdminAdTemplateResponse.ProtoReflect.Descriptor instead. +func (*UpdateAdminAdTemplateResponse) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{45} +} + +func (x *UpdateAdminAdTemplateResponse) GetTemplate() *AdminAdTemplate { + if x != nil { + return x.Template + } + return nil +} + +type DeleteAdminAdTemplateRequest 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 *DeleteAdminAdTemplateRequest) Reset() { + *x = DeleteAdminAdTemplateRequest{} + mi := &file_app_v1_admin_proto_msgTypes[46] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeleteAdminAdTemplateRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteAdminAdTemplateRequest) ProtoMessage() {} + +func (x *DeleteAdminAdTemplateRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[46] + 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 DeleteAdminAdTemplateRequest.ProtoReflect.Descriptor instead. +func (*DeleteAdminAdTemplateRequest) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{46} +} + +func (x *DeleteAdminAdTemplateRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type ListAdminJobsRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Offset int32 `protobuf:"varint,1,opt,name=offset,proto3" json:"offset,omitempty"` + Limit int32 `protobuf:"varint,2,opt,name=limit,proto3" json:"limit,omitempty"` + AgentId *string `protobuf:"bytes,3,opt,name=agent_id,json=agentId,proto3,oneof" json:"agent_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListAdminJobsRequest) Reset() { + *x = ListAdminJobsRequest{} + mi := &file_app_v1_admin_proto_msgTypes[47] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListAdminJobsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListAdminJobsRequest) ProtoMessage() {} + +func (x *ListAdminJobsRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[47] + 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 ListAdminJobsRequest.ProtoReflect.Descriptor instead. +func (*ListAdminJobsRequest) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{47} +} + +func (x *ListAdminJobsRequest) GetOffset() int32 { + if x != nil { + return x.Offset + } + return 0 +} + +func (x *ListAdminJobsRequest) GetLimit() int32 { + if x != nil { + return x.Limit + } + return 0 +} + +func (x *ListAdminJobsRequest) GetAgentId() string { + if x != nil && x.AgentId != nil { + return *x.AgentId + } + return "" +} + +type ListAdminJobsResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Jobs []*AdminJob `protobuf:"bytes,1,rep,name=jobs,proto3" json:"jobs,omitempty"` + Total int64 `protobuf:"varint,2,opt,name=total,proto3" json:"total,omitempty"` + Offset int32 `protobuf:"varint,3,opt,name=offset,proto3" json:"offset,omitempty"` + Limit int32 `protobuf:"varint,4,opt,name=limit,proto3" json:"limit,omitempty"` + HasMore bool `protobuf:"varint,5,opt,name=has_more,json=hasMore,proto3" json:"has_more,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListAdminJobsResponse) Reset() { + *x = ListAdminJobsResponse{} + mi := &file_app_v1_admin_proto_msgTypes[48] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListAdminJobsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListAdminJobsResponse) ProtoMessage() {} + +func (x *ListAdminJobsResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[48] + 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 ListAdminJobsResponse.ProtoReflect.Descriptor instead. +func (*ListAdminJobsResponse) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{48} +} + +func (x *ListAdminJobsResponse) GetJobs() []*AdminJob { + if x != nil { + return x.Jobs + } + return nil +} + +func (x *ListAdminJobsResponse) GetTotal() int64 { + if x != nil { + return x.Total + } + return 0 +} + +func (x *ListAdminJobsResponse) GetOffset() int32 { + if x != nil { + return x.Offset + } + return 0 +} + +func (x *ListAdminJobsResponse) GetLimit() int32 { + if x != nil { + return x.Limit + } + return 0 +} + +func (x *ListAdminJobsResponse) GetHasMore() bool { + if x != nil { + return x.HasMore + } + return false +} + +type GetAdminJobRequest 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 *GetAdminJobRequest) Reset() { + *x = GetAdminJobRequest{} + mi := &file_app_v1_admin_proto_msgTypes[49] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetAdminJobRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetAdminJobRequest) ProtoMessage() {} + +func (x *GetAdminJobRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[49] + 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 GetAdminJobRequest.ProtoReflect.Descriptor instead. +func (*GetAdminJobRequest) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{49} +} + +func (x *GetAdminJobRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type GetAdminJobResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Job *AdminJob `protobuf:"bytes,1,opt,name=job,proto3" json:"job,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetAdminJobResponse) Reset() { + *x = GetAdminJobResponse{} + mi := &file_app_v1_admin_proto_msgTypes[50] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetAdminJobResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetAdminJobResponse) ProtoMessage() {} + +func (x *GetAdminJobResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[50] + 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 GetAdminJobResponse.ProtoReflect.Descriptor instead. +func (*GetAdminJobResponse) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{50} +} + +func (x *GetAdminJobResponse) GetJob() *AdminJob { + if x != nil { + return x.Job + } + return nil +} + +type GetAdminJobLogsRequest 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 *GetAdminJobLogsRequest) Reset() { + *x = GetAdminJobLogsRequest{} + mi := &file_app_v1_admin_proto_msgTypes[51] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetAdminJobLogsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetAdminJobLogsRequest) ProtoMessage() {} + +func (x *GetAdminJobLogsRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[51] + 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 GetAdminJobLogsRequest.ProtoReflect.Descriptor instead. +func (*GetAdminJobLogsRequest) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{51} +} + +func (x *GetAdminJobLogsRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type GetAdminJobLogsResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Logs string `protobuf:"bytes,1,opt,name=logs,proto3" json:"logs,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetAdminJobLogsResponse) Reset() { + *x = GetAdminJobLogsResponse{} + mi := &file_app_v1_admin_proto_msgTypes[52] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetAdminJobLogsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetAdminJobLogsResponse) ProtoMessage() {} + +func (x *GetAdminJobLogsResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[52] + 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 GetAdminJobLogsResponse.ProtoReflect.Descriptor instead. +func (*GetAdminJobLogsResponse) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{52} +} + +func (x *GetAdminJobLogsResponse) GetLogs() string { + if x != nil { + return x.Logs + } + return "" +} + +type CreateAdminJobRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Command string `protobuf:"bytes,1,opt,name=command,proto3" json:"command,omitempty"` + Image *string `protobuf:"bytes,2,opt,name=image,proto3,oneof" json:"image,omitempty"` + Env map[string]string `protobuf:"bytes,3,rep,name=env,proto3" json:"env,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + Priority int32 `protobuf:"varint,4,opt,name=priority,proto3" json:"priority,omitempty"` + UserId *string `protobuf:"bytes,5,opt,name=user_id,json=userId,proto3,oneof" json:"user_id,omitempty"` + Name *string `protobuf:"bytes,6,opt,name=name,proto3,oneof" json:"name,omitempty"` + TimeLimit int64 `protobuf:"varint,7,opt,name=time_limit,json=timeLimit,proto3" json:"time_limit,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateAdminJobRequest) Reset() { + *x = CreateAdminJobRequest{} + mi := &file_app_v1_admin_proto_msgTypes[53] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateAdminJobRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateAdminJobRequest) ProtoMessage() {} + +func (x *CreateAdminJobRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[53] + 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 CreateAdminJobRequest.ProtoReflect.Descriptor instead. +func (*CreateAdminJobRequest) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{53} +} + +func (x *CreateAdminJobRequest) GetCommand() string { + if x != nil { + return x.Command + } + return "" +} + +func (x *CreateAdminJobRequest) GetImage() string { + if x != nil && x.Image != nil { + return *x.Image + } + return "" +} + +func (x *CreateAdminJobRequest) GetEnv() map[string]string { + if x != nil { + return x.Env + } + return nil +} + +func (x *CreateAdminJobRequest) GetPriority() int32 { + if x != nil { + return x.Priority + } + return 0 +} + +func (x *CreateAdminJobRequest) GetUserId() string { + if x != nil && x.UserId != nil { + return *x.UserId + } + return "" +} + +func (x *CreateAdminJobRequest) GetName() string { + if x != nil && x.Name != nil { + return *x.Name + } + return "" +} + +func (x *CreateAdminJobRequest) GetTimeLimit() int64 { + if x != nil { + return x.TimeLimit + } + return 0 +} + +type CreateAdminJobResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Job *AdminJob `protobuf:"bytes,1,opt,name=job,proto3" json:"job,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateAdminJobResponse) Reset() { + *x = CreateAdminJobResponse{} + mi := &file_app_v1_admin_proto_msgTypes[54] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateAdminJobResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateAdminJobResponse) ProtoMessage() {} + +func (x *CreateAdminJobResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[54] + 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 CreateAdminJobResponse.ProtoReflect.Descriptor instead. +func (*CreateAdminJobResponse) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{54} +} + +func (x *CreateAdminJobResponse) GetJob() *AdminJob { + if x != nil { + return x.Job + } + return nil +} + +type CancelAdminJobRequest 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 *CancelAdminJobRequest) Reset() { + *x = CancelAdminJobRequest{} + mi := &file_app_v1_admin_proto_msgTypes[55] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CancelAdminJobRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CancelAdminJobRequest) ProtoMessage() {} + +func (x *CancelAdminJobRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[55] + 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 CancelAdminJobRequest.ProtoReflect.Descriptor instead. +func (*CancelAdminJobRequest) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{55} +} + +func (x *CancelAdminJobRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type CancelAdminJobResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` + JobId string `protobuf:"bytes,2,opt,name=job_id,json=jobId,proto3" json:"job_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CancelAdminJobResponse) Reset() { + *x = CancelAdminJobResponse{} + mi := &file_app_v1_admin_proto_msgTypes[56] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CancelAdminJobResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CancelAdminJobResponse) ProtoMessage() {} + +func (x *CancelAdminJobResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[56] + 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 CancelAdminJobResponse.ProtoReflect.Descriptor instead. +func (*CancelAdminJobResponse) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{56} +} + +func (x *CancelAdminJobResponse) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *CancelAdminJobResponse) GetJobId() string { + if x != nil { + return x.JobId + } + return "" +} + +type RetryAdminJobRequest 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 *RetryAdminJobRequest) Reset() { + *x = RetryAdminJobRequest{} + mi := &file_app_v1_admin_proto_msgTypes[57] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RetryAdminJobRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RetryAdminJobRequest) ProtoMessage() {} + +func (x *RetryAdminJobRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[57] + 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 RetryAdminJobRequest.ProtoReflect.Descriptor instead. +func (*RetryAdminJobRequest) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{57} +} + +func (x *RetryAdminJobRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type RetryAdminJobResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Job *AdminJob `protobuf:"bytes,1,opt,name=job,proto3" json:"job,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RetryAdminJobResponse) Reset() { + *x = RetryAdminJobResponse{} + mi := &file_app_v1_admin_proto_msgTypes[58] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RetryAdminJobResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RetryAdminJobResponse) ProtoMessage() {} + +func (x *RetryAdminJobResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[58] + 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 RetryAdminJobResponse.ProtoReflect.Descriptor instead. +func (*RetryAdminJobResponse) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{58} +} + +func (x *RetryAdminJobResponse) GetJob() *AdminJob { + if x != nil { + return x.Job + } + return nil +} + +type ListAdminAgentsRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListAdminAgentsRequest) Reset() { + *x = ListAdminAgentsRequest{} + mi := &file_app_v1_admin_proto_msgTypes[59] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListAdminAgentsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListAdminAgentsRequest) ProtoMessage() {} + +func (x *ListAdminAgentsRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[59] + 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 ListAdminAgentsRequest.ProtoReflect.Descriptor instead. +func (*ListAdminAgentsRequest) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{59} +} + +type ListAdminAgentsResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Agents []*AdminAgent `protobuf:"bytes,1,rep,name=agents,proto3" json:"agents,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListAdminAgentsResponse) Reset() { + *x = ListAdminAgentsResponse{} + mi := &file_app_v1_admin_proto_msgTypes[60] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListAdminAgentsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListAdminAgentsResponse) ProtoMessage() {} + +func (x *ListAdminAgentsResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[60] + 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 ListAdminAgentsResponse.ProtoReflect.Descriptor instead. +func (*ListAdminAgentsResponse) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{60} +} + +func (x *ListAdminAgentsResponse) GetAgents() []*AdminAgent { + if x != nil { + return x.Agents + } + return nil +} + +type RestartAdminAgentRequest 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 *RestartAdminAgentRequest) Reset() { + *x = RestartAdminAgentRequest{} + mi := &file_app_v1_admin_proto_msgTypes[61] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RestartAdminAgentRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RestartAdminAgentRequest) ProtoMessage() {} + +func (x *RestartAdminAgentRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[61] + 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 RestartAdminAgentRequest.ProtoReflect.Descriptor instead. +func (*RestartAdminAgentRequest) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{61} +} + +func (x *RestartAdminAgentRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type UpdateAdminAgentRequest 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 *UpdateAdminAgentRequest) Reset() { + *x = UpdateAdminAgentRequest{} + mi := &file_app_v1_admin_proto_msgTypes[62] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdateAdminAgentRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateAdminAgentRequest) ProtoMessage() {} + +func (x *UpdateAdminAgentRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[62] + 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 UpdateAdminAgentRequest.ProtoReflect.Descriptor instead. +func (*UpdateAdminAgentRequest) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{62} +} + +func (x *UpdateAdminAgentRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type AdminAgentCommandResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AdminAgentCommandResponse) Reset() { + *x = AdminAgentCommandResponse{} + mi := &file_app_v1_admin_proto_msgTypes[63] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AdminAgentCommandResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AdminAgentCommandResponse) ProtoMessage() {} + +func (x *AdminAgentCommandResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_admin_proto_msgTypes[63] + 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 AdminAgentCommandResponse.ProtoReflect.Descriptor instead. +func (*AdminAgentCommandResponse) Descriptor() ([]byte, []int) { + return file_app_v1_admin_proto_rawDescGZIP(), []int{63} +} + +func (x *AdminAgentCommandResponse) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +var File_app_v1_admin_proto protoreflect.FileDescriptor + +const file_app_v1_admin_proto_rawDesc = "" + + "\n" + + "\x12app/v1/admin.proto\x12\rstream.app.v1\x1a\x13app/v1/common.proto\"\x1a\n" + + "\x18GetAdminDashboardRequest\"X\n" + + "\x19GetAdminDashboardResponse\x12;\n" + + "\tdashboard\x18\x01 \x01(\v2\x1d.stream.app.v1.AdminDashboardR\tdashboard\"\x8b\x01\n" + + "\x15ListAdminUsersRequest\x12\x12\n" + + "\x04page\x18\x01 \x01(\x05R\x04page\x12\x14\n" + + "\x05limit\x18\x02 \x01(\x05R\x05limit\x12\x1b\n" + + "\x06search\x18\x03 \x01(\tH\x00R\x06search\x88\x01\x01\x12\x17\n" + + "\x04role\x18\x04 \x01(\tH\x01R\x04role\x88\x01\x01B\t\n" + + "\a_searchB\a\n" + + "\x05_role\"\x88\x01\n" + + "\x16ListAdminUsersResponse\x12.\n" + + "\x05users\x18\x01 \x03(\v2\x18.stream.app.v1.AdminUserR\x05users\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\"%\n" + + "\x13GetAdminUserRequest\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\"J\n" + + "\x14GetAdminUserResponse\x122\n" + + "\x04user\x18\x01 \x01(\v2\x1e.stream.app.v1.AdminUserDetailR\x04user\"\xc4\x01\n" + + "\x16CreateAdminUserRequest\x12\x14\n" + + "\x05email\x18\x01 \x01(\tR\x05email\x12\x1f\n" + + "\busername\x18\x02 \x01(\tH\x00R\busername\x88\x01\x01\x12\x1a\n" + + "\bpassword\x18\x03 \x01(\tR\bpassword\x12\x17\n" + + "\x04role\x18\x04 \x01(\tH\x01R\x04role\x88\x01\x01\x12\x1c\n" + + "\aplan_id\x18\x05 \x01(\tH\x02R\x06planId\x88\x01\x01B\v\n" + + "\t_usernameB\a\n" + + "\x05_roleB\n" + + "\n" + + "\b_plan_id\"G\n" + + "\x17CreateAdminUserResponse\x12,\n" + + "\x04user\x18\x01 \x01(\v2\x18.stream.app.v1.AdminUserR\x04user\"\xf5\x01\n" + + "\x16UpdateAdminUserRequest\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\x12\x19\n" + + "\x05email\x18\x02 \x01(\tH\x00R\x05email\x88\x01\x01\x12\x1f\n" + + "\busername\x18\x03 \x01(\tH\x01R\busername\x88\x01\x01\x12\x1f\n" + + "\bpassword\x18\x04 \x01(\tH\x02R\bpassword\x88\x01\x01\x12\x17\n" + + "\x04role\x18\x05 \x01(\tH\x03R\x04role\x88\x01\x01\x12\x1c\n" + + "\aplan_id\x18\x06 \x01(\tH\x04R\x06planId\x88\x01\x01B\b\n" + + "\x06_emailB\v\n" + + "\t_usernameB\v\n" + + "\t_passwordB\a\n" + + "\x05_roleB\n" + + "\n" + + "\b_plan_id\"G\n" + + "\x17UpdateAdminUserResponse\x12,\n" + + "\x04user\x18\x01 \x01(\v2\x18.stream.app.v1.AdminUserR\x04user\"@\n" + + "\x1aUpdateAdminUserRoleRequest\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n" + + "\x04role\x18\x02 \x01(\tR\x04role\"K\n" + + "\x1bUpdateAdminUserRoleResponse\x12\x18\n" + + "\amessage\x18\x01 \x01(\tR\amessage\x12\x12\n" + + "\x04role\x18\x02 \x01(\tR\x04role\"(\n" + + "\x16DeleteAdminUserRequest\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\"\xbc\x01\n" + + "\x16ListAdminVideosRequest\x12\x12\n" + + "\x04page\x18\x01 \x01(\x05R\x04page\x12\x14\n" + + "\x05limit\x18\x02 \x01(\x05R\x05limit\x12\x1b\n" + + "\x06search\x18\x03 \x01(\tH\x00R\x06search\x88\x01\x01\x12\x1c\n" + + "\auser_id\x18\x04 \x01(\tH\x01R\x06userId\x88\x01\x01\x12\x1b\n" + + "\x06status\x18\x05 \x01(\tH\x02R\x06status\x88\x01\x01B\t\n" + + "\a_searchB\n" + + "\n" + + "\b_user_idB\t\n" + + "\a_status\"\x8c\x01\n" + + "\x17ListAdminVideosResponse\x121\n" + + "\x06videos\x18\x01 \x03(\v2\x19.stream.app.v1.AdminVideoR\x06videos\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\"&\n" + + "\x14GetAdminVideoRequest\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\"H\n" + + "\x15GetAdminVideoResponse\x12/\n" + + "\x05video\x18\x01 \x01(\v2\x19.stream.app.v1.AdminVideoR\x05video\"\xcf\x02\n" + + "\x17CreateAdminVideoRequest\x12\x17\n" + + "\auser_id\x18\x01 \x01(\tR\x06userId\x12\x14\n" + + "\x05title\x18\x02 \x01(\tR\x05title\x12%\n" + + "\vdescription\x18\x03 \x01(\tH\x00R\vdescription\x88\x01\x01\x12\x10\n" + + "\x03url\x18\x04 \x01(\tR\x03url\x12\x12\n" + + "\x04size\x18\x05 \x01(\x03R\x04size\x12\x1a\n" + + "\bduration\x18\x06 \x01(\x05R\bduration\x12\x1b\n" + + "\x06format\x18\a \x01(\tH\x01R\x06format\x88\x01\x01\x12\x1b\n" + + "\x06status\x18\b \x01(\tH\x02R\x06status\x88\x01\x01\x12)\n" + + "\x0ead_template_id\x18\t \x01(\tH\x03R\fadTemplateId\x88\x01\x01B\x0e\n" + + "\f_descriptionB\t\n" + + "\a_formatB\t\n" + + "\a_statusB\x11\n" + + "\x0f_ad_template_id\"K\n" + + "\x18CreateAdminVideoResponse\x12/\n" + + "\x05video\x18\x01 \x01(\v2\x19.stream.app.v1.AdminVideoR\x05video\"\xdf\x02\n" + + "\x17UpdateAdminVideoRequest\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\x12\x17\n" + + "\auser_id\x18\x02 \x01(\tR\x06userId\x12\x14\n" + + "\x05title\x18\x03 \x01(\tR\x05title\x12%\n" + + "\vdescription\x18\x04 \x01(\tH\x00R\vdescription\x88\x01\x01\x12\x10\n" + + "\x03url\x18\x05 \x01(\tR\x03url\x12\x12\n" + + "\x04size\x18\x06 \x01(\x03R\x04size\x12\x1a\n" + + "\bduration\x18\a \x01(\x05R\bduration\x12\x1b\n" + + "\x06format\x18\b \x01(\tH\x01R\x06format\x88\x01\x01\x12\x1b\n" + + "\x06status\x18\t \x01(\tH\x02R\x06status\x88\x01\x01\x12)\n" + + "\x0ead_template_id\x18\n" + + " \x01(\tH\x03R\fadTemplateId\x88\x01\x01B\x0e\n" + + "\f_descriptionB\t\n" + + "\a_formatB\t\n" + + "\a_statusB\x11\n" + + "\x0f_ad_template_id\"K\n" + + "\x18UpdateAdminVideoResponse\x12/\n" + + "\x05video\x18\x01 \x01(\v2\x19.stream.app.v1.AdminVideoR\x05video\")\n" + + "\x17DeleteAdminVideoRequest\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\"\x96\x01\n" + + "\x18ListAdminPaymentsRequest\x12\x12\n" + + "\x04page\x18\x01 \x01(\x05R\x04page\x12\x14\n" + + "\x05limit\x18\x02 \x01(\x05R\x05limit\x12\x1c\n" + + "\auser_id\x18\x03 \x01(\tH\x00R\x06userId\x88\x01\x01\x12\x1b\n" + + "\x06status\x18\x04 \x01(\tH\x01R\x06status\x88\x01\x01B\n" + + "\n" + + "\b_user_idB\t\n" + + "\a_status\"\x94\x01\n" + + "\x19ListAdminPaymentsResponse\x127\n" + + "\bpayments\x18\x01 \x03(\v2\x1b.stream.app.v1.AdminPaymentR\bpayments\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\"(\n" + + "\x16GetAdminPaymentRequest\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\"P\n" + + "\x17GetAdminPaymentResponse\x125\n" + + "\apayment\x18\x01 \x01(\v2\x1b.stream.app.v1.AdminPaymentR\apayment\"\xce\x01\n" + + "\x19CreateAdminPaymentRequest\x12\x17\n" + + "\auser_id\x18\x01 \x01(\tR\x06userId\x12\x17\n" + + "\aplan_id\x18\x02 \x01(\tR\x06planId\x12\x1f\n" + + "\vterm_months\x18\x03 \x01(\x05R\n" + + "termMonths\x12%\n" + + "\x0epayment_method\x18\x04 \x01(\tR\rpaymentMethod\x12&\n" + + "\ftopup_amount\x18\x05 \x01(\x01H\x00R\vtopupAmount\x88\x01\x01B\x0f\n" + + "\r_topup_amount\"\xde\x01\n" + + "\x1aCreateAdminPaymentResponse\x125\n" + + "\apayment\x18\x01 \x01(\v2\x1b.stream.app.v1.AdminPaymentR\apayment\x12C\n" + + "\fsubscription\x18\x02 \x01(\v2\x1f.stream.app.v1.PlanSubscriptionR\fsubscription\x12%\n" + + "\x0ewallet_balance\x18\x03 \x01(\x01R\rwalletBalance\x12\x1d\n" + + "\n" + + "invoice_id\x18\x04 \x01(\tR\tinvoiceId\"C\n" + + "\x19UpdateAdminPaymentRequest\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\x12\x16\n" + + "\x06status\x18\x02 \x01(\tR\x06status\"S\n" + + "\x1aUpdateAdminPaymentResponse\x125\n" + + "\apayment\x18\x01 \x01(\v2\x1b.stream.app.v1.AdminPaymentR\apayment\"\x17\n" + + "\x15ListAdminPlansRequest\"H\n" + + "\x16ListAdminPlansResponse\x12.\n" + + "\x05plans\x18\x01 \x03(\v2\x18.stream.app.v1.AdminPlanR\x05plans\"\xa3\x02\n" + + "\x16CreateAdminPlanRequest\x12\x12\n" + + "\x04name\x18\x01 \x01(\tR\x04name\x12%\n" + + "\vdescription\x18\x02 \x01(\tH\x00R\vdescription\x88\x01\x01\x12\x1a\n" + + "\bfeatures\x18\x03 \x03(\tR\bfeatures\x12\x14\n" + + "\x05price\x18\x04 \x01(\x01R\x05price\x12\x14\n" + + "\x05cycle\x18\x05 \x01(\tR\x05cycle\x12#\n" + + "\rstorage_limit\x18\x06 \x01(\x03R\fstorageLimit\x12!\n" + + "\fupload_limit\x18\a \x01(\x05R\vuploadLimit\x12 \n" + + "\tis_active\x18\b \x01(\bH\x01R\bisActive\x88\x01\x01B\x0e\n" + + "\f_descriptionB\f\n" + + "\n" + + "_is_active\"G\n" + + "\x17CreateAdminPlanResponse\x12,\n" + + "\x04plan\x18\x01 \x01(\v2\x18.stream.app.v1.AdminPlanR\x04plan\"\xb3\x02\n" + + "\x16UpdateAdminPlanRequest\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n" + + "\x04name\x18\x02 \x01(\tR\x04name\x12%\n" + + "\vdescription\x18\x03 \x01(\tH\x00R\vdescription\x88\x01\x01\x12\x1a\n" + + "\bfeatures\x18\x04 \x03(\tR\bfeatures\x12\x14\n" + + "\x05price\x18\x05 \x01(\x01R\x05price\x12\x14\n" + + "\x05cycle\x18\x06 \x01(\tR\x05cycle\x12#\n" + + "\rstorage_limit\x18\a \x01(\x03R\fstorageLimit\x12!\n" + + "\fupload_limit\x18\b \x01(\x05R\vuploadLimit\x12 \n" + + "\tis_active\x18\t \x01(\bH\x01R\bisActive\x88\x01\x01B\x0e\n" + + "\f_descriptionB\f\n" + + "\n" + + "_is_active\"G\n" + + "\x17UpdateAdminPlanResponse\x12,\n" + + "\x04plan\x18\x01 \x01(\v2\x18.stream.app.v1.AdminPlanR\x04plan\"(\n" + + "\x16DeleteAdminPlanRequest\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\"G\n" + + "\x17DeleteAdminPlanResponse\x12\x18\n" + + "\amessage\x18\x01 \x01(\tR\amessage\x12\x12\n" + + "\x04mode\x18\x02 \x01(\tR\x04mode\"\x99\x01\n" + + "\x1bListAdminAdTemplatesRequest\x12\x12\n" + + "\x04page\x18\x01 \x01(\x05R\x04page\x12\x14\n" + + "\x05limit\x18\x02 \x01(\x05R\x05limit\x12\x1c\n" + + "\auser_id\x18\x03 \x01(\tH\x00R\x06userId\x88\x01\x01\x12\x1b\n" + + "\x06search\x18\x04 \x01(\tH\x01R\x06search\x88\x01\x01B\n" + + "\n" + + "\b_user_idB\t\n" + + "\a_search\"\x9c\x01\n" + + "\x1cListAdminAdTemplatesResponse\x12<\n" + + "\ttemplates\x18\x01 \x03(\v2\x1e.stream.app.v1.AdminAdTemplateR\ttemplates\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\"+\n" + + "\x19GetAdminAdTemplateRequest\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\"X\n" + + "\x1aGetAdminAdTemplateResponse\x12:\n" + + "\btemplate\x18\x01 \x01(\v2\x1e.stream.app.v1.AdminAdTemplateR\btemplate\"\xe5\x02\n" + + "\x1cCreateAdminAdTemplateRequest\x12\x17\n" + + "\auser_id\x18\x01 \x01(\tR\x06userId\x12\x12\n" + + "\x04name\x18\x02 \x01(\tR\x04name\x12%\n" + + "\vdescription\x18\x03 \x01(\tH\x00R\vdescription\x88\x01\x01\x12 \n" + + "\fvast_tag_url\x18\x04 \x01(\tR\n" + + "vastTagUrl\x12 \n" + + "\tad_format\x18\x05 \x01(\tH\x01R\badFormat\x88\x01\x01\x12\x1f\n" + + "\bduration\x18\x06 \x01(\x03H\x02R\bduration\x88\x01\x01\x12 \n" + + "\tis_active\x18\a \x01(\bH\x03R\bisActive\x88\x01\x01\x12\"\n" + + "\n" + + "is_default\x18\b \x01(\bH\x04R\tisDefault\x88\x01\x01B\x0e\n" + + "\f_descriptionB\f\n" + + "\n" + + "_ad_formatB\v\n" + + "\t_durationB\f\n" + + "\n" + + "_is_activeB\r\n" + + "\v_is_default\"[\n" + + "\x1dCreateAdminAdTemplateResponse\x12:\n" + + "\btemplate\x18\x01 \x01(\v2\x1e.stream.app.v1.AdminAdTemplateR\btemplate\"\xf5\x02\n" + + "\x1cUpdateAdminAdTemplateRequest\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\x12\x17\n" + + "\auser_id\x18\x02 \x01(\tR\x06userId\x12\x12\n" + + "\x04name\x18\x03 \x01(\tR\x04name\x12%\n" + + "\vdescription\x18\x04 \x01(\tH\x00R\vdescription\x88\x01\x01\x12 \n" + + "\fvast_tag_url\x18\x05 \x01(\tR\n" + + "vastTagUrl\x12 \n" + + "\tad_format\x18\x06 \x01(\tH\x01R\badFormat\x88\x01\x01\x12\x1f\n" + + "\bduration\x18\a \x01(\x03H\x02R\bduration\x88\x01\x01\x12 \n" + + "\tis_active\x18\b \x01(\bH\x03R\bisActive\x88\x01\x01\x12\"\n" + + "\n" + + "is_default\x18\t \x01(\bH\x04R\tisDefault\x88\x01\x01B\x0e\n" + + "\f_descriptionB\f\n" + + "\n" + + "_ad_formatB\v\n" + + "\t_durationB\f\n" + + "\n" + + "_is_activeB\r\n" + + "\v_is_default\"[\n" + + "\x1dUpdateAdminAdTemplateResponse\x12:\n" + + "\btemplate\x18\x01 \x01(\v2\x1e.stream.app.v1.AdminAdTemplateR\btemplate\".\n" + + "\x1cDeleteAdminAdTemplateRequest\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\"q\n" + + "\x14ListAdminJobsRequest\x12\x16\n" + + "\x06offset\x18\x01 \x01(\x05R\x06offset\x12\x14\n" + + "\x05limit\x18\x02 \x01(\x05R\x05limit\x12\x1e\n" + + "\bagent_id\x18\x03 \x01(\tH\x00R\aagentId\x88\x01\x01B\v\n" + + "\t_agent_id\"\xa3\x01\n" + + "\x15ListAdminJobsResponse\x12+\n" + + "\x04jobs\x18\x01 \x03(\v2\x17.stream.app.v1.AdminJobR\x04jobs\x12\x14\n" + + "\x05total\x18\x02 \x01(\x03R\x05total\x12\x16\n" + + "\x06offset\x18\x03 \x01(\x05R\x06offset\x12\x14\n" + + "\x05limit\x18\x04 \x01(\x05R\x05limit\x12\x19\n" + + "\bhas_more\x18\x05 \x01(\bR\ahasMore\"$\n" + + "\x12GetAdminJobRequest\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\"@\n" + + "\x13GetAdminJobResponse\x12)\n" + + "\x03job\x18\x01 \x01(\v2\x17.stream.app.v1.AdminJobR\x03job\"(\n" + + "\x16GetAdminJobLogsRequest\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\"-\n" + + "\x17GetAdminJobLogsResponse\x12\x12\n" + + "\x04logs\x18\x01 \x01(\tR\x04logs\"\xd6\x02\n" + + "\x15CreateAdminJobRequest\x12\x18\n" + + "\acommand\x18\x01 \x01(\tR\acommand\x12\x19\n" + + "\x05image\x18\x02 \x01(\tH\x00R\x05image\x88\x01\x01\x12?\n" + + "\x03env\x18\x03 \x03(\v2-.stream.app.v1.CreateAdminJobRequest.EnvEntryR\x03env\x12\x1a\n" + + "\bpriority\x18\x04 \x01(\x05R\bpriority\x12\x1c\n" + + "\auser_id\x18\x05 \x01(\tH\x01R\x06userId\x88\x01\x01\x12\x17\n" + + "\x04name\x18\x06 \x01(\tH\x02R\x04name\x88\x01\x01\x12\x1d\n" + + "\n" + + "time_limit\x18\a \x01(\x03R\ttimeLimit\x1a6\n" + + "\bEnvEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01B\b\n" + + "\x06_imageB\n" + + "\n" + + "\b_user_idB\a\n" + + "\x05_name\"C\n" + + "\x16CreateAdminJobResponse\x12)\n" + + "\x03job\x18\x01 \x01(\v2\x17.stream.app.v1.AdminJobR\x03job\"'\n" + + "\x15CancelAdminJobRequest\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\"G\n" + + "\x16CancelAdminJobResponse\x12\x16\n" + + "\x06status\x18\x01 \x01(\tR\x06status\x12\x15\n" + + "\x06job_id\x18\x02 \x01(\tR\x05jobId\"&\n" + + "\x14RetryAdminJobRequest\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\"B\n" + + "\x15RetryAdminJobResponse\x12)\n" + + "\x03job\x18\x01 \x01(\v2\x17.stream.app.v1.AdminJobR\x03job\"\x18\n" + + "\x16ListAdminAgentsRequest\"L\n" + + "\x17ListAdminAgentsResponse\x121\n" + + "\x06agents\x18\x01 \x03(\v2\x19.stream.app.v1.AdminAgentR\x06agents\"*\n" + + "\x18RestartAdminAgentRequest\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\")\n" + + "\x17UpdateAdminAgentRequest\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\"3\n" + + "\x19AdminAgentCommandResponse\x12\x16\n" + + "\x06status\x18\x01 \x01(\tR\x06status2\xcb\x1a\n" + + "\fAdminService\x12f\n" + + "\x11GetAdminDashboard\x12'.stream.app.v1.GetAdminDashboardRequest\x1a(.stream.app.v1.GetAdminDashboardResponse\x12]\n" + + "\x0eListAdminUsers\x12$.stream.app.v1.ListAdminUsersRequest\x1a%.stream.app.v1.ListAdminUsersResponse\x12W\n" + + "\fGetAdminUser\x12\".stream.app.v1.GetAdminUserRequest\x1a#.stream.app.v1.GetAdminUserResponse\x12`\n" + + "\x0fCreateAdminUser\x12%.stream.app.v1.CreateAdminUserRequest\x1a&.stream.app.v1.CreateAdminUserResponse\x12`\n" + + "\x0fUpdateAdminUser\x12%.stream.app.v1.UpdateAdminUserRequest\x1a&.stream.app.v1.UpdateAdminUserResponse\x12l\n" + + "\x13UpdateAdminUserRole\x12).stream.app.v1.UpdateAdminUserRoleRequest\x1a*.stream.app.v1.UpdateAdminUserRoleResponse\x12X\n" + + "\x0fDeleteAdminUser\x12%.stream.app.v1.DeleteAdminUserRequest\x1a\x1e.stream.app.v1.MessageResponse\x12`\n" + + "\x0fListAdminVideos\x12%.stream.app.v1.ListAdminVideosRequest\x1a&.stream.app.v1.ListAdminVideosResponse\x12Z\n" + + "\rGetAdminVideo\x12#.stream.app.v1.GetAdminVideoRequest\x1a$.stream.app.v1.GetAdminVideoResponse\x12c\n" + + "\x10CreateAdminVideo\x12&.stream.app.v1.CreateAdminVideoRequest\x1a'.stream.app.v1.CreateAdminVideoResponse\x12c\n" + + "\x10UpdateAdminVideo\x12&.stream.app.v1.UpdateAdminVideoRequest\x1a'.stream.app.v1.UpdateAdminVideoResponse\x12Z\n" + + "\x10DeleteAdminVideo\x12&.stream.app.v1.DeleteAdminVideoRequest\x1a\x1e.stream.app.v1.MessageResponse\x12f\n" + + "\x11ListAdminPayments\x12'.stream.app.v1.ListAdminPaymentsRequest\x1a(.stream.app.v1.ListAdminPaymentsResponse\x12`\n" + + "\x0fGetAdminPayment\x12%.stream.app.v1.GetAdminPaymentRequest\x1a&.stream.app.v1.GetAdminPaymentResponse\x12i\n" + + "\x12CreateAdminPayment\x12(.stream.app.v1.CreateAdminPaymentRequest\x1a).stream.app.v1.CreateAdminPaymentResponse\x12i\n" + + "\x12UpdateAdminPayment\x12(.stream.app.v1.UpdateAdminPaymentRequest\x1a).stream.app.v1.UpdateAdminPaymentResponse\x12]\n" + + "\x0eListAdminPlans\x12$.stream.app.v1.ListAdminPlansRequest\x1a%.stream.app.v1.ListAdminPlansResponse\x12`\n" + + "\x0fCreateAdminPlan\x12%.stream.app.v1.CreateAdminPlanRequest\x1a&.stream.app.v1.CreateAdminPlanResponse\x12`\n" + + "\x0fUpdateAdminPlan\x12%.stream.app.v1.UpdateAdminPlanRequest\x1a&.stream.app.v1.UpdateAdminPlanResponse\x12`\n" + + "\x0fDeleteAdminPlan\x12%.stream.app.v1.DeleteAdminPlanRequest\x1a&.stream.app.v1.DeleteAdminPlanResponse\x12o\n" + + "\x14ListAdminAdTemplates\x12*.stream.app.v1.ListAdminAdTemplatesRequest\x1a+.stream.app.v1.ListAdminAdTemplatesResponse\x12i\n" + + "\x12GetAdminAdTemplate\x12(.stream.app.v1.GetAdminAdTemplateRequest\x1a).stream.app.v1.GetAdminAdTemplateResponse\x12r\n" + + "\x15CreateAdminAdTemplate\x12+.stream.app.v1.CreateAdminAdTemplateRequest\x1a,.stream.app.v1.CreateAdminAdTemplateResponse\x12r\n" + + "\x15UpdateAdminAdTemplate\x12+.stream.app.v1.UpdateAdminAdTemplateRequest\x1a,.stream.app.v1.UpdateAdminAdTemplateResponse\x12d\n" + + "\x15DeleteAdminAdTemplate\x12+.stream.app.v1.DeleteAdminAdTemplateRequest\x1a\x1e.stream.app.v1.MessageResponse\x12Z\n" + + "\rListAdminJobs\x12#.stream.app.v1.ListAdminJobsRequest\x1a$.stream.app.v1.ListAdminJobsResponse\x12T\n" + + "\vGetAdminJob\x12!.stream.app.v1.GetAdminJobRequest\x1a\".stream.app.v1.GetAdminJobResponse\x12`\n" + + "\x0fGetAdminJobLogs\x12%.stream.app.v1.GetAdminJobLogsRequest\x1a&.stream.app.v1.GetAdminJobLogsResponse\x12]\n" + + "\x0eCreateAdminJob\x12$.stream.app.v1.CreateAdminJobRequest\x1a%.stream.app.v1.CreateAdminJobResponse\x12]\n" + + "\x0eCancelAdminJob\x12$.stream.app.v1.CancelAdminJobRequest\x1a%.stream.app.v1.CancelAdminJobResponse\x12Z\n" + + "\rRetryAdminJob\x12#.stream.app.v1.RetryAdminJobRequest\x1a$.stream.app.v1.RetryAdminJobResponse\x12`\n" + + "\x0fListAdminAgents\x12%.stream.app.v1.ListAdminAgentsRequest\x1a&.stream.app.v1.ListAdminAgentsResponse\x12f\n" + + "\x11RestartAdminAgent\x12'.stream.app.v1.RestartAdminAgentRequest\x1a(.stream.app.v1.AdminAgentCommandResponse\x12d\n" + + "\x10UpdateAdminAgent\x12&.stream.app.v1.UpdateAdminAgentRequest\x1a(.stream.app.v1.AdminAgentCommandResponseB,Z*stream.api/internal/gen/proto/app/v1;appv1b\x06proto3" + +var ( + file_app_v1_admin_proto_rawDescOnce sync.Once + file_app_v1_admin_proto_rawDescData []byte +) + +func file_app_v1_admin_proto_rawDescGZIP() []byte { + file_app_v1_admin_proto_rawDescOnce.Do(func() { + file_app_v1_admin_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_v1_admin_proto_rawDesc), len(file_app_v1_admin_proto_rawDesc))) + }) + return file_app_v1_admin_proto_rawDescData +} + +var file_app_v1_admin_proto_msgTypes = make([]protoimpl.MessageInfo, 65) +var file_app_v1_admin_proto_goTypes = []any{ + (*GetAdminDashboardRequest)(nil), // 0: stream.app.v1.GetAdminDashboardRequest + (*GetAdminDashboardResponse)(nil), // 1: stream.app.v1.GetAdminDashboardResponse + (*ListAdminUsersRequest)(nil), // 2: stream.app.v1.ListAdminUsersRequest + (*ListAdminUsersResponse)(nil), // 3: stream.app.v1.ListAdminUsersResponse + (*GetAdminUserRequest)(nil), // 4: stream.app.v1.GetAdminUserRequest + (*GetAdminUserResponse)(nil), // 5: stream.app.v1.GetAdminUserResponse + (*CreateAdminUserRequest)(nil), // 6: stream.app.v1.CreateAdminUserRequest + (*CreateAdminUserResponse)(nil), // 7: stream.app.v1.CreateAdminUserResponse + (*UpdateAdminUserRequest)(nil), // 8: stream.app.v1.UpdateAdminUserRequest + (*UpdateAdminUserResponse)(nil), // 9: stream.app.v1.UpdateAdminUserResponse + (*UpdateAdminUserRoleRequest)(nil), // 10: stream.app.v1.UpdateAdminUserRoleRequest + (*UpdateAdminUserRoleResponse)(nil), // 11: stream.app.v1.UpdateAdminUserRoleResponse + (*DeleteAdminUserRequest)(nil), // 12: stream.app.v1.DeleteAdminUserRequest + (*ListAdminVideosRequest)(nil), // 13: stream.app.v1.ListAdminVideosRequest + (*ListAdminVideosResponse)(nil), // 14: stream.app.v1.ListAdminVideosResponse + (*GetAdminVideoRequest)(nil), // 15: stream.app.v1.GetAdminVideoRequest + (*GetAdminVideoResponse)(nil), // 16: stream.app.v1.GetAdminVideoResponse + (*CreateAdminVideoRequest)(nil), // 17: stream.app.v1.CreateAdminVideoRequest + (*CreateAdminVideoResponse)(nil), // 18: stream.app.v1.CreateAdminVideoResponse + (*UpdateAdminVideoRequest)(nil), // 19: stream.app.v1.UpdateAdminVideoRequest + (*UpdateAdminVideoResponse)(nil), // 20: stream.app.v1.UpdateAdminVideoResponse + (*DeleteAdminVideoRequest)(nil), // 21: stream.app.v1.DeleteAdminVideoRequest + (*ListAdminPaymentsRequest)(nil), // 22: stream.app.v1.ListAdminPaymentsRequest + (*ListAdminPaymentsResponse)(nil), // 23: stream.app.v1.ListAdminPaymentsResponse + (*GetAdminPaymentRequest)(nil), // 24: stream.app.v1.GetAdminPaymentRequest + (*GetAdminPaymentResponse)(nil), // 25: stream.app.v1.GetAdminPaymentResponse + (*CreateAdminPaymentRequest)(nil), // 26: stream.app.v1.CreateAdminPaymentRequest + (*CreateAdminPaymentResponse)(nil), // 27: stream.app.v1.CreateAdminPaymentResponse + (*UpdateAdminPaymentRequest)(nil), // 28: stream.app.v1.UpdateAdminPaymentRequest + (*UpdateAdminPaymentResponse)(nil), // 29: stream.app.v1.UpdateAdminPaymentResponse + (*ListAdminPlansRequest)(nil), // 30: stream.app.v1.ListAdminPlansRequest + (*ListAdminPlansResponse)(nil), // 31: stream.app.v1.ListAdminPlansResponse + (*CreateAdminPlanRequest)(nil), // 32: stream.app.v1.CreateAdminPlanRequest + (*CreateAdminPlanResponse)(nil), // 33: stream.app.v1.CreateAdminPlanResponse + (*UpdateAdminPlanRequest)(nil), // 34: stream.app.v1.UpdateAdminPlanRequest + (*UpdateAdminPlanResponse)(nil), // 35: stream.app.v1.UpdateAdminPlanResponse + (*DeleteAdminPlanRequest)(nil), // 36: stream.app.v1.DeleteAdminPlanRequest + (*DeleteAdminPlanResponse)(nil), // 37: stream.app.v1.DeleteAdminPlanResponse + (*ListAdminAdTemplatesRequest)(nil), // 38: stream.app.v1.ListAdminAdTemplatesRequest + (*ListAdminAdTemplatesResponse)(nil), // 39: stream.app.v1.ListAdminAdTemplatesResponse + (*GetAdminAdTemplateRequest)(nil), // 40: stream.app.v1.GetAdminAdTemplateRequest + (*GetAdminAdTemplateResponse)(nil), // 41: stream.app.v1.GetAdminAdTemplateResponse + (*CreateAdminAdTemplateRequest)(nil), // 42: stream.app.v1.CreateAdminAdTemplateRequest + (*CreateAdminAdTemplateResponse)(nil), // 43: stream.app.v1.CreateAdminAdTemplateResponse + (*UpdateAdminAdTemplateRequest)(nil), // 44: stream.app.v1.UpdateAdminAdTemplateRequest + (*UpdateAdminAdTemplateResponse)(nil), // 45: stream.app.v1.UpdateAdminAdTemplateResponse + (*DeleteAdminAdTemplateRequest)(nil), // 46: stream.app.v1.DeleteAdminAdTemplateRequest + (*ListAdminJobsRequest)(nil), // 47: stream.app.v1.ListAdminJobsRequest + (*ListAdminJobsResponse)(nil), // 48: stream.app.v1.ListAdminJobsResponse + (*GetAdminJobRequest)(nil), // 49: stream.app.v1.GetAdminJobRequest + (*GetAdminJobResponse)(nil), // 50: stream.app.v1.GetAdminJobResponse + (*GetAdminJobLogsRequest)(nil), // 51: stream.app.v1.GetAdminJobLogsRequest + (*GetAdminJobLogsResponse)(nil), // 52: stream.app.v1.GetAdminJobLogsResponse + (*CreateAdminJobRequest)(nil), // 53: stream.app.v1.CreateAdminJobRequest + (*CreateAdminJobResponse)(nil), // 54: stream.app.v1.CreateAdminJobResponse + (*CancelAdminJobRequest)(nil), // 55: stream.app.v1.CancelAdminJobRequest + (*CancelAdminJobResponse)(nil), // 56: stream.app.v1.CancelAdminJobResponse + (*RetryAdminJobRequest)(nil), // 57: stream.app.v1.RetryAdminJobRequest + (*RetryAdminJobResponse)(nil), // 58: stream.app.v1.RetryAdminJobResponse + (*ListAdminAgentsRequest)(nil), // 59: stream.app.v1.ListAdminAgentsRequest + (*ListAdminAgentsResponse)(nil), // 60: stream.app.v1.ListAdminAgentsResponse + (*RestartAdminAgentRequest)(nil), // 61: stream.app.v1.RestartAdminAgentRequest + (*UpdateAdminAgentRequest)(nil), // 62: stream.app.v1.UpdateAdminAgentRequest + (*AdminAgentCommandResponse)(nil), // 63: stream.app.v1.AdminAgentCommandResponse + nil, // 64: stream.app.v1.CreateAdminJobRequest.EnvEntry + (*AdminDashboard)(nil), // 65: stream.app.v1.AdminDashboard + (*AdminUser)(nil), // 66: stream.app.v1.AdminUser + (*AdminUserDetail)(nil), // 67: stream.app.v1.AdminUserDetail + (*AdminVideo)(nil), // 68: stream.app.v1.AdminVideo + (*AdminPayment)(nil), // 69: stream.app.v1.AdminPayment + (*PlanSubscription)(nil), // 70: stream.app.v1.PlanSubscription + (*AdminPlan)(nil), // 71: stream.app.v1.AdminPlan + (*AdminAdTemplate)(nil), // 72: stream.app.v1.AdminAdTemplate + (*AdminJob)(nil), // 73: stream.app.v1.AdminJob + (*AdminAgent)(nil), // 74: stream.app.v1.AdminAgent + (*MessageResponse)(nil), // 75: stream.app.v1.MessageResponse +} +var file_app_v1_admin_proto_depIdxs = []int32{ + 65, // 0: stream.app.v1.GetAdminDashboardResponse.dashboard:type_name -> stream.app.v1.AdminDashboard + 66, // 1: stream.app.v1.ListAdminUsersResponse.users:type_name -> stream.app.v1.AdminUser + 67, // 2: stream.app.v1.GetAdminUserResponse.user:type_name -> stream.app.v1.AdminUserDetail + 66, // 3: stream.app.v1.CreateAdminUserResponse.user:type_name -> stream.app.v1.AdminUser + 66, // 4: stream.app.v1.UpdateAdminUserResponse.user:type_name -> stream.app.v1.AdminUser + 68, // 5: stream.app.v1.ListAdminVideosResponse.videos:type_name -> stream.app.v1.AdminVideo + 68, // 6: stream.app.v1.GetAdminVideoResponse.video:type_name -> stream.app.v1.AdminVideo + 68, // 7: stream.app.v1.CreateAdminVideoResponse.video:type_name -> stream.app.v1.AdminVideo + 68, // 8: stream.app.v1.UpdateAdminVideoResponse.video:type_name -> stream.app.v1.AdminVideo + 69, // 9: stream.app.v1.ListAdminPaymentsResponse.payments:type_name -> stream.app.v1.AdminPayment + 69, // 10: stream.app.v1.GetAdminPaymentResponse.payment:type_name -> stream.app.v1.AdminPayment + 69, // 11: stream.app.v1.CreateAdminPaymentResponse.payment:type_name -> stream.app.v1.AdminPayment + 70, // 12: stream.app.v1.CreateAdminPaymentResponse.subscription:type_name -> stream.app.v1.PlanSubscription + 69, // 13: stream.app.v1.UpdateAdminPaymentResponse.payment:type_name -> stream.app.v1.AdminPayment + 71, // 14: stream.app.v1.ListAdminPlansResponse.plans:type_name -> stream.app.v1.AdminPlan + 71, // 15: stream.app.v1.CreateAdminPlanResponse.plan:type_name -> stream.app.v1.AdminPlan + 71, // 16: stream.app.v1.UpdateAdminPlanResponse.plan:type_name -> stream.app.v1.AdminPlan + 72, // 17: stream.app.v1.ListAdminAdTemplatesResponse.templates:type_name -> stream.app.v1.AdminAdTemplate + 72, // 18: stream.app.v1.GetAdminAdTemplateResponse.template:type_name -> stream.app.v1.AdminAdTemplate + 72, // 19: stream.app.v1.CreateAdminAdTemplateResponse.template:type_name -> stream.app.v1.AdminAdTemplate + 72, // 20: stream.app.v1.UpdateAdminAdTemplateResponse.template:type_name -> stream.app.v1.AdminAdTemplate + 73, // 21: stream.app.v1.ListAdminJobsResponse.jobs:type_name -> stream.app.v1.AdminJob + 73, // 22: stream.app.v1.GetAdminJobResponse.job:type_name -> stream.app.v1.AdminJob + 64, // 23: stream.app.v1.CreateAdminJobRequest.env:type_name -> stream.app.v1.CreateAdminJobRequest.EnvEntry + 73, // 24: stream.app.v1.CreateAdminJobResponse.job:type_name -> stream.app.v1.AdminJob + 73, // 25: stream.app.v1.RetryAdminJobResponse.job:type_name -> stream.app.v1.AdminJob + 74, // 26: stream.app.v1.ListAdminAgentsResponse.agents:type_name -> stream.app.v1.AdminAgent + 0, // 27: stream.app.v1.AdminService.GetAdminDashboard:input_type -> stream.app.v1.GetAdminDashboardRequest + 2, // 28: stream.app.v1.AdminService.ListAdminUsers:input_type -> stream.app.v1.ListAdminUsersRequest + 4, // 29: stream.app.v1.AdminService.GetAdminUser:input_type -> stream.app.v1.GetAdminUserRequest + 6, // 30: stream.app.v1.AdminService.CreateAdminUser:input_type -> stream.app.v1.CreateAdminUserRequest + 8, // 31: stream.app.v1.AdminService.UpdateAdminUser:input_type -> stream.app.v1.UpdateAdminUserRequest + 10, // 32: stream.app.v1.AdminService.UpdateAdminUserRole:input_type -> stream.app.v1.UpdateAdminUserRoleRequest + 12, // 33: stream.app.v1.AdminService.DeleteAdminUser:input_type -> stream.app.v1.DeleteAdminUserRequest + 13, // 34: stream.app.v1.AdminService.ListAdminVideos:input_type -> stream.app.v1.ListAdminVideosRequest + 15, // 35: stream.app.v1.AdminService.GetAdminVideo:input_type -> stream.app.v1.GetAdminVideoRequest + 17, // 36: stream.app.v1.AdminService.CreateAdminVideo:input_type -> stream.app.v1.CreateAdminVideoRequest + 19, // 37: stream.app.v1.AdminService.UpdateAdminVideo:input_type -> stream.app.v1.UpdateAdminVideoRequest + 21, // 38: stream.app.v1.AdminService.DeleteAdminVideo:input_type -> stream.app.v1.DeleteAdminVideoRequest + 22, // 39: stream.app.v1.AdminService.ListAdminPayments:input_type -> stream.app.v1.ListAdminPaymentsRequest + 24, // 40: stream.app.v1.AdminService.GetAdminPayment:input_type -> stream.app.v1.GetAdminPaymentRequest + 26, // 41: stream.app.v1.AdminService.CreateAdminPayment:input_type -> stream.app.v1.CreateAdminPaymentRequest + 28, // 42: stream.app.v1.AdminService.UpdateAdminPayment:input_type -> stream.app.v1.UpdateAdminPaymentRequest + 30, // 43: stream.app.v1.AdminService.ListAdminPlans:input_type -> stream.app.v1.ListAdminPlansRequest + 32, // 44: stream.app.v1.AdminService.CreateAdminPlan:input_type -> stream.app.v1.CreateAdminPlanRequest + 34, // 45: stream.app.v1.AdminService.UpdateAdminPlan:input_type -> stream.app.v1.UpdateAdminPlanRequest + 36, // 46: stream.app.v1.AdminService.DeleteAdminPlan:input_type -> stream.app.v1.DeleteAdminPlanRequest + 38, // 47: stream.app.v1.AdminService.ListAdminAdTemplates:input_type -> stream.app.v1.ListAdminAdTemplatesRequest + 40, // 48: stream.app.v1.AdminService.GetAdminAdTemplate:input_type -> stream.app.v1.GetAdminAdTemplateRequest + 42, // 49: stream.app.v1.AdminService.CreateAdminAdTemplate:input_type -> stream.app.v1.CreateAdminAdTemplateRequest + 44, // 50: stream.app.v1.AdminService.UpdateAdminAdTemplate:input_type -> stream.app.v1.UpdateAdminAdTemplateRequest + 46, // 51: stream.app.v1.AdminService.DeleteAdminAdTemplate:input_type -> stream.app.v1.DeleteAdminAdTemplateRequest + 47, // 52: stream.app.v1.AdminService.ListAdminJobs:input_type -> stream.app.v1.ListAdminJobsRequest + 49, // 53: stream.app.v1.AdminService.GetAdminJob:input_type -> stream.app.v1.GetAdminJobRequest + 51, // 54: stream.app.v1.AdminService.GetAdminJobLogs:input_type -> stream.app.v1.GetAdminJobLogsRequest + 53, // 55: stream.app.v1.AdminService.CreateAdminJob:input_type -> stream.app.v1.CreateAdminJobRequest + 55, // 56: stream.app.v1.AdminService.CancelAdminJob:input_type -> stream.app.v1.CancelAdminJobRequest + 57, // 57: stream.app.v1.AdminService.RetryAdminJob:input_type -> stream.app.v1.RetryAdminJobRequest + 59, // 58: stream.app.v1.AdminService.ListAdminAgents:input_type -> stream.app.v1.ListAdminAgentsRequest + 61, // 59: stream.app.v1.AdminService.RestartAdminAgent:input_type -> stream.app.v1.RestartAdminAgentRequest + 62, // 60: stream.app.v1.AdminService.UpdateAdminAgent:input_type -> stream.app.v1.UpdateAdminAgentRequest + 1, // 61: stream.app.v1.AdminService.GetAdminDashboard:output_type -> stream.app.v1.GetAdminDashboardResponse + 3, // 62: stream.app.v1.AdminService.ListAdminUsers:output_type -> stream.app.v1.ListAdminUsersResponse + 5, // 63: stream.app.v1.AdminService.GetAdminUser:output_type -> stream.app.v1.GetAdminUserResponse + 7, // 64: stream.app.v1.AdminService.CreateAdminUser:output_type -> stream.app.v1.CreateAdminUserResponse + 9, // 65: stream.app.v1.AdminService.UpdateAdminUser:output_type -> stream.app.v1.UpdateAdminUserResponse + 11, // 66: stream.app.v1.AdminService.UpdateAdminUserRole:output_type -> stream.app.v1.UpdateAdminUserRoleResponse + 75, // 67: stream.app.v1.AdminService.DeleteAdminUser:output_type -> stream.app.v1.MessageResponse + 14, // 68: stream.app.v1.AdminService.ListAdminVideos:output_type -> stream.app.v1.ListAdminVideosResponse + 16, // 69: stream.app.v1.AdminService.GetAdminVideo:output_type -> stream.app.v1.GetAdminVideoResponse + 18, // 70: stream.app.v1.AdminService.CreateAdminVideo:output_type -> stream.app.v1.CreateAdminVideoResponse + 20, // 71: stream.app.v1.AdminService.UpdateAdminVideo:output_type -> stream.app.v1.UpdateAdminVideoResponse + 75, // 72: stream.app.v1.AdminService.DeleteAdminVideo:output_type -> stream.app.v1.MessageResponse + 23, // 73: stream.app.v1.AdminService.ListAdminPayments:output_type -> stream.app.v1.ListAdminPaymentsResponse + 25, // 74: stream.app.v1.AdminService.GetAdminPayment:output_type -> stream.app.v1.GetAdminPaymentResponse + 27, // 75: stream.app.v1.AdminService.CreateAdminPayment:output_type -> stream.app.v1.CreateAdminPaymentResponse + 29, // 76: stream.app.v1.AdminService.UpdateAdminPayment:output_type -> stream.app.v1.UpdateAdminPaymentResponse + 31, // 77: stream.app.v1.AdminService.ListAdminPlans:output_type -> stream.app.v1.ListAdminPlansResponse + 33, // 78: stream.app.v1.AdminService.CreateAdminPlan:output_type -> stream.app.v1.CreateAdminPlanResponse + 35, // 79: stream.app.v1.AdminService.UpdateAdminPlan:output_type -> stream.app.v1.UpdateAdminPlanResponse + 37, // 80: stream.app.v1.AdminService.DeleteAdminPlan:output_type -> stream.app.v1.DeleteAdminPlanResponse + 39, // 81: stream.app.v1.AdminService.ListAdminAdTemplates:output_type -> stream.app.v1.ListAdminAdTemplatesResponse + 41, // 82: stream.app.v1.AdminService.GetAdminAdTemplate:output_type -> stream.app.v1.GetAdminAdTemplateResponse + 43, // 83: stream.app.v1.AdminService.CreateAdminAdTemplate:output_type -> stream.app.v1.CreateAdminAdTemplateResponse + 45, // 84: stream.app.v1.AdminService.UpdateAdminAdTemplate:output_type -> stream.app.v1.UpdateAdminAdTemplateResponse + 75, // 85: stream.app.v1.AdminService.DeleteAdminAdTemplate:output_type -> stream.app.v1.MessageResponse + 48, // 86: stream.app.v1.AdminService.ListAdminJobs:output_type -> stream.app.v1.ListAdminJobsResponse + 50, // 87: stream.app.v1.AdminService.GetAdminJob:output_type -> stream.app.v1.GetAdminJobResponse + 52, // 88: stream.app.v1.AdminService.GetAdminJobLogs:output_type -> stream.app.v1.GetAdminJobLogsResponse + 54, // 89: stream.app.v1.AdminService.CreateAdminJob:output_type -> stream.app.v1.CreateAdminJobResponse + 56, // 90: stream.app.v1.AdminService.CancelAdminJob:output_type -> stream.app.v1.CancelAdminJobResponse + 58, // 91: stream.app.v1.AdminService.RetryAdminJob:output_type -> stream.app.v1.RetryAdminJobResponse + 60, // 92: stream.app.v1.AdminService.ListAdminAgents:output_type -> stream.app.v1.ListAdminAgentsResponse + 63, // 93: stream.app.v1.AdminService.RestartAdminAgent:output_type -> stream.app.v1.AdminAgentCommandResponse + 63, // 94: stream.app.v1.AdminService.UpdateAdminAgent:output_type -> stream.app.v1.AdminAgentCommandResponse + 61, // [61:95] is the sub-list for method output_type + 27, // [27:61] is the sub-list for method input_type + 27, // [27:27] is the sub-list for extension type_name + 27, // [27:27] is the sub-list for extension extendee + 0, // [0:27] is the sub-list for field type_name +} + +func init() { file_app_v1_admin_proto_init() } +func file_app_v1_admin_proto_init() { + if File_app_v1_admin_proto != nil { + return + } + file_app_v1_common_proto_init() + file_app_v1_admin_proto_msgTypes[2].OneofWrappers = []any{} + file_app_v1_admin_proto_msgTypes[6].OneofWrappers = []any{} + file_app_v1_admin_proto_msgTypes[8].OneofWrappers = []any{} + file_app_v1_admin_proto_msgTypes[13].OneofWrappers = []any{} + file_app_v1_admin_proto_msgTypes[17].OneofWrappers = []any{} + file_app_v1_admin_proto_msgTypes[19].OneofWrappers = []any{} + file_app_v1_admin_proto_msgTypes[22].OneofWrappers = []any{} + file_app_v1_admin_proto_msgTypes[26].OneofWrappers = []any{} + file_app_v1_admin_proto_msgTypes[32].OneofWrappers = []any{} + file_app_v1_admin_proto_msgTypes[34].OneofWrappers = []any{} + file_app_v1_admin_proto_msgTypes[38].OneofWrappers = []any{} + file_app_v1_admin_proto_msgTypes[42].OneofWrappers = []any{} + file_app_v1_admin_proto_msgTypes[44].OneofWrappers = []any{} + file_app_v1_admin_proto_msgTypes[47].OneofWrappers = []any{} + file_app_v1_admin_proto_msgTypes[53].OneofWrappers = []any{} + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_v1_admin_proto_rawDesc), len(file_app_v1_admin_proto_rawDesc)), + NumEnums: 0, + NumMessages: 65, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_app_v1_admin_proto_goTypes, + DependencyIndexes: file_app_v1_admin_proto_depIdxs, + MessageInfos: file_app_v1_admin_proto_msgTypes, + }.Build() + File_app_v1_admin_proto = out.File + file_app_v1_admin_proto_goTypes = nil + file_app_v1_admin_proto_depIdxs = nil +} diff --git a/internal/gen/proto/app/v1/admin_grpc.pb.go b/internal/gen/proto/app/v1/admin_grpc.pb.go new file mode 100644 index 0000000..ad8e7b3 --- /dev/null +++ b/internal/gen/proto/app/v1/admin_grpc.pb.go @@ -0,0 +1,1375 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.6.1 +// - protoc (unknown) +// source: app/v1/admin.proto + +package appv1 + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + AdminService_GetAdminDashboard_FullMethodName = "/stream.app.v1.AdminService/GetAdminDashboard" + AdminService_ListAdminUsers_FullMethodName = "/stream.app.v1.AdminService/ListAdminUsers" + AdminService_GetAdminUser_FullMethodName = "/stream.app.v1.AdminService/GetAdminUser" + AdminService_CreateAdminUser_FullMethodName = "/stream.app.v1.AdminService/CreateAdminUser" + AdminService_UpdateAdminUser_FullMethodName = "/stream.app.v1.AdminService/UpdateAdminUser" + AdminService_UpdateAdminUserRole_FullMethodName = "/stream.app.v1.AdminService/UpdateAdminUserRole" + AdminService_DeleteAdminUser_FullMethodName = "/stream.app.v1.AdminService/DeleteAdminUser" + AdminService_ListAdminVideos_FullMethodName = "/stream.app.v1.AdminService/ListAdminVideos" + AdminService_GetAdminVideo_FullMethodName = "/stream.app.v1.AdminService/GetAdminVideo" + AdminService_CreateAdminVideo_FullMethodName = "/stream.app.v1.AdminService/CreateAdminVideo" + AdminService_UpdateAdminVideo_FullMethodName = "/stream.app.v1.AdminService/UpdateAdminVideo" + AdminService_DeleteAdminVideo_FullMethodName = "/stream.app.v1.AdminService/DeleteAdminVideo" + AdminService_ListAdminPayments_FullMethodName = "/stream.app.v1.AdminService/ListAdminPayments" + AdminService_GetAdminPayment_FullMethodName = "/stream.app.v1.AdminService/GetAdminPayment" + AdminService_CreateAdminPayment_FullMethodName = "/stream.app.v1.AdminService/CreateAdminPayment" + AdminService_UpdateAdminPayment_FullMethodName = "/stream.app.v1.AdminService/UpdateAdminPayment" + AdminService_ListAdminPlans_FullMethodName = "/stream.app.v1.AdminService/ListAdminPlans" + AdminService_CreateAdminPlan_FullMethodName = "/stream.app.v1.AdminService/CreateAdminPlan" + AdminService_UpdateAdminPlan_FullMethodName = "/stream.app.v1.AdminService/UpdateAdminPlan" + AdminService_DeleteAdminPlan_FullMethodName = "/stream.app.v1.AdminService/DeleteAdminPlan" + AdminService_ListAdminAdTemplates_FullMethodName = "/stream.app.v1.AdminService/ListAdminAdTemplates" + AdminService_GetAdminAdTemplate_FullMethodName = "/stream.app.v1.AdminService/GetAdminAdTemplate" + AdminService_CreateAdminAdTemplate_FullMethodName = "/stream.app.v1.AdminService/CreateAdminAdTemplate" + AdminService_UpdateAdminAdTemplate_FullMethodName = "/stream.app.v1.AdminService/UpdateAdminAdTemplate" + AdminService_DeleteAdminAdTemplate_FullMethodName = "/stream.app.v1.AdminService/DeleteAdminAdTemplate" + AdminService_ListAdminJobs_FullMethodName = "/stream.app.v1.AdminService/ListAdminJobs" + AdminService_GetAdminJob_FullMethodName = "/stream.app.v1.AdminService/GetAdminJob" + AdminService_GetAdminJobLogs_FullMethodName = "/stream.app.v1.AdminService/GetAdminJobLogs" + AdminService_CreateAdminJob_FullMethodName = "/stream.app.v1.AdminService/CreateAdminJob" + AdminService_CancelAdminJob_FullMethodName = "/stream.app.v1.AdminService/CancelAdminJob" + AdminService_RetryAdminJob_FullMethodName = "/stream.app.v1.AdminService/RetryAdminJob" + AdminService_ListAdminAgents_FullMethodName = "/stream.app.v1.AdminService/ListAdminAgents" + AdminService_RestartAdminAgent_FullMethodName = "/stream.app.v1.AdminService/RestartAdminAgent" + AdminService_UpdateAdminAgent_FullMethodName = "/stream.app.v1.AdminService/UpdateAdminAgent" +) + +// AdminServiceClient is the client API for AdminService 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 AdminServiceClient interface { + GetAdminDashboard(ctx context.Context, in *GetAdminDashboardRequest, opts ...grpc.CallOption) (*GetAdminDashboardResponse, error) + ListAdminUsers(ctx context.Context, in *ListAdminUsersRequest, opts ...grpc.CallOption) (*ListAdminUsersResponse, error) + GetAdminUser(ctx context.Context, in *GetAdminUserRequest, opts ...grpc.CallOption) (*GetAdminUserResponse, error) + CreateAdminUser(ctx context.Context, in *CreateAdminUserRequest, opts ...grpc.CallOption) (*CreateAdminUserResponse, error) + UpdateAdminUser(ctx context.Context, in *UpdateAdminUserRequest, opts ...grpc.CallOption) (*UpdateAdminUserResponse, error) + UpdateAdminUserRole(ctx context.Context, in *UpdateAdminUserRoleRequest, opts ...grpc.CallOption) (*UpdateAdminUserRoleResponse, error) + DeleteAdminUser(ctx context.Context, in *DeleteAdminUserRequest, opts ...grpc.CallOption) (*MessageResponse, error) + ListAdminVideos(ctx context.Context, in *ListAdminVideosRequest, opts ...grpc.CallOption) (*ListAdminVideosResponse, error) + GetAdminVideo(ctx context.Context, in *GetAdminVideoRequest, opts ...grpc.CallOption) (*GetAdminVideoResponse, error) + CreateAdminVideo(ctx context.Context, in *CreateAdminVideoRequest, opts ...grpc.CallOption) (*CreateAdminVideoResponse, error) + UpdateAdminVideo(ctx context.Context, in *UpdateAdminVideoRequest, opts ...grpc.CallOption) (*UpdateAdminVideoResponse, error) + DeleteAdminVideo(ctx context.Context, in *DeleteAdminVideoRequest, opts ...grpc.CallOption) (*MessageResponse, error) + ListAdminPayments(ctx context.Context, in *ListAdminPaymentsRequest, opts ...grpc.CallOption) (*ListAdminPaymentsResponse, error) + GetAdminPayment(ctx context.Context, in *GetAdminPaymentRequest, opts ...grpc.CallOption) (*GetAdminPaymentResponse, error) + CreateAdminPayment(ctx context.Context, in *CreateAdminPaymentRequest, opts ...grpc.CallOption) (*CreateAdminPaymentResponse, error) + UpdateAdminPayment(ctx context.Context, in *UpdateAdminPaymentRequest, opts ...grpc.CallOption) (*UpdateAdminPaymentResponse, error) + ListAdminPlans(ctx context.Context, in *ListAdminPlansRequest, opts ...grpc.CallOption) (*ListAdminPlansResponse, error) + CreateAdminPlan(ctx context.Context, in *CreateAdminPlanRequest, opts ...grpc.CallOption) (*CreateAdminPlanResponse, error) + UpdateAdminPlan(ctx context.Context, in *UpdateAdminPlanRequest, opts ...grpc.CallOption) (*UpdateAdminPlanResponse, error) + DeleteAdminPlan(ctx context.Context, in *DeleteAdminPlanRequest, opts ...grpc.CallOption) (*DeleteAdminPlanResponse, error) + ListAdminAdTemplates(ctx context.Context, in *ListAdminAdTemplatesRequest, opts ...grpc.CallOption) (*ListAdminAdTemplatesResponse, error) + GetAdminAdTemplate(ctx context.Context, in *GetAdminAdTemplateRequest, opts ...grpc.CallOption) (*GetAdminAdTemplateResponse, error) + 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) + ListAdminJobs(ctx context.Context, in *ListAdminJobsRequest, opts ...grpc.CallOption) (*ListAdminJobsResponse, error) + GetAdminJob(ctx context.Context, in *GetAdminJobRequest, opts ...grpc.CallOption) (*GetAdminJobResponse, error) + GetAdminJobLogs(ctx context.Context, in *GetAdminJobLogsRequest, opts ...grpc.CallOption) (*GetAdminJobLogsResponse, error) + CreateAdminJob(ctx context.Context, in *CreateAdminJobRequest, opts ...grpc.CallOption) (*CreateAdminJobResponse, error) + CancelAdminJob(ctx context.Context, in *CancelAdminJobRequest, opts ...grpc.CallOption) (*CancelAdminJobResponse, error) + RetryAdminJob(ctx context.Context, in *RetryAdminJobRequest, opts ...grpc.CallOption) (*RetryAdminJobResponse, error) + ListAdminAgents(ctx context.Context, in *ListAdminAgentsRequest, opts ...grpc.CallOption) (*ListAdminAgentsResponse, error) + RestartAdminAgent(ctx context.Context, in *RestartAdminAgentRequest, opts ...grpc.CallOption) (*AdminAgentCommandResponse, error) + UpdateAdminAgent(ctx context.Context, in *UpdateAdminAgentRequest, opts ...grpc.CallOption) (*AdminAgentCommandResponse, error) +} + +type adminServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewAdminServiceClient(cc grpc.ClientConnInterface) AdminServiceClient { + return &adminServiceClient{cc} +} + +func (c *adminServiceClient) GetAdminDashboard(ctx context.Context, in *GetAdminDashboardRequest, opts ...grpc.CallOption) (*GetAdminDashboardResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetAdminDashboardResponse) + err := c.cc.Invoke(ctx, AdminService_GetAdminDashboard_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adminServiceClient) ListAdminUsers(ctx context.Context, in *ListAdminUsersRequest, opts ...grpc.CallOption) (*ListAdminUsersResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(ListAdminUsersResponse) + err := c.cc.Invoke(ctx, AdminService_ListAdminUsers_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adminServiceClient) GetAdminUser(ctx context.Context, in *GetAdminUserRequest, opts ...grpc.CallOption) (*GetAdminUserResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetAdminUserResponse) + err := c.cc.Invoke(ctx, AdminService_GetAdminUser_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adminServiceClient) CreateAdminUser(ctx context.Context, in *CreateAdminUserRequest, opts ...grpc.CallOption) (*CreateAdminUserResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(CreateAdminUserResponse) + err := c.cc.Invoke(ctx, AdminService_CreateAdminUser_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adminServiceClient) UpdateAdminUser(ctx context.Context, in *UpdateAdminUserRequest, opts ...grpc.CallOption) (*UpdateAdminUserResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(UpdateAdminUserResponse) + err := c.cc.Invoke(ctx, AdminService_UpdateAdminUser_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adminServiceClient) UpdateAdminUserRole(ctx context.Context, in *UpdateAdminUserRoleRequest, opts ...grpc.CallOption) (*UpdateAdminUserRoleResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(UpdateAdminUserRoleResponse) + err := c.cc.Invoke(ctx, AdminService_UpdateAdminUserRole_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adminServiceClient) DeleteAdminUser(ctx context.Context, in *DeleteAdminUserRequest, opts ...grpc.CallOption) (*MessageResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(MessageResponse) + err := c.cc.Invoke(ctx, AdminService_DeleteAdminUser_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adminServiceClient) ListAdminVideos(ctx context.Context, in *ListAdminVideosRequest, opts ...grpc.CallOption) (*ListAdminVideosResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(ListAdminVideosResponse) + err := c.cc.Invoke(ctx, AdminService_ListAdminVideos_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adminServiceClient) GetAdminVideo(ctx context.Context, in *GetAdminVideoRequest, opts ...grpc.CallOption) (*GetAdminVideoResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetAdminVideoResponse) + err := c.cc.Invoke(ctx, AdminService_GetAdminVideo_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adminServiceClient) CreateAdminVideo(ctx context.Context, in *CreateAdminVideoRequest, opts ...grpc.CallOption) (*CreateAdminVideoResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(CreateAdminVideoResponse) + err := c.cc.Invoke(ctx, AdminService_CreateAdminVideo_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adminServiceClient) UpdateAdminVideo(ctx context.Context, in *UpdateAdminVideoRequest, opts ...grpc.CallOption) (*UpdateAdminVideoResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(UpdateAdminVideoResponse) + err := c.cc.Invoke(ctx, AdminService_UpdateAdminVideo_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adminServiceClient) DeleteAdminVideo(ctx context.Context, in *DeleteAdminVideoRequest, opts ...grpc.CallOption) (*MessageResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(MessageResponse) + err := c.cc.Invoke(ctx, AdminService_DeleteAdminVideo_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adminServiceClient) ListAdminPayments(ctx context.Context, in *ListAdminPaymentsRequest, opts ...grpc.CallOption) (*ListAdminPaymentsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(ListAdminPaymentsResponse) + err := c.cc.Invoke(ctx, AdminService_ListAdminPayments_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adminServiceClient) GetAdminPayment(ctx context.Context, in *GetAdminPaymentRequest, opts ...grpc.CallOption) (*GetAdminPaymentResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetAdminPaymentResponse) + err := c.cc.Invoke(ctx, AdminService_GetAdminPayment_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adminServiceClient) CreateAdminPayment(ctx context.Context, in *CreateAdminPaymentRequest, opts ...grpc.CallOption) (*CreateAdminPaymentResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(CreateAdminPaymentResponse) + err := c.cc.Invoke(ctx, AdminService_CreateAdminPayment_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adminServiceClient) UpdateAdminPayment(ctx context.Context, in *UpdateAdminPaymentRequest, opts ...grpc.CallOption) (*UpdateAdminPaymentResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(UpdateAdminPaymentResponse) + err := c.cc.Invoke(ctx, AdminService_UpdateAdminPayment_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adminServiceClient) ListAdminPlans(ctx context.Context, in *ListAdminPlansRequest, opts ...grpc.CallOption) (*ListAdminPlansResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(ListAdminPlansResponse) + err := c.cc.Invoke(ctx, AdminService_ListAdminPlans_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adminServiceClient) CreateAdminPlan(ctx context.Context, in *CreateAdminPlanRequest, opts ...grpc.CallOption) (*CreateAdminPlanResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(CreateAdminPlanResponse) + err := c.cc.Invoke(ctx, AdminService_CreateAdminPlan_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adminServiceClient) UpdateAdminPlan(ctx context.Context, in *UpdateAdminPlanRequest, opts ...grpc.CallOption) (*UpdateAdminPlanResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(UpdateAdminPlanResponse) + err := c.cc.Invoke(ctx, AdminService_UpdateAdminPlan_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adminServiceClient) DeleteAdminPlan(ctx context.Context, in *DeleteAdminPlanRequest, opts ...grpc.CallOption) (*DeleteAdminPlanResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(DeleteAdminPlanResponse) + err := c.cc.Invoke(ctx, AdminService_DeleteAdminPlan_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adminServiceClient) ListAdminAdTemplates(ctx context.Context, in *ListAdminAdTemplatesRequest, opts ...grpc.CallOption) (*ListAdminAdTemplatesResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(ListAdminAdTemplatesResponse) + err := c.cc.Invoke(ctx, AdminService_ListAdminAdTemplates_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adminServiceClient) GetAdminAdTemplate(ctx context.Context, in *GetAdminAdTemplateRequest, opts ...grpc.CallOption) (*GetAdminAdTemplateResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetAdminAdTemplateResponse) + err := c.cc.Invoke(ctx, AdminService_GetAdminAdTemplate_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adminServiceClient) CreateAdminAdTemplate(ctx context.Context, in *CreateAdminAdTemplateRequest, opts ...grpc.CallOption) (*CreateAdminAdTemplateResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(CreateAdminAdTemplateResponse) + err := c.cc.Invoke(ctx, AdminService_CreateAdminAdTemplate_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adminServiceClient) UpdateAdminAdTemplate(ctx context.Context, in *UpdateAdminAdTemplateRequest, opts ...grpc.CallOption) (*UpdateAdminAdTemplateResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(UpdateAdminAdTemplateResponse) + err := c.cc.Invoke(ctx, AdminService_UpdateAdminAdTemplate_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adminServiceClient) DeleteAdminAdTemplate(ctx context.Context, in *DeleteAdminAdTemplateRequest, opts ...grpc.CallOption) (*MessageResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(MessageResponse) + err := c.cc.Invoke(ctx, AdminService_DeleteAdminAdTemplate_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adminServiceClient) ListAdminJobs(ctx context.Context, in *ListAdminJobsRequest, opts ...grpc.CallOption) (*ListAdminJobsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(ListAdminJobsResponse) + err := c.cc.Invoke(ctx, AdminService_ListAdminJobs_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adminServiceClient) GetAdminJob(ctx context.Context, in *GetAdminJobRequest, opts ...grpc.CallOption) (*GetAdminJobResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetAdminJobResponse) + err := c.cc.Invoke(ctx, AdminService_GetAdminJob_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adminServiceClient) GetAdminJobLogs(ctx context.Context, in *GetAdminJobLogsRequest, opts ...grpc.CallOption) (*GetAdminJobLogsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetAdminJobLogsResponse) + err := c.cc.Invoke(ctx, AdminService_GetAdminJobLogs_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adminServiceClient) CreateAdminJob(ctx context.Context, in *CreateAdminJobRequest, opts ...grpc.CallOption) (*CreateAdminJobResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(CreateAdminJobResponse) + err := c.cc.Invoke(ctx, AdminService_CreateAdminJob_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adminServiceClient) CancelAdminJob(ctx context.Context, in *CancelAdminJobRequest, opts ...grpc.CallOption) (*CancelAdminJobResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(CancelAdminJobResponse) + err := c.cc.Invoke(ctx, AdminService_CancelAdminJob_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adminServiceClient) RetryAdminJob(ctx context.Context, in *RetryAdminJobRequest, opts ...grpc.CallOption) (*RetryAdminJobResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(RetryAdminJobResponse) + err := c.cc.Invoke(ctx, AdminService_RetryAdminJob_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adminServiceClient) ListAdminAgents(ctx context.Context, in *ListAdminAgentsRequest, opts ...grpc.CallOption) (*ListAdminAgentsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(ListAdminAgentsResponse) + err := c.cc.Invoke(ctx, AdminService_ListAdminAgents_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adminServiceClient) RestartAdminAgent(ctx context.Context, in *RestartAdminAgentRequest, opts ...grpc.CallOption) (*AdminAgentCommandResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(AdminAgentCommandResponse) + err := c.cc.Invoke(ctx, AdminService_RestartAdminAgent_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adminServiceClient) UpdateAdminAgent(ctx context.Context, in *UpdateAdminAgentRequest, opts ...grpc.CallOption) (*AdminAgentCommandResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(AdminAgentCommandResponse) + err := c.cc.Invoke(ctx, AdminService_UpdateAdminAgent_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// AdminServiceServer is the server API for AdminService service. +// All implementations must embed UnimplementedAdminServiceServer +// for forward compatibility. +type AdminServiceServer interface { + GetAdminDashboard(context.Context, *GetAdminDashboardRequest) (*GetAdminDashboardResponse, error) + ListAdminUsers(context.Context, *ListAdminUsersRequest) (*ListAdminUsersResponse, error) + GetAdminUser(context.Context, *GetAdminUserRequest) (*GetAdminUserResponse, error) + CreateAdminUser(context.Context, *CreateAdminUserRequest) (*CreateAdminUserResponse, error) + UpdateAdminUser(context.Context, *UpdateAdminUserRequest) (*UpdateAdminUserResponse, error) + UpdateAdminUserRole(context.Context, *UpdateAdminUserRoleRequest) (*UpdateAdminUserRoleResponse, error) + DeleteAdminUser(context.Context, *DeleteAdminUserRequest) (*MessageResponse, error) + ListAdminVideos(context.Context, *ListAdminVideosRequest) (*ListAdminVideosResponse, error) + GetAdminVideo(context.Context, *GetAdminVideoRequest) (*GetAdminVideoResponse, error) + CreateAdminVideo(context.Context, *CreateAdminVideoRequest) (*CreateAdminVideoResponse, error) + UpdateAdminVideo(context.Context, *UpdateAdminVideoRequest) (*UpdateAdminVideoResponse, error) + DeleteAdminVideo(context.Context, *DeleteAdminVideoRequest) (*MessageResponse, error) + ListAdminPayments(context.Context, *ListAdminPaymentsRequest) (*ListAdminPaymentsResponse, error) + GetAdminPayment(context.Context, *GetAdminPaymentRequest) (*GetAdminPaymentResponse, error) + CreateAdminPayment(context.Context, *CreateAdminPaymentRequest) (*CreateAdminPaymentResponse, error) + UpdateAdminPayment(context.Context, *UpdateAdminPaymentRequest) (*UpdateAdminPaymentResponse, error) + ListAdminPlans(context.Context, *ListAdminPlansRequest) (*ListAdminPlansResponse, error) + CreateAdminPlan(context.Context, *CreateAdminPlanRequest) (*CreateAdminPlanResponse, error) + UpdateAdminPlan(context.Context, *UpdateAdminPlanRequest) (*UpdateAdminPlanResponse, error) + DeleteAdminPlan(context.Context, *DeleteAdminPlanRequest) (*DeleteAdminPlanResponse, error) + ListAdminAdTemplates(context.Context, *ListAdminAdTemplatesRequest) (*ListAdminAdTemplatesResponse, error) + GetAdminAdTemplate(context.Context, *GetAdminAdTemplateRequest) (*GetAdminAdTemplateResponse, error) + CreateAdminAdTemplate(context.Context, *CreateAdminAdTemplateRequest) (*CreateAdminAdTemplateResponse, error) + UpdateAdminAdTemplate(context.Context, *UpdateAdminAdTemplateRequest) (*UpdateAdminAdTemplateResponse, error) + DeleteAdminAdTemplate(context.Context, *DeleteAdminAdTemplateRequest) (*MessageResponse, error) + ListAdminJobs(context.Context, *ListAdminJobsRequest) (*ListAdminJobsResponse, error) + GetAdminJob(context.Context, *GetAdminJobRequest) (*GetAdminJobResponse, error) + GetAdminJobLogs(context.Context, *GetAdminJobLogsRequest) (*GetAdminJobLogsResponse, error) + CreateAdminJob(context.Context, *CreateAdminJobRequest) (*CreateAdminJobResponse, error) + CancelAdminJob(context.Context, *CancelAdminJobRequest) (*CancelAdminJobResponse, error) + RetryAdminJob(context.Context, *RetryAdminJobRequest) (*RetryAdminJobResponse, error) + ListAdminAgents(context.Context, *ListAdminAgentsRequest) (*ListAdminAgentsResponse, error) + RestartAdminAgent(context.Context, *RestartAdminAgentRequest) (*AdminAgentCommandResponse, error) + UpdateAdminAgent(context.Context, *UpdateAdminAgentRequest) (*AdminAgentCommandResponse, error) + mustEmbedUnimplementedAdminServiceServer() +} + +// UnimplementedAdminServiceServer 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 UnimplementedAdminServiceServer struct{} + +func (UnimplementedAdminServiceServer) GetAdminDashboard(context.Context, *GetAdminDashboardRequest) (*GetAdminDashboardResponse, error) { + return nil, status.Error(codes.Unimplemented, "method GetAdminDashboard not implemented") +} +func (UnimplementedAdminServiceServer) ListAdminUsers(context.Context, *ListAdminUsersRequest) (*ListAdminUsersResponse, error) { + return nil, status.Error(codes.Unimplemented, "method ListAdminUsers not implemented") +} +func (UnimplementedAdminServiceServer) GetAdminUser(context.Context, *GetAdminUserRequest) (*GetAdminUserResponse, error) { + return nil, status.Error(codes.Unimplemented, "method GetAdminUser not implemented") +} +func (UnimplementedAdminServiceServer) CreateAdminUser(context.Context, *CreateAdminUserRequest) (*CreateAdminUserResponse, error) { + return nil, status.Error(codes.Unimplemented, "method CreateAdminUser not implemented") +} +func (UnimplementedAdminServiceServer) UpdateAdminUser(context.Context, *UpdateAdminUserRequest) (*UpdateAdminUserResponse, error) { + return nil, status.Error(codes.Unimplemented, "method UpdateAdminUser not implemented") +} +func (UnimplementedAdminServiceServer) UpdateAdminUserRole(context.Context, *UpdateAdminUserRoleRequest) (*UpdateAdminUserRoleResponse, error) { + return nil, status.Error(codes.Unimplemented, "method UpdateAdminUserRole not implemented") +} +func (UnimplementedAdminServiceServer) DeleteAdminUser(context.Context, *DeleteAdminUserRequest) (*MessageResponse, error) { + return nil, status.Error(codes.Unimplemented, "method DeleteAdminUser not implemented") +} +func (UnimplementedAdminServiceServer) ListAdminVideos(context.Context, *ListAdminVideosRequest) (*ListAdminVideosResponse, error) { + return nil, status.Error(codes.Unimplemented, "method ListAdminVideos not implemented") +} +func (UnimplementedAdminServiceServer) GetAdminVideo(context.Context, *GetAdminVideoRequest) (*GetAdminVideoResponse, error) { + return nil, status.Error(codes.Unimplemented, "method GetAdminVideo not implemented") +} +func (UnimplementedAdminServiceServer) CreateAdminVideo(context.Context, *CreateAdminVideoRequest) (*CreateAdminVideoResponse, error) { + return nil, status.Error(codes.Unimplemented, "method CreateAdminVideo not implemented") +} +func (UnimplementedAdminServiceServer) UpdateAdminVideo(context.Context, *UpdateAdminVideoRequest) (*UpdateAdminVideoResponse, error) { + return nil, status.Error(codes.Unimplemented, "method UpdateAdminVideo not implemented") +} +func (UnimplementedAdminServiceServer) DeleteAdminVideo(context.Context, *DeleteAdminVideoRequest) (*MessageResponse, error) { + return nil, status.Error(codes.Unimplemented, "method DeleteAdminVideo not implemented") +} +func (UnimplementedAdminServiceServer) ListAdminPayments(context.Context, *ListAdminPaymentsRequest) (*ListAdminPaymentsResponse, error) { + return nil, status.Error(codes.Unimplemented, "method ListAdminPayments not implemented") +} +func (UnimplementedAdminServiceServer) GetAdminPayment(context.Context, *GetAdminPaymentRequest) (*GetAdminPaymentResponse, error) { + return nil, status.Error(codes.Unimplemented, "method GetAdminPayment not implemented") +} +func (UnimplementedAdminServiceServer) CreateAdminPayment(context.Context, *CreateAdminPaymentRequest) (*CreateAdminPaymentResponse, error) { + return nil, status.Error(codes.Unimplemented, "method CreateAdminPayment not implemented") +} +func (UnimplementedAdminServiceServer) UpdateAdminPayment(context.Context, *UpdateAdminPaymentRequest) (*UpdateAdminPaymentResponse, error) { + return nil, status.Error(codes.Unimplemented, "method UpdateAdminPayment not implemented") +} +func (UnimplementedAdminServiceServer) ListAdminPlans(context.Context, *ListAdminPlansRequest) (*ListAdminPlansResponse, error) { + return nil, status.Error(codes.Unimplemented, "method ListAdminPlans not implemented") +} +func (UnimplementedAdminServiceServer) CreateAdminPlan(context.Context, *CreateAdminPlanRequest) (*CreateAdminPlanResponse, error) { + return nil, status.Error(codes.Unimplemented, "method CreateAdminPlan not implemented") +} +func (UnimplementedAdminServiceServer) UpdateAdminPlan(context.Context, *UpdateAdminPlanRequest) (*UpdateAdminPlanResponse, error) { + return nil, status.Error(codes.Unimplemented, "method UpdateAdminPlan not implemented") +} +func (UnimplementedAdminServiceServer) DeleteAdminPlan(context.Context, *DeleteAdminPlanRequest) (*DeleteAdminPlanResponse, error) { + return nil, status.Error(codes.Unimplemented, "method DeleteAdminPlan not implemented") +} +func (UnimplementedAdminServiceServer) ListAdminAdTemplates(context.Context, *ListAdminAdTemplatesRequest) (*ListAdminAdTemplatesResponse, error) { + return nil, status.Error(codes.Unimplemented, "method ListAdminAdTemplates not implemented") +} +func (UnimplementedAdminServiceServer) GetAdminAdTemplate(context.Context, *GetAdminAdTemplateRequest) (*GetAdminAdTemplateResponse, error) { + return nil, status.Error(codes.Unimplemented, "method GetAdminAdTemplate not implemented") +} +func (UnimplementedAdminServiceServer) CreateAdminAdTemplate(context.Context, *CreateAdminAdTemplateRequest) (*CreateAdminAdTemplateResponse, error) { + return nil, status.Error(codes.Unimplemented, "method CreateAdminAdTemplate not implemented") +} +func (UnimplementedAdminServiceServer) UpdateAdminAdTemplate(context.Context, *UpdateAdminAdTemplateRequest) (*UpdateAdminAdTemplateResponse, error) { + return nil, status.Error(codes.Unimplemented, "method UpdateAdminAdTemplate not implemented") +} +func (UnimplementedAdminServiceServer) DeleteAdminAdTemplate(context.Context, *DeleteAdminAdTemplateRequest) (*MessageResponse, error) { + return nil, status.Error(codes.Unimplemented, "method DeleteAdminAdTemplate not implemented") +} +func (UnimplementedAdminServiceServer) ListAdminJobs(context.Context, *ListAdminJobsRequest) (*ListAdminJobsResponse, error) { + return nil, status.Error(codes.Unimplemented, "method ListAdminJobs not implemented") +} +func (UnimplementedAdminServiceServer) GetAdminJob(context.Context, *GetAdminJobRequest) (*GetAdminJobResponse, error) { + return nil, status.Error(codes.Unimplemented, "method GetAdminJob not implemented") +} +func (UnimplementedAdminServiceServer) GetAdminJobLogs(context.Context, *GetAdminJobLogsRequest) (*GetAdminJobLogsResponse, error) { + return nil, status.Error(codes.Unimplemented, "method GetAdminJobLogs not implemented") +} +func (UnimplementedAdminServiceServer) CreateAdminJob(context.Context, *CreateAdminJobRequest) (*CreateAdminJobResponse, error) { + return nil, status.Error(codes.Unimplemented, "method CreateAdminJob not implemented") +} +func (UnimplementedAdminServiceServer) CancelAdminJob(context.Context, *CancelAdminJobRequest) (*CancelAdminJobResponse, error) { + return nil, status.Error(codes.Unimplemented, "method CancelAdminJob not implemented") +} +func (UnimplementedAdminServiceServer) RetryAdminJob(context.Context, *RetryAdminJobRequest) (*RetryAdminJobResponse, error) { + return nil, status.Error(codes.Unimplemented, "method RetryAdminJob not implemented") +} +func (UnimplementedAdminServiceServer) ListAdminAgents(context.Context, *ListAdminAgentsRequest) (*ListAdminAgentsResponse, error) { + return nil, status.Error(codes.Unimplemented, "method ListAdminAgents not implemented") +} +func (UnimplementedAdminServiceServer) RestartAdminAgent(context.Context, *RestartAdminAgentRequest) (*AdminAgentCommandResponse, error) { + return nil, status.Error(codes.Unimplemented, "method RestartAdminAgent not implemented") +} +func (UnimplementedAdminServiceServer) UpdateAdminAgent(context.Context, *UpdateAdminAgentRequest) (*AdminAgentCommandResponse, error) { + return nil, status.Error(codes.Unimplemented, "method UpdateAdminAgent not implemented") +} +func (UnimplementedAdminServiceServer) mustEmbedUnimplementedAdminServiceServer() {} +func (UnimplementedAdminServiceServer) testEmbeddedByValue() {} + +// UnsafeAdminServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to AdminServiceServer will +// result in compilation errors. +type UnsafeAdminServiceServer interface { + mustEmbedUnimplementedAdminServiceServer() +} + +func RegisterAdminServiceServer(s grpc.ServiceRegistrar, srv AdminServiceServer) { + // If the following call panics, it indicates UnimplementedAdminServiceServer 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(&AdminService_ServiceDesc, srv) +} + +func _AdminService_GetAdminDashboard_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetAdminDashboardRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminServiceServer).GetAdminDashboard(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminService_GetAdminDashboard_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminServiceServer).GetAdminDashboard(ctx, req.(*GetAdminDashboardRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdminService_ListAdminUsers_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListAdminUsersRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminServiceServer).ListAdminUsers(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminService_ListAdminUsers_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminServiceServer).ListAdminUsers(ctx, req.(*ListAdminUsersRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdminService_GetAdminUser_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetAdminUserRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminServiceServer).GetAdminUser(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminService_GetAdminUser_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminServiceServer).GetAdminUser(ctx, req.(*GetAdminUserRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdminService_CreateAdminUser_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateAdminUserRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminServiceServer).CreateAdminUser(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminService_CreateAdminUser_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminServiceServer).CreateAdminUser(ctx, req.(*CreateAdminUserRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdminService_UpdateAdminUser_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateAdminUserRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminServiceServer).UpdateAdminUser(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminService_UpdateAdminUser_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminServiceServer).UpdateAdminUser(ctx, req.(*UpdateAdminUserRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdminService_UpdateAdminUserRole_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateAdminUserRoleRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminServiceServer).UpdateAdminUserRole(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminService_UpdateAdminUserRole_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminServiceServer).UpdateAdminUserRole(ctx, req.(*UpdateAdminUserRoleRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdminService_DeleteAdminUser_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteAdminUserRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminServiceServer).DeleteAdminUser(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminService_DeleteAdminUser_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminServiceServer).DeleteAdminUser(ctx, req.(*DeleteAdminUserRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdminService_ListAdminVideos_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListAdminVideosRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminServiceServer).ListAdminVideos(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminService_ListAdminVideos_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminServiceServer).ListAdminVideos(ctx, req.(*ListAdminVideosRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdminService_GetAdminVideo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetAdminVideoRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminServiceServer).GetAdminVideo(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminService_GetAdminVideo_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminServiceServer).GetAdminVideo(ctx, req.(*GetAdminVideoRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdminService_CreateAdminVideo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateAdminVideoRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminServiceServer).CreateAdminVideo(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminService_CreateAdminVideo_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminServiceServer).CreateAdminVideo(ctx, req.(*CreateAdminVideoRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdminService_UpdateAdminVideo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateAdminVideoRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminServiceServer).UpdateAdminVideo(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminService_UpdateAdminVideo_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminServiceServer).UpdateAdminVideo(ctx, req.(*UpdateAdminVideoRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdminService_DeleteAdminVideo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteAdminVideoRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminServiceServer).DeleteAdminVideo(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminService_DeleteAdminVideo_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminServiceServer).DeleteAdminVideo(ctx, req.(*DeleteAdminVideoRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdminService_ListAdminPayments_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListAdminPaymentsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminServiceServer).ListAdminPayments(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminService_ListAdminPayments_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminServiceServer).ListAdminPayments(ctx, req.(*ListAdminPaymentsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdminService_GetAdminPayment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetAdminPaymentRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminServiceServer).GetAdminPayment(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminService_GetAdminPayment_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminServiceServer).GetAdminPayment(ctx, req.(*GetAdminPaymentRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdminService_CreateAdminPayment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateAdminPaymentRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminServiceServer).CreateAdminPayment(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminService_CreateAdminPayment_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminServiceServer).CreateAdminPayment(ctx, req.(*CreateAdminPaymentRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdminService_UpdateAdminPayment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateAdminPaymentRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminServiceServer).UpdateAdminPayment(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminService_UpdateAdminPayment_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminServiceServer).UpdateAdminPayment(ctx, req.(*UpdateAdminPaymentRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdminService_ListAdminPlans_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListAdminPlansRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminServiceServer).ListAdminPlans(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminService_ListAdminPlans_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminServiceServer).ListAdminPlans(ctx, req.(*ListAdminPlansRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdminService_CreateAdminPlan_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateAdminPlanRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminServiceServer).CreateAdminPlan(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminService_CreateAdminPlan_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminServiceServer).CreateAdminPlan(ctx, req.(*CreateAdminPlanRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdminService_UpdateAdminPlan_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateAdminPlanRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminServiceServer).UpdateAdminPlan(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminService_UpdateAdminPlan_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminServiceServer).UpdateAdminPlan(ctx, req.(*UpdateAdminPlanRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdminService_DeleteAdminPlan_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteAdminPlanRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminServiceServer).DeleteAdminPlan(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminService_DeleteAdminPlan_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminServiceServer).DeleteAdminPlan(ctx, req.(*DeleteAdminPlanRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdminService_ListAdminAdTemplates_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListAdminAdTemplatesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminServiceServer).ListAdminAdTemplates(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminService_ListAdminAdTemplates_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminServiceServer).ListAdminAdTemplates(ctx, req.(*ListAdminAdTemplatesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdminService_GetAdminAdTemplate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetAdminAdTemplateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminServiceServer).GetAdminAdTemplate(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminService_GetAdminAdTemplate_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminServiceServer).GetAdminAdTemplate(ctx, req.(*GetAdminAdTemplateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdminService_CreateAdminAdTemplate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateAdminAdTemplateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminServiceServer).CreateAdminAdTemplate(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminService_CreateAdminAdTemplate_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminServiceServer).CreateAdminAdTemplate(ctx, req.(*CreateAdminAdTemplateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdminService_UpdateAdminAdTemplate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateAdminAdTemplateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminServiceServer).UpdateAdminAdTemplate(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminService_UpdateAdminAdTemplate_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminServiceServer).UpdateAdminAdTemplate(ctx, req.(*UpdateAdminAdTemplateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdminService_DeleteAdminAdTemplate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteAdminAdTemplateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminServiceServer).DeleteAdminAdTemplate(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminService_DeleteAdminAdTemplate_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminServiceServer).DeleteAdminAdTemplate(ctx, req.(*DeleteAdminAdTemplateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdminService_ListAdminJobs_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListAdminJobsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminServiceServer).ListAdminJobs(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminService_ListAdminJobs_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminServiceServer).ListAdminJobs(ctx, req.(*ListAdminJobsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdminService_GetAdminJob_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetAdminJobRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminServiceServer).GetAdminJob(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminService_GetAdminJob_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminServiceServer).GetAdminJob(ctx, req.(*GetAdminJobRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdminService_GetAdminJobLogs_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetAdminJobLogsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminServiceServer).GetAdminJobLogs(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminService_GetAdminJobLogs_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminServiceServer).GetAdminJobLogs(ctx, req.(*GetAdminJobLogsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdminService_CreateAdminJob_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateAdminJobRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminServiceServer).CreateAdminJob(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminService_CreateAdminJob_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminServiceServer).CreateAdminJob(ctx, req.(*CreateAdminJobRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdminService_CancelAdminJob_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CancelAdminJobRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminServiceServer).CancelAdminJob(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminService_CancelAdminJob_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminServiceServer).CancelAdminJob(ctx, req.(*CancelAdminJobRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdminService_RetryAdminJob_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RetryAdminJobRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminServiceServer).RetryAdminJob(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminService_RetryAdminJob_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminServiceServer).RetryAdminJob(ctx, req.(*RetryAdminJobRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdminService_ListAdminAgents_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListAdminAgentsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminServiceServer).ListAdminAgents(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminService_ListAdminAgents_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminServiceServer).ListAdminAgents(ctx, req.(*ListAdminAgentsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdminService_RestartAdminAgent_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RestartAdminAgentRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminServiceServer).RestartAdminAgent(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminService_RestartAdminAgent_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminServiceServer).RestartAdminAgent(ctx, req.(*RestartAdminAgentRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdminService_UpdateAdminAgent_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateAdminAgentRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdminServiceServer).UpdateAdminAgent(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdminService_UpdateAdminAgent_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdminServiceServer).UpdateAdminAgent(ctx, req.(*UpdateAdminAgentRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// AdminService_ServiceDesc is the grpc.ServiceDesc for AdminService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var AdminService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "stream.app.v1.AdminService", + HandlerType: (*AdminServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetAdminDashboard", + Handler: _AdminService_GetAdminDashboard_Handler, + }, + { + MethodName: "ListAdminUsers", + Handler: _AdminService_ListAdminUsers_Handler, + }, + { + MethodName: "GetAdminUser", + Handler: _AdminService_GetAdminUser_Handler, + }, + { + MethodName: "CreateAdminUser", + Handler: _AdminService_CreateAdminUser_Handler, + }, + { + MethodName: "UpdateAdminUser", + Handler: _AdminService_UpdateAdminUser_Handler, + }, + { + MethodName: "UpdateAdminUserRole", + Handler: _AdminService_UpdateAdminUserRole_Handler, + }, + { + MethodName: "DeleteAdminUser", + Handler: _AdminService_DeleteAdminUser_Handler, + }, + { + MethodName: "ListAdminVideos", + Handler: _AdminService_ListAdminVideos_Handler, + }, + { + MethodName: "GetAdminVideo", + Handler: _AdminService_GetAdminVideo_Handler, + }, + { + MethodName: "CreateAdminVideo", + Handler: _AdminService_CreateAdminVideo_Handler, + }, + { + MethodName: "UpdateAdminVideo", + Handler: _AdminService_UpdateAdminVideo_Handler, + }, + { + MethodName: "DeleteAdminVideo", + Handler: _AdminService_DeleteAdminVideo_Handler, + }, + { + MethodName: "ListAdminPayments", + Handler: _AdminService_ListAdminPayments_Handler, + }, + { + MethodName: "GetAdminPayment", + Handler: _AdminService_GetAdminPayment_Handler, + }, + { + MethodName: "CreateAdminPayment", + Handler: _AdminService_CreateAdminPayment_Handler, + }, + { + MethodName: "UpdateAdminPayment", + Handler: _AdminService_UpdateAdminPayment_Handler, + }, + { + MethodName: "ListAdminPlans", + Handler: _AdminService_ListAdminPlans_Handler, + }, + { + MethodName: "CreateAdminPlan", + Handler: _AdminService_CreateAdminPlan_Handler, + }, + { + MethodName: "UpdateAdminPlan", + Handler: _AdminService_UpdateAdminPlan_Handler, + }, + { + MethodName: "DeleteAdminPlan", + Handler: _AdminService_DeleteAdminPlan_Handler, + }, + { + MethodName: "ListAdminAdTemplates", + Handler: _AdminService_ListAdminAdTemplates_Handler, + }, + { + MethodName: "GetAdminAdTemplate", + Handler: _AdminService_GetAdminAdTemplate_Handler, + }, + { + MethodName: "CreateAdminAdTemplate", + Handler: _AdminService_CreateAdminAdTemplate_Handler, + }, + { + MethodName: "UpdateAdminAdTemplate", + Handler: _AdminService_UpdateAdminAdTemplate_Handler, + }, + { + MethodName: "DeleteAdminAdTemplate", + Handler: _AdminService_DeleteAdminAdTemplate_Handler, + }, + { + MethodName: "ListAdminJobs", + Handler: _AdminService_ListAdminJobs_Handler, + }, + { + MethodName: "GetAdminJob", + Handler: _AdminService_GetAdminJob_Handler, + }, + { + MethodName: "GetAdminJobLogs", + Handler: _AdminService_GetAdminJobLogs_Handler, + }, + { + MethodName: "CreateAdminJob", + Handler: _AdminService_CreateAdminJob_Handler, + }, + { + MethodName: "CancelAdminJob", + Handler: _AdminService_CancelAdminJob_Handler, + }, + { + MethodName: "RetryAdminJob", + Handler: _AdminService_RetryAdminJob_Handler, + }, + { + MethodName: "ListAdminAgents", + Handler: _AdminService_ListAdminAgents_Handler, + }, + { + MethodName: "RestartAdminAgent", + Handler: _AdminService_RestartAdminAgent_Handler, + }, + { + MethodName: "UpdateAdminAgent", + Handler: _AdminService_UpdateAdminAgent_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "app/v1/admin.proto", +} diff --git a/internal/gen/proto/app/v1/auth.pb.go b/internal/gen/proto/app/v1/auth.pb.go new file mode 100644 index 0000000..29dd742 --- /dev/null +++ b/internal/gen/proto/app/v1/auth.pb.go @@ -0,0 +1,697 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc (unknown) +// source: app/v1/auth.proto + +package appv1 + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type LoginRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Email string `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"` + Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *LoginRequest) Reset() { + *x = LoginRequest{} + mi := &file_app_v1_auth_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *LoginRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LoginRequest) ProtoMessage() {} + +func (x *LoginRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_auth_proto_msgTypes[0] + 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 LoginRequest.ProtoReflect.Descriptor instead. +func (*LoginRequest) Descriptor() ([]byte, []int) { + return file_app_v1_auth_proto_rawDescGZIP(), []int{0} +} + +func (x *LoginRequest) GetEmail() string { + if x != nil { + return x.Email + } + return "" +} + +func (x *LoginRequest) GetPassword() string { + if x != nil { + return x.Password + } + return "" +} + +type LoginResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + User *User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *LoginResponse) Reset() { + *x = LoginResponse{} + mi := &file_app_v1_auth_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *LoginResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LoginResponse) ProtoMessage() {} + +func (x *LoginResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_auth_proto_msgTypes[1] + 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 LoginResponse.ProtoReflect.Descriptor instead. +func (*LoginResponse) Descriptor() ([]byte, []int) { + return file_app_v1_auth_proto_rawDescGZIP(), []int{1} +} + +func (x *LoginResponse) GetUser() *User { + if x != nil { + return x.User + } + return nil +} + +type RegisterRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` + Email string `protobuf:"bytes,2,opt,name=email,proto3" json:"email,omitempty"` + Password string `protobuf:"bytes,3,opt,name=password,proto3" json:"password,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RegisterRequest) Reset() { + *x = RegisterRequest{} + mi := &file_app_v1_auth_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RegisterRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegisterRequest) ProtoMessage() {} + +func (x *RegisterRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_auth_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RegisterRequest.ProtoReflect.Descriptor instead. +func (*RegisterRequest) Descriptor() ([]byte, []int) { + return file_app_v1_auth_proto_rawDescGZIP(), []int{2} +} + +func (x *RegisterRequest) GetUsername() string { + if x != nil { + return x.Username + } + return "" +} + +func (x *RegisterRequest) GetEmail() string { + if x != nil { + return x.Email + } + return "" +} + +func (x *RegisterRequest) GetPassword() string { + if x != nil { + return x.Password + } + return "" +} + +type RegisterResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + User *User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RegisterResponse) Reset() { + *x = RegisterResponse{} + mi := &file_app_v1_auth_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RegisterResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegisterResponse) ProtoMessage() {} + +func (x *RegisterResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_auth_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RegisterResponse.ProtoReflect.Descriptor instead. +func (*RegisterResponse) Descriptor() ([]byte, []int) { + return file_app_v1_auth_proto_rawDescGZIP(), []int{3} +} + +func (x *RegisterResponse) GetUser() *User { + if x != nil { + return x.User + } + return nil +} + +type LogoutRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *LogoutRequest) Reset() { + *x = LogoutRequest{} + mi := &file_app_v1_auth_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *LogoutRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LogoutRequest) ProtoMessage() {} + +func (x *LogoutRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_auth_proto_msgTypes[4] + 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 LogoutRequest.ProtoReflect.Descriptor instead. +func (*LogoutRequest) Descriptor() ([]byte, []int) { + return file_app_v1_auth_proto_rawDescGZIP(), []int{4} +} + +type ChangePasswordRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + CurrentPassword string `protobuf:"bytes,1,opt,name=current_password,json=currentPassword,proto3" json:"current_password,omitempty"` + NewPassword string `protobuf:"bytes,2,opt,name=new_password,json=newPassword,proto3" json:"new_password,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ChangePasswordRequest) Reset() { + *x = ChangePasswordRequest{} + mi := &file_app_v1_auth_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ChangePasswordRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ChangePasswordRequest) ProtoMessage() {} + +func (x *ChangePasswordRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_auth_proto_msgTypes[5] + 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 ChangePasswordRequest.ProtoReflect.Descriptor instead. +func (*ChangePasswordRequest) Descriptor() ([]byte, []int) { + return file_app_v1_auth_proto_rawDescGZIP(), []int{5} +} + +func (x *ChangePasswordRequest) GetCurrentPassword() string { + if x != nil { + return x.CurrentPassword + } + return "" +} + +func (x *ChangePasswordRequest) GetNewPassword() string { + if x != nil { + return x.NewPassword + } + return "" +} + +type ForgotPasswordRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Email string `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ForgotPasswordRequest) Reset() { + *x = ForgotPasswordRequest{} + mi := &file_app_v1_auth_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ForgotPasswordRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ForgotPasswordRequest) ProtoMessage() {} + +func (x *ForgotPasswordRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_auth_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 ForgotPasswordRequest.ProtoReflect.Descriptor instead. +func (*ForgotPasswordRequest) Descriptor() ([]byte, []int) { + return file_app_v1_auth_proto_rawDescGZIP(), []int{6} +} + +func (x *ForgotPasswordRequest) GetEmail() string { + if x != nil { + return x.Email + } + return "" +} + +type ResetPasswordRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Token string `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"` + NewPassword string `protobuf:"bytes,2,opt,name=new_password,json=newPassword,proto3" json:"new_password,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ResetPasswordRequest) Reset() { + *x = ResetPasswordRequest{} + mi := &file_app_v1_auth_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ResetPasswordRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ResetPasswordRequest) ProtoMessage() {} + +func (x *ResetPasswordRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_auth_proto_msgTypes[7] + 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 ResetPasswordRequest.ProtoReflect.Descriptor instead. +func (*ResetPasswordRequest) Descriptor() ([]byte, []int) { + return file_app_v1_auth_proto_rawDescGZIP(), []int{7} +} + +func (x *ResetPasswordRequest) GetToken() string { + if x != nil { + return x.Token + } + return "" +} + +func (x *ResetPasswordRequest) GetNewPassword() string { + if x != nil { + return x.NewPassword + } + return "" +} + +type GetGoogleLoginUrlRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetGoogleLoginUrlRequest) Reset() { + *x = GetGoogleLoginUrlRequest{} + mi := &file_app_v1_auth_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetGoogleLoginUrlRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetGoogleLoginUrlRequest) ProtoMessage() {} + +func (x *GetGoogleLoginUrlRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_auth_proto_msgTypes[8] + 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 GetGoogleLoginUrlRequest.ProtoReflect.Descriptor instead. +func (*GetGoogleLoginUrlRequest) Descriptor() ([]byte, []int) { + return file_app_v1_auth_proto_rawDescGZIP(), []int{8} +} + +type GetGoogleLoginUrlResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetGoogleLoginUrlResponse) Reset() { + *x = GetGoogleLoginUrlResponse{} + mi := &file_app_v1_auth_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetGoogleLoginUrlResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetGoogleLoginUrlResponse) ProtoMessage() {} + +func (x *GetGoogleLoginUrlResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_auth_proto_msgTypes[9] + 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 GetGoogleLoginUrlResponse.ProtoReflect.Descriptor instead. +func (*GetGoogleLoginUrlResponse) Descriptor() ([]byte, []int) { + return file_app_v1_auth_proto_rawDescGZIP(), []int{9} +} + +func (x *GetGoogleLoginUrlResponse) GetUrl() string { + if x != nil { + return x.Url + } + return "" +} + +type CompleteGoogleLoginRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Code string `protobuf:"bytes,1,opt,name=code,proto3" json:"code,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CompleteGoogleLoginRequest) Reset() { + *x = CompleteGoogleLoginRequest{} + mi := &file_app_v1_auth_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CompleteGoogleLoginRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CompleteGoogleLoginRequest) ProtoMessage() {} + +func (x *CompleteGoogleLoginRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_auth_proto_msgTypes[10] + 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 CompleteGoogleLoginRequest.ProtoReflect.Descriptor instead. +func (*CompleteGoogleLoginRequest) Descriptor() ([]byte, []int) { + return file_app_v1_auth_proto_rawDescGZIP(), []int{10} +} + +func (x *CompleteGoogleLoginRequest) GetCode() string { + if x != nil { + return x.Code + } + return "" +} + +type CompleteGoogleLoginResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + User *User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CompleteGoogleLoginResponse) Reset() { + *x = CompleteGoogleLoginResponse{} + mi := &file_app_v1_auth_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CompleteGoogleLoginResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CompleteGoogleLoginResponse) ProtoMessage() {} + +func (x *CompleteGoogleLoginResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_auth_proto_msgTypes[11] + 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 CompleteGoogleLoginResponse.ProtoReflect.Descriptor instead. +func (*CompleteGoogleLoginResponse) Descriptor() ([]byte, []int) { + return file_app_v1_auth_proto_rawDescGZIP(), []int{11} +} + +func (x *CompleteGoogleLoginResponse) GetUser() *User { + if x != nil { + return x.User + } + return nil +} + +var File_app_v1_auth_proto protoreflect.FileDescriptor + +const file_app_v1_auth_proto_rawDesc = "" + + "\n" + + "\x11app/v1/auth.proto\x12\rstream.app.v1\x1a\x13app/v1/common.proto\"@\n" + + "\fLoginRequest\x12\x14\n" + + "\x05email\x18\x01 \x01(\tR\x05email\x12\x1a\n" + + "\bpassword\x18\x02 \x01(\tR\bpassword\"8\n" + + "\rLoginResponse\x12'\n" + + "\x04user\x18\x01 \x01(\v2\x13.stream.app.v1.UserR\x04user\"_\n" + + "\x0fRegisterRequest\x12\x1a\n" + + "\busername\x18\x01 \x01(\tR\busername\x12\x14\n" + + "\x05email\x18\x02 \x01(\tR\x05email\x12\x1a\n" + + "\bpassword\x18\x03 \x01(\tR\bpassword\";\n" + + "\x10RegisterResponse\x12'\n" + + "\x04user\x18\x01 \x01(\v2\x13.stream.app.v1.UserR\x04user\"\x0f\n" + + "\rLogoutRequest\"e\n" + + "\x15ChangePasswordRequest\x12)\n" + + "\x10current_password\x18\x01 \x01(\tR\x0fcurrentPassword\x12!\n" + + "\fnew_password\x18\x02 \x01(\tR\vnewPassword\"-\n" + + "\x15ForgotPasswordRequest\x12\x14\n" + + "\x05email\x18\x01 \x01(\tR\x05email\"O\n" + + "\x14ResetPasswordRequest\x12\x14\n" + + "\x05token\x18\x01 \x01(\tR\x05token\x12!\n" + + "\fnew_password\x18\x02 \x01(\tR\vnewPassword\"\x1a\n" + + "\x18GetGoogleLoginUrlRequest\"-\n" + + "\x19GetGoogleLoginUrlResponse\x12\x10\n" + + "\x03url\x18\x01 \x01(\tR\x03url\"0\n" + + "\x1aCompleteGoogleLoginRequest\x12\x12\n" + + "\x04code\x18\x01 \x01(\tR\x04code\"F\n" + + "\x1bCompleteGoogleLoginResponse\x12'\n" + + "\x04user\x18\x01 \x01(\v2\x13.stream.app.v1.UserR\x04user2\xc2\x05\n" + + "\vAuthService\x12B\n" + + "\x05Login\x12\x1b.stream.app.v1.LoginRequest\x1a\x1c.stream.app.v1.LoginResponse\x12K\n" + + "\bRegister\x12\x1e.stream.app.v1.RegisterRequest\x1a\x1f.stream.app.v1.RegisterResponse\x12F\n" + + "\x06Logout\x12\x1c.stream.app.v1.LogoutRequest\x1a\x1e.stream.app.v1.MessageResponse\x12V\n" + + "\x0eChangePassword\x12$.stream.app.v1.ChangePasswordRequest\x1a\x1e.stream.app.v1.MessageResponse\x12V\n" + + "\x0eForgotPassword\x12$.stream.app.v1.ForgotPasswordRequest\x1a\x1e.stream.app.v1.MessageResponse\x12T\n" + + "\rResetPassword\x12#.stream.app.v1.ResetPasswordRequest\x1a\x1e.stream.app.v1.MessageResponse\x12f\n" + + "\x11GetGoogleLoginUrl\x12'.stream.app.v1.GetGoogleLoginUrlRequest\x1a(.stream.app.v1.GetGoogleLoginUrlResponse\x12l\n" + + "\x13CompleteGoogleLogin\x12).stream.app.v1.CompleteGoogleLoginRequest\x1a*.stream.app.v1.CompleteGoogleLoginResponseB,Z*stream.api/internal/gen/proto/app/v1;appv1b\x06proto3" + +var ( + file_app_v1_auth_proto_rawDescOnce sync.Once + file_app_v1_auth_proto_rawDescData []byte +) + +func file_app_v1_auth_proto_rawDescGZIP() []byte { + file_app_v1_auth_proto_rawDescOnce.Do(func() { + file_app_v1_auth_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_v1_auth_proto_rawDesc), len(file_app_v1_auth_proto_rawDesc))) + }) + return file_app_v1_auth_proto_rawDescData +} + +var file_app_v1_auth_proto_msgTypes = make([]protoimpl.MessageInfo, 12) +var file_app_v1_auth_proto_goTypes = []any{ + (*LoginRequest)(nil), // 0: stream.app.v1.LoginRequest + (*LoginResponse)(nil), // 1: stream.app.v1.LoginResponse + (*RegisterRequest)(nil), // 2: stream.app.v1.RegisterRequest + (*RegisterResponse)(nil), // 3: stream.app.v1.RegisterResponse + (*LogoutRequest)(nil), // 4: stream.app.v1.LogoutRequest + (*ChangePasswordRequest)(nil), // 5: stream.app.v1.ChangePasswordRequest + (*ForgotPasswordRequest)(nil), // 6: stream.app.v1.ForgotPasswordRequest + (*ResetPasswordRequest)(nil), // 7: stream.app.v1.ResetPasswordRequest + (*GetGoogleLoginUrlRequest)(nil), // 8: stream.app.v1.GetGoogleLoginUrlRequest + (*GetGoogleLoginUrlResponse)(nil), // 9: stream.app.v1.GetGoogleLoginUrlResponse + (*CompleteGoogleLoginRequest)(nil), // 10: stream.app.v1.CompleteGoogleLoginRequest + (*CompleteGoogleLoginResponse)(nil), // 11: stream.app.v1.CompleteGoogleLoginResponse + (*User)(nil), // 12: stream.app.v1.User + (*MessageResponse)(nil), // 13: stream.app.v1.MessageResponse +} +var file_app_v1_auth_proto_depIdxs = []int32{ + 12, // 0: stream.app.v1.LoginResponse.user:type_name -> stream.app.v1.User + 12, // 1: stream.app.v1.RegisterResponse.user:type_name -> stream.app.v1.User + 12, // 2: stream.app.v1.CompleteGoogleLoginResponse.user:type_name -> stream.app.v1.User + 0, // 3: stream.app.v1.AuthService.Login:input_type -> stream.app.v1.LoginRequest + 2, // 4: stream.app.v1.AuthService.Register:input_type -> stream.app.v1.RegisterRequest + 4, // 5: stream.app.v1.AuthService.Logout:input_type -> stream.app.v1.LogoutRequest + 5, // 6: stream.app.v1.AuthService.ChangePassword:input_type -> stream.app.v1.ChangePasswordRequest + 6, // 7: stream.app.v1.AuthService.ForgotPassword:input_type -> stream.app.v1.ForgotPasswordRequest + 7, // 8: stream.app.v1.AuthService.ResetPassword:input_type -> stream.app.v1.ResetPasswordRequest + 8, // 9: stream.app.v1.AuthService.GetGoogleLoginUrl:input_type -> stream.app.v1.GetGoogleLoginUrlRequest + 10, // 10: stream.app.v1.AuthService.CompleteGoogleLogin:input_type -> stream.app.v1.CompleteGoogleLoginRequest + 1, // 11: stream.app.v1.AuthService.Login:output_type -> stream.app.v1.LoginResponse + 3, // 12: stream.app.v1.AuthService.Register:output_type -> stream.app.v1.RegisterResponse + 13, // 13: stream.app.v1.AuthService.Logout:output_type -> stream.app.v1.MessageResponse + 13, // 14: stream.app.v1.AuthService.ChangePassword:output_type -> stream.app.v1.MessageResponse + 13, // 15: stream.app.v1.AuthService.ForgotPassword:output_type -> stream.app.v1.MessageResponse + 13, // 16: stream.app.v1.AuthService.ResetPassword:output_type -> stream.app.v1.MessageResponse + 9, // 17: stream.app.v1.AuthService.GetGoogleLoginUrl:output_type -> stream.app.v1.GetGoogleLoginUrlResponse + 11, // 18: stream.app.v1.AuthService.CompleteGoogleLogin:output_type -> stream.app.v1.CompleteGoogleLoginResponse + 11, // [11:19] is the sub-list for method output_type + 3, // [3:11] is the sub-list for method input_type + 3, // [3:3] is the sub-list for extension type_name + 3, // [3:3] is the sub-list for extension extendee + 0, // [0:3] is the sub-list for field type_name +} + +func init() { file_app_v1_auth_proto_init() } +func file_app_v1_auth_proto_init() { + if File_app_v1_auth_proto != nil { + return + } + file_app_v1_common_proto_init() + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_v1_auth_proto_rawDesc), len(file_app_v1_auth_proto_rawDesc)), + NumEnums: 0, + NumMessages: 12, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_app_v1_auth_proto_goTypes, + DependencyIndexes: file_app_v1_auth_proto_depIdxs, + MessageInfos: file_app_v1_auth_proto_msgTypes, + }.Build() + File_app_v1_auth_proto = out.File + file_app_v1_auth_proto_goTypes = nil + file_app_v1_auth_proto_depIdxs = nil +} diff --git a/internal/gen/proto/app/v1/auth_grpc.pb.go b/internal/gen/proto/app/v1/auth_grpc.pb.go new file mode 100644 index 0000000..cd0f1e0 --- /dev/null +++ b/internal/gen/proto/app/v1/auth_grpc.pb.go @@ -0,0 +1,387 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.6.1 +// - protoc (unknown) +// source: app/v1/auth.proto + +package appv1 + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + AuthService_Login_FullMethodName = "/stream.app.v1.AuthService/Login" + AuthService_Register_FullMethodName = "/stream.app.v1.AuthService/Register" + AuthService_Logout_FullMethodName = "/stream.app.v1.AuthService/Logout" + AuthService_ChangePassword_FullMethodName = "/stream.app.v1.AuthService/ChangePassword" + AuthService_ForgotPassword_FullMethodName = "/stream.app.v1.AuthService/ForgotPassword" + AuthService_ResetPassword_FullMethodName = "/stream.app.v1.AuthService/ResetPassword" + AuthService_GetGoogleLoginUrl_FullMethodName = "/stream.app.v1.AuthService/GetGoogleLoginUrl" + AuthService_CompleteGoogleLogin_FullMethodName = "/stream.app.v1.AuthService/CompleteGoogleLogin" +) + +// AuthServiceClient is the client API for AuthService 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 AuthServiceClient interface { + Login(ctx context.Context, in *LoginRequest, opts ...grpc.CallOption) (*LoginResponse, error) + Register(ctx context.Context, in *RegisterRequest, opts ...grpc.CallOption) (*RegisterResponse, error) + Logout(ctx context.Context, in *LogoutRequest, opts ...grpc.CallOption) (*MessageResponse, error) + ChangePassword(ctx context.Context, in *ChangePasswordRequest, opts ...grpc.CallOption) (*MessageResponse, error) + ForgotPassword(ctx context.Context, in *ForgotPasswordRequest, opts ...grpc.CallOption) (*MessageResponse, error) + ResetPassword(ctx context.Context, in *ResetPasswordRequest, opts ...grpc.CallOption) (*MessageResponse, error) + GetGoogleLoginUrl(ctx context.Context, in *GetGoogleLoginUrlRequest, opts ...grpc.CallOption) (*GetGoogleLoginUrlResponse, error) + CompleteGoogleLogin(ctx context.Context, in *CompleteGoogleLoginRequest, opts ...grpc.CallOption) (*CompleteGoogleLoginResponse, error) +} + +type authServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewAuthServiceClient(cc grpc.ClientConnInterface) AuthServiceClient { + return &authServiceClient{cc} +} + +func (c *authServiceClient) Login(ctx context.Context, in *LoginRequest, opts ...grpc.CallOption) (*LoginResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(LoginResponse) + err := c.cc.Invoke(ctx, AuthService_Login_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *authServiceClient) Register(ctx context.Context, in *RegisterRequest, opts ...grpc.CallOption) (*RegisterResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(RegisterResponse) + err := c.cc.Invoke(ctx, AuthService_Register_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *authServiceClient) Logout(ctx context.Context, in *LogoutRequest, opts ...grpc.CallOption) (*MessageResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(MessageResponse) + err := c.cc.Invoke(ctx, AuthService_Logout_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *authServiceClient) ChangePassword(ctx context.Context, in *ChangePasswordRequest, opts ...grpc.CallOption) (*MessageResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(MessageResponse) + err := c.cc.Invoke(ctx, AuthService_ChangePassword_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *authServiceClient) ForgotPassword(ctx context.Context, in *ForgotPasswordRequest, opts ...grpc.CallOption) (*MessageResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(MessageResponse) + err := c.cc.Invoke(ctx, AuthService_ForgotPassword_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *authServiceClient) ResetPassword(ctx context.Context, in *ResetPasswordRequest, opts ...grpc.CallOption) (*MessageResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(MessageResponse) + err := c.cc.Invoke(ctx, AuthService_ResetPassword_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *authServiceClient) GetGoogleLoginUrl(ctx context.Context, in *GetGoogleLoginUrlRequest, opts ...grpc.CallOption) (*GetGoogleLoginUrlResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetGoogleLoginUrlResponse) + err := c.cc.Invoke(ctx, AuthService_GetGoogleLoginUrl_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *authServiceClient) CompleteGoogleLogin(ctx context.Context, in *CompleteGoogleLoginRequest, opts ...grpc.CallOption) (*CompleteGoogleLoginResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(CompleteGoogleLoginResponse) + err := c.cc.Invoke(ctx, AuthService_CompleteGoogleLogin_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// AuthServiceServer is the server API for AuthService service. +// All implementations must embed UnimplementedAuthServiceServer +// for forward compatibility. +type AuthServiceServer interface { + Login(context.Context, *LoginRequest) (*LoginResponse, error) + Register(context.Context, *RegisterRequest) (*RegisterResponse, error) + Logout(context.Context, *LogoutRequest) (*MessageResponse, error) + ChangePassword(context.Context, *ChangePasswordRequest) (*MessageResponse, error) + ForgotPassword(context.Context, *ForgotPasswordRequest) (*MessageResponse, error) + ResetPassword(context.Context, *ResetPasswordRequest) (*MessageResponse, error) + GetGoogleLoginUrl(context.Context, *GetGoogleLoginUrlRequest) (*GetGoogleLoginUrlResponse, error) + CompleteGoogleLogin(context.Context, *CompleteGoogleLoginRequest) (*CompleteGoogleLoginResponse, error) + mustEmbedUnimplementedAuthServiceServer() +} + +// UnimplementedAuthServiceServer 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 UnimplementedAuthServiceServer struct{} + +func (UnimplementedAuthServiceServer) Login(context.Context, *LoginRequest) (*LoginResponse, error) { + return nil, status.Error(codes.Unimplemented, "method Login not implemented") +} +func (UnimplementedAuthServiceServer) Register(context.Context, *RegisterRequest) (*RegisterResponse, error) { + return nil, status.Error(codes.Unimplemented, "method Register not implemented") +} +func (UnimplementedAuthServiceServer) Logout(context.Context, *LogoutRequest) (*MessageResponse, error) { + return nil, status.Error(codes.Unimplemented, "method Logout not implemented") +} +func (UnimplementedAuthServiceServer) ChangePassword(context.Context, *ChangePasswordRequest) (*MessageResponse, error) { + return nil, status.Error(codes.Unimplemented, "method ChangePassword not implemented") +} +func (UnimplementedAuthServiceServer) ForgotPassword(context.Context, *ForgotPasswordRequest) (*MessageResponse, error) { + return nil, status.Error(codes.Unimplemented, "method ForgotPassword not implemented") +} +func (UnimplementedAuthServiceServer) ResetPassword(context.Context, *ResetPasswordRequest) (*MessageResponse, error) { + return nil, status.Error(codes.Unimplemented, "method ResetPassword not implemented") +} +func (UnimplementedAuthServiceServer) GetGoogleLoginUrl(context.Context, *GetGoogleLoginUrlRequest) (*GetGoogleLoginUrlResponse, error) { + return nil, status.Error(codes.Unimplemented, "method GetGoogleLoginUrl not implemented") +} +func (UnimplementedAuthServiceServer) CompleteGoogleLogin(context.Context, *CompleteGoogleLoginRequest) (*CompleteGoogleLoginResponse, error) { + return nil, status.Error(codes.Unimplemented, "method CompleteGoogleLogin not implemented") +} +func (UnimplementedAuthServiceServer) mustEmbedUnimplementedAuthServiceServer() {} +func (UnimplementedAuthServiceServer) testEmbeddedByValue() {} + +// UnsafeAuthServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to AuthServiceServer will +// result in compilation errors. +type UnsafeAuthServiceServer interface { + mustEmbedUnimplementedAuthServiceServer() +} + +func RegisterAuthServiceServer(s grpc.ServiceRegistrar, srv AuthServiceServer) { + // If the following call panics, it indicates UnimplementedAuthServiceServer 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(&AuthService_ServiceDesc, srv) +} + +func _AuthService_Login_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(LoginRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AuthServiceServer).Login(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AuthService_Login_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AuthServiceServer).Login(ctx, req.(*LoginRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AuthService_Register_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RegisterRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AuthServiceServer).Register(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AuthService_Register_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AuthServiceServer).Register(ctx, req.(*RegisterRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AuthService_Logout_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(LogoutRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AuthServiceServer).Logout(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AuthService_Logout_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AuthServiceServer).Logout(ctx, req.(*LogoutRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AuthService_ChangePassword_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ChangePasswordRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AuthServiceServer).ChangePassword(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AuthService_ChangePassword_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AuthServiceServer).ChangePassword(ctx, req.(*ChangePasswordRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AuthService_ForgotPassword_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ForgotPasswordRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AuthServiceServer).ForgotPassword(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AuthService_ForgotPassword_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AuthServiceServer).ForgotPassword(ctx, req.(*ForgotPasswordRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AuthService_ResetPassword_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ResetPasswordRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AuthServiceServer).ResetPassword(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AuthService_ResetPassword_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AuthServiceServer).ResetPassword(ctx, req.(*ResetPasswordRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AuthService_GetGoogleLoginUrl_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetGoogleLoginUrlRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AuthServiceServer).GetGoogleLoginUrl(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AuthService_GetGoogleLoginUrl_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AuthServiceServer).GetGoogleLoginUrl(ctx, req.(*GetGoogleLoginUrlRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AuthService_CompleteGoogleLogin_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CompleteGoogleLoginRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AuthServiceServer).CompleteGoogleLogin(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AuthService_CompleteGoogleLogin_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AuthServiceServer).CompleteGoogleLogin(ctx, req.(*CompleteGoogleLoginRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// AuthService_ServiceDesc is the grpc.ServiceDesc for AuthService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var AuthService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "stream.app.v1.AuthService", + HandlerType: (*AuthServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Login", + Handler: _AuthService_Login_Handler, + }, + { + MethodName: "Register", + Handler: _AuthService_Register_Handler, + }, + { + MethodName: "Logout", + Handler: _AuthService_Logout_Handler, + }, + { + MethodName: "ChangePassword", + Handler: _AuthService_ChangePassword_Handler, + }, + { + MethodName: "ForgotPassword", + Handler: _AuthService_ForgotPassword_Handler, + }, + { + MethodName: "ResetPassword", + Handler: _AuthService_ResetPassword_Handler, + }, + { + MethodName: "GetGoogleLoginUrl", + Handler: _AuthService_GetGoogleLoginUrl_Handler, + }, + { + MethodName: "CompleteGoogleLogin", + Handler: _AuthService_CompleteGoogleLogin_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "app/v1/auth.proto", +} diff --git a/internal/gen/proto/app/v1/catalog.pb.go b/internal/gen/proto/app/v1/catalog.pb.go new file mode 100644 index 0000000..cb804d5 --- /dev/null +++ b/internal/gen/proto/app/v1/catalog.pb.go @@ -0,0 +1,881 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc (unknown) +// source: app/v1/catalog.proto + +package appv1 + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type ListDomainsRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListDomainsRequest) Reset() { + *x = ListDomainsRequest{} + mi := &file_app_v1_catalog_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListDomainsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListDomainsRequest) ProtoMessage() {} + +func (x *ListDomainsRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_catalog_proto_msgTypes[0] + 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 ListDomainsRequest.ProtoReflect.Descriptor instead. +func (*ListDomainsRequest) Descriptor() ([]byte, []int) { + return file_app_v1_catalog_proto_rawDescGZIP(), []int{0} +} + +type ListDomainsResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Domains []*Domain `protobuf:"bytes,1,rep,name=domains,proto3" json:"domains,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListDomainsResponse) Reset() { + *x = ListDomainsResponse{} + mi := &file_app_v1_catalog_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListDomainsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListDomainsResponse) ProtoMessage() {} + +func (x *ListDomainsResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_catalog_proto_msgTypes[1] + 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 ListDomainsResponse.ProtoReflect.Descriptor instead. +func (*ListDomainsResponse) Descriptor() ([]byte, []int) { + return file_app_v1_catalog_proto_rawDescGZIP(), []int{1} +} + +func (x *ListDomainsResponse) GetDomains() []*Domain { + if x != nil { + return x.Domains + } + return nil +} + +type CreateDomainRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateDomainRequest) Reset() { + *x = CreateDomainRequest{} + mi := &file_app_v1_catalog_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateDomainRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateDomainRequest) ProtoMessage() {} + +func (x *CreateDomainRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_catalog_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateDomainRequest.ProtoReflect.Descriptor instead. +func (*CreateDomainRequest) Descriptor() ([]byte, []int) { + return file_app_v1_catalog_proto_rawDescGZIP(), []int{2} +} + +func (x *CreateDomainRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type CreateDomainResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Domain *Domain `protobuf:"bytes,1,opt,name=domain,proto3" json:"domain,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateDomainResponse) Reset() { + *x = CreateDomainResponse{} + mi := &file_app_v1_catalog_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateDomainResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateDomainResponse) ProtoMessage() {} + +func (x *CreateDomainResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_catalog_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateDomainResponse.ProtoReflect.Descriptor instead. +func (*CreateDomainResponse) Descriptor() ([]byte, []int) { + return file_app_v1_catalog_proto_rawDescGZIP(), []int{3} +} + +func (x *CreateDomainResponse) GetDomain() *Domain { + if x != nil { + return x.Domain + } + return nil +} + +type DeleteDomainRequest 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 *DeleteDomainRequest) Reset() { + *x = DeleteDomainRequest{} + mi := &file_app_v1_catalog_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeleteDomainRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteDomainRequest) ProtoMessage() {} + +func (x *DeleteDomainRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_catalog_proto_msgTypes[4] + 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 DeleteDomainRequest.ProtoReflect.Descriptor instead. +func (*DeleteDomainRequest) Descriptor() ([]byte, []int) { + return file_app_v1_catalog_proto_rawDescGZIP(), []int{4} +} + +func (x *DeleteDomainRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type ListAdTemplatesRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListAdTemplatesRequest) Reset() { + *x = ListAdTemplatesRequest{} + mi := &file_app_v1_catalog_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListAdTemplatesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListAdTemplatesRequest) ProtoMessage() {} + +func (x *ListAdTemplatesRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_catalog_proto_msgTypes[5] + 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 ListAdTemplatesRequest.ProtoReflect.Descriptor instead. +func (*ListAdTemplatesRequest) Descriptor() ([]byte, []int) { + return file_app_v1_catalog_proto_rawDescGZIP(), []int{5} +} + +type ListAdTemplatesResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Templates []*AdTemplate `protobuf:"bytes,1,rep,name=templates,proto3" json:"templates,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListAdTemplatesResponse) Reset() { + *x = ListAdTemplatesResponse{} + mi := &file_app_v1_catalog_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListAdTemplatesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListAdTemplatesResponse) ProtoMessage() {} + +func (x *ListAdTemplatesResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_catalog_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 ListAdTemplatesResponse.ProtoReflect.Descriptor instead. +func (*ListAdTemplatesResponse) Descriptor() ([]byte, []int) { + return file_app_v1_catalog_proto_rawDescGZIP(), []int{6} +} + +func (x *ListAdTemplatesResponse) GetTemplates() []*AdTemplate { + if x != nil { + return x.Templates + } + return nil +} + +type CreateAdTemplateRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Description *string `protobuf:"bytes,2,opt,name=description,proto3,oneof" json:"description,omitempty"` + VastTagUrl string `protobuf:"bytes,3,opt,name=vast_tag_url,json=vastTagUrl,proto3" json:"vast_tag_url,omitempty"` + AdFormat *string `protobuf:"bytes,4,opt,name=ad_format,json=adFormat,proto3,oneof" json:"ad_format,omitempty"` + Duration *int32 `protobuf:"varint,5,opt,name=duration,proto3,oneof" json:"duration,omitempty"` + IsActive *bool `protobuf:"varint,6,opt,name=is_active,json=isActive,proto3,oneof" json:"is_active,omitempty"` + IsDefault *bool `protobuf:"varint,7,opt,name=is_default,json=isDefault,proto3,oneof" json:"is_default,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateAdTemplateRequest) Reset() { + *x = CreateAdTemplateRequest{} + mi := &file_app_v1_catalog_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateAdTemplateRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateAdTemplateRequest) ProtoMessage() {} + +func (x *CreateAdTemplateRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_catalog_proto_msgTypes[7] + 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 CreateAdTemplateRequest.ProtoReflect.Descriptor instead. +func (*CreateAdTemplateRequest) Descriptor() ([]byte, []int) { + return file_app_v1_catalog_proto_rawDescGZIP(), []int{7} +} + +func (x *CreateAdTemplateRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *CreateAdTemplateRequest) GetDescription() string { + if x != nil && x.Description != nil { + return *x.Description + } + return "" +} + +func (x *CreateAdTemplateRequest) GetVastTagUrl() string { + if x != nil { + return x.VastTagUrl + } + return "" +} + +func (x *CreateAdTemplateRequest) GetAdFormat() string { + if x != nil && x.AdFormat != nil { + return *x.AdFormat + } + return "" +} + +func (x *CreateAdTemplateRequest) GetDuration() int32 { + if x != nil && x.Duration != nil { + return *x.Duration + } + return 0 +} + +func (x *CreateAdTemplateRequest) GetIsActive() bool { + if x != nil && x.IsActive != nil { + return *x.IsActive + } + return false +} + +func (x *CreateAdTemplateRequest) GetIsDefault() bool { + if x != nil && x.IsDefault != nil { + return *x.IsDefault + } + return false +} + +type CreateAdTemplateResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Template *AdTemplate `protobuf:"bytes,1,opt,name=template,proto3" json:"template,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateAdTemplateResponse) Reset() { + *x = CreateAdTemplateResponse{} + mi := &file_app_v1_catalog_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateAdTemplateResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateAdTemplateResponse) ProtoMessage() {} + +func (x *CreateAdTemplateResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_catalog_proto_msgTypes[8] + 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 CreateAdTemplateResponse.ProtoReflect.Descriptor instead. +func (*CreateAdTemplateResponse) Descriptor() ([]byte, []int) { + return file_app_v1_catalog_proto_rawDescGZIP(), []int{8} +} + +func (x *CreateAdTemplateResponse) GetTemplate() *AdTemplate { + if x != nil { + return x.Template + } + return nil +} + +type UpdateAdTemplateRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Description *string `protobuf:"bytes,3,opt,name=description,proto3,oneof" json:"description,omitempty"` + VastTagUrl string `protobuf:"bytes,4,opt,name=vast_tag_url,json=vastTagUrl,proto3" json:"vast_tag_url,omitempty"` + AdFormat *string `protobuf:"bytes,5,opt,name=ad_format,json=adFormat,proto3,oneof" json:"ad_format,omitempty"` + Duration *int32 `protobuf:"varint,6,opt,name=duration,proto3,oneof" json:"duration,omitempty"` + IsActive *bool `protobuf:"varint,7,opt,name=is_active,json=isActive,proto3,oneof" json:"is_active,omitempty"` + IsDefault *bool `protobuf:"varint,8,opt,name=is_default,json=isDefault,proto3,oneof" json:"is_default,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UpdateAdTemplateRequest) Reset() { + *x = UpdateAdTemplateRequest{} + mi := &file_app_v1_catalog_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdateAdTemplateRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateAdTemplateRequest) ProtoMessage() {} + +func (x *UpdateAdTemplateRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_catalog_proto_msgTypes[9] + 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 UpdateAdTemplateRequest.ProtoReflect.Descriptor instead. +func (*UpdateAdTemplateRequest) Descriptor() ([]byte, []int) { + return file_app_v1_catalog_proto_rawDescGZIP(), []int{9} +} + +func (x *UpdateAdTemplateRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *UpdateAdTemplateRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *UpdateAdTemplateRequest) GetDescription() string { + if x != nil && x.Description != nil { + return *x.Description + } + return "" +} + +func (x *UpdateAdTemplateRequest) GetVastTagUrl() string { + if x != nil { + return x.VastTagUrl + } + return "" +} + +func (x *UpdateAdTemplateRequest) GetAdFormat() string { + if x != nil && x.AdFormat != nil { + return *x.AdFormat + } + return "" +} + +func (x *UpdateAdTemplateRequest) GetDuration() int32 { + if x != nil && x.Duration != nil { + return *x.Duration + } + return 0 +} + +func (x *UpdateAdTemplateRequest) GetIsActive() bool { + if x != nil && x.IsActive != nil { + return *x.IsActive + } + return false +} + +func (x *UpdateAdTemplateRequest) GetIsDefault() bool { + if x != nil && x.IsDefault != nil { + return *x.IsDefault + } + return false +} + +type UpdateAdTemplateResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Template *AdTemplate `protobuf:"bytes,1,opt,name=template,proto3" json:"template,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UpdateAdTemplateResponse) Reset() { + *x = UpdateAdTemplateResponse{} + mi := &file_app_v1_catalog_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdateAdTemplateResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateAdTemplateResponse) ProtoMessage() {} + +func (x *UpdateAdTemplateResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_catalog_proto_msgTypes[10] + 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 UpdateAdTemplateResponse.ProtoReflect.Descriptor instead. +func (*UpdateAdTemplateResponse) Descriptor() ([]byte, []int) { + return file_app_v1_catalog_proto_rawDescGZIP(), []int{10} +} + +func (x *UpdateAdTemplateResponse) GetTemplate() *AdTemplate { + if x != nil { + return x.Template + } + return nil +} + +type DeleteAdTemplateRequest 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 *DeleteAdTemplateRequest) Reset() { + *x = DeleteAdTemplateRequest{} + mi := &file_app_v1_catalog_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeleteAdTemplateRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteAdTemplateRequest) ProtoMessage() {} + +func (x *DeleteAdTemplateRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_catalog_proto_msgTypes[11] + 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 DeleteAdTemplateRequest.ProtoReflect.Descriptor instead. +func (*DeleteAdTemplateRequest) Descriptor() ([]byte, []int) { + return file_app_v1_catalog_proto_rawDescGZIP(), []int{11} +} + +func (x *DeleteAdTemplateRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type ListPlansRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListPlansRequest) Reset() { + *x = ListPlansRequest{} + mi := &file_app_v1_catalog_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListPlansRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListPlansRequest) ProtoMessage() {} + +func (x *ListPlansRequest) 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 ListPlansRequest.ProtoReflect.Descriptor instead. +func (*ListPlansRequest) Descriptor() ([]byte, []int) { + return file_app_v1_catalog_proto_rawDescGZIP(), []int{12} +} + +type ListPlansResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Plans []*Plan `protobuf:"bytes,1,rep,name=plans,proto3" json:"plans,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListPlansResponse) Reset() { + *x = ListPlansResponse{} + mi := &file_app_v1_catalog_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListPlansResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListPlansResponse) ProtoMessage() {} + +func (x *ListPlansResponse) 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 ListPlansResponse.ProtoReflect.Descriptor instead. +func (*ListPlansResponse) Descriptor() ([]byte, []int) { + return file_app_v1_catalog_proto_rawDescGZIP(), []int{13} +} + +func (x *ListPlansResponse) GetPlans() []*Plan { + if x != nil { + return x.Plans + } + return nil +} + +var File_app_v1_catalog_proto protoreflect.FileDescriptor + +const file_app_v1_catalog_proto_rawDesc = "" + + "\n" + + "\x14app/v1/catalog.proto\x12\rstream.app.v1\x1a\x13app/v1/common.proto\"\x14\n" + + "\x12ListDomainsRequest\"F\n" + + "\x13ListDomainsResponse\x12/\n" + + "\adomains\x18\x01 \x03(\v2\x15.stream.app.v1.DomainR\adomains\")\n" + + "\x13CreateDomainRequest\x12\x12\n" + + "\x04name\x18\x01 \x01(\tR\x04name\"E\n" + + "\x14CreateDomainResponse\x12-\n" + + "\x06domain\x18\x01 \x01(\v2\x15.stream.app.v1.DomainR\x06domain\"%\n" + + "\x13DeleteDomainRequest\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\"\x18\n" + + "\x16ListAdTemplatesRequest\"R\n" + + "\x17ListAdTemplatesResponse\x127\n" + + "\ttemplates\x18\x01 \x03(\v2\x19.stream.app.v1.AdTemplateR\ttemplates\"\xc7\x02\n" + + "\x17CreateAdTemplateRequest\x12\x12\n" + + "\x04name\x18\x01 \x01(\tR\x04name\x12%\n" + + "\vdescription\x18\x02 \x01(\tH\x00R\vdescription\x88\x01\x01\x12 \n" + + "\fvast_tag_url\x18\x03 \x01(\tR\n" + + "vastTagUrl\x12 \n" + + "\tad_format\x18\x04 \x01(\tH\x01R\badFormat\x88\x01\x01\x12\x1f\n" + + "\bduration\x18\x05 \x01(\x05H\x02R\bduration\x88\x01\x01\x12 \n" + + "\tis_active\x18\x06 \x01(\bH\x03R\bisActive\x88\x01\x01\x12\"\n" + + "\n" + + "is_default\x18\a \x01(\bH\x04R\tisDefault\x88\x01\x01B\x0e\n" + + "\f_descriptionB\f\n" + + "\n" + + "_ad_formatB\v\n" + + "\t_durationB\f\n" + + "\n" + + "_is_activeB\r\n" + + "\v_is_default\"Q\n" + + "\x18CreateAdTemplateResponse\x125\n" + + "\btemplate\x18\x01 \x01(\v2\x19.stream.app.v1.AdTemplateR\btemplate\"\xd7\x02\n" + + "\x17UpdateAdTemplateRequest\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n" + + "\x04name\x18\x02 \x01(\tR\x04name\x12%\n" + + "\vdescription\x18\x03 \x01(\tH\x00R\vdescription\x88\x01\x01\x12 \n" + + "\fvast_tag_url\x18\x04 \x01(\tR\n" + + "vastTagUrl\x12 \n" + + "\tad_format\x18\x05 \x01(\tH\x01R\badFormat\x88\x01\x01\x12\x1f\n" + + "\bduration\x18\x06 \x01(\x05H\x02R\bduration\x88\x01\x01\x12 \n" + + "\tis_active\x18\a \x01(\bH\x03R\bisActive\x88\x01\x01\x12\"\n" + + "\n" + + "is_default\x18\b \x01(\bH\x04R\tisDefault\x88\x01\x01B\x0e\n" + + "\f_descriptionB\f\n" + + "\n" + + "_ad_formatB\v\n" + + "\t_durationB\f\n" + + "\n" + + "_is_activeB\r\n" + + "\v_is_default\"Q\n" + + "\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\"\x12\n" + + "\x10ListPlansRequest\">\n" + + "\x11ListPlansResponse\x12)\n" + + "\x05plans\x18\x01 \x03(\v2\x13.stream.app.v1.PlanR\x05plans2\x93\x02\n" + + "\x0eDomainsService\x12T\n" + + "\vListDomains\x12!.stream.app.v1.ListDomainsRequest\x1a\".stream.app.v1.ListDomainsResponse\x12W\n" + + "\fCreateDomain\x12\".stream.app.v1.CreateDomainRequest\x1a#.stream.app.v1.CreateDomainResponse\x12R\n" + + "\fDeleteDomain\x12\".stream.app.v1.DeleteDomainRequest\x1a\x1e.stream.app.v1.MessageResponse2\x9c\x03\n" + + "\x12AdTemplatesService\x12`\n" + + "\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^\n" + + "\fPlansService\x12N\n" + + "\tListPlans\x12\x1f.stream.app.v1.ListPlansRequest\x1a .stream.app.v1.ListPlansResponseB,Z*stream.api/internal/gen/proto/app/v1;appv1b\x06proto3" + +var ( + file_app_v1_catalog_proto_rawDescOnce sync.Once + file_app_v1_catalog_proto_rawDescData []byte +) + +func file_app_v1_catalog_proto_rawDescGZIP() []byte { + file_app_v1_catalog_proto_rawDescOnce.Do(func() { + file_app_v1_catalog_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_v1_catalog_proto_rawDesc), len(file_app_v1_catalog_proto_rawDesc))) + }) + return file_app_v1_catalog_proto_rawDescData +} + +var file_app_v1_catalog_proto_msgTypes = make([]protoimpl.MessageInfo, 14) +var file_app_v1_catalog_proto_goTypes = []any{ + (*ListDomainsRequest)(nil), // 0: stream.app.v1.ListDomainsRequest + (*ListDomainsResponse)(nil), // 1: stream.app.v1.ListDomainsResponse + (*CreateDomainRequest)(nil), // 2: stream.app.v1.CreateDomainRequest + (*CreateDomainResponse)(nil), // 3: stream.app.v1.CreateDomainResponse + (*DeleteDomainRequest)(nil), // 4: stream.app.v1.DeleteDomainRequest + (*ListAdTemplatesRequest)(nil), // 5: stream.app.v1.ListAdTemplatesRequest + (*ListAdTemplatesResponse)(nil), // 6: stream.app.v1.ListAdTemplatesResponse + (*CreateAdTemplateRequest)(nil), // 7: stream.app.v1.CreateAdTemplateRequest + (*CreateAdTemplateResponse)(nil), // 8: stream.app.v1.CreateAdTemplateResponse + (*UpdateAdTemplateRequest)(nil), // 9: stream.app.v1.UpdateAdTemplateRequest + (*UpdateAdTemplateResponse)(nil), // 10: stream.app.v1.UpdateAdTemplateResponse + (*DeleteAdTemplateRequest)(nil), // 11: stream.app.v1.DeleteAdTemplateRequest + (*ListPlansRequest)(nil), // 12: stream.app.v1.ListPlansRequest + (*ListPlansResponse)(nil), // 13: stream.app.v1.ListPlansResponse + (*Domain)(nil), // 14: stream.app.v1.Domain + (*AdTemplate)(nil), // 15: stream.app.v1.AdTemplate + (*Plan)(nil), // 16: stream.app.v1.Plan + (*MessageResponse)(nil), // 17: stream.app.v1.MessageResponse +} +var file_app_v1_catalog_proto_depIdxs = []int32{ + 14, // 0: stream.app.v1.ListDomainsResponse.domains:type_name -> stream.app.v1.Domain + 14, // 1: stream.app.v1.CreateDomainResponse.domain:type_name -> stream.app.v1.Domain + 15, // 2: stream.app.v1.ListAdTemplatesResponse.templates:type_name -> stream.app.v1.AdTemplate + 15, // 3: stream.app.v1.CreateAdTemplateResponse.template:type_name -> stream.app.v1.AdTemplate + 15, // 4: stream.app.v1.UpdateAdTemplateResponse.template:type_name -> stream.app.v1.AdTemplate + 16, // 5: stream.app.v1.ListPlansResponse.plans:type_name -> stream.app.v1.Plan + 0, // 6: stream.app.v1.DomainsService.ListDomains:input_type -> stream.app.v1.ListDomainsRequest + 2, // 7: stream.app.v1.DomainsService.CreateDomain:input_type -> stream.app.v1.CreateDomainRequest + 4, // 8: stream.app.v1.DomainsService.DeleteDomain:input_type -> stream.app.v1.DeleteDomainRequest + 5, // 9: stream.app.v1.AdTemplatesService.ListAdTemplates:input_type -> stream.app.v1.ListAdTemplatesRequest + 7, // 10: stream.app.v1.AdTemplatesService.CreateAdTemplate:input_type -> stream.app.v1.CreateAdTemplateRequest + 9, // 11: stream.app.v1.AdTemplatesService.UpdateAdTemplate:input_type -> stream.app.v1.UpdateAdTemplateRequest + 11, // 12: stream.app.v1.AdTemplatesService.DeleteAdTemplate:input_type -> stream.app.v1.DeleteAdTemplateRequest + 12, // 13: stream.app.v1.PlansService.ListPlans:input_type -> stream.app.v1.ListPlansRequest + 1, // 14: stream.app.v1.DomainsService.ListDomains:output_type -> stream.app.v1.ListDomainsResponse + 3, // 15: stream.app.v1.DomainsService.CreateDomain:output_type -> stream.app.v1.CreateDomainResponse + 17, // 16: stream.app.v1.DomainsService.DeleteDomain:output_type -> stream.app.v1.MessageResponse + 6, // 17: stream.app.v1.AdTemplatesService.ListAdTemplates:output_type -> stream.app.v1.ListAdTemplatesResponse + 8, // 18: stream.app.v1.AdTemplatesService.CreateAdTemplate:output_type -> stream.app.v1.CreateAdTemplateResponse + 10, // 19: stream.app.v1.AdTemplatesService.UpdateAdTemplate:output_type -> stream.app.v1.UpdateAdTemplateResponse + 17, // 20: stream.app.v1.AdTemplatesService.DeleteAdTemplate:output_type -> stream.app.v1.MessageResponse + 13, // 21: stream.app.v1.PlansService.ListPlans:output_type -> stream.app.v1.ListPlansResponse + 14, // [14:22] is the sub-list for method output_type + 6, // [6:14] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name +} + +func init() { file_app_v1_catalog_proto_init() } +func file_app_v1_catalog_proto_init() { + if File_app_v1_catalog_proto != nil { + return + } + file_app_v1_common_proto_init() + file_app_v1_catalog_proto_msgTypes[7].OneofWrappers = []any{} + file_app_v1_catalog_proto_msgTypes[9].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: 14, + NumExtensions: 0, + NumServices: 3, + }, + GoTypes: file_app_v1_catalog_proto_goTypes, + DependencyIndexes: file_app_v1_catalog_proto_depIdxs, + MessageInfos: file_app_v1_catalog_proto_msgTypes, + }.Build() + File_app_v1_catalog_proto = out.File + file_app_v1_catalog_proto_goTypes = nil + file_app_v1_catalog_proto_depIdxs = nil +} diff --git a/internal/gen/proto/app/v1/catalog_grpc.pb.go b/internal/gen/proto/app/v1/catalog_grpc.pb.go new file mode 100644 index 0000000..53a235f --- /dev/null +++ b/internal/gen/proto/app/v1/catalog_grpc.pb.go @@ -0,0 +1,515 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.6.1 +// - protoc (unknown) +// source: app/v1/catalog.proto + +package appv1 + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + DomainsService_ListDomains_FullMethodName = "/stream.app.v1.DomainsService/ListDomains" + DomainsService_CreateDomain_FullMethodName = "/stream.app.v1.DomainsService/CreateDomain" + DomainsService_DeleteDomain_FullMethodName = "/stream.app.v1.DomainsService/DeleteDomain" +) + +// DomainsServiceClient is the client API for DomainsService 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 DomainsServiceClient interface { + ListDomains(ctx context.Context, in *ListDomainsRequest, opts ...grpc.CallOption) (*ListDomainsResponse, error) + CreateDomain(ctx context.Context, in *CreateDomainRequest, opts ...grpc.CallOption) (*CreateDomainResponse, error) + DeleteDomain(ctx context.Context, in *DeleteDomainRequest, opts ...grpc.CallOption) (*MessageResponse, error) +} + +type domainsServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewDomainsServiceClient(cc grpc.ClientConnInterface) DomainsServiceClient { + return &domainsServiceClient{cc} +} + +func (c *domainsServiceClient) ListDomains(ctx context.Context, in *ListDomainsRequest, opts ...grpc.CallOption) (*ListDomainsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(ListDomainsResponse) + err := c.cc.Invoke(ctx, DomainsService_ListDomains_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *domainsServiceClient) CreateDomain(ctx context.Context, in *CreateDomainRequest, opts ...grpc.CallOption) (*CreateDomainResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(CreateDomainResponse) + err := c.cc.Invoke(ctx, DomainsService_CreateDomain_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *domainsServiceClient) DeleteDomain(ctx context.Context, in *DeleteDomainRequest, opts ...grpc.CallOption) (*MessageResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(MessageResponse) + err := c.cc.Invoke(ctx, DomainsService_DeleteDomain_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// DomainsServiceServer is the server API for DomainsService service. +// All implementations must embed UnimplementedDomainsServiceServer +// for forward compatibility. +type DomainsServiceServer interface { + ListDomains(context.Context, *ListDomainsRequest) (*ListDomainsResponse, error) + CreateDomain(context.Context, *CreateDomainRequest) (*CreateDomainResponse, error) + DeleteDomain(context.Context, *DeleteDomainRequest) (*MessageResponse, error) + mustEmbedUnimplementedDomainsServiceServer() +} + +// UnimplementedDomainsServiceServer 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 UnimplementedDomainsServiceServer struct{} + +func (UnimplementedDomainsServiceServer) ListDomains(context.Context, *ListDomainsRequest) (*ListDomainsResponse, error) { + return nil, status.Error(codes.Unimplemented, "method ListDomains not implemented") +} +func (UnimplementedDomainsServiceServer) CreateDomain(context.Context, *CreateDomainRequest) (*CreateDomainResponse, error) { + return nil, status.Error(codes.Unimplemented, "method CreateDomain not implemented") +} +func (UnimplementedDomainsServiceServer) DeleteDomain(context.Context, *DeleteDomainRequest) (*MessageResponse, error) { + return nil, status.Error(codes.Unimplemented, "method DeleteDomain not implemented") +} +func (UnimplementedDomainsServiceServer) mustEmbedUnimplementedDomainsServiceServer() {} +func (UnimplementedDomainsServiceServer) testEmbeddedByValue() {} + +// UnsafeDomainsServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to DomainsServiceServer will +// result in compilation errors. +type UnsafeDomainsServiceServer interface { + mustEmbedUnimplementedDomainsServiceServer() +} + +func RegisterDomainsServiceServer(s grpc.ServiceRegistrar, srv DomainsServiceServer) { + // If the following call panics, it indicates UnimplementedDomainsServiceServer 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(&DomainsService_ServiceDesc, srv) +} + +func _DomainsService_ListDomains_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListDomainsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DomainsServiceServer).ListDomains(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: DomainsService_ListDomains_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DomainsServiceServer).ListDomains(ctx, req.(*ListDomainsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _DomainsService_CreateDomain_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateDomainRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DomainsServiceServer).CreateDomain(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: DomainsService_CreateDomain_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DomainsServiceServer).CreateDomain(ctx, req.(*CreateDomainRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _DomainsService_DeleteDomain_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteDomainRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DomainsServiceServer).DeleteDomain(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: DomainsService_DeleteDomain_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DomainsServiceServer).DeleteDomain(ctx, req.(*DeleteDomainRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// DomainsService_ServiceDesc is the grpc.ServiceDesc for DomainsService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var DomainsService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "stream.app.v1.DomainsService", + HandlerType: (*DomainsServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "ListDomains", + Handler: _DomainsService_ListDomains_Handler, + }, + { + MethodName: "CreateDomain", + Handler: _DomainsService_CreateDomain_Handler, + }, + { + MethodName: "DeleteDomain", + Handler: _DomainsService_DeleteDomain_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "app/v1/catalog.proto", +} + +const ( + AdTemplatesService_ListAdTemplates_FullMethodName = "/stream.app.v1.AdTemplatesService/ListAdTemplates" + AdTemplatesService_CreateAdTemplate_FullMethodName = "/stream.app.v1.AdTemplatesService/CreateAdTemplate" + AdTemplatesService_UpdateAdTemplate_FullMethodName = "/stream.app.v1.AdTemplatesService/UpdateAdTemplate" + AdTemplatesService_DeleteAdTemplate_FullMethodName = "/stream.app.v1.AdTemplatesService/DeleteAdTemplate" +) + +// AdTemplatesServiceClient is the client API for AdTemplatesService 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 AdTemplatesServiceClient interface { + ListAdTemplates(ctx context.Context, in *ListAdTemplatesRequest, opts ...grpc.CallOption) (*ListAdTemplatesResponse, error) + CreateAdTemplate(ctx context.Context, in *CreateAdTemplateRequest, opts ...grpc.CallOption) (*CreateAdTemplateResponse, error) + UpdateAdTemplate(ctx context.Context, in *UpdateAdTemplateRequest, opts ...grpc.CallOption) (*UpdateAdTemplateResponse, error) + DeleteAdTemplate(ctx context.Context, in *DeleteAdTemplateRequest, opts ...grpc.CallOption) (*MessageResponse, error) +} + +type adTemplatesServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewAdTemplatesServiceClient(cc grpc.ClientConnInterface) AdTemplatesServiceClient { + return &adTemplatesServiceClient{cc} +} + +func (c *adTemplatesServiceClient) ListAdTemplates(ctx context.Context, in *ListAdTemplatesRequest, opts ...grpc.CallOption) (*ListAdTemplatesResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(ListAdTemplatesResponse) + err := c.cc.Invoke(ctx, AdTemplatesService_ListAdTemplates_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adTemplatesServiceClient) CreateAdTemplate(ctx context.Context, in *CreateAdTemplateRequest, opts ...grpc.CallOption) (*CreateAdTemplateResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(CreateAdTemplateResponse) + err := c.cc.Invoke(ctx, AdTemplatesService_CreateAdTemplate_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adTemplatesServiceClient) UpdateAdTemplate(ctx context.Context, in *UpdateAdTemplateRequest, opts ...grpc.CallOption) (*UpdateAdTemplateResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(UpdateAdTemplateResponse) + err := c.cc.Invoke(ctx, AdTemplatesService_UpdateAdTemplate_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adTemplatesServiceClient) DeleteAdTemplate(ctx context.Context, in *DeleteAdTemplateRequest, opts ...grpc.CallOption) (*MessageResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(MessageResponse) + err := c.cc.Invoke(ctx, AdTemplatesService_DeleteAdTemplate_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// AdTemplatesServiceServer is the server API for AdTemplatesService service. +// All implementations must embed UnimplementedAdTemplatesServiceServer +// for forward compatibility. +type AdTemplatesServiceServer interface { + ListAdTemplates(context.Context, *ListAdTemplatesRequest) (*ListAdTemplatesResponse, error) + CreateAdTemplate(context.Context, *CreateAdTemplateRequest) (*CreateAdTemplateResponse, error) + UpdateAdTemplate(context.Context, *UpdateAdTemplateRequest) (*UpdateAdTemplateResponse, error) + DeleteAdTemplate(context.Context, *DeleteAdTemplateRequest) (*MessageResponse, error) + mustEmbedUnimplementedAdTemplatesServiceServer() +} + +// UnimplementedAdTemplatesServiceServer 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 UnimplementedAdTemplatesServiceServer struct{} + +func (UnimplementedAdTemplatesServiceServer) ListAdTemplates(context.Context, *ListAdTemplatesRequest) (*ListAdTemplatesResponse, error) { + return nil, status.Error(codes.Unimplemented, "method ListAdTemplates not implemented") +} +func (UnimplementedAdTemplatesServiceServer) CreateAdTemplate(context.Context, *CreateAdTemplateRequest) (*CreateAdTemplateResponse, error) { + return nil, status.Error(codes.Unimplemented, "method CreateAdTemplate not implemented") +} +func (UnimplementedAdTemplatesServiceServer) UpdateAdTemplate(context.Context, *UpdateAdTemplateRequest) (*UpdateAdTemplateResponse, error) { + return nil, status.Error(codes.Unimplemented, "method UpdateAdTemplate not implemented") +} +func (UnimplementedAdTemplatesServiceServer) DeleteAdTemplate(context.Context, *DeleteAdTemplateRequest) (*MessageResponse, error) { + return nil, status.Error(codes.Unimplemented, "method DeleteAdTemplate not implemented") +} +func (UnimplementedAdTemplatesServiceServer) mustEmbedUnimplementedAdTemplatesServiceServer() {} +func (UnimplementedAdTemplatesServiceServer) testEmbeddedByValue() {} + +// UnsafeAdTemplatesServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to AdTemplatesServiceServer will +// result in compilation errors. +type UnsafeAdTemplatesServiceServer interface { + mustEmbedUnimplementedAdTemplatesServiceServer() +} + +func RegisterAdTemplatesServiceServer(s grpc.ServiceRegistrar, srv AdTemplatesServiceServer) { + // If the following call panics, it indicates UnimplementedAdTemplatesServiceServer 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(&AdTemplatesService_ServiceDesc, srv) +} + +func _AdTemplatesService_ListAdTemplates_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListAdTemplatesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdTemplatesServiceServer).ListAdTemplates(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdTemplatesService_ListAdTemplates_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdTemplatesServiceServer).ListAdTemplates(ctx, req.(*ListAdTemplatesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdTemplatesService_CreateAdTemplate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateAdTemplateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdTemplatesServiceServer).CreateAdTemplate(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdTemplatesService_CreateAdTemplate_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdTemplatesServiceServer).CreateAdTemplate(ctx, req.(*CreateAdTemplateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdTemplatesService_UpdateAdTemplate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateAdTemplateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdTemplatesServiceServer).UpdateAdTemplate(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdTemplatesService_UpdateAdTemplate_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdTemplatesServiceServer).UpdateAdTemplate(ctx, req.(*UpdateAdTemplateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdTemplatesService_DeleteAdTemplate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteAdTemplateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdTemplatesServiceServer).DeleteAdTemplate(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdTemplatesService_DeleteAdTemplate_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdTemplatesServiceServer).DeleteAdTemplate(ctx, req.(*DeleteAdTemplateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// AdTemplatesService_ServiceDesc is the grpc.ServiceDesc for AdTemplatesService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var AdTemplatesService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "stream.app.v1.AdTemplatesService", + HandlerType: (*AdTemplatesServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "ListAdTemplates", + Handler: _AdTemplatesService_ListAdTemplates_Handler, + }, + { + MethodName: "CreateAdTemplate", + Handler: _AdTemplatesService_CreateAdTemplate_Handler, + }, + { + MethodName: "UpdateAdTemplate", + Handler: _AdTemplatesService_UpdateAdTemplate_Handler, + }, + { + MethodName: "DeleteAdTemplate", + Handler: _AdTemplatesService_DeleteAdTemplate_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "app/v1/catalog.proto", +} + +const ( + PlansService_ListPlans_FullMethodName = "/stream.app.v1.PlansService/ListPlans" +) + +// PlansServiceClient is the client API for PlansService 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 PlansServiceClient interface { + ListPlans(ctx context.Context, in *ListPlansRequest, opts ...grpc.CallOption) (*ListPlansResponse, error) +} + +type plansServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewPlansServiceClient(cc grpc.ClientConnInterface) PlansServiceClient { + return &plansServiceClient{cc} +} + +func (c *plansServiceClient) ListPlans(ctx context.Context, in *ListPlansRequest, opts ...grpc.CallOption) (*ListPlansResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(ListPlansResponse) + err := c.cc.Invoke(ctx, PlansService_ListPlans_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// PlansServiceServer is the server API for PlansService service. +// All implementations must embed UnimplementedPlansServiceServer +// for forward compatibility. +type PlansServiceServer interface { + ListPlans(context.Context, *ListPlansRequest) (*ListPlansResponse, error) + mustEmbedUnimplementedPlansServiceServer() +} + +// UnimplementedPlansServiceServer 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 UnimplementedPlansServiceServer struct{} + +func (UnimplementedPlansServiceServer) ListPlans(context.Context, *ListPlansRequest) (*ListPlansResponse, error) { + return nil, status.Error(codes.Unimplemented, "method ListPlans not implemented") +} +func (UnimplementedPlansServiceServer) mustEmbedUnimplementedPlansServiceServer() {} +func (UnimplementedPlansServiceServer) testEmbeddedByValue() {} + +// UnsafePlansServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to PlansServiceServer will +// result in compilation errors. +type UnsafePlansServiceServer interface { + mustEmbedUnimplementedPlansServiceServer() +} + +func RegisterPlansServiceServer(s grpc.ServiceRegistrar, srv PlansServiceServer) { + // If the following call panics, it indicates UnimplementedPlansServiceServer 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(&PlansService_ServiceDesc, srv) +} + +func _PlansService_ListPlans_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListPlansRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(PlansServiceServer).ListPlans(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: PlansService_ListPlans_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(PlansServiceServer).ListPlans(ctx, req.(*ListPlansRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// PlansService_ServiceDesc is the grpc.ServiceDesc for PlansService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var PlansService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "stream.app.v1.PlansService", + HandlerType: (*PlansServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "ListPlans", + Handler: _PlansService_ListPlans_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "app/v1/catalog.proto", +} diff --git a/internal/gen/proto/app/v1/common.pb.go b/internal/gen/proto/app/v1/common.pb.go new file mode 100644 index 0000000..8112666 --- /dev/null +++ b/internal/gen/proto/app/v1/common.pb.go @@ -0,0 +1,3187 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc (unknown) +// source: app/v1/common.proto + +package appv1 + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type MessageResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *MessageResponse) Reset() { + *x = MessageResponse{} + mi := &file_app_v1_common_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *MessageResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MessageResponse) ProtoMessage() {} + +func (x *MessageResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_common_proto_msgTypes[0] + 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 MessageResponse.ProtoReflect.Descriptor instead. +func (*MessageResponse) Descriptor() ([]byte, []int) { + return file_app_v1_common_proto_rawDescGZIP(), []int{0} +} + +func (x *MessageResponse) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +type User struct { + state protoimpl.MessageState `protogen:"open.v1"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Email string `protobuf:"bytes,2,opt,name=email,proto3" json:"email,omitempty"` + Username *string `protobuf:"bytes,3,opt,name=username,proto3,oneof" json:"username,omitempty"` + Avatar *string `protobuf:"bytes,4,opt,name=avatar,proto3,oneof" json:"avatar,omitempty"` + Role *string `protobuf:"bytes,5,opt,name=role,proto3,oneof" json:"role,omitempty"` + GoogleId *string `protobuf:"bytes,6,opt,name=google_id,json=googleId,proto3,oneof" json:"google_id,omitempty"` + StorageUsed int64 `protobuf:"varint,7,opt,name=storage_used,json=storageUsed,proto3" json:"storage_used,omitempty"` + PlanId *string `protobuf:"bytes,8,opt,name=plan_id,json=planId,proto3,oneof" json:"plan_id,omitempty"` + PlanStartedAt *timestamppb.Timestamp `protobuf:"bytes,9,opt,name=plan_started_at,json=planStartedAt,proto3" json:"plan_started_at,omitempty"` + PlanExpiresAt *timestamppb.Timestamp `protobuf:"bytes,10,opt,name=plan_expires_at,json=planExpiresAt,proto3" json:"plan_expires_at,omitempty"` + PlanTermMonths *int32 `protobuf:"varint,11,opt,name=plan_term_months,json=planTermMonths,proto3,oneof" json:"plan_term_months,omitempty"` + PlanPaymentMethod *string `protobuf:"bytes,12,opt,name=plan_payment_method,json=planPaymentMethod,proto3,oneof" json:"plan_payment_method,omitempty"` + PlanExpiringSoon bool `protobuf:"varint,13,opt,name=plan_expiring_soon,json=planExpiringSoon,proto3" json:"plan_expiring_soon,omitempty"` + WalletBalance float64 `protobuf:"fixed64,14,opt,name=wallet_balance,json=walletBalance,proto3" json:"wallet_balance,omitempty"` + Language string `protobuf:"bytes,15,opt,name=language,proto3" json:"language,omitempty"` + Locale string `protobuf:"bytes,16,opt,name=locale,proto3" json:"locale,omitempty"` + CreatedAt *timestamppb.Timestamp `protobuf:"bytes,17,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,18,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *User) Reset() { + *x = User{} + mi := &file_app_v1_common_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *User) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*User) ProtoMessage() {} + +func (x *User) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_common_proto_msgTypes[1] + 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 User.ProtoReflect.Descriptor instead. +func (*User) Descriptor() ([]byte, []int) { + return file_app_v1_common_proto_rawDescGZIP(), []int{1} +} + +func (x *User) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *User) GetEmail() string { + if x != nil { + return x.Email + } + return "" +} + +func (x *User) GetUsername() string { + if x != nil && x.Username != nil { + return *x.Username + } + return "" +} + +func (x *User) GetAvatar() string { + if x != nil && x.Avatar != nil { + return *x.Avatar + } + return "" +} + +func (x *User) GetRole() string { + if x != nil && x.Role != nil { + return *x.Role + } + return "" +} + +func (x *User) GetGoogleId() string { + if x != nil && x.GoogleId != nil { + return *x.GoogleId + } + return "" +} + +func (x *User) GetStorageUsed() int64 { + if x != nil { + return x.StorageUsed + } + return 0 +} + +func (x *User) GetPlanId() string { + if x != nil && x.PlanId != nil { + return *x.PlanId + } + return "" +} + +func (x *User) GetPlanStartedAt() *timestamppb.Timestamp { + if x != nil { + return x.PlanStartedAt + } + return nil +} + +func (x *User) GetPlanExpiresAt() *timestamppb.Timestamp { + if x != nil { + return x.PlanExpiresAt + } + return nil +} + +func (x *User) GetPlanTermMonths() int32 { + if x != nil && x.PlanTermMonths != nil { + return *x.PlanTermMonths + } + return 0 +} + +func (x *User) GetPlanPaymentMethod() string { + if x != nil && x.PlanPaymentMethod != nil { + return *x.PlanPaymentMethod + } + return "" +} + +func (x *User) GetPlanExpiringSoon() bool { + if x != nil { + return x.PlanExpiringSoon + } + return false +} + +func (x *User) GetWalletBalance() float64 { + if x != nil { + return x.WalletBalance + } + return 0 +} + +func (x *User) GetLanguage() string { + if x != nil { + return x.Language + } + return "" +} + +func (x *User) GetLocale() string { + if x != nil { + return x.Locale + } + return "" +} + +func (x *User) GetCreatedAt() *timestamppb.Timestamp { + if x != nil { + return x.CreatedAt + } + return nil +} + +func (x *User) GetUpdatedAt() *timestamppb.Timestamp { + if x != nil { + return x.UpdatedAt + } + return nil +} + +type Preferences struct { + state protoimpl.MessageState `protogen:"open.v1"` + EmailNotifications bool `protobuf:"varint,1,opt,name=email_notifications,json=emailNotifications,proto3" json:"email_notifications,omitempty"` + PushNotifications bool `protobuf:"varint,2,opt,name=push_notifications,json=pushNotifications,proto3" json:"push_notifications,omitempty"` + MarketingNotifications bool `protobuf:"varint,3,opt,name=marketing_notifications,json=marketingNotifications,proto3" json:"marketing_notifications,omitempty"` + TelegramNotifications bool `protobuf:"varint,4,opt,name=telegram_notifications,json=telegramNotifications,proto3" json:"telegram_notifications,omitempty"` + Autoplay bool `protobuf:"varint,5,opt,name=autoplay,proto3" json:"autoplay,omitempty"` + Loop bool `protobuf:"varint,6,opt,name=loop,proto3" json:"loop,omitempty"` + Muted bool `protobuf:"varint,7,opt,name=muted,proto3" json:"muted,omitempty"` + ShowControls bool `protobuf:"varint,8,opt,name=show_controls,json=showControls,proto3" json:"show_controls,omitempty"` + Pip bool `protobuf:"varint,9,opt,name=pip,proto3" json:"pip,omitempty"` + Airplay bool `protobuf:"varint,10,opt,name=airplay,proto3" json:"airplay,omitempty"` + Chromecast bool `protobuf:"varint,11,opt,name=chromecast,proto3" json:"chromecast,omitempty"` + Language string `protobuf:"bytes,12,opt,name=language,proto3" json:"language,omitempty"` + Locale string `protobuf:"bytes,13,opt,name=locale,proto3" json:"locale,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Preferences) Reset() { + *x = Preferences{} + mi := &file_app_v1_common_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Preferences) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Preferences) ProtoMessage() {} + +func (x *Preferences) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_common_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Preferences.ProtoReflect.Descriptor instead. +func (*Preferences) Descriptor() ([]byte, []int) { + return file_app_v1_common_proto_rawDescGZIP(), []int{2} +} + +func (x *Preferences) GetEmailNotifications() bool { + if x != nil { + return x.EmailNotifications + } + return false +} + +func (x *Preferences) GetPushNotifications() bool { + if x != nil { + return x.PushNotifications + } + return false +} + +func (x *Preferences) GetMarketingNotifications() bool { + if x != nil { + return x.MarketingNotifications + } + return false +} + +func (x *Preferences) GetTelegramNotifications() bool { + if x != nil { + return x.TelegramNotifications + } + return false +} + +func (x *Preferences) GetAutoplay() bool { + if x != nil { + return x.Autoplay + } + return false +} + +func (x *Preferences) GetLoop() bool { + if x != nil { + return x.Loop + } + return false +} + +func (x *Preferences) GetMuted() bool { + if x != nil { + return x.Muted + } + return false +} + +func (x *Preferences) GetShowControls() bool { + if x != nil { + return x.ShowControls + } + return false +} + +func (x *Preferences) GetPip() bool { + if x != nil { + return x.Pip + } + return false +} + +func (x *Preferences) GetAirplay() bool { + if x != nil { + return x.Airplay + } + return false +} + +func (x *Preferences) GetChromecast() bool { + if x != nil { + return x.Chromecast + } + return false +} + +func (x *Preferences) GetLanguage() string { + if x != nil { + return x.Language + } + return "" +} + +func (x *Preferences) GetLocale() string { + if x != nil { + return x.Locale + } + return "" +} + +type Notification 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"` + Title string `protobuf:"bytes,3,opt,name=title,proto3" json:"title,omitempty"` + Message string `protobuf:"bytes,4,opt,name=message,proto3" json:"message,omitempty"` + Read bool `protobuf:"varint,5,opt,name=read,proto3" json:"read,omitempty"` + ActionUrl *string `protobuf:"bytes,6,opt,name=action_url,json=actionUrl,proto3,oneof" json:"action_url,omitempty"` + ActionLabel *string `protobuf:"bytes,7,opt,name=action_label,json=actionLabel,proto3,oneof" json:"action_label,omitempty"` + CreatedAt *timestamppb.Timestamp `protobuf:"bytes,8,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Notification) Reset() { + *x = Notification{} + mi := &file_app_v1_common_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Notification) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Notification) ProtoMessage() {} + +func (x *Notification) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_common_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Notification.ProtoReflect.Descriptor instead. +func (*Notification) Descriptor() ([]byte, []int) { + return file_app_v1_common_proto_rawDescGZIP(), []int{3} +} + +func (x *Notification) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *Notification) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *Notification) GetTitle() string { + if x != nil { + return x.Title + } + return "" +} + +func (x *Notification) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +func (x *Notification) GetRead() bool { + if x != nil { + return x.Read + } + return false +} + +func (x *Notification) GetActionUrl() string { + if x != nil && x.ActionUrl != nil { + return *x.ActionUrl + } + return "" +} + +func (x *Notification) GetActionLabel() string { + if x != nil && x.ActionLabel != nil { + return *x.ActionLabel + } + return "" +} + +func (x *Notification) GetCreatedAt() *timestamppb.Timestamp { + if x != nil { + return x.CreatedAt + } + return nil +} + +type Domain struct { + state protoimpl.MessageState `protogen:"open.v1"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + CreatedAt *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Domain) Reset() { + *x = Domain{} + mi := &file_app_v1_common_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Domain) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Domain) ProtoMessage() {} + +func (x *Domain) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_common_proto_msgTypes[4] + 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 Domain.ProtoReflect.Descriptor instead. +func (*Domain) Descriptor() ([]byte, []int) { + return file_app_v1_common_proto_rawDescGZIP(), []int{4} +} + +func (x *Domain) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *Domain) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Domain) GetCreatedAt() *timestamppb.Timestamp { + if x != nil { + return x.CreatedAt + } + return nil +} + +func (x *Domain) GetUpdatedAt() *timestamppb.Timestamp { + if x != nil { + return x.UpdatedAt + } + return nil +} + +type AdTemplate struct { + state protoimpl.MessageState `protogen:"open.v1"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Description *string `protobuf:"bytes,3,opt,name=description,proto3,oneof" json:"description,omitempty"` + VastTagUrl string `protobuf:"bytes,4,opt,name=vast_tag_url,json=vastTagUrl,proto3" json:"vast_tag_url,omitempty"` + AdFormat string `protobuf:"bytes,5,opt,name=ad_format,json=adFormat,proto3" json:"ad_format,omitempty"` + Duration *int32 `protobuf:"varint,6,opt,name=duration,proto3,oneof" json:"duration,omitempty"` + IsActive bool `protobuf:"varint,7,opt,name=is_active,json=isActive,proto3" json:"is_active,omitempty"` + IsDefault bool `protobuf:"varint,8,opt,name=is_default,json=isDefault,proto3" json:"is_default,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 *AdTemplate) Reset() { + *x = AdTemplate{} + mi := &file_app_v1_common_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AdTemplate) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AdTemplate) ProtoMessage() {} + +func (x *AdTemplate) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_common_proto_msgTypes[5] + 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 AdTemplate.ProtoReflect.Descriptor instead. +func (*AdTemplate) Descriptor() ([]byte, []int) { + return file_app_v1_common_proto_rawDescGZIP(), []int{5} +} + +func (x *AdTemplate) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *AdTemplate) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *AdTemplate) GetDescription() string { + if x != nil && x.Description != nil { + return *x.Description + } + return "" +} + +func (x *AdTemplate) GetVastTagUrl() string { + if x != nil { + return x.VastTagUrl + } + return "" +} + +func (x *AdTemplate) GetAdFormat() string { + if x != nil { + return x.AdFormat + } + return "" +} + +func (x *AdTemplate) GetDuration() int32 { + if x != nil && x.Duration != nil { + return *x.Duration + } + return 0 +} + +func (x *AdTemplate) GetIsActive() bool { + if x != nil { + return x.IsActive + } + return false +} + +func (x *AdTemplate) GetIsDefault() bool { + if x != nil { + return x.IsDefault + } + return false +} + +func (x *AdTemplate) GetCreatedAt() *timestamppb.Timestamp { + if x != nil { + return x.CreatedAt + } + return nil +} + +func (x *AdTemplate) GetUpdatedAt() *timestamppb.Timestamp { + if x != nil { + return x.UpdatedAt + } + return nil +} + +type Plan struct { + state protoimpl.MessageState `protogen:"open.v1"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Description *string `protobuf:"bytes,3,opt,name=description,proto3,oneof" json:"description,omitempty"` + Price float64 `protobuf:"fixed64,4,opt,name=price,proto3" json:"price,omitempty"` + Cycle string `protobuf:"bytes,5,opt,name=cycle,proto3" json:"cycle,omitempty"` + StorageLimit int64 `protobuf:"varint,6,opt,name=storage_limit,json=storageLimit,proto3" json:"storage_limit,omitempty"` + UploadLimit int32 `protobuf:"varint,7,opt,name=upload_limit,json=uploadLimit,proto3" json:"upload_limit,omitempty"` + DurationLimit int32 `protobuf:"varint,8,opt,name=duration_limit,json=durationLimit,proto3" json:"duration_limit,omitempty"` + QualityLimit string `protobuf:"bytes,9,opt,name=quality_limit,json=qualityLimit,proto3" json:"quality_limit,omitempty"` + Features []string `protobuf:"bytes,10,rep,name=features,proto3" json:"features,omitempty"` + IsActive bool `protobuf:"varint,11,opt,name=is_active,json=isActive,proto3" json:"is_active,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Plan) Reset() { + *x = Plan{} + mi := &file_app_v1_common_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Plan) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Plan) ProtoMessage() {} + +func (x *Plan) 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 Plan.ProtoReflect.Descriptor instead. +func (*Plan) Descriptor() ([]byte, []int) { + return file_app_v1_common_proto_rawDescGZIP(), []int{6} +} + +func (x *Plan) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *Plan) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Plan) GetDescription() string { + if x != nil && x.Description != nil { + return *x.Description + } + return "" +} + +func (x *Plan) GetPrice() float64 { + if x != nil { + return x.Price + } + return 0 +} + +func (x *Plan) GetCycle() string { + if x != nil { + return x.Cycle + } + return "" +} + +func (x *Plan) GetStorageLimit() int64 { + if x != nil { + return x.StorageLimit + } + return 0 +} + +func (x *Plan) GetUploadLimit() int32 { + if x != nil { + return x.UploadLimit + } + return 0 +} + +func (x *Plan) GetDurationLimit() int32 { + if x != nil { + return x.DurationLimit + } + return 0 +} + +func (x *Plan) GetQualityLimit() string { + if x != nil { + return x.QualityLimit + } + return "" +} + +func (x *Plan) GetFeatures() []string { + if x != nil { + return x.Features + } + return nil +} + +func (x *Plan) GetIsActive() bool { + if x != nil { + return x.IsActive + } + return false +} + +type Payment 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"` + PlanId *string `protobuf:"bytes,3,opt,name=plan_id,json=planId,proto3,oneof" json:"plan_id,omitempty"` + Amount float64 `protobuf:"fixed64,4,opt,name=amount,proto3" json:"amount,omitempty"` + Currency string `protobuf:"bytes,5,opt,name=currency,proto3" json:"currency,omitempty"` + Status string `protobuf:"bytes,6,opt,name=status,proto3" json:"status,omitempty"` + Provider string `protobuf:"bytes,7,opt,name=provider,proto3" json:"provider,omitempty"` + TransactionId *string `protobuf:"bytes,8,opt,name=transaction_id,json=transactionId,proto3,oneof" json:"transaction_id,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 *Payment) Reset() { + *x = Payment{} + mi := &file_app_v1_common_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Payment) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Payment) ProtoMessage() {} + +func (x *Payment) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_common_proto_msgTypes[7] + 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 Payment.ProtoReflect.Descriptor instead. +func (*Payment) Descriptor() ([]byte, []int) { + return file_app_v1_common_proto_rawDescGZIP(), []int{7} +} + +func (x *Payment) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *Payment) GetUserId() string { + if x != nil { + return x.UserId + } + return "" +} + +func (x *Payment) GetPlanId() string { + if x != nil && x.PlanId != nil { + return *x.PlanId + } + return "" +} + +func (x *Payment) GetAmount() float64 { + if x != nil { + return x.Amount + } + return 0 +} + +func (x *Payment) GetCurrency() string { + if x != nil { + return x.Currency + } + return "" +} + +func (x *Payment) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *Payment) GetProvider() string { + if x != nil { + return x.Provider + } + return "" +} + +func (x *Payment) GetTransactionId() string { + if x != nil && x.TransactionId != nil { + return *x.TransactionId + } + return "" +} + +func (x *Payment) GetCreatedAt() *timestamppb.Timestamp { + if x != nil { + return x.CreatedAt + } + return nil +} + +func (x *Payment) GetUpdatedAt() *timestamppb.Timestamp { + if x != nil { + return x.UpdatedAt + } + return nil +} + +type PlanSubscription 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"` + PaymentId string `protobuf:"bytes,3,opt,name=payment_id,json=paymentId,proto3" json:"payment_id,omitempty"` + PlanId string `protobuf:"bytes,4,opt,name=plan_id,json=planId,proto3" json:"plan_id,omitempty"` + TermMonths int32 `protobuf:"varint,5,opt,name=term_months,json=termMonths,proto3" json:"term_months,omitempty"` + PaymentMethod string `protobuf:"bytes,6,opt,name=payment_method,json=paymentMethod,proto3" json:"payment_method,omitempty"` + WalletAmount float64 `protobuf:"fixed64,7,opt,name=wallet_amount,json=walletAmount,proto3" json:"wallet_amount,omitempty"` + TopupAmount float64 `protobuf:"fixed64,8,opt,name=topup_amount,json=topupAmount,proto3" json:"topup_amount,omitempty"` + StartedAt *timestamppb.Timestamp `protobuf:"bytes,9,opt,name=started_at,json=startedAt,proto3" json:"started_at,omitempty"` + ExpiresAt *timestamppb.Timestamp `protobuf:"bytes,10,opt,name=expires_at,json=expiresAt,proto3" json:"expires_at,omitempty"` + CreatedAt *timestamppb.Timestamp `protobuf:"bytes,11,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *PlanSubscription) Reset() { + *x = PlanSubscription{} + mi := &file_app_v1_common_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *PlanSubscription) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PlanSubscription) ProtoMessage() {} + +func (x *PlanSubscription) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_common_proto_msgTypes[8] + 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 PlanSubscription.ProtoReflect.Descriptor instead. +func (*PlanSubscription) Descriptor() ([]byte, []int) { + return file_app_v1_common_proto_rawDescGZIP(), []int{8} +} + +func (x *PlanSubscription) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *PlanSubscription) GetUserId() string { + if x != nil { + return x.UserId + } + return "" +} + +func (x *PlanSubscription) GetPaymentId() string { + if x != nil { + return x.PaymentId + } + return "" +} + +func (x *PlanSubscription) GetPlanId() string { + if x != nil { + return x.PlanId + } + return "" +} + +func (x *PlanSubscription) GetTermMonths() int32 { + if x != nil { + return x.TermMonths + } + return 0 +} + +func (x *PlanSubscription) GetPaymentMethod() string { + if x != nil { + return x.PaymentMethod + } + return "" +} + +func (x *PlanSubscription) GetWalletAmount() float64 { + if x != nil { + return x.WalletAmount + } + return 0 +} + +func (x *PlanSubscription) GetTopupAmount() float64 { + if x != nil { + return x.TopupAmount + } + return 0 +} + +func (x *PlanSubscription) GetStartedAt() *timestamppb.Timestamp { + if x != nil { + return x.StartedAt + } + return nil +} + +func (x *PlanSubscription) GetExpiresAt() *timestamppb.Timestamp { + if x != nil { + return x.ExpiresAt + } + return nil +} + +func (x *PlanSubscription) GetCreatedAt() *timestamppb.Timestamp { + if x != nil { + return x.CreatedAt + } + return nil +} + +func (x *PlanSubscription) GetUpdatedAt() *timestamppb.Timestamp { + if x != nil { + return x.UpdatedAt + } + return nil +} + +type WalletTransaction 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"` + Amount float64 `protobuf:"fixed64,4,opt,name=amount,proto3" json:"amount,omitempty"` + Currency string `protobuf:"bytes,5,opt,name=currency,proto3" json:"currency,omitempty"` + Note *string `protobuf:"bytes,6,opt,name=note,proto3,oneof" json:"note,omitempty"` + PaymentId *string `protobuf:"bytes,7,opt,name=payment_id,json=paymentId,proto3,oneof" json:"payment_id,omitempty"` + PlanId *string `protobuf:"bytes,8,opt,name=plan_id,json=planId,proto3,oneof" json:"plan_id,omitempty"` + TermMonths *int32 `protobuf:"varint,9,opt,name=term_months,json=termMonths,proto3,oneof" json:"term_months,omitempty"` + CreatedAt *timestamppb.Timestamp `protobuf:"bytes,10,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,11,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WalletTransaction) Reset() { + *x = WalletTransaction{} + mi := &file_app_v1_common_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WalletTransaction) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WalletTransaction) ProtoMessage() {} + +func (x *WalletTransaction) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_common_proto_msgTypes[9] + 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 WalletTransaction.ProtoReflect.Descriptor instead. +func (*WalletTransaction) Descriptor() ([]byte, []int) { + return file_app_v1_common_proto_rawDescGZIP(), []int{9} +} + +func (x *WalletTransaction) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *WalletTransaction) GetUserId() string { + if x != nil { + return x.UserId + } + return "" +} + +func (x *WalletTransaction) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *WalletTransaction) GetAmount() float64 { + if x != nil { + return x.Amount + } + return 0 +} + +func (x *WalletTransaction) GetCurrency() string { + if x != nil { + return x.Currency + } + return "" +} + +func (x *WalletTransaction) GetNote() string { + if x != nil && x.Note != nil { + return *x.Note + } + return "" +} + +func (x *WalletTransaction) GetPaymentId() string { + if x != nil && x.PaymentId != nil { + return *x.PaymentId + } + return "" +} + +func (x *WalletTransaction) GetPlanId() string { + if x != nil && x.PlanId != nil { + return *x.PlanId + } + return "" +} + +func (x *WalletTransaction) GetTermMonths() int32 { + if x != nil && x.TermMonths != nil { + return *x.TermMonths + } + return 0 +} + +func (x *WalletTransaction) GetCreatedAt() *timestamppb.Timestamp { + if x != nil { + return x.CreatedAt + } + return nil +} + +func (x *WalletTransaction) GetUpdatedAt() *timestamppb.Timestamp { + if x != nil { + return x.UpdatedAt + } + return nil +} + +type PaymentHistoryItem struct { + state protoimpl.MessageState `protogen:"open.v1"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Amount float64 `protobuf:"fixed64,2,opt,name=amount,proto3" json:"amount,omitempty"` + Currency string `protobuf:"bytes,3,opt,name=currency,proto3" json:"currency,omitempty"` + Status string `protobuf:"bytes,4,opt,name=status,proto3" json:"status,omitempty"` + PlanId *string `protobuf:"bytes,5,opt,name=plan_id,json=planId,proto3,oneof" json:"plan_id,omitempty"` + PlanName *string `protobuf:"bytes,6,opt,name=plan_name,json=planName,proto3,oneof" json:"plan_name,omitempty"` + InvoiceId string `protobuf:"bytes,7,opt,name=invoice_id,json=invoiceId,proto3" json:"invoice_id,omitempty"` + Kind string `protobuf:"bytes,8,opt,name=kind,proto3" json:"kind,omitempty"` + TermMonths *int32 `protobuf:"varint,9,opt,name=term_months,json=termMonths,proto3,oneof" json:"term_months,omitempty"` + PaymentMethod *string `protobuf:"bytes,10,opt,name=payment_method,json=paymentMethod,proto3,oneof" json:"payment_method,omitempty"` + ExpiresAt *timestamppb.Timestamp `protobuf:"bytes,11,opt,name=expires_at,json=expiresAt,proto3" json:"expires_at,omitempty"` + CreatedAt *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *PaymentHistoryItem) Reset() { + *x = PaymentHistoryItem{} + mi := &file_app_v1_common_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *PaymentHistoryItem) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PaymentHistoryItem) ProtoMessage() {} + +func (x *PaymentHistoryItem) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_common_proto_msgTypes[10] + 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 PaymentHistoryItem.ProtoReflect.Descriptor instead. +func (*PaymentHistoryItem) Descriptor() ([]byte, []int) { + return file_app_v1_common_proto_rawDescGZIP(), []int{10} +} + +func (x *PaymentHistoryItem) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *PaymentHistoryItem) GetAmount() float64 { + if x != nil { + return x.Amount + } + return 0 +} + +func (x *PaymentHistoryItem) GetCurrency() string { + if x != nil { + return x.Currency + } + return "" +} + +func (x *PaymentHistoryItem) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *PaymentHistoryItem) GetPlanId() string { + if x != nil && x.PlanId != nil { + return *x.PlanId + } + return "" +} + +func (x *PaymentHistoryItem) GetPlanName() string { + if x != nil && x.PlanName != nil { + return *x.PlanName + } + return "" +} + +func (x *PaymentHistoryItem) GetInvoiceId() string { + if x != nil { + return x.InvoiceId + } + return "" +} + +func (x *PaymentHistoryItem) GetKind() string { + if x != nil { + return x.Kind + } + return "" +} + +func (x *PaymentHistoryItem) GetTermMonths() int32 { + if x != nil && x.TermMonths != nil { + return *x.TermMonths + } + return 0 +} + +func (x *PaymentHistoryItem) GetPaymentMethod() string { + if x != nil && x.PaymentMethod != nil { + return *x.PaymentMethod + } + return "" +} + +func (x *PaymentHistoryItem) GetExpiresAt() *timestamppb.Timestamp { + if x != nil { + return x.ExpiresAt + } + return nil +} + +func (x *PaymentHistoryItem) GetCreatedAt() *timestamppb.Timestamp { + if x != nil { + return x.CreatedAt + } + return nil +} + +type Video 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"` + Title string `protobuf:"bytes,3,opt,name=title,proto3" json:"title,omitempty"` + Description *string `protobuf:"bytes,4,opt,name=description,proto3,oneof" json:"description,omitempty"` + Url string `protobuf:"bytes,5,opt,name=url,proto3" json:"url,omitempty"` + Status string `protobuf:"bytes,6,opt,name=status,proto3" json:"status,omitempty"` + Size int64 `protobuf:"varint,7,opt,name=size,proto3" json:"size,omitempty"` + Duration int32 `protobuf:"varint,8,opt,name=duration,proto3" json:"duration,omitempty"` + Format string `protobuf:"bytes,9,opt,name=format,proto3" json:"format,omitempty"` + Thumbnail *string `protobuf:"bytes,10,opt,name=thumbnail,proto3,oneof" json:"thumbnail,omitempty"` + ProcessingStatus *string `protobuf:"bytes,11,opt,name=processing_status,json=processingStatus,proto3,oneof" json:"processing_status,omitempty"` + StorageType *string `protobuf:"bytes,12,opt,name=storage_type,json=storageType,proto3,oneof" json:"storage_type,omitempty"` + CreatedAt *timestamppb.Timestamp `protobuf:"bytes,13,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,14,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Video) Reset() { + *x = Video{} + mi := &file_app_v1_common_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Video) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Video) ProtoMessage() {} + +func (x *Video) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_common_proto_msgTypes[11] + 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 Video.ProtoReflect.Descriptor instead. +func (*Video) Descriptor() ([]byte, []int) { + return file_app_v1_common_proto_rawDescGZIP(), []int{11} +} + +func (x *Video) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *Video) GetUserId() string { + if x != nil { + return x.UserId + } + return "" +} + +func (x *Video) GetTitle() string { + if x != nil { + return x.Title + } + return "" +} + +func (x *Video) GetDescription() string { + if x != nil && x.Description != nil { + return *x.Description + } + return "" +} + +func (x *Video) GetUrl() string { + if x != nil { + return x.Url + } + return "" +} + +func (x *Video) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *Video) GetSize() int64 { + if x != nil { + return x.Size + } + return 0 +} + +func (x *Video) GetDuration() int32 { + if x != nil { + return x.Duration + } + return 0 +} + +func (x *Video) GetFormat() string { + if x != nil { + return x.Format + } + return "" +} + +func (x *Video) GetThumbnail() string { + if x != nil && x.Thumbnail != nil { + return *x.Thumbnail + } + return "" +} + +func (x *Video) GetProcessingStatus() string { + if x != nil && x.ProcessingStatus != nil { + return *x.ProcessingStatus + } + return "" +} + +func (x *Video) GetStorageType() string { + if x != nil && x.StorageType != nil { + return *x.StorageType + } + return "" +} + +func (x *Video) GetCreatedAt() *timestamppb.Timestamp { + if x != nil { + return x.CreatedAt + } + return nil +} + +func (x *Video) GetUpdatedAt() *timestamppb.Timestamp { + if x != nil { + return x.UpdatedAt + } + return nil +} + +type AdminDashboard struct { + state protoimpl.MessageState `protogen:"open.v1"` + TotalUsers int64 `protobuf:"varint,1,opt,name=total_users,json=totalUsers,proto3" json:"total_users,omitempty"` + TotalVideos int64 `protobuf:"varint,2,opt,name=total_videos,json=totalVideos,proto3" json:"total_videos,omitempty"` + TotalStorageUsed int64 `protobuf:"varint,3,opt,name=total_storage_used,json=totalStorageUsed,proto3" json:"total_storage_used,omitempty"` + TotalPayments int64 `protobuf:"varint,4,opt,name=total_payments,json=totalPayments,proto3" json:"total_payments,omitempty"` + TotalRevenue float64 `protobuf:"fixed64,5,opt,name=total_revenue,json=totalRevenue,proto3" json:"total_revenue,omitempty"` + ActiveSubscriptions int64 `protobuf:"varint,6,opt,name=active_subscriptions,json=activeSubscriptions,proto3" json:"active_subscriptions,omitempty"` + TotalAdTemplates int64 `protobuf:"varint,7,opt,name=total_ad_templates,json=totalAdTemplates,proto3" json:"total_ad_templates,omitempty"` + NewUsersToday int64 `protobuf:"varint,8,opt,name=new_users_today,json=newUsersToday,proto3" json:"new_users_today,omitempty"` + NewVideosToday int64 `protobuf:"varint,9,opt,name=new_videos_today,json=newVideosToday,proto3" json:"new_videos_today,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AdminDashboard) Reset() { + *x = AdminDashboard{} + mi := &file_app_v1_common_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AdminDashboard) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AdminDashboard) ProtoMessage() {} + +func (x *AdminDashboard) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_common_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 AdminDashboard.ProtoReflect.Descriptor instead. +func (*AdminDashboard) Descriptor() ([]byte, []int) { + return file_app_v1_common_proto_rawDescGZIP(), []int{12} +} + +func (x *AdminDashboard) GetTotalUsers() int64 { + if x != nil { + return x.TotalUsers + } + return 0 +} + +func (x *AdminDashboard) GetTotalVideos() int64 { + if x != nil { + return x.TotalVideos + } + return 0 +} + +func (x *AdminDashboard) GetTotalStorageUsed() int64 { + if x != nil { + return x.TotalStorageUsed + } + return 0 +} + +func (x *AdminDashboard) GetTotalPayments() int64 { + if x != nil { + return x.TotalPayments + } + return 0 +} + +func (x *AdminDashboard) GetTotalRevenue() float64 { + if x != nil { + return x.TotalRevenue + } + return 0 +} + +func (x *AdminDashboard) GetActiveSubscriptions() int64 { + if x != nil { + return x.ActiveSubscriptions + } + return 0 +} + +func (x *AdminDashboard) GetTotalAdTemplates() int64 { + if x != nil { + return x.TotalAdTemplates + } + return 0 +} + +func (x *AdminDashboard) GetNewUsersToday() int64 { + if x != nil { + return x.NewUsersToday + } + return 0 +} + +func (x *AdminDashboard) GetNewVideosToday() int64 { + if x != nil { + return x.NewVideosToday + } + return 0 +} + +type AdminUser struct { + state protoimpl.MessageState `protogen:"open.v1"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Email string `protobuf:"bytes,2,opt,name=email,proto3" json:"email,omitempty"` + Username *string `protobuf:"bytes,3,opt,name=username,proto3,oneof" json:"username,omitempty"` + Avatar *string `protobuf:"bytes,4,opt,name=avatar,proto3,oneof" json:"avatar,omitempty"` + Role *string `protobuf:"bytes,5,opt,name=role,proto3,oneof" json:"role,omitempty"` + PlanId *string `protobuf:"bytes,6,opt,name=plan_id,json=planId,proto3,oneof" json:"plan_id,omitempty"` + PlanName *string `protobuf:"bytes,7,opt,name=plan_name,json=planName,proto3,oneof" json:"plan_name,omitempty"` + StorageUsed int64 `protobuf:"varint,8,opt,name=storage_used,json=storageUsed,proto3" json:"storage_used,omitempty"` + VideoCount int64 `protobuf:"varint,9,opt,name=video_count,json=videoCount,proto3" json:"video_count,omitempty"` + WalletBalance float64 `protobuf:"fixed64,10,opt,name=wallet_balance,json=walletBalance,proto3" json:"wallet_balance,omitempty"` + CreatedAt *timestamppb.Timestamp `protobuf:"bytes,11,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AdminUser) Reset() { + *x = AdminUser{} + mi := &file_app_v1_common_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AdminUser) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AdminUser) ProtoMessage() {} + +func (x *AdminUser) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_common_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 AdminUser.ProtoReflect.Descriptor instead. +func (*AdminUser) Descriptor() ([]byte, []int) { + return file_app_v1_common_proto_rawDescGZIP(), []int{13} +} + +func (x *AdminUser) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *AdminUser) GetEmail() string { + if x != nil { + return x.Email + } + return "" +} + +func (x *AdminUser) GetUsername() string { + if x != nil && x.Username != nil { + return *x.Username + } + return "" +} + +func (x *AdminUser) GetAvatar() string { + if x != nil && x.Avatar != nil { + return *x.Avatar + } + return "" +} + +func (x *AdminUser) GetRole() string { + if x != nil && x.Role != nil { + return *x.Role + } + return "" +} + +func (x *AdminUser) GetPlanId() string { + if x != nil && x.PlanId != nil { + return *x.PlanId + } + return "" +} + +func (x *AdminUser) GetPlanName() string { + if x != nil && x.PlanName != nil { + return *x.PlanName + } + return "" +} + +func (x *AdminUser) GetStorageUsed() int64 { + if x != nil { + return x.StorageUsed + } + return 0 +} + +func (x *AdminUser) GetVideoCount() int64 { + if x != nil { + return x.VideoCount + } + return 0 +} + +func (x *AdminUser) GetWalletBalance() float64 { + if x != nil { + return x.WalletBalance + } + return 0 +} + +func (x *AdminUser) GetCreatedAt() *timestamppb.Timestamp { + if x != nil { + return x.CreatedAt + } + return nil +} + +func (x *AdminUser) GetUpdatedAt() *timestamppb.Timestamp { + if x != nil { + return x.UpdatedAt + } + return nil +} + +type AdminUserDetail struct { + state protoimpl.MessageState `protogen:"open.v1"` + User *AdminUser `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"` + Subscription *PlanSubscription `protobuf:"bytes,2,opt,name=subscription,proto3,oneof" json:"subscription,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AdminUserDetail) Reset() { + *x = AdminUserDetail{} + mi := &file_app_v1_common_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AdminUserDetail) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AdminUserDetail) ProtoMessage() {} + +func (x *AdminUserDetail) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_common_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 AdminUserDetail.ProtoReflect.Descriptor instead. +func (*AdminUserDetail) Descriptor() ([]byte, []int) { + return file_app_v1_common_proto_rawDescGZIP(), []int{14} +} + +func (x *AdminUserDetail) GetUser() *AdminUser { + if x != nil { + return x.User + } + return nil +} + +func (x *AdminUserDetail) GetSubscription() *PlanSubscription { + if x != nil { + return x.Subscription + } + return nil +} + +type AdminVideo 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"` + Title string `protobuf:"bytes,3,opt,name=title,proto3" json:"title,omitempty"` + Description *string `protobuf:"bytes,4,opt,name=description,proto3,oneof" json:"description,omitempty"` + Url string `protobuf:"bytes,5,opt,name=url,proto3" json:"url,omitempty"` + Status string `protobuf:"bytes,6,opt,name=status,proto3" json:"status,omitempty"` + Size int64 `protobuf:"varint,7,opt,name=size,proto3" json:"size,omitempty"` + Duration int32 `protobuf:"varint,8,opt,name=duration,proto3" json:"duration,omitempty"` + Format string `protobuf:"bytes,9,opt,name=format,proto3" json:"format,omitempty"` + OwnerEmail *string `protobuf:"bytes,10,opt,name=owner_email,json=ownerEmail,proto3,oneof" json:"owner_email,omitempty"` + AdTemplateId *string `protobuf:"bytes,11,opt,name=ad_template_id,json=adTemplateId,proto3,oneof" json:"ad_template_id,omitempty"` + AdTemplateName *string `protobuf:"bytes,12,opt,name=ad_template_name,json=adTemplateName,proto3,oneof" json:"ad_template_name,omitempty"` + CreatedAt *timestamppb.Timestamp `protobuf:"bytes,13,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,14,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AdminVideo) Reset() { + *x = AdminVideo{} + mi := &file_app_v1_common_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AdminVideo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AdminVideo) ProtoMessage() {} + +func (x *AdminVideo) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_common_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 AdminVideo.ProtoReflect.Descriptor instead. +func (*AdminVideo) Descriptor() ([]byte, []int) { + return file_app_v1_common_proto_rawDescGZIP(), []int{15} +} + +func (x *AdminVideo) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *AdminVideo) GetUserId() string { + if x != nil { + return x.UserId + } + return "" +} + +func (x *AdminVideo) GetTitle() string { + if x != nil { + return x.Title + } + return "" +} + +func (x *AdminVideo) GetDescription() string { + if x != nil && x.Description != nil { + return *x.Description + } + return "" +} + +func (x *AdminVideo) GetUrl() string { + if x != nil { + return x.Url + } + return "" +} + +func (x *AdminVideo) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *AdminVideo) GetSize() int64 { + if x != nil { + return x.Size + } + return 0 +} + +func (x *AdminVideo) GetDuration() int32 { + if x != nil { + return x.Duration + } + return 0 +} + +func (x *AdminVideo) GetFormat() string { + if x != nil { + return x.Format + } + return "" +} + +func (x *AdminVideo) GetOwnerEmail() string { + if x != nil && x.OwnerEmail != nil { + return *x.OwnerEmail + } + return "" +} + +func (x *AdminVideo) GetAdTemplateId() string { + if x != nil && x.AdTemplateId != nil { + return *x.AdTemplateId + } + return "" +} + +func (x *AdminVideo) GetAdTemplateName() string { + if x != nil && x.AdTemplateName != nil { + return *x.AdTemplateName + } + return "" +} + +func (x *AdminVideo) GetCreatedAt() *timestamppb.Timestamp { + if x != nil { + return x.CreatedAt + } + return nil +} + +func (x *AdminVideo) GetUpdatedAt() *timestamppb.Timestamp { + if x != nil { + return x.UpdatedAt + } + return nil +} + +type AdminPayment 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"` + PlanId *string `protobuf:"bytes,3,opt,name=plan_id,json=planId,proto3,oneof" json:"plan_id,omitempty"` + Amount float64 `protobuf:"fixed64,4,opt,name=amount,proto3" json:"amount,omitempty"` + Currency string `protobuf:"bytes,5,opt,name=currency,proto3" json:"currency,omitempty"` + Status string `protobuf:"bytes,6,opt,name=status,proto3" json:"status,omitempty"` + Provider string `protobuf:"bytes,7,opt,name=provider,proto3" json:"provider,omitempty"` + TransactionId *string `protobuf:"bytes,8,opt,name=transaction_id,json=transactionId,proto3,oneof" json:"transaction_id,omitempty"` + UserEmail *string `protobuf:"bytes,9,opt,name=user_email,json=userEmail,proto3,oneof" json:"user_email,omitempty"` + PlanName *string `protobuf:"bytes,10,opt,name=plan_name,json=planName,proto3,oneof" json:"plan_name,omitempty"` + InvoiceId string `protobuf:"bytes,11,opt,name=invoice_id,json=invoiceId,proto3" json:"invoice_id,omitempty"` + TermMonths *int32 `protobuf:"varint,12,opt,name=term_months,json=termMonths,proto3,oneof" json:"term_months,omitempty"` + PaymentMethod *string `protobuf:"bytes,13,opt,name=payment_method,json=paymentMethod,proto3,oneof" json:"payment_method,omitempty"` + ExpiresAt *string `protobuf:"bytes,14,opt,name=expires_at,json=expiresAt,proto3,oneof" json:"expires_at,omitempty"` + WalletAmount *float64 `protobuf:"fixed64,15,opt,name=wallet_amount,json=walletAmount,proto3,oneof" json:"wallet_amount,omitempty"` + TopupAmount *float64 `protobuf:"fixed64,16,opt,name=topup_amount,json=topupAmount,proto3,oneof" json:"topup_amount,omitempty"` + CreatedAt *timestamppb.Timestamp `protobuf:"bytes,17,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,18,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AdminPayment) Reset() { + *x = AdminPayment{} + mi := &file_app_v1_common_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AdminPayment) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AdminPayment) ProtoMessage() {} + +func (x *AdminPayment) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_common_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 AdminPayment.ProtoReflect.Descriptor instead. +func (*AdminPayment) Descriptor() ([]byte, []int) { + return file_app_v1_common_proto_rawDescGZIP(), []int{16} +} + +func (x *AdminPayment) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *AdminPayment) GetUserId() string { + if x != nil { + return x.UserId + } + return "" +} + +func (x *AdminPayment) GetPlanId() string { + if x != nil && x.PlanId != nil { + return *x.PlanId + } + return "" +} + +func (x *AdminPayment) GetAmount() float64 { + if x != nil { + return x.Amount + } + return 0 +} + +func (x *AdminPayment) GetCurrency() string { + if x != nil { + return x.Currency + } + return "" +} + +func (x *AdminPayment) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *AdminPayment) GetProvider() string { + if x != nil { + return x.Provider + } + return "" +} + +func (x *AdminPayment) GetTransactionId() string { + if x != nil && x.TransactionId != nil { + return *x.TransactionId + } + return "" +} + +func (x *AdminPayment) GetUserEmail() string { + if x != nil && x.UserEmail != nil { + return *x.UserEmail + } + return "" +} + +func (x *AdminPayment) GetPlanName() string { + if x != nil && x.PlanName != nil { + return *x.PlanName + } + return "" +} + +func (x *AdminPayment) GetInvoiceId() string { + if x != nil { + return x.InvoiceId + } + return "" +} + +func (x *AdminPayment) GetTermMonths() int32 { + if x != nil && x.TermMonths != nil { + return *x.TermMonths + } + return 0 +} + +func (x *AdminPayment) GetPaymentMethod() string { + if x != nil && x.PaymentMethod != nil { + return *x.PaymentMethod + } + return "" +} + +func (x *AdminPayment) GetExpiresAt() string { + if x != nil && x.ExpiresAt != nil { + return *x.ExpiresAt + } + return "" +} + +func (x *AdminPayment) GetWalletAmount() float64 { + if x != nil && x.WalletAmount != nil { + return *x.WalletAmount + } + return 0 +} + +func (x *AdminPayment) GetTopupAmount() float64 { + if x != nil && x.TopupAmount != nil { + return *x.TopupAmount + } + return 0 +} + +func (x *AdminPayment) GetCreatedAt() *timestamppb.Timestamp { + if x != nil { + return x.CreatedAt + } + return nil +} + +func (x *AdminPayment) GetUpdatedAt() *timestamppb.Timestamp { + if x != nil { + return x.UpdatedAt + } + return nil +} + +type AdminPlan struct { + state protoimpl.MessageState `protogen:"open.v1"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Description *string `protobuf:"bytes,3,opt,name=description,proto3,oneof" json:"description,omitempty"` + Features []string `protobuf:"bytes,4,rep,name=features,proto3" json:"features,omitempty"` + Price float64 `protobuf:"fixed64,5,opt,name=price,proto3" json:"price,omitempty"` + Cycle string `protobuf:"bytes,6,opt,name=cycle,proto3" json:"cycle,omitempty"` + StorageLimit int64 `protobuf:"varint,7,opt,name=storage_limit,json=storageLimit,proto3" json:"storage_limit,omitempty"` + UploadLimit int32 `protobuf:"varint,8,opt,name=upload_limit,json=uploadLimit,proto3" json:"upload_limit,omitempty"` + DurationLimit int32 `protobuf:"varint,9,opt,name=duration_limit,json=durationLimit,proto3" json:"duration_limit,omitempty"` + QualityLimit string `protobuf:"bytes,10,opt,name=quality_limit,json=qualityLimit,proto3" json:"quality_limit,omitempty"` + IsActive bool `protobuf:"varint,11,opt,name=is_active,json=isActive,proto3" json:"is_active,omitempty"` + UserCount int64 `protobuf:"varint,12,opt,name=user_count,json=userCount,proto3" json:"user_count,omitempty"` + PaymentCount int64 `protobuf:"varint,13,opt,name=payment_count,json=paymentCount,proto3" json:"payment_count,omitempty"` + SubscriptionCount int64 `protobuf:"varint,14,opt,name=subscription_count,json=subscriptionCount,proto3" json:"subscription_count,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AdminPlan) Reset() { + *x = AdminPlan{} + mi := &file_app_v1_common_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AdminPlan) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AdminPlan) ProtoMessage() {} + +func (x *AdminPlan) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_common_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 AdminPlan.ProtoReflect.Descriptor instead. +func (*AdminPlan) Descriptor() ([]byte, []int) { + return file_app_v1_common_proto_rawDescGZIP(), []int{17} +} + +func (x *AdminPlan) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *AdminPlan) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *AdminPlan) GetDescription() string { + if x != nil && x.Description != nil { + return *x.Description + } + return "" +} + +func (x *AdminPlan) GetFeatures() []string { + if x != nil { + return x.Features + } + return nil +} + +func (x *AdminPlan) GetPrice() float64 { + if x != nil { + return x.Price + } + return 0 +} + +func (x *AdminPlan) GetCycle() string { + if x != nil { + return x.Cycle + } + return "" +} + +func (x *AdminPlan) GetStorageLimit() int64 { + if x != nil { + return x.StorageLimit + } + return 0 +} + +func (x *AdminPlan) GetUploadLimit() int32 { + if x != nil { + return x.UploadLimit + } + return 0 +} + +func (x *AdminPlan) GetDurationLimit() int32 { + if x != nil { + return x.DurationLimit + } + return 0 +} + +func (x *AdminPlan) GetQualityLimit() string { + if x != nil { + return x.QualityLimit + } + return "" +} + +func (x *AdminPlan) GetIsActive() bool { + if x != nil { + return x.IsActive + } + return false +} + +func (x *AdminPlan) GetUserCount() int64 { + if x != nil { + return x.UserCount + } + return 0 +} + +func (x *AdminPlan) GetPaymentCount() int64 { + if x != nil { + return x.PaymentCount + } + return 0 +} + +func (x *AdminPlan) GetSubscriptionCount() int64 { + if x != nil { + return x.SubscriptionCount + } + return 0 +} + +type AdminAdTemplate 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"` + Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` + Description *string `protobuf:"bytes,4,opt,name=description,proto3,oneof" json:"description,omitempty"` + VastTagUrl string `protobuf:"bytes,5,opt,name=vast_tag_url,json=vastTagUrl,proto3" json:"vast_tag_url,omitempty"` + AdFormat string `protobuf:"bytes,6,opt,name=ad_format,json=adFormat,proto3" json:"ad_format,omitempty"` + Duration *int64 `protobuf:"varint,7,opt,name=duration,proto3,oneof" json:"duration,omitempty"` + IsActive bool `protobuf:"varint,8,opt,name=is_active,json=isActive,proto3" json:"is_active,omitempty"` + IsDefault bool `protobuf:"varint,9,opt,name=is_default,json=isDefault,proto3" json:"is_default,omitempty"` + OwnerEmail *string `protobuf:"bytes,10,opt,name=owner_email,json=ownerEmail,proto3,oneof" json:"owner_email,omitempty"` + CreatedAt *timestamppb.Timestamp `protobuf:"bytes,11,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AdminAdTemplate) Reset() { + *x = AdminAdTemplate{} + mi := &file_app_v1_common_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AdminAdTemplate) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AdminAdTemplate) ProtoMessage() {} + +func (x *AdminAdTemplate) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_common_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 AdminAdTemplate.ProtoReflect.Descriptor instead. +func (*AdminAdTemplate) Descriptor() ([]byte, []int) { + return file_app_v1_common_proto_rawDescGZIP(), []int{18} +} + +func (x *AdminAdTemplate) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *AdminAdTemplate) GetUserId() string { + if x != nil { + return x.UserId + } + return "" +} + +func (x *AdminAdTemplate) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *AdminAdTemplate) GetDescription() string { + if x != nil && x.Description != nil { + return *x.Description + } + return "" +} + +func (x *AdminAdTemplate) GetVastTagUrl() string { + if x != nil { + return x.VastTagUrl + } + return "" +} + +func (x *AdminAdTemplate) GetAdFormat() string { + if x != nil { + return x.AdFormat + } + return "" +} + +func (x *AdminAdTemplate) GetDuration() int64 { + if x != nil && x.Duration != nil { + return *x.Duration + } + return 0 +} + +func (x *AdminAdTemplate) GetIsActive() bool { + if x != nil { + return x.IsActive + } + return false +} + +func (x *AdminAdTemplate) GetIsDefault() bool { + if x != nil { + return x.IsDefault + } + return false +} + +func (x *AdminAdTemplate) GetOwnerEmail() string { + if x != nil && x.OwnerEmail != nil { + return *x.OwnerEmail + } + return "" +} + +func (x *AdminAdTemplate) GetCreatedAt() *timestamppb.Timestamp { + if x != nil { + return x.CreatedAt + } + return nil +} + +func (x *AdminAdTemplate) 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"` + Status string `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` + Priority int32 `protobuf:"varint,3,opt,name=priority,proto3" json:"priority,omitempty"` + UserId string `protobuf:"bytes,4,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` + Name string `protobuf:"bytes,5,opt,name=name,proto3" json:"name,omitempty"` + TimeLimit int64 `protobuf:"varint,6,opt,name=time_limit,json=timeLimit,proto3" json:"time_limit,omitempty"` + InputUrl string `protobuf:"bytes,7,opt,name=input_url,json=inputUrl,proto3" json:"input_url,omitempty"` + OutputUrl string `protobuf:"bytes,8,opt,name=output_url,json=outputUrl,proto3" json:"output_url,omitempty"` + TotalDuration int64 `protobuf:"varint,9,opt,name=total_duration,json=totalDuration,proto3" json:"total_duration,omitempty"` + CurrentTime int64 `protobuf:"varint,10,opt,name=current_time,json=currentTime,proto3" json:"current_time,omitempty"` + Progress float64 `protobuf:"fixed64,11,opt,name=progress,proto3" json:"progress,omitempty"` + AgentId *string `protobuf:"bytes,12,opt,name=agent_id,json=agentId,proto3,oneof" json:"agent_id,omitempty"` + Logs string `protobuf:"bytes,13,opt,name=logs,proto3" json:"logs,omitempty"` + Config string `protobuf:"bytes,14,opt,name=config,proto3" json:"config,omitempty"` + Cancelled bool `protobuf:"varint,15,opt,name=cancelled,proto3" json:"cancelled,omitempty"` + RetryCount int32 `protobuf:"varint,16,opt,name=retry_count,json=retryCount,proto3" json:"retry_count,omitempty"` + MaxRetries int32 `protobuf:"varint,17,opt,name=max_retries,json=maxRetries,proto3" json:"max_retries,omitempty"` + CreatedAt *timestamppb.Timestamp `protobuf:"bytes,18,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,19,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AdminJob) Reset() { + *x = AdminJob{} + mi := &file_app_v1_common_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AdminJob) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AdminJob) ProtoMessage() {} + +func (x *AdminJob) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_common_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 AdminJob.ProtoReflect.Descriptor instead. +func (*AdminJob) Descriptor() ([]byte, []int) { + return file_app_v1_common_proto_rawDescGZIP(), []int{19} +} + +func (x *AdminJob) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *AdminJob) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *AdminJob) GetPriority() int32 { + if x != nil { + return x.Priority + } + return 0 +} + +func (x *AdminJob) GetUserId() string { + if x != nil { + return x.UserId + } + return "" +} + +func (x *AdminJob) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *AdminJob) GetTimeLimit() int64 { + if x != nil { + return x.TimeLimit + } + return 0 +} + +func (x *AdminJob) GetInputUrl() string { + if x != nil { + return x.InputUrl + } + return "" +} + +func (x *AdminJob) GetOutputUrl() string { + if x != nil { + return x.OutputUrl + } + return "" +} + +func (x *AdminJob) GetTotalDuration() int64 { + if x != nil { + return x.TotalDuration + } + return 0 +} + +func (x *AdminJob) GetCurrentTime() int64 { + if x != nil { + return x.CurrentTime + } + return 0 +} + +func (x *AdminJob) GetProgress() float64 { + if x != nil { + return x.Progress + } + return 0 +} + +func (x *AdminJob) GetAgentId() string { + if x != nil && x.AgentId != nil { + return *x.AgentId + } + return "" +} + +func (x *AdminJob) GetLogs() string { + if x != nil { + return x.Logs + } + return "" +} + +func (x *AdminJob) GetConfig() string { + if x != nil { + return x.Config + } + return "" +} + +func (x *AdminJob) GetCancelled() bool { + if x != nil { + return x.Cancelled + } + return false +} + +func (x *AdminJob) GetRetryCount() int32 { + if x != nil { + return x.RetryCount + } + return 0 +} + +func (x *AdminJob) GetMaxRetries() int32 { + if x != nil { + return x.MaxRetries + } + return 0 +} + +func (x *AdminJob) GetCreatedAt() *timestamppb.Timestamp { + if x != nil { + return x.CreatedAt + } + return nil +} + +func (x *AdminJob) GetUpdatedAt() *timestamppb.Timestamp { + if x != nil { + return x.UpdatedAt + } + return nil +} + +type AdminAgent struct { + state protoimpl.MessageState `protogen:"open.v1"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Platform string `protobuf:"bytes,3,opt,name=platform,proto3" json:"platform,omitempty"` + Backend string `protobuf:"bytes,4,opt,name=backend,proto3" json:"backend,omitempty"` + Version string `protobuf:"bytes,5,opt,name=version,proto3" json:"version,omitempty"` + Capacity int32 `protobuf:"varint,6,opt,name=capacity,proto3" json:"capacity,omitempty"` + Status string `protobuf:"bytes,7,opt,name=status,proto3" json:"status,omitempty"` + Cpu float64 `protobuf:"fixed64,8,opt,name=cpu,proto3" json:"cpu,omitempty"` + Ram float64 `protobuf:"fixed64,9,opt,name=ram,proto3" json:"ram,omitempty"` + LastHeartbeat *timestamppb.Timestamp `protobuf:"bytes,10,opt,name=last_heartbeat,json=lastHeartbeat,proto3" json:"last_heartbeat,omitempty"` + CreatedAt *timestamppb.Timestamp `protobuf:"bytes,11,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AdminAgent) Reset() { + *x = AdminAgent{} + mi := &file_app_v1_common_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AdminAgent) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AdminAgent) ProtoMessage() {} + +func (x *AdminAgent) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_common_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 AdminAgent.ProtoReflect.Descriptor instead. +func (*AdminAgent) Descriptor() ([]byte, []int) { + return file_app_v1_common_proto_rawDescGZIP(), []int{20} +} + +func (x *AdminAgent) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *AdminAgent) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *AdminAgent) GetPlatform() string { + if x != nil { + return x.Platform + } + return "" +} + +func (x *AdminAgent) GetBackend() string { + if x != nil { + return x.Backend + } + return "" +} + +func (x *AdminAgent) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +func (x *AdminAgent) GetCapacity() int32 { + if x != nil { + return x.Capacity + } + return 0 +} + +func (x *AdminAgent) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *AdminAgent) GetCpu() float64 { + if x != nil { + return x.Cpu + } + return 0 +} + +func (x *AdminAgent) GetRam() float64 { + if x != nil { + return x.Ram + } + return 0 +} + +func (x *AdminAgent) GetLastHeartbeat() *timestamppb.Timestamp { + if x != nil { + return x.LastHeartbeat + } + return nil +} + +func (x *AdminAgent) GetCreatedAt() *timestamppb.Timestamp { + if x != nil { + return x.CreatedAt + } + return nil +} + +func (x *AdminAgent) GetUpdatedAt() *timestamppb.Timestamp { + if x != nil { + return x.UpdatedAt + } + return nil +} + +var File_app_v1_common_proto protoreflect.FileDescriptor + +const file_app_v1_common_proto_rawDesc = "" + + "\n" + + "\x13app/v1/common.proto\x12\rstream.app.v1\x1a\x1fgoogle/protobuf/timestamp.proto\"+\n" + + "\x0fMessageResponse\x12\x18\n" + + "\amessage\x18\x01 \x01(\tR\amessage\"\xb9\x06\n" + + "\x04User\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\x12\x14\n" + + "\x05email\x18\x02 \x01(\tR\x05email\x12\x1f\n" + + "\busername\x18\x03 \x01(\tH\x00R\busername\x88\x01\x01\x12\x1b\n" + + "\x06avatar\x18\x04 \x01(\tH\x01R\x06avatar\x88\x01\x01\x12\x17\n" + + "\x04role\x18\x05 \x01(\tH\x02R\x04role\x88\x01\x01\x12 \n" + + "\tgoogle_id\x18\x06 \x01(\tH\x03R\bgoogleId\x88\x01\x01\x12!\n" + + "\fstorage_used\x18\a \x01(\x03R\vstorageUsed\x12\x1c\n" + + "\aplan_id\x18\b \x01(\tH\x04R\x06planId\x88\x01\x01\x12B\n" + + "\x0fplan_started_at\x18\t \x01(\v2\x1a.google.protobuf.TimestampR\rplanStartedAt\x12B\n" + + "\x0fplan_expires_at\x18\n" + + " \x01(\v2\x1a.google.protobuf.TimestampR\rplanExpiresAt\x12-\n" + + "\x10plan_term_months\x18\v \x01(\x05H\x05R\x0eplanTermMonths\x88\x01\x01\x123\n" + + "\x13plan_payment_method\x18\f \x01(\tH\x06R\x11planPaymentMethod\x88\x01\x01\x12,\n" + + "\x12plan_expiring_soon\x18\r \x01(\bR\x10planExpiringSoon\x12%\n" + + "\x0ewallet_balance\x18\x0e \x01(\x01R\rwalletBalance\x12\x1a\n" + + "\blanguage\x18\x0f \x01(\tR\blanguage\x12\x16\n" + + "\x06locale\x18\x10 \x01(\tR\x06locale\x129\n" + + "\n" + + "created_at\x18\x11 \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\x129\n" + + "\n" + + "updated_at\x18\x12 \x01(\v2\x1a.google.protobuf.TimestampR\tupdatedAtB\v\n" + + "\t_usernameB\t\n" + + "\a_avatarB\a\n" + + "\x05_roleB\f\n" + + "\n" + + "_google_idB\n" + + "\n" + + "\b_plan_idB\x13\n" + + "\x11_plan_term_monthsB\x16\n" + + "\x14_plan_payment_method\"\xc8\x03\n" + + "\vPreferences\x12/\n" + + "\x13email_notifications\x18\x01 \x01(\bR\x12emailNotifications\x12-\n" + + "\x12push_notifications\x18\x02 \x01(\bR\x11pushNotifications\x127\n" + + "\x17marketing_notifications\x18\x03 \x01(\bR\x16marketingNotifications\x125\n" + + "\x16telegram_notifications\x18\x04 \x01(\bR\x15telegramNotifications\x12\x1a\n" + + "\bautoplay\x18\x05 \x01(\bR\bautoplay\x12\x12\n" + + "\x04loop\x18\x06 \x01(\bR\x04loop\x12\x14\n" + + "\x05muted\x18\a \x01(\bR\x05muted\x12#\n" + + "\rshow_controls\x18\b \x01(\bR\fshowControls\x12\x10\n" + + "\x03pip\x18\t \x01(\bR\x03pip\x12\x18\n" + + "\aairplay\x18\n" + + " \x01(\bR\aairplay\x12\x1e\n" + + "\n" + + "chromecast\x18\v \x01(\bR\n" + + "chromecast\x12\x1a\n" + + "\blanguage\x18\f \x01(\tR\blanguage\x12\x16\n" + + "\x06locale\x18\r \x01(\tR\x06locale\"\x9d\x02\n" + + "\fNotification\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n" + + "\x04type\x18\x02 \x01(\tR\x04type\x12\x14\n" + + "\x05title\x18\x03 \x01(\tR\x05title\x12\x18\n" + + "\amessage\x18\x04 \x01(\tR\amessage\x12\x12\n" + + "\x04read\x18\x05 \x01(\bR\x04read\x12\"\n" + + "\n" + + "action_url\x18\x06 \x01(\tH\x00R\tactionUrl\x88\x01\x01\x12&\n" + + "\faction_label\x18\a \x01(\tH\x01R\vactionLabel\x88\x01\x01\x129\n" + + "\n" + + "created_at\x18\b \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAtB\r\n" + + "\v_action_urlB\x0f\n" + + "\r_action_label\"\xa2\x01\n" + + "\x06Domain\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n" + + "\x04name\x18\x02 \x01(\tR\x04name\x129\n" + + "\n" + + "created_at\x18\x03 \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\x129\n" + + "\n" + + "updated_at\x18\x04 \x01(\v2\x1a.google.protobuf.TimestampR\tupdatedAt\"\x86\x03\n" + + "\n" + + "AdTemplate\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n" + + "\x04name\x18\x02 \x01(\tR\x04name\x12%\n" + + "\vdescription\x18\x03 \x01(\tH\x00R\vdescription\x88\x01\x01\x12 \n" + + "\fvast_tag_url\x18\x04 \x01(\tR\n" + + "vastTagUrl\x12\x1b\n" + + "\tad_format\x18\x05 \x01(\tR\badFormat\x12\x1f\n" + + "\bduration\x18\x06 \x01(\x05H\x01R\bduration\x88\x01\x01\x12\x1b\n" + + "\tis_active\x18\a \x01(\bR\bisActive\x12\x1d\n" + + "\n" + + "is_default\x18\b \x01(\bR\tisDefault\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_descriptionB\v\n" + + "\t_duration\"\xda\x02\n" + + "\x04Plan\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n" + + "\x04name\x18\x02 \x01(\tR\x04name\x12%\n" + + "\vdescription\x18\x03 \x01(\tH\x00R\vdescription\x88\x01\x01\x12\x14\n" + + "\x05price\x18\x04 \x01(\x01R\x05price\x12\x14\n" + + "\x05cycle\x18\x05 \x01(\tR\x05cycle\x12#\n" + + "\rstorage_limit\x18\x06 \x01(\x03R\fstorageLimit\x12!\n" + + "\fupload_limit\x18\a \x01(\x05R\vuploadLimit\x12%\n" + + "\x0eduration_limit\x18\b \x01(\x05R\rdurationLimit\x12#\n" + + "\rquality_limit\x18\t \x01(\tR\fqualityLimit\x12\x1a\n" + + "\bfeatures\x18\n" + + " \x03(\tR\bfeatures\x12\x1b\n" + + "\tis_active\x18\v \x01(\bR\bisActiveB\x0e\n" + + "\f_description\"\xf9\x02\n" + + "\aPayment\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\x12\x17\n" + + "\auser_id\x18\x02 \x01(\tR\x06userId\x12\x1c\n" + + "\aplan_id\x18\x03 \x01(\tH\x00R\x06planId\x88\x01\x01\x12\x16\n" + + "\x06amount\x18\x04 \x01(\x01R\x06amount\x12\x1a\n" + + "\bcurrency\x18\x05 \x01(\tR\bcurrency\x12\x16\n" + + "\x06status\x18\x06 \x01(\tR\x06status\x12\x1a\n" + + "\bprovider\x18\a \x01(\tR\bprovider\x12*\n" + + "\x0etransaction_id\x18\b \x01(\tH\x01R\rtransactionId\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\n" + + "\n" + + "\b_plan_idB\x11\n" + + "\x0f_transaction_id\"\xef\x03\n" + + "\x10PlanSubscription\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\x12\x17\n" + + "\auser_id\x18\x02 \x01(\tR\x06userId\x12\x1d\n" + + "\n" + + "payment_id\x18\x03 \x01(\tR\tpaymentId\x12\x17\n" + + "\aplan_id\x18\x04 \x01(\tR\x06planId\x12\x1f\n" + + "\vterm_months\x18\x05 \x01(\x05R\n" + + "termMonths\x12%\n" + + "\x0epayment_method\x18\x06 \x01(\tR\rpaymentMethod\x12#\n" + + "\rwallet_amount\x18\a \x01(\x01R\fwalletAmount\x12!\n" + + "\ftopup_amount\x18\b \x01(\x01R\vtopupAmount\x129\n" + + "\n" + + "started_at\x18\t \x01(\v2\x1a.google.protobuf.TimestampR\tstartedAt\x129\n" + + "\n" + + "expires_at\x18\n" + + " \x01(\v2\x1a.google.protobuf.TimestampR\texpiresAt\x129\n" + + "\n" + + "created_at\x18\v \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\x129\n" + + "\n" + + "updated_at\x18\f \x01(\v2\x1a.google.protobuf.TimestampR\tupdatedAt\"\xaf\x03\n" + + "\x11WalletTransaction\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\x16\n" + + "\x06amount\x18\x04 \x01(\x01R\x06amount\x12\x1a\n" + + "\bcurrency\x18\x05 \x01(\tR\bcurrency\x12\x17\n" + + "\x04note\x18\x06 \x01(\tH\x00R\x04note\x88\x01\x01\x12\"\n" + + "\n" + + "payment_id\x18\a \x01(\tH\x01R\tpaymentId\x88\x01\x01\x12\x1c\n" + + "\aplan_id\x18\b \x01(\tH\x02R\x06planId\x88\x01\x01\x12$\n" + + "\vterm_months\x18\t \x01(\x05H\x03R\n" + + "termMonths\x88\x01\x01\x129\n" + + "\n" + + "created_at\x18\n" + + " \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\x129\n" + + "\n" + + "updated_at\x18\v \x01(\v2\x1a.google.protobuf.TimestampR\tupdatedAtB\a\n" + + "\x05_noteB\r\n" + + "\v_payment_idB\n" + + "\n" + + "\b_plan_idB\x0e\n" + + "\f_term_months\"\xe8\x03\n" + + "\x12PaymentHistoryItem\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\x12\x16\n" + + "\x06amount\x18\x02 \x01(\x01R\x06amount\x12\x1a\n" + + "\bcurrency\x18\x03 \x01(\tR\bcurrency\x12\x16\n" + + "\x06status\x18\x04 \x01(\tR\x06status\x12\x1c\n" + + "\aplan_id\x18\x05 \x01(\tH\x00R\x06planId\x88\x01\x01\x12 \n" + + "\tplan_name\x18\x06 \x01(\tH\x01R\bplanName\x88\x01\x01\x12\x1d\n" + + "\n" + + "invoice_id\x18\a \x01(\tR\tinvoiceId\x12\x12\n" + + "\x04kind\x18\b \x01(\tR\x04kind\x12$\n" + + "\vterm_months\x18\t \x01(\x05H\x02R\n" + + "termMonths\x88\x01\x01\x12*\n" + + "\x0epayment_method\x18\n" + + " \x01(\tH\x03R\rpaymentMethod\x88\x01\x01\x129\n" + + "\n" + + "expires_at\x18\v \x01(\v2\x1a.google.protobuf.TimestampR\texpiresAt\x129\n" + + "\n" + + "created_at\x18\f \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAtB\n" + + "\n" + + "\b_plan_idB\f\n" + + "\n" + + "_plan_nameB\x0e\n" + + "\f_term_monthsB\x11\n" + + "\x0f_payment_method\"\x97\x04\n" + + "\x05Video\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\x12\x17\n" + + "\auser_id\x18\x02 \x01(\tR\x06userId\x12\x14\n" + + "\x05title\x18\x03 \x01(\tR\x05title\x12%\n" + + "\vdescription\x18\x04 \x01(\tH\x00R\vdescription\x88\x01\x01\x12\x10\n" + + "\x03url\x18\x05 \x01(\tR\x03url\x12\x16\n" + + "\x06status\x18\x06 \x01(\tR\x06status\x12\x12\n" + + "\x04size\x18\a \x01(\x03R\x04size\x12\x1a\n" + + "\bduration\x18\b \x01(\x05R\bduration\x12\x16\n" + + "\x06format\x18\t \x01(\tR\x06format\x12!\n" + + "\tthumbnail\x18\n" + + " \x01(\tH\x01R\tthumbnail\x88\x01\x01\x120\n" + + "\x11processing_status\x18\v \x01(\tH\x02R\x10processingStatus\x88\x01\x01\x12&\n" + + "\fstorage_type\x18\f \x01(\tH\x03R\vstorageType\x88\x01\x01\x129\n" + + "\n" + + "created_at\x18\r \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\x129\n" + + "\n" + + "updated_at\x18\x0e \x01(\v2\x1a.google.protobuf.TimestampR\tupdatedAtB\x0e\n" + + "\f_descriptionB\f\n" + + "\n" + + "_thumbnailB\x14\n" + + "\x12_processing_statusB\x0f\n" + + "\r_storage_type\"\x81\x03\n" + + "\x0eAdminDashboard\x12\x1f\n" + + "\vtotal_users\x18\x01 \x01(\x03R\n" + + "totalUsers\x12!\n" + + "\ftotal_videos\x18\x02 \x01(\x03R\vtotalVideos\x12,\n" + + "\x12total_storage_used\x18\x03 \x01(\x03R\x10totalStorageUsed\x12%\n" + + "\x0etotal_payments\x18\x04 \x01(\x03R\rtotalPayments\x12#\n" + + "\rtotal_revenue\x18\x05 \x01(\x01R\ftotalRevenue\x121\n" + + "\x14active_subscriptions\x18\x06 \x01(\x03R\x13activeSubscriptions\x12,\n" + + "\x12total_ad_templates\x18\a \x01(\x03R\x10totalAdTemplates\x12&\n" + + "\x0fnew_users_today\x18\b \x01(\x03R\rnewUsersToday\x12(\n" + + "\x10new_videos_today\x18\t \x01(\x03R\x0enewVideosToday\"\xe4\x03\n" + + "\tAdminUser\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\x12\x14\n" + + "\x05email\x18\x02 \x01(\tR\x05email\x12\x1f\n" + + "\busername\x18\x03 \x01(\tH\x00R\busername\x88\x01\x01\x12\x1b\n" + + "\x06avatar\x18\x04 \x01(\tH\x01R\x06avatar\x88\x01\x01\x12\x17\n" + + "\x04role\x18\x05 \x01(\tH\x02R\x04role\x88\x01\x01\x12\x1c\n" + + "\aplan_id\x18\x06 \x01(\tH\x03R\x06planId\x88\x01\x01\x12 \n" + + "\tplan_name\x18\a \x01(\tH\x04R\bplanName\x88\x01\x01\x12!\n" + + "\fstorage_used\x18\b \x01(\x03R\vstorageUsed\x12\x1f\n" + + "\vvideo_count\x18\t \x01(\x03R\n" + + "videoCount\x12%\n" + + "\x0ewallet_balance\x18\n" + + " \x01(\x01R\rwalletBalance\x129\n" + + "\n" + + "created_at\x18\v \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\x129\n" + + "\n" + + "updated_at\x18\f \x01(\v2\x1a.google.protobuf.TimestampR\tupdatedAtB\v\n" + + "\t_usernameB\t\n" + + "\a_avatarB\a\n" + + "\x05_roleB\n" + + "\n" + + "\b_plan_idB\f\n" + + "\n" + + "_plan_name\"\x9a\x01\n" + + "\x0fAdminUserDetail\x12,\n" + + "\x04user\x18\x01 \x01(\v2\x18.stream.app.v1.AdminUserR\x04user\x12H\n" + + "\fsubscription\x18\x02 \x01(\v2\x1f.stream.app.v1.PlanSubscriptionH\x00R\fsubscription\x88\x01\x01B\x0f\n" + + "\r_subscription\"\xa2\x04\n" + + "\n" + + "AdminVideo\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\x12\x17\n" + + "\auser_id\x18\x02 \x01(\tR\x06userId\x12\x14\n" + + "\x05title\x18\x03 \x01(\tR\x05title\x12%\n" + + "\vdescription\x18\x04 \x01(\tH\x00R\vdescription\x88\x01\x01\x12\x10\n" + + "\x03url\x18\x05 \x01(\tR\x03url\x12\x16\n" + + "\x06status\x18\x06 \x01(\tR\x06status\x12\x12\n" + + "\x04size\x18\a \x01(\x03R\x04size\x12\x1a\n" + + "\bduration\x18\b \x01(\x05R\bduration\x12\x16\n" + + "\x06format\x18\t \x01(\tR\x06format\x12$\n" + + "\vowner_email\x18\n" + + " \x01(\tH\x01R\n" + + "ownerEmail\x88\x01\x01\x12)\n" + + "\x0ead_template_id\x18\v \x01(\tH\x02R\fadTemplateId\x88\x01\x01\x12-\n" + + "\x10ad_template_name\x18\f \x01(\tH\x03R\x0eadTemplateName\x88\x01\x01\x129\n" + + "\n" + + "created_at\x18\r \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\x129\n" + + "\n" + + "updated_at\x18\x0e \x01(\v2\x1a.google.protobuf.TimestampR\tupdatedAtB\x0e\n" + + "\f_descriptionB\x0e\n" + + "\f_owner_emailB\x11\n" + + "\x0f_ad_template_idB\x13\n" + + "\x11_ad_template_name\"\x9d\x06\n" + + "\fAdminPayment\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\x12\x17\n" + + "\auser_id\x18\x02 \x01(\tR\x06userId\x12\x1c\n" + + "\aplan_id\x18\x03 \x01(\tH\x00R\x06planId\x88\x01\x01\x12\x16\n" + + "\x06amount\x18\x04 \x01(\x01R\x06amount\x12\x1a\n" + + "\bcurrency\x18\x05 \x01(\tR\bcurrency\x12\x16\n" + + "\x06status\x18\x06 \x01(\tR\x06status\x12\x1a\n" + + "\bprovider\x18\a \x01(\tR\bprovider\x12*\n" + + "\x0etransaction_id\x18\b \x01(\tH\x01R\rtransactionId\x88\x01\x01\x12\"\n" + + "\n" + + "user_email\x18\t \x01(\tH\x02R\tuserEmail\x88\x01\x01\x12 \n" + + "\tplan_name\x18\n" + + " \x01(\tH\x03R\bplanName\x88\x01\x01\x12\x1d\n" + + "\n" + + "invoice_id\x18\v \x01(\tR\tinvoiceId\x12$\n" + + "\vterm_months\x18\f \x01(\x05H\x04R\n" + + "termMonths\x88\x01\x01\x12*\n" + + "\x0epayment_method\x18\r \x01(\tH\x05R\rpaymentMethod\x88\x01\x01\x12\"\n" + + "\n" + + "expires_at\x18\x0e \x01(\tH\x06R\texpiresAt\x88\x01\x01\x12(\n" + + "\rwallet_amount\x18\x0f \x01(\x01H\aR\fwalletAmount\x88\x01\x01\x12&\n" + + "\ftopup_amount\x18\x10 \x01(\x01H\bR\vtopupAmount\x88\x01\x01\x129\n" + + "\n" + + "created_at\x18\x11 \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\x129\n" + + "\n" + + "updated_at\x18\x12 \x01(\v2\x1a.google.protobuf.TimestampR\tupdatedAtB\n" + + "\n" + + "\b_plan_idB\x11\n" + + "\x0f_transaction_idB\r\n" + + "\v_user_emailB\f\n" + + "\n" + + "_plan_nameB\x0e\n" + + "\f_term_monthsB\x11\n" + + "\x0f_payment_methodB\r\n" + + "\v_expires_atB\x10\n" + + "\x0e_wallet_amountB\x0f\n" + + "\r_topup_amount\"\xd2\x03\n" + + "\tAdminPlan\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n" + + "\x04name\x18\x02 \x01(\tR\x04name\x12%\n" + + "\vdescription\x18\x03 \x01(\tH\x00R\vdescription\x88\x01\x01\x12\x1a\n" + + "\bfeatures\x18\x04 \x03(\tR\bfeatures\x12\x14\n" + + "\x05price\x18\x05 \x01(\x01R\x05price\x12\x14\n" + + "\x05cycle\x18\x06 \x01(\tR\x05cycle\x12#\n" + + "\rstorage_limit\x18\a \x01(\x03R\fstorageLimit\x12!\n" + + "\fupload_limit\x18\b \x01(\x05R\vuploadLimit\x12%\n" + + "\x0eduration_limit\x18\t \x01(\x05R\rdurationLimit\x12#\n" + + "\rquality_limit\x18\n" + + " \x01(\tR\fqualityLimit\x12\x1b\n" + + "\tis_active\x18\v \x01(\bR\bisActive\x12\x1d\n" + + "\n" + + "user_count\x18\f \x01(\x03R\tuserCount\x12#\n" + + "\rpayment_count\x18\r \x01(\x03R\fpaymentCount\x12-\n" + + "\x12subscription_count\x18\x0e \x01(\x03R\x11subscriptionCountB\x0e\n" + + "\f_description\"\xda\x03\n" + + "\x0fAdminAdTemplate\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\x12\x17\n" + + "\auser_id\x18\x02 \x01(\tR\x06userId\x12\x12\n" + + "\x04name\x18\x03 \x01(\tR\x04name\x12%\n" + + "\vdescription\x18\x04 \x01(\tH\x00R\vdescription\x88\x01\x01\x12 \n" + + "\fvast_tag_url\x18\x05 \x01(\tR\n" + + "vastTagUrl\x12\x1b\n" + + "\tad_format\x18\x06 \x01(\tR\badFormat\x12\x1f\n" + + "\bduration\x18\a \x01(\x03H\x01R\bduration\x88\x01\x01\x12\x1b\n" + + "\tis_active\x18\b \x01(\bR\bisActive\x12\x1d\n" + + "\n" + + "is_default\x18\t \x01(\bR\tisDefault\x12$\n" + + "\vowner_email\x18\n" + + " \x01(\tH\x02R\n" + + "ownerEmail\x88\x01\x01\x129\n" + + "\n" + + "created_at\x18\v \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\x129\n" + + "\n" + + "updated_at\x18\f \x01(\v2\x1a.google.protobuf.TimestampR\tupdatedAtB\x0e\n" + + "\f_descriptionB\v\n" + + "\t_durationB\x0e\n" + + "\f_owner_email\"\xeb\x04\n" + + "\bAdminJob\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\x12\x16\n" + + "\x06status\x18\x02 \x01(\tR\x06status\x12\x1a\n" + + "\bpriority\x18\x03 \x01(\x05R\bpriority\x12\x17\n" + + "\auser_id\x18\x04 \x01(\tR\x06userId\x12\x12\n" + + "\x04name\x18\x05 \x01(\tR\x04name\x12\x1d\n" + + "\n" + + "time_limit\x18\x06 \x01(\x03R\ttimeLimit\x12\x1b\n" + + "\tinput_url\x18\a \x01(\tR\binputUrl\x12\x1d\n" + + "\n" + + "output_url\x18\b \x01(\tR\toutputUrl\x12%\n" + + "\x0etotal_duration\x18\t \x01(\x03R\rtotalDuration\x12!\n" + + "\fcurrent_time\x18\n" + + " \x01(\x03R\vcurrentTime\x12\x1a\n" + + "\bprogress\x18\v \x01(\x01R\bprogress\x12\x1e\n" + + "\bagent_id\x18\f \x01(\tH\x00R\aagentId\x88\x01\x01\x12\x12\n" + + "\x04logs\x18\r \x01(\tR\x04logs\x12\x16\n" + + "\x06config\x18\x0e \x01(\tR\x06config\x12\x1c\n" + + "\tcancelled\x18\x0f \x01(\bR\tcancelled\x12\x1f\n" + + "\vretry_count\x18\x10 \x01(\x05R\n" + + "retryCount\x12\x1f\n" + + "\vmax_retries\x18\x11 \x01(\x05R\n" + + "maxRetries\x129\n" + + "\n" + + "created_at\x18\x12 \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\x129\n" + + "\n" + + "updated_at\x18\x13 \x01(\v2\x1a.google.protobuf.TimestampR\tupdatedAtB\v\n" + + "\t_agent_id\"\x91\x03\n" + + "\n" + + "AdminAgent\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n" + + "\x04name\x18\x02 \x01(\tR\x04name\x12\x1a\n" + + "\bplatform\x18\x03 \x01(\tR\bplatform\x12\x18\n" + + "\abackend\x18\x04 \x01(\tR\abackend\x12\x18\n" + + "\aversion\x18\x05 \x01(\tR\aversion\x12\x1a\n" + + "\bcapacity\x18\x06 \x01(\x05R\bcapacity\x12\x16\n" + + "\x06status\x18\a \x01(\tR\x06status\x12\x10\n" + + "\x03cpu\x18\b \x01(\x01R\x03cpu\x12\x10\n" + + "\x03ram\x18\t \x01(\x01R\x03ram\x12A\n" + + "\x0elast_heartbeat\x18\n" + + " \x01(\v2\x1a.google.protobuf.TimestampR\rlastHeartbeat\x129\n" + + "\n" + + "created_at\x18\v \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\x129\n" + + "\n" + + "updated_at\x18\f \x01(\v2\x1a.google.protobuf.TimestampR\tupdatedAtB,Z*stream.api/internal/gen/proto/app/v1;appv1b\x06proto3" + +var ( + file_app_v1_common_proto_rawDescOnce sync.Once + file_app_v1_common_proto_rawDescData []byte +) + +func file_app_v1_common_proto_rawDescGZIP() []byte { + file_app_v1_common_proto_rawDescOnce.Do(func() { + file_app_v1_common_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_v1_common_proto_rawDesc), len(file_app_v1_common_proto_rawDesc))) + }) + return file_app_v1_common_proto_rawDescData +} + +var file_app_v1_common_proto_msgTypes = make([]protoimpl.MessageInfo, 21) +var file_app_v1_common_proto_goTypes = []any{ + (*MessageResponse)(nil), // 0: stream.app.v1.MessageResponse + (*User)(nil), // 1: stream.app.v1.User + (*Preferences)(nil), // 2: stream.app.v1.Preferences + (*Notification)(nil), // 3: stream.app.v1.Notification + (*Domain)(nil), // 4: stream.app.v1.Domain + (*AdTemplate)(nil), // 5: stream.app.v1.AdTemplate + (*Plan)(nil), // 6: stream.app.v1.Plan + (*Payment)(nil), // 7: stream.app.v1.Payment + (*PlanSubscription)(nil), // 8: stream.app.v1.PlanSubscription + (*WalletTransaction)(nil), // 9: stream.app.v1.WalletTransaction + (*PaymentHistoryItem)(nil), // 10: stream.app.v1.PaymentHistoryItem + (*Video)(nil), // 11: stream.app.v1.Video + (*AdminDashboard)(nil), // 12: stream.app.v1.AdminDashboard + (*AdminUser)(nil), // 13: stream.app.v1.AdminUser + (*AdminUserDetail)(nil), // 14: stream.app.v1.AdminUserDetail + (*AdminVideo)(nil), // 15: stream.app.v1.AdminVideo + (*AdminPayment)(nil), // 16: stream.app.v1.AdminPayment + (*AdminPlan)(nil), // 17: stream.app.v1.AdminPlan + (*AdminAdTemplate)(nil), // 18: stream.app.v1.AdminAdTemplate + (*AdminJob)(nil), // 19: stream.app.v1.AdminJob + (*AdminAgent)(nil), // 20: stream.app.v1.AdminAgent + (*timestamppb.Timestamp)(nil), // 21: google.protobuf.Timestamp +} +var file_app_v1_common_proto_depIdxs = []int32{ + 21, // 0: stream.app.v1.User.plan_started_at:type_name -> google.protobuf.Timestamp + 21, // 1: stream.app.v1.User.plan_expires_at:type_name -> google.protobuf.Timestamp + 21, // 2: stream.app.v1.User.created_at:type_name -> google.protobuf.Timestamp + 21, // 3: stream.app.v1.User.updated_at:type_name -> google.protobuf.Timestamp + 21, // 4: stream.app.v1.Notification.created_at:type_name -> google.protobuf.Timestamp + 21, // 5: stream.app.v1.Domain.created_at:type_name -> google.protobuf.Timestamp + 21, // 6: stream.app.v1.Domain.updated_at:type_name -> google.protobuf.Timestamp + 21, // 7: stream.app.v1.AdTemplate.created_at:type_name -> google.protobuf.Timestamp + 21, // 8: stream.app.v1.AdTemplate.updated_at:type_name -> google.protobuf.Timestamp + 21, // 9: stream.app.v1.Payment.created_at:type_name -> google.protobuf.Timestamp + 21, // 10: stream.app.v1.Payment.updated_at:type_name -> google.protobuf.Timestamp + 21, // 11: stream.app.v1.PlanSubscription.started_at:type_name -> google.protobuf.Timestamp + 21, // 12: stream.app.v1.PlanSubscription.expires_at:type_name -> google.protobuf.Timestamp + 21, // 13: stream.app.v1.PlanSubscription.created_at:type_name -> google.protobuf.Timestamp + 21, // 14: stream.app.v1.PlanSubscription.updated_at:type_name -> google.protobuf.Timestamp + 21, // 15: stream.app.v1.WalletTransaction.created_at:type_name -> google.protobuf.Timestamp + 21, // 16: stream.app.v1.WalletTransaction.updated_at:type_name -> google.protobuf.Timestamp + 21, // 17: stream.app.v1.PaymentHistoryItem.expires_at:type_name -> google.protobuf.Timestamp + 21, // 18: stream.app.v1.PaymentHistoryItem.created_at:type_name -> google.protobuf.Timestamp + 21, // 19: stream.app.v1.Video.created_at:type_name -> google.protobuf.Timestamp + 21, // 20: stream.app.v1.Video.updated_at:type_name -> google.protobuf.Timestamp + 21, // 21: stream.app.v1.AdminUser.created_at:type_name -> google.protobuf.Timestamp + 21, // 22: stream.app.v1.AdminUser.updated_at:type_name -> google.protobuf.Timestamp + 13, // 23: stream.app.v1.AdminUserDetail.user:type_name -> stream.app.v1.AdminUser + 8, // 24: stream.app.v1.AdminUserDetail.subscription:type_name -> stream.app.v1.PlanSubscription + 21, // 25: stream.app.v1.AdminVideo.created_at:type_name -> google.protobuf.Timestamp + 21, // 26: stream.app.v1.AdminVideo.updated_at:type_name -> google.protobuf.Timestamp + 21, // 27: stream.app.v1.AdminPayment.created_at:type_name -> google.protobuf.Timestamp + 21, // 28: stream.app.v1.AdminPayment.updated_at:type_name -> google.protobuf.Timestamp + 21, // 29: stream.app.v1.AdminAdTemplate.created_at:type_name -> google.protobuf.Timestamp + 21, // 30: stream.app.v1.AdminAdTemplate.updated_at:type_name -> google.protobuf.Timestamp + 21, // 31: stream.app.v1.AdminJob.created_at:type_name -> google.protobuf.Timestamp + 21, // 32: stream.app.v1.AdminJob.updated_at:type_name -> google.protobuf.Timestamp + 21, // 33: stream.app.v1.AdminAgent.last_heartbeat:type_name -> google.protobuf.Timestamp + 21, // 34: stream.app.v1.AdminAgent.created_at:type_name -> google.protobuf.Timestamp + 21, // 35: stream.app.v1.AdminAgent.updated_at:type_name -> google.protobuf.Timestamp + 36, // [36:36] is the sub-list for method output_type + 36, // [36:36] is the sub-list for method input_type + 36, // [36:36] is the sub-list for extension type_name + 36, // [36:36] is the sub-list for extension extendee + 0, // [0:36] is the sub-list for field type_name +} + +func init() { file_app_v1_common_proto_init() } +func file_app_v1_common_proto_init() { + if File_app_v1_common_proto != nil { + return + } + 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[9].OneofWrappers = []any{} + file_app_v1_common_proto_msgTypes[10].OneofWrappers = []any{} + file_app_v1_common_proto_msgTypes[11].OneofWrappers = []any{} + file_app_v1_common_proto_msgTypes[13].OneofWrappers = []any{} + file_app_v1_common_proto_msgTypes[14].OneofWrappers = []any{} + file_app_v1_common_proto_msgTypes[15].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{} + file_app_v1_common_proto_msgTypes[19].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: 21, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_app_v1_common_proto_goTypes, + DependencyIndexes: file_app_v1_common_proto_depIdxs, + MessageInfos: file_app_v1_common_proto_msgTypes, + }.Build() + File_app_v1_common_proto = out.File + file_app_v1_common_proto_goTypes = nil + file_app_v1_common_proto_depIdxs = nil +} diff --git a/internal/gen/proto/app/v1/payments.pb.go b/internal/gen/proto/app/v1/payments.pb.go new file mode 100644 index 0000000..1a1bcda --- /dev/null +++ b/internal/gen/proto/app/v1/payments.pb.go @@ -0,0 +1,568 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc (unknown) +// source: app/v1/payments.proto + +package appv1 + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type CreatePaymentRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + PlanId string `protobuf:"bytes,1,opt,name=plan_id,json=planId,proto3" json:"plan_id,omitempty"` + TermMonths int32 `protobuf:"varint,2,opt,name=term_months,json=termMonths,proto3" json:"term_months,omitempty"` + PaymentMethod string `protobuf:"bytes,3,opt,name=payment_method,json=paymentMethod,proto3" json:"payment_method,omitempty"` + TopupAmount *float64 `protobuf:"fixed64,4,opt,name=topup_amount,json=topupAmount,proto3,oneof" json:"topup_amount,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreatePaymentRequest) Reset() { + *x = CreatePaymentRequest{} + mi := &file_app_v1_payments_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreatePaymentRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreatePaymentRequest) ProtoMessage() {} + +func (x *CreatePaymentRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_payments_proto_msgTypes[0] + 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 CreatePaymentRequest.ProtoReflect.Descriptor instead. +func (*CreatePaymentRequest) Descriptor() ([]byte, []int) { + return file_app_v1_payments_proto_rawDescGZIP(), []int{0} +} + +func (x *CreatePaymentRequest) GetPlanId() string { + if x != nil { + return x.PlanId + } + return "" +} + +func (x *CreatePaymentRequest) GetTermMonths() int32 { + if x != nil { + return x.TermMonths + } + return 0 +} + +func (x *CreatePaymentRequest) GetPaymentMethod() string { + if x != nil { + return x.PaymentMethod + } + return "" +} + +func (x *CreatePaymentRequest) GetTopupAmount() float64 { + if x != nil && x.TopupAmount != nil { + return *x.TopupAmount + } + return 0 +} + +type CreatePaymentResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Payment *Payment `protobuf:"bytes,1,opt,name=payment,proto3" json:"payment,omitempty"` + Subscription *PlanSubscription `protobuf:"bytes,2,opt,name=subscription,proto3" json:"subscription,omitempty"` + WalletBalance float64 `protobuf:"fixed64,3,opt,name=wallet_balance,json=walletBalance,proto3" json:"wallet_balance,omitempty"` + InvoiceId string `protobuf:"bytes,4,opt,name=invoice_id,json=invoiceId,proto3" json:"invoice_id,omitempty"` + Message string `protobuf:"bytes,5,opt,name=message,proto3" json:"message,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreatePaymentResponse) Reset() { + *x = CreatePaymentResponse{} + mi := &file_app_v1_payments_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreatePaymentResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreatePaymentResponse) ProtoMessage() {} + +func (x *CreatePaymentResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_payments_proto_msgTypes[1] + 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 CreatePaymentResponse.ProtoReflect.Descriptor instead. +func (*CreatePaymentResponse) Descriptor() ([]byte, []int) { + return file_app_v1_payments_proto_rawDescGZIP(), []int{1} +} + +func (x *CreatePaymentResponse) GetPayment() *Payment { + if x != nil { + return x.Payment + } + return nil +} + +func (x *CreatePaymentResponse) GetSubscription() *PlanSubscription { + if x != nil { + return x.Subscription + } + return nil +} + +func (x *CreatePaymentResponse) GetWalletBalance() float64 { + if x != nil { + return x.WalletBalance + } + return 0 +} + +func (x *CreatePaymentResponse) GetInvoiceId() string { + if x != nil { + return x.InvoiceId + } + return "" +} + +func (x *CreatePaymentResponse) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +type ListPaymentHistoryRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListPaymentHistoryRequest) Reset() { + *x = ListPaymentHistoryRequest{} + mi := &file_app_v1_payments_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListPaymentHistoryRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListPaymentHistoryRequest) ProtoMessage() {} + +func (x *ListPaymentHistoryRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_payments_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListPaymentHistoryRequest.ProtoReflect.Descriptor instead. +func (*ListPaymentHistoryRequest) Descriptor() ([]byte, []int) { + return file_app_v1_payments_proto_rawDescGZIP(), []int{2} +} + +type ListPaymentHistoryResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Payments []*PaymentHistoryItem `protobuf:"bytes,1,rep,name=payments,proto3" json:"payments,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListPaymentHistoryResponse) Reset() { + *x = ListPaymentHistoryResponse{} + mi := &file_app_v1_payments_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListPaymentHistoryResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListPaymentHistoryResponse) ProtoMessage() {} + +func (x *ListPaymentHistoryResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_payments_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListPaymentHistoryResponse.ProtoReflect.Descriptor instead. +func (*ListPaymentHistoryResponse) Descriptor() ([]byte, []int) { + return file_app_v1_payments_proto_rawDescGZIP(), []int{3} +} + +func (x *ListPaymentHistoryResponse) GetPayments() []*PaymentHistoryItem { + if x != nil { + return x.Payments + } + return nil +} + +type TopupWalletRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Amount float64 `protobuf:"fixed64,1,opt,name=amount,proto3" json:"amount,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TopupWalletRequest) Reset() { + *x = TopupWalletRequest{} + mi := &file_app_v1_payments_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TopupWalletRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TopupWalletRequest) ProtoMessage() {} + +func (x *TopupWalletRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_payments_proto_msgTypes[4] + 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 TopupWalletRequest.ProtoReflect.Descriptor instead. +func (*TopupWalletRequest) Descriptor() ([]byte, []int) { + return file_app_v1_payments_proto_rawDescGZIP(), []int{4} +} + +func (x *TopupWalletRequest) GetAmount() float64 { + if x != nil { + return x.Amount + } + return 0 +} + +type TopupWalletResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + WalletTransaction *WalletTransaction `protobuf:"bytes,1,opt,name=wallet_transaction,json=walletTransaction,proto3" json:"wallet_transaction,omitempty"` + WalletBalance float64 `protobuf:"fixed64,2,opt,name=wallet_balance,json=walletBalance,proto3" json:"wallet_balance,omitempty"` + InvoiceId string `protobuf:"bytes,3,opt,name=invoice_id,json=invoiceId,proto3" json:"invoice_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TopupWalletResponse) Reset() { + *x = TopupWalletResponse{} + mi := &file_app_v1_payments_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TopupWalletResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TopupWalletResponse) ProtoMessage() {} + +func (x *TopupWalletResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_payments_proto_msgTypes[5] + 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 TopupWalletResponse.ProtoReflect.Descriptor instead. +func (*TopupWalletResponse) Descriptor() ([]byte, []int) { + return file_app_v1_payments_proto_rawDescGZIP(), []int{5} +} + +func (x *TopupWalletResponse) GetWalletTransaction() *WalletTransaction { + if x != nil { + return x.WalletTransaction + } + return nil +} + +func (x *TopupWalletResponse) GetWalletBalance() float64 { + if x != nil { + return x.WalletBalance + } + return 0 +} + +func (x *TopupWalletResponse) GetInvoiceId() string { + if x != nil { + return x.InvoiceId + } + return "" +} + +type DownloadInvoiceRequest 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 *DownloadInvoiceRequest) Reset() { + *x = DownloadInvoiceRequest{} + mi := &file_app_v1_payments_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DownloadInvoiceRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DownloadInvoiceRequest) ProtoMessage() {} + +func (x *DownloadInvoiceRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_payments_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 DownloadInvoiceRequest.ProtoReflect.Descriptor instead. +func (*DownloadInvoiceRequest) Descriptor() ([]byte, []int) { + return file_app_v1_payments_proto_rawDescGZIP(), []int{6} +} + +func (x *DownloadInvoiceRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type DownloadInvoiceResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Filename string `protobuf:"bytes,1,opt,name=filename,proto3" json:"filename,omitempty"` + ContentType string `protobuf:"bytes,2,opt,name=content_type,json=contentType,proto3" json:"content_type,omitempty"` + Content string `protobuf:"bytes,3,opt,name=content,proto3" json:"content,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DownloadInvoiceResponse) Reset() { + *x = DownloadInvoiceResponse{} + mi := &file_app_v1_payments_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DownloadInvoiceResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DownloadInvoiceResponse) ProtoMessage() {} + +func (x *DownloadInvoiceResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_payments_proto_msgTypes[7] + 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 DownloadInvoiceResponse.ProtoReflect.Descriptor instead. +func (*DownloadInvoiceResponse) Descriptor() ([]byte, []int) { + return file_app_v1_payments_proto_rawDescGZIP(), []int{7} +} + +func (x *DownloadInvoiceResponse) GetFilename() string { + if x != nil { + return x.Filename + } + return "" +} + +func (x *DownloadInvoiceResponse) GetContentType() string { + if x != nil { + return x.ContentType + } + return "" +} + +func (x *DownloadInvoiceResponse) GetContent() string { + if x != nil { + return x.Content + } + return "" +} + +var File_app_v1_payments_proto protoreflect.FileDescriptor + +const file_app_v1_payments_proto_rawDesc = "" + + "\n" + + "\x15app/v1/payments.proto\x12\rstream.app.v1\x1a\x13app/v1/common.proto\"\xb0\x01\n" + + "\x14CreatePaymentRequest\x12\x17\n" + + "\aplan_id\x18\x01 \x01(\tR\x06planId\x12\x1f\n" + + "\vterm_months\x18\x02 \x01(\x05R\n" + + "termMonths\x12%\n" + + "\x0epayment_method\x18\x03 \x01(\tR\rpaymentMethod\x12&\n" + + "\ftopup_amount\x18\x04 \x01(\x01H\x00R\vtopupAmount\x88\x01\x01B\x0f\n" + + "\r_topup_amount\"\xee\x01\n" + + "\x15CreatePaymentResponse\x120\n" + + "\apayment\x18\x01 \x01(\v2\x16.stream.app.v1.PaymentR\apayment\x12C\n" + + "\fsubscription\x18\x02 \x01(\v2\x1f.stream.app.v1.PlanSubscriptionR\fsubscription\x12%\n" + + "\x0ewallet_balance\x18\x03 \x01(\x01R\rwalletBalance\x12\x1d\n" + + "\n" + + "invoice_id\x18\x04 \x01(\tR\tinvoiceId\x12\x18\n" + + "\amessage\x18\x05 \x01(\tR\amessage\"\x1b\n" + + "\x19ListPaymentHistoryRequest\"[\n" + + "\x1aListPaymentHistoryResponse\x12=\n" + + "\bpayments\x18\x01 \x03(\v2!.stream.app.v1.PaymentHistoryItemR\bpayments\",\n" + + "\x12TopupWalletRequest\x12\x16\n" + + "\x06amount\x18\x01 \x01(\x01R\x06amount\"\xac\x01\n" + + "\x13TopupWalletResponse\x12O\n" + + "\x12wallet_transaction\x18\x01 \x01(\v2 .stream.app.v1.WalletTransactionR\x11walletTransaction\x12%\n" + + "\x0ewallet_balance\x18\x02 \x01(\x01R\rwalletBalance\x12\x1d\n" + + "\n" + + "invoice_id\x18\x03 \x01(\tR\tinvoiceId\"(\n" + + "\x16DownloadInvoiceRequest\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\"r\n" + + "\x17DownloadInvoiceResponse\x12\x1a\n" + + "\bfilename\x18\x01 \x01(\tR\bfilename\x12!\n" + + "\fcontent_type\x18\x02 \x01(\tR\vcontentType\x12\x18\n" + + "\acontent\x18\x03 \x01(\tR\acontent2\x90\x03\n" + + "\x0fPaymentsService\x12Z\n" + + "\rCreatePayment\x12#.stream.app.v1.CreatePaymentRequest\x1a$.stream.app.v1.CreatePaymentResponse\x12i\n" + + "\x12ListPaymentHistory\x12(.stream.app.v1.ListPaymentHistoryRequest\x1a).stream.app.v1.ListPaymentHistoryResponse\x12T\n" + + "\vTopupWallet\x12!.stream.app.v1.TopupWalletRequest\x1a\".stream.app.v1.TopupWalletResponse\x12`\n" + + "\x0fDownloadInvoice\x12%.stream.app.v1.DownloadInvoiceRequest\x1a&.stream.app.v1.DownloadInvoiceResponseB,Z*stream.api/internal/gen/proto/app/v1;appv1b\x06proto3" + +var ( + file_app_v1_payments_proto_rawDescOnce sync.Once + file_app_v1_payments_proto_rawDescData []byte +) + +func file_app_v1_payments_proto_rawDescGZIP() []byte { + file_app_v1_payments_proto_rawDescOnce.Do(func() { + file_app_v1_payments_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_v1_payments_proto_rawDesc), len(file_app_v1_payments_proto_rawDesc))) + }) + return file_app_v1_payments_proto_rawDescData +} + +var file_app_v1_payments_proto_msgTypes = make([]protoimpl.MessageInfo, 8) +var file_app_v1_payments_proto_goTypes = []any{ + (*CreatePaymentRequest)(nil), // 0: stream.app.v1.CreatePaymentRequest + (*CreatePaymentResponse)(nil), // 1: stream.app.v1.CreatePaymentResponse + (*ListPaymentHistoryRequest)(nil), // 2: stream.app.v1.ListPaymentHistoryRequest + (*ListPaymentHistoryResponse)(nil), // 3: stream.app.v1.ListPaymentHistoryResponse + (*TopupWalletRequest)(nil), // 4: stream.app.v1.TopupWalletRequest + (*TopupWalletResponse)(nil), // 5: stream.app.v1.TopupWalletResponse + (*DownloadInvoiceRequest)(nil), // 6: stream.app.v1.DownloadInvoiceRequest + (*DownloadInvoiceResponse)(nil), // 7: stream.app.v1.DownloadInvoiceResponse + (*Payment)(nil), // 8: stream.app.v1.Payment + (*PlanSubscription)(nil), // 9: stream.app.v1.PlanSubscription + (*PaymentHistoryItem)(nil), // 10: stream.app.v1.PaymentHistoryItem + (*WalletTransaction)(nil), // 11: stream.app.v1.WalletTransaction +} +var file_app_v1_payments_proto_depIdxs = []int32{ + 8, // 0: stream.app.v1.CreatePaymentResponse.payment:type_name -> stream.app.v1.Payment + 9, // 1: stream.app.v1.CreatePaymentResponse.subscription:type_name -> stream.app.v1.PlanSubscription + 10, // 2: stream.app.v1.ListPaymentHistoryResponse.payments:type_name -> stream.app.v1.PaymentHistoryItem + 11, // 3: stream.app.v1.TopupWalletResponse.wallet_transaction:type_name -> stream.app.v1.WalletTransaction + 0, // 4: stream.app.v1.PaymentsService.CreatePayment:input_type -> stream.app.v1.CreatePaymentRequest + 2, // 5: stream.app.v1.PaymentsService.ListPaymentHistory:input_type -> stream.app.v1.ListPaymentHistoryRequest + 4, // 6: stream.app.v1.PaymentsService.TopupWallet:input_type -> stream.app.v1.TopupWalletRequest + 6, // 7: stream.app.v1.PaymentsService.DownloadInvoice:input_type -> stream.app.v1.DownloadInvoiceRequest + 1, // 8: stream.app.v1.PaymentsService.CreatePayment:output_type -> stream.app.v1.CreatePaymentResponse + 3, // 9: stream.app.v1.PaymentsService.ListPaymentHistory:output_type -> stream.app.v1.ListPaymentHistoryResponse + 5, // 10: stream.app.v1.PaymentsService.TopupWallet:output_type -> stream.app.v1.TopupWalletResponse + 7, // 11: stream.app.v1.PaymentsService.DownloadInvoice:output_type -> stream.app.v1.DownloadInvoiceResponse + 8, // [8:12] is the sub-list for method output_type + 4, // [4:8] is the sub-list for method input_type + 4, // [4:4] is the sub-list for extension type_name + 4, // [4:4] is the sub-list for extension extendee + 0, // [0:4] is the sub-list for field type_name +} + +func init() { file_app_v1_payments_proto_init() } +func file_app_v1_payments_proto_init() { + if File_app_v1_payments_proto != nil { + return + } + file_app_v1_common_proto_init() + file_app_v1_payments_proto_msgTypes[0].OneofWrappers = []any{} + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_v1_payments_proto_rawDesc), len(file_app_v1_payments_proto_rawDesc)), + NumEnums: 0, + NumMessages: 8, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_app_v1_payments_proto_goTypes, + DependencyIndexes: file_app_v1_payments_proto_depIdxs, + MessageInfos: file_app_v1_payments_proto_msgTypes, + }.Build() + File_app_v1_payments_proto = out.File + file_app_v1_payments_proto_goTypes = nil + file_app_v1_payments_proto_depIdxs = nil +} diff --git a/internal/gen/proto/app/v1/payments_grpc.pb.go b/internal/gen/proto/app/v1/payments_grpc.pb.go new file mode 100644 index 0000000..f42bd33 --- /dev/null +++ b/internal/gen/proto/app/v1/payments_grpc.pb.go @@ -0,0 +1,235 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.6.1 +// - protoc (unknown) +// source: app/v1/payments.proto + +package appv1 + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + PaymentsService_CreatePayment_FullMethodName = "/stream.app.v1.PaymentsService/CreatePayment" + PaymentsService_ListPaymentHistory_FullMethodName = "/stream.app.v1.PaymentsService/ListPaymentHistory" + PaymentsService_TopupWallet_FullMethodName = "/stream.app.v1.PaymentsService/TopupWallet" + PaymentsService_DownloadInvoice_FullMethodName = "/stream.app.v1.PaymentsService/DownloadInvoice" +) + +// PaymentsServiceClient is the client API for PaymentsService 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 PaymentsServiceClient interface { + CreatePayment(ctx context.Context, in *CreatePaymentRequest, opts ...grpc.CallOption) (*CreatePaymentResponse, error) + ListPaymentHistory(ctx context.Context, in *ListPaymentHistoryRequest, opts ...grpc.CallOption) (*ListPaymentHistoryResponse, error) + TopupWallet(ctx context.Context, in *TopupWalletRequest, opts ...grpc.CallOption) (*TopupWalletResponse, error) + DownloadInvoice(ctx context.Context, in *DownloadInvoiceRequest, opts ...grpc.CallOption) (*DownloadInvoiceResponse, error) +} + +type paymentsServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewPaymentsServiceClient(cc grpc.ClientConnInterface) PaymentsServiceClient { + return &paymentsServiceClient{cc} +} + +func (c *paymentsServiceClient) CreatePayment(ctx context.Context, in *CreatePaymentRequest, opts ...grpc.CallOption) (*CreatePaymentResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(CreatePaymentResponse) + err := c.cc.Invoke(ctx, PaymentsService_CreatePayment_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *paymentsServiceClient) ListPaymentHistory(ctx context.Context, in *ListPaymentHistoryRequest, opts ...grpc.CallOption) (*ListPaymentHistoryResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(ListPaymentHistoryResponse) + err := c.cc.Invoke(ctx, PaymentsService_ListPaymentHistory_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *paymentsServiceClient) TopupWallet(ctx context.Context, in *TopupWalletRequest, opts ...grpc.CallOption) (*TopupWalletResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(TopupWalletResponse) + err := c.cc.Invoke(ctx, PaymentsService_TopupWallet_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *paymentsServiceClient) DownloadInvoice(ctx context.Context, in *DownloadInvoiceRequest, opts ...grpc.CallOption) (*DownloadInvoiceResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(DownloadInvoiceResponse) + err := c.cc.Invoke(ctx, PaymentsService_DownloadInvoice_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// PaymentsServiceServer is the server API for PaymentsService service. +// All implementations must embed UnimplementedPaymentsServiceServer +// for forward compatibility. +type PaymentsServiceServer interface { + CreatePayment(context.Context, *CreatePaymentRequest) (*CreatePaymentResponse, error) + ListPaymentHistory(context.Context, *ListPaymentHistoryRequest) (*ListPaymentHistoryResponse, error) + TopupWallet(context.Context, *TopupWalletRequest) (*TopupWalletResponse, error) + DownloadInvoice(context.Context, *DownloadInvoiceRequest) (*DownloadInvoiceResponse, error) + mustEmbedUnimplementedPaymentsServiceServer() +} + +// UnimplementedPaymentsServiceServer 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 UnimplementedPaymentsServiceServer struct{} + +func (UnimplementedPaymentsServiceServer) CreatePayment(context.Context, *CreatePaymentRequest) (*CreatePaymentResponse, error) { + return nil, status.Error(codes.Unimplemented, "method CreatePayment not implemented") +} +func (UnimplementedPaymentsServiceServer) ListPaymentHistory(context.Context, *ListPaymentHistoryRequest) (*ListPaymentHistoryResponse, error) { + return nil, status.Error(codes.Unimplemented, "method ListPaymentHistory not implemented") +} +func (UnimplementedPaymentsServiceServer) TopupWallet(context.Context, *TopupWalletRequest) (*TopupWalletResponse, error) { + return nil, status.Error(codes.Unimplemented, "method TopupWallet not implemented") +} +func (UnimplementedPaymentsServiceServer) DownloadInvoice(context.Context, *DownloadInvoiceRequest) (*DownloadInvoiceResponse, error) { + return nil, status.Error(codes.Unimplemented, "method DownloadInvoice not implemented") +} +func (UnimplementedPaymentsServiceServer) mustEmbedUnimplementedPaymentsServiceServer() {} +func (UnimplementedPaymentsServiceServer) testEmbeddedByValue() {} + +// UnsafePaymentsServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to PaymentsServiceServer will +// result in compilation errors. +type UnsafePaymentsServiceServer interface { + mustEmbedUnimplementedPaymentsServiceServer() +} + +func RegisterPaymentsServiceServer(s grpc.ServiceRegistrar, srv PaymentsServiceServer) { + // If the following call panics, it indicates UnimplementedPaymentsServiceServer 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(&PaymentsService_ServiceDesc, srv) +} + +func _PaymentsService_CreatePayment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreatePaymentRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(PaymentsServiceServer).CreatePayment(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: PaymentsService_CreatePayment_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(PaymentsServiceServer).CreatePayment(ctx, req.(*CreatePaymentRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _PaymentsService_ListPaymentHistory_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListPaymentHistoryRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(PaymentsServiceServer).ListPaymentHistory(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: PaymentsService_ListPaymentHistory_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(PaymentsServiceServer).ListPaymentHistory(ctx, req.(*ListPaymentHistoryRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _PaymentsService_TopupWallet_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(TopupWalletRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(PaymentsServiceServer).TopupWallet(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: PaymentsService_TopupWallet_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(PaymentsServiceServer).TopupWallet(ctx, req.(*TopupWalletRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _PaymentsService_DownloadInvoice_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DownloadInvoiceRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(PaymentsServiceServer).DownloadInvoice(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: PaymentsService_DownloadInvoice_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(PaymentsServiceServer).DownloadInvoice(ctx, req.(*DownloadInvoiceRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// PaymentsService_ServiceDesc is the grpc.ServiceDesc for PaymentsService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var PaymentsService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "stream.app.v1.PaymentsService", + HandlerType: (*PaymentsServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "CreatePayment", + Handler: _PaymentsService_CreatePayment_Handler, + }, + { + MethodName: "ListPaymentHistory", + Handler: _PaymentsService_ListPaymentHistory_Handler, + }, + { + MethodName: "TopupWallet", + Handler: _PaymentsService_TopupWallet_Handler, + }, + { + MethodName: "DownloadInvoice", + Handler: _PaymentsService_DownloadInvoice_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "app/v1/payments.proto", +} diff --git a/internal/gen/proto/app/v1/videos.pb.go b/internal/gen/proto/app/v1/videos.pb.go new file mode 100644 index 0000000..6c1e93b --- /dev/null +++ b/internal/gen/proto/app/v1/videos.pb.go @@ -0,0 +1,810 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc (unknown) +// source: app/v1/videos.proto + +package appv1 + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type GetUploadUrlRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Filename string `protobuf:"bytes,1,opt,name=filename,proto3" json:"filename,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetUploadUrlRequest) Reset() { + *x = GetUploadUrlRequest{} + mi := &file_app_v1_videos_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetUploadUrlRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetUploadUrlRequest) ProtoMessage() {} + +func (x *GetUploadUrlRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_videos_proto_msgTypes[0] + 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 GetUploadUrlRequest.ProtoReflect.Descriptor instead. +func (*GetUploadUrlRequest) Descriptor() ([]byte, []int) { + return file_app_v1_videos_proto_rawDescGZIP(), []int{0} +} + +func (x *GetUploadUrlRequest) GetFilename() string { + if x != nil { + return x.Filename + } + return "" +} + +type GetUploadUrlResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + UploadUrl string `protobuf:"bytes,1,opt,name=upload_url,json=uploadUrl,proto3" json:"upload_url,omitempty"` + Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` + FileId string `protobuf:"bytes,3,opt,name=file_id,json=fileId,proto3" json:"file_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetUploadUrlResponse) Reset() { + *x = GetUploadUrlResponse{} + mi := &file_app_v1_videos_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetUploadUrlResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetUploadUrlResponse) ProtoMessage() {} + +func (x *GetUploadUrlResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_videos_proto_msgTypes[1] + 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 GetUploadUrlResponse.ProtoReflect.Descriptor instead. +func (*GetUploadUrlResponse) Descriptor() ([]byte, []int) { + return file_app_v1_videos_proto_rawDescGZIP(), []int{1} +} + +func (x *GetUploadUrlResponse) GetUploadUrl() string { + if x != nil { + return x.UploadUrl + } + return "" +} + +func (x *GetUploadUrlResponse) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *GetUploadUrlResponse) GetFileId() string { + if x != nil { + return x.FileId + } + return "" +} + +type CreateVideoRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Title string `protobuf:"bytes,1,opt,name=title,proto3" json:"title,omitempty"` + Description *string `protobuf:"bytes,2,opt,name=description,proto3,oneof" json:"description,omitempty"` + Url string `protobuf:"bytes,3,opt,name=url,proto3" json:"url,omitempty"` + Size int64 `protobuf:"varint,4,opt,name=size,proto3" json:"size,omitempty"` + Duration int32 `protobuf:"varint,5,opt,name=duration,proto3" json:"duration,omitempty"` + Format *string `protobuf:"bytes,6,opt,name=format,proto3,oneof" json:"format,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateVideoRequest) Reset() { + *x = CreateVideoRequest{} + mi := &file_app_v1_videos_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateVideoRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateVideoRequest) ProtoMessage() {} + +func (x *CreateVideoRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_videos_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateVideoRequest.ProtoReflect.Descriptor instead. +func (*CreateVideoRequest) Descriptor() ([]byte, []int) { + return file_app_v1_videos_proto_rawDescGZIP(), []int{2} +} + +func (x *CreateVideoRequest) GetTitle() string { + if x != nil { + return x.Title + } + return "" +} + +func (x *CreateVideoRequest) GetDescription() string { + if x != nil && x.Description != nil { + return *x.Description + } + return "" +} + +func (x *CreateVideoRequest) GetUrl() string { + if x != nil { + return x.Url + } + return "" +} + +func (x *CreateVideoRequest) GetSize() int64 { + if x != nil { + return x.Size + } + return 0 +} + +func (x *CreateVideoRequest) GetDuration() int32 { + if x != nil { + return x.Duration + } + return 0 +} + +func (x *CreateVideoRequest) GetFormat() string { + if x != nil && x.Format != nil { + return *x.Format + } + return "" +} + +type CreateVideoResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Video *Video `protobuf:"bytes,1,opt,name=video,proto3" json:"video,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateVideoResponse) Reset() { + *x = CreateVideoResponse{} + mi := &file_app_v1_videos_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateVideoResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateVideoResponse) ProtoMessage() {} + +func (x *CreateVideoResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_videos_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateVideoResponse.ProtoReflect.Descriptor instead. +func (*CreateVideoResponse) Descriptor() ([]byte, []int) { + return file_app_v1_videos_proto_rawDescGZIP(), []int{3} +} + +func (x *CreateVideoResponse) GetVideo() *Video { + if x != nil { + return x.Video + } + return nil +} + +type ListVideosRequest 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"` + Search *string `protobuf:"bytes,3,opt,name=search,proto3,oneof" json:"search,omitempty"` + Status *string `protobuf:"bytes,4,opt,name=status,proto3,oneof" json:"status,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListVideosRequest) Reset() { + *x = ListVideosRequest{} + mi := &file_app_v1_videos_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListVideosRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListVideosRequest) ProtoMessage() {} + +func (x *ListVideosRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_videos_proto_msgTypes[4] + 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 ListVideosRequest.ProtoReflect.Descriptor instead. +func (*ListVideosRequest) Descriptor() ([]byte, []int) { + return file_app_v1_videos_proto_rawDescGZIP(), []int{4} +} + +func (x *ListVideosRequest) GetPage() int32 { + if x != nil { + return x.Page + } + return 0 +} + +func (x *ListVideosRequest) GetLimit() int32 { + if x != nil { + return x.Limit + } + return 0 +} + +func (x *ListVideosRequest) GetSearch() string { + if x != nil && x.Search != nil { + return *x.Search + } + return "" +} + +func (x *ListVideosRequest) GetStatus() string { + if x != nil && x.Status != nil { + return *x.Status + } + return "" +} + +type ListVideosResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Videos []*Video `protobuf:"bytes,1,rep,name=videos,proto3" json:"videos,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 *ListVideosResponse) Reset() { + *x = ListVideosResponse{} + mi := &file_app_v1_videos_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListVideosResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListVideosResponse) ProtoMessage() {} + +func (x *ListVideosResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_videos_proto_msgTypes[5] + 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 ListVideosResponse.ProtoReflect.Descriptor instead. +func (*ListVideosResponse) Descriptor() ([]byte, []int) { + return file_app_v1_videos_proto_rawDescGZIP(), []int{5} +} + +func (x *ListVideosResponse) GetVideos() []*Video { + if x != nil { + return x.Videos + } + return nil +} + +func (x *ListVideosResponse) GetTotal() int64 { + if x != nil { + return x.Total + } + return 0 +} + +func (x *ListVideosResponse) GetPage() int32 { + if x != nil { + return x.Page + } + return 0 +} + +func (x *ListVideosResponse) GetLimit() int32 { + if x != nil { + return x.Limit + } + return 0 +} + +type GetVideoRequest 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 *GetVideoRequest) Reset() { + *x = GetVideoRequest{} + mi := &file_app_v1_videos_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetVideoRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetVideoRequest) ProtoMessage() {} + +func (x *GetVideoRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_videos_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 GetVideoRequest.ProtoReflect.Descriptor instead. +func (*GetVideoRequest) Descriptor() ([]byte, []int) { + return file_app_v1_videos_proto_rawDescGZIP(), []int{6} +} + +func (x *GetVideoRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type GetVideoResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Video *Video `protobuf:"bytes,1,opt,name=video,proto3" json:"video,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetVideoResponse) Reset() { + *x = GetVideoResponse{} + mi := &file_app_v1_videos_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetVideoResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetVideoResponse) ProtoMessage() {} + +func (x *GetVideoResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_videos_proto_msgTypes[7] + 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 GetVideoResponse.ProtoReflect.Descriptor instead. +func (*GetVideoResponse) Descriptor() ([]byte, []int) { + return file_app_v1_videos_proto_rawDescGZIP(), []int{7} +} + +func (x *GetVideoResponse) GetVideo() *Video { + if x != nil { + return x.Video + } + return nil +} + +type UpdateVideoRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Title string `protobuf:"bytes,2,opt,name=title,proto3" json:"title,omitempty"` + Description *string `protobuf:"bytes,3,opt,name=description,proto3,oneof" json:"description,omitempty"` + Url string `protobuf:"bytes,4,opt,name=url,proto3" json:"url,omitempty"` + Size int64 `protobuf:"varint,5,opt,name=size,proto3" json:"size,omitempty"` + Duration int32 `protobuf:"varint,6,opt,name=duration,proto3" json:"duration,omitempty"` + Format *string `protobuf:"bytes,7,opt,name=format,proto3,oneof" json:"format,omitempty"` + Status *string `protobuf:"bytes,8,opt,name=status,proto3,oneof" json:"status,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UpdateVideoRequest) Reset() { + *x = UpdateVideoRequest{} + mi := &file_app_v1_videos_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdateVideoRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateVideoRequest) ProtoMessage() {} + +func (x *UpdateVideoRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_videos_proto_msgTypes[8] + 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 UpdateVideoRequest.ProtoReflect.Descriptor instead. +func (*UpdateVideoRequest) Descriptor() ([]byte, []int) { + return file_app_v1_videos_proto_rawDescGZIP(), []int{8} +} + +func (x *UpdateVideoRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *UpdateVideoRequest) GetTitle() string { + if x != nil { + return x.Title + } + return "" +} + +func (x *UpdateVideoRequest) GetDescription() string { + if x != nil && x.Description != nil { + return *x.Description + } + return "" +} + +func (x *UpdateVideoRequest) GetUrl() string { + if x != nil { + return x.Url + } + return "" +} + +func (x *UpdateVideoRequest) GetSize() int64 { + if x != nil { + return x.Size + } + return 0 +} + +func (x *UpdateVideoRequest) GetDuration() int32 { + if x != nil { + return x.Duration + } + return 0 +} + +func (x *UpdateVideoRequest) GetFormat() string { + if x != nil && x.Format != nil { + return *x.Format + } + return "" +} + +func (x *UpdateVideoRequest) GetStatus() string { + if x != nil && x.Status != nil { + return *x.Status + } + return "" +} + +type UpdateVideoResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Video *Video `protobuf:"bytes,1,opt,name=video,proto3" json:"video,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UpdateVideoResponse) Reset() { + *x = UpdateVideoResponse{} + mi := &file_app_v1_videos_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdateVideoResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateVideoResponse) ProtoMessage() {} + +func (x *UpdateVideoResponse) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_videos_proto_msgTypes[9] + 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 UpdateVideoResponse.ProtoReflect.Descriptor instead. +func (*UpdateVideoResponse) Descriptor() ([]byte, []int) { + return file_app_v1_videos_proto_rawDescGZIP(), []int{9} +} + +func (x *UpdateVideoResponse) GetVideo() *Video { + if x != nil { + return x.Video + } + return nil +} + +type DeleteVideoRequest 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 *DeleteVideoRequest) Reset() { + *x = DeleteVideoRequest{} + mi := &file_app_v1_videos_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeleteVideoRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteVideoRequest) ProtoMessage() {} + +func (x *DeleteVideoRequest) ProtoReflect() protoreflect.Message { + mi := &file_app_v1_videos_proto_msgTypes[10] + 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 DeleteVideoRequest.ProtoReflect.Descriptor instead. +func (*DeleteVideoRequest) Descriptor() ([]byte, []int) { + return file_app_v1_videos_proto_rawDescGZIP(), []int{10} +} + +func (x *DeleteVideoRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +var File_app_v1_videos_proto protoreflect.FileDescriptor + +const file_app_v1_videos_proto_rawDesc = "" + + "\n" + + "\x13app/v1/videos.proto\x12\rstream.app.v1\x1a\x13app/v1/common.proto\"1\n" + + "\x13GetUploadUrlRequest\x12\x1a\n" + + "\bfilename\x18\x01 \x01(\tR\bfilename\"`\n" + + "\x14GetUploadUrlResponse\x12\x1d\n" + + "\n" + + "upload_url\x18\x01 \x01(\tR\tuploadUrl\x12\x10\n" + + "\x03key\x18\x02 \x01(\tR\x03key\x12\x17\n" + + "\afile_id\x18\x03 \x01(\tR\x06fileId\"\xcb\x01\n" + + "\x12CreateVideoRequest\x12\x14\n" + + "\x05title\x18\x01 \x01(\tR\x05title\x12%\n" + + "\vdescription\x18\x02 \x01(\tH\x00R\vdescription\x88\x01\x01\x12\x10\n" + + "\x03url\x18\x03 \x01(\tR\x03url\x12\x12\n" + + "\x04size\x18\x04 \x01(\x03R\x04size\x12\x1a\n" + + "\bduration\x18\x05 \x01(\x05R\bduration\x12\x1b\n" + + "\x06format\x18\x06 \x01(\tH\x01R\x06format\x88\x01\x01B\x0e\n" + + "\f_descriptionB\t\n" + + "\a_format\"A\n" + + "\x13CreateVideoResponse\x12*\n" + + "\x05video\x18\x01 \x01(\v2\x14.stream.app.v1.VideoR\x05video\"\x8d\x01\n" + + "\x11ListVideosRequest\x12\x12\n" + + "\x04page\x18\x01 \x01(\x05R\x04page\x12\x14\n" + + "\x05limit\x18\x02 \x01(\x05R\x05limit\x12\x1b\n" + + "\x06search\x18\x03 \x01(\tH\x00R\x06search\x88\x01\x01\x12\x1b\n" + + "\x06status\x18\x04 \x01(\tH\x01R\x06status\x88\x01\x01B\t\n" + + "\a_searchB\t\n" + + "\a_status\"\x82\x01\n" + + "\x12ListVideosResponse\x12,\n" + + "\x06videos\x18\x01 \x03(\v2\x14.stream.app.v1.VideoR\x06videos\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\"!\n" + + "\x0fGetVideoRequest\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\">\n" + + "\x10GetVideoResponse\x12*\n" + + "\x05video\x18\x01 \x01(\v2\x14.stream.app.v1.VideoR\x05video\"\x83\x02\n" + + "\x12UpdateVideoRequest\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\x12\x14\n" + + "\x05title\x18\x02 \x01(\tR\x05title\x12%\n" + + "\vdescription\x18\x03 \x01(\tH\x00R\vdescription\x88\x01\x01\x12\x10\n" + + "\x03url\x18\x04 \x01(\tR\x03url\x12\x12\n" + + "\x04size\x18\x05 \x01(\x03R\x04size\x12\x1a\n" + + "\bduration\x18\x06 \x01(\x05R\bduration\x12\x1b\n" + + "\x06format\x18\a \x01(\tH\x01R\x06format\x88\x01\x01\x12\x1b\n" + + "\x06status\x18\b \x01(\tH\x02R\x06status\x88\x01\x01B\x0e\n" + + "\f_descriptionB\t\n" + + "\a_formatB\t\n" + + "\a_status\"A\n" + + "\x13UpdateVideoResponse\x12*\n" + + "\x05video\x18\x01 \x01(\v2\x14.stream.app.v1.VideoR\x05video\"$\n" + + "\x12DeleteVideoRequest\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id2\x86\x04\n" + + "\rVideosService\x12W\n" + + "\fGetUploadUrl\x12\".stream.app.v1.GetUploadUrlRequest\x1a#.stream.app.v1.GetUploadUrlResponse\x12T\n" + + "\vCreateVideo\x12!.stream.app.v1.CreateVideoRequest\x1a\".stream.app.v1.CreateVideoResponse\x12Q\n" + + "\n" + + "ListVideos\x12 .stream.app.v1.ListVideosRequest\x1a!.stream.app.v1.ListVideosResponse\x12K\n" + + "\bGetVideo\x12\x1e.stream.app.v1.GetVideoRequest\x1a\x1f.stream.app.v1.GetVideoResponse\x12T\n" + + "\vUpdateVideo\x12!.stream.app.v1.UpdateVideoRequest\x1a\".stream.app.v1.UpdateVideoResponse\x12P\n" + + "\vDeleteVideo\x12!.stream.app.v1.DeleteVideoRequest\x1a\x1e.stream.app.v1.MessageResponseB,Z*stream.api/internal/gen/proto/app/v1;appv1b\x06proto3" + +var ( + file_app_v1_videos_proto_rawDescOnce sync.Once + file_app_v1_videos_proto_rawDescData []byte +) + +func file_app_v1_videos_proto_rawDescGZIP() []byte { + file_app_v1_videos_proto_rawDescOnce.Do(func() { + file_app_v1_videos_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_v1_videos_proto_rawDesc), len(file_app_v1_videos_proto_rawDesc))) + }) + return file_app_v1_videos_proto_rawDescData +} + +var file_app_v1_videos_proto_msgTypes = make([]protoimpl.MessageInfo, 11) +var file_app_v1_videos_proto_goTypes = []any{ + (*GetUploadUrlRequest)(nil), // 0: stream.app.v1.GetUploadUrlRequest + (*GetUploadUrlResponse)(nil), // 1: stream.app.v1.GetUploadUrlResponse + (*CreateVideoRequest)(nil), // 2: stream.app.v1.CreateVideoRequest + (*CreateVideoResponse)(nil), // 3: stream.app.v1.CreateVideoResponse + (*ListVideosRequest)(nil), // 4: stream.app.v1.ListVideosRequest + (*ListVideosResponse)(nil), // 5: stream.app.v1.ListVideosResponse + (*GetVideoRequest)(nil), // 6: stream.app.v1.GetVideoRequest + (*GetVideoResponse)(nil), // 7: stream.app.v1.GetVideoResponse + (*UpdateVideoRequest)(nil), // 8: stream.app.v1.UpdateVideoRequest + (*UpdateVideoResponse)(nil), // 9: stream.app.v1.UpdateVideoResponse + (*DeleteVideoRequest)(nil), // 10: stream.app.v1.DeleteVideoRequest + (*Video)(nil), // 11: stream.app.v1.Video + (*MessageResponse)(nil), // 12: stream.app.v1.MessageResponse +} +var file_app_v1_videos_proto_depIdxs = []int32{ + 11, // 0: stream.app.v1.CreateVideoResponse.video:type_name -> stream.app.v1.Video + 11, // 1: stream.app.v1.ListVideosResponse.videos:type_name -> stream.app.v1.Video + 11, // 2: stream.app.v1.GetVideoResponse.video:type_name -> stream.app.v1.Video + 11, // 3: stream.app.v1.UpdateVideoResponse.video:type_name -> stream.app.v1.Video + 0, // 4: stream.app.v1.VideosService.GetUploadUrl:input_type -> stream.app.v1.GetUploadUrlRequest + 2, // 5: stream.app.v1.VideosService.CreateVideo:input_type -> stream.app.v1.CreateVideoRequest + 4, // 6: stream.app.v1.VideosService.ListVideos:input_type -> stream.app.v1.ListVideosRequest + 6, // 7: stream.app.v1.VideosService.GetVideo:input_type -> stream.app.v1.GetVideoRequest + 8, // 8: stream.app.v1.VideosService.UpdateVideo:input_type -> stream.app.v1.UpdateVideoRequest + 10, // 9: stream.app.v1.VideosService.DeleteVideo:input_type -> stream.app.v1.DeleteVideoRequest + 1, // 10: stream.app.v1.VideosService.GetUploadUrl:output_type -> stream.app.v1.GetUploadUrlResponse + 3, // 11: stream.app.v1.VideosService.CreateVideo:output_type -> stream.app.v1.CreateVideoResponse + 5, // 12: stream.app.v1.VideosService.ListVideos:output_type -> stream.app.v1.ListVideosResponse + 7, // 13: stream.app.v1.VideosService.GetVideo:output_type -> stream.app.v1.GetVideoResponse + 9, // 14: stream.app.v1.VideosService.UpdateVideo:output_type -> stream.app.v1.UpdateVideoResponse + 12, // 15: stream.app.v1.VideosService.DeleteVideo:output_type -> stream.app.v1.MessageResponse + 10, // [10:16] is the sub-list for method output_type + 4, // [4:10] is the sub-list for method input_type + 4, // [4:4] is the sub-list for extension type_name + 4, // [4:4] is the sub-list for extension extendee + 0, // [0:4] is the sub-list for field type_name +} + +func init() { file_app_v1_videos_proto_init() } +func file_app_v1_videos_proto_init() { + if File_app_v1_videos_proto != nil { + return + } + file_app_v1_common_proto_init() + file_app_v1_videos_proto_msgTypes[2].OneofWrappers = []any{} + file_app_v1_videos_proto_msgTypes[4].OneofWrappers = []any{} + file_app_v1_videos_proto_msgTypes[8].OneofWrappers = []any{} + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_v1_videos_proto_rawDesc), len(file_app_v1_videos_proto_rawDesc)), + NumEnums: 0, + NumMessages: 11, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_app_v1_videos_proto_goTypes, + DependencyIndexes: file_app_v1_videos_proto_depIdxs, + MessageInfos: file_app_v1_videos_proto_msgTypes, + }.Build() + File_app_v1_videos_proto = out.File + file_app_v1_videos_proto_goTypes = nil + file_app_v1_videos_proto_depIdxs = nil +} diff --git a/internal/gen/proto/app/v1/videos_grpc.pb.go b/internal/gen/proto/app/v1/videos_grpc.pb.go new file mode 100644 index 0000000..59641bc --- /dev/null +++ b/internal/gen/proto/app/v1/videos_grpc.pb.go @@ -0,0 +1,311 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.6.1 +// - protoc (unknown) +// source: app/v1/videos.proto + +package appv1 + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + VideosService_GetUploadUrl_FullMethodName = "/stream.app.v1.VideosService/GetUploadUrl" + VideosService_CreateVideo_FullMethodName = "/stream.app.v1.VideosService/CreateVideo" + VideosService_ListVideos_FullMethodName = "/stream.app.v1.VideosService/ListVideos" + VideosService_GetVideo_FullMethodName = "/stream.app.v1.VideosService/GetVideo" + VideosService_UpdateVideo_FullMethodName = "/stream.app.v1.VideosService/UpdateVideo" + VideosService_DeleteVideo_FullMethodName = "/stream.app.v1.VideosService/DeleteVideo" +) + +// VideosServiceClient is the client API for VideosService 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 VideosServiceClient interface { + GetUploadUrl(ctx context.Context, in *GetUploadUrlRequest, opts ...grpc.CallOption) (*GetUploadUrlResponse, error) + CreateVideo(ctx context.Context, in *CreateVideoRequest, opts ...grpc.CallOption) (*CreateVideoResponse, error) + ListVideos(ctx context.Context, in *ListVideosRequest, opts ...grpc.CallOption) (*ListVideosResponse, error) + GetVideo(ctx context.Context, in *GetVideoRequest, opts ...grpc.CallOption) (*GetVideoResponse, error) + UpdateVideo(ctx context.Context, in *UpdateVideoRequest, opts ...grpc.CallOption) (*UpdateVideoResponse, error) + DeleteVideo(ctx context.Context, in *DeleteVideoRequest, opts ...grpc.CallOption) (*MessageResponse, error) +} + +type videosServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewVideosServiceClient(cc grpc.ClientConnInterface) VideosServiceClient { + return &videosServiceClient{cc} +} + +func (c *videosServiceClient) GetUploadUrl(ctx context.Context, in *GetUploadUrlRequest, opts ...grpc.CallOption) (*GetUploadUrlResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetUploadUrlResponse) + err := c.cc.Invoke(ctx, VideosService_GetUploadUrl_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *videosServiceClient) CreateVideo(ctx context.Context, in *CreateVideoRequest, opts ...grpc.CallOption) (*CreateVideoResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(CreateVideoResponse) + err := c.cc.Invoke(ctx, VideosService_CreateVideo_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *videosServiceClient) ListVideos(ctx context.Context, in *ListVideosRequest, opts ...grpc.CallOption) (*ListVideosResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(ListVideosResponse) + err := c.cc.Invoke(ctx, VideosService_ListVideos_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *videosServiceClient) GetVideo(ctx context.Context, in *GetVideoRequest, opts ...grpc.CallOption) (*GetVideoResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetVideoResponse) + err := c.cc.Invoke(ctx, VideosService_GetVideo_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *videosServiceClient) UpdateVideo(ctx context.Context, in *UpdateVideoRequest, opts ...grpc.CallOption) (*UpdateVideoResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(UpdateVideoResponse) + err := c.cc.Invoke(ctx, VideosService_UpdateVideo_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *videosServiceClient) DeleteVideo(ctx context.Context, in *DeleteVideoRequest, opts ...grpc.CallOption) (*MessageResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(MessageResponse) + err := c.cc.Invoke(ctx, VideosService_DeleteVideo_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// VideosServiceServer is the server API for VideosService service. +// All implementations must embed UnimplementedVideosServiceServer +// for forward compatibility. +type VideosServiceServer interface { + GetUploadUrl(context.Context, *GetUploadUrlRequest) (*GetUploadUrlResponse, error) + CreateVideo(context.Context, *CreateVideoRequest) (*CreateVideoResponse, error) + ListVideos(context.Context, *ListVideosRequest) (*ListVideosResponse, error) + GetVideo(context.Context, *GetVideoRequest) (*GetVideoResponse, error) + UpdateVideo(context.Context, *UpdateVideoRequest) (*UpdateVideoResponse, error) + DeleteVideo(context.Context, *DeleteVideoRequest) (*MessageResponse, error) + mustEmbedUnimplementedVideosServiceServer() +} + +// UnimplementedVideosServiceServer 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 UnimplementedVideosServiceServer struct{} + +func (UnimplementedVideosServiceServer) GetUploadUrl(context.Context, *GetUploadUrlRequest) (*GetUploadUrlResponse, error) { + return nil, status.Error(codes.Unimplemented, "method GetUploadUrl not implemented") +} +func (UnimplementedVideosServiceServer) CreateVideo(context.Context, *CreateVideoRequest) (*CreateVideoResponse, error) { + return nil, status.Error(codes.Unimplemented, "method CreateVideo not implemented") +} +func (UnimplementedVideosServiceServer) ListVideos(context.Context, *ListVideosRequest) (*ListVideosResponse, error) { + return nil, status.Error(codes.Unimplemented, "method ListVideos not implemented") +} +func (UnimplementedVideosServiceServer) GetVideo(context.Context, *GetVideoRequest) (*GetVideoResponse, error) { + return nil, status.Error(codes.Unimplemented, "method GetVideo not implemented") +} +func (UnimplementedVideosServiceServer) UpdateVideo(context.Context, *UpdateVideoRequest) (*UpdateVideoResponse, error) { + return nil, status.Error(codes.Unimplemented, "method UpdateVideo not implemented") +} +func (UnimplementedVideosServiceServer) DeleteVideo(context.Context, *DeleteVideoRequest) (*MessageResponse, error) { + return nil, status.Error(codes.Unimplemented, "method DeleteVideo not implemented") +} +func (UnimplementedVideosServiceServer) mustEmbedUnimplementedVideosServiceServer() {} +func (UnimplementedVideosServiceServer) testEmbeddedByValue() {} + +// UnsafeVideosServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to VideosServiceServer will +// result in compilation errors. +type UnsafeVideosServiceServer interface { + mustEmbedUnimplementedVideosServiceServer() +} + +func RegisterVideosServiceServer(s grpc.ServiceRegistrar, srv VideosServiceServer) { + // If the following call panics, it indicates UnimplementedVideosServiceServer 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(&VideosService_ServiceDesc, srv) +} + +func _VideosService_GetUploadUrl_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetUploadUrlRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VideosServiceServer).GetUploadUrl(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: VideosService_GetUploadUrl_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VideosServiceServer).GetUploadUrl(ctx, req.(*GetUploadUrlRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _VideosService_CreateVideo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateVideoRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VideosServiceServer).CreateVideo(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: VideosService_CreateVideo_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VideosServiceServer).CreateVideo(ctx, req.(*CreateVideoRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _VideosService_ListVideos_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListVideosRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VideosServiceServer).ListVideos(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: VideosService_ListVideos_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VideosServiceServer).ListVideos(ctx, req.(*ListVideosRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _VideosService_GetVideo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetVideoRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VideosServiceServer).GetVideo(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: VideosService_GetVideo_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VideosServiceServer).GetVideo(ctx, req.(*GetVideoRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _VideosService_UpdateVideo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateVideoRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VideosServiceServer).UpdateVideo(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: VideosService_UpdateVideo_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VideosServiceServer).UpdateVideo(ctx, req.(*UpdateVideoRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _VideosService_DeleteVideo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteVideoRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VideosServiceServer).DeleteVideo(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: VideosService_DeleteVideo_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VideosServiceServer).DeleteVideo(ctx, req.(*DeleteVideoRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// VideosService_ServiceDesc is the grpc.ServiceDesc for VideosService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var VideosService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "stream.app.v1.VideosService", + HandlerType: (*VideosServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetUploadUrl", + Handler: _VideosService_GetUploadUrl_Handler, + }, + { + MethodName: "CreateVideo", + Handler: _VideosService_CreateVideo_Handler, + }, + { + MethodName: "ListVideos", + Handler: _VideosService_ListVideos_Handler, + }, + { + MethodName: "GetVideo", + Handler: _VideosService_GetVideo_Handler, + }, + { + MethodName: "UpdateVideo", + Handler: _VideosService_UpdateVideo_Handler, + }, + { + MethodName: "DeleteVideo", + Handler: _VideosService_DeleteVideo_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "app/v1/videos.proto", +} diff --git a/internal/middleware/admin.go b/internal/middleware/admin.go new file mode 100644 index 0000000..f5ff467 --- /dev/null +++ b/internal/middleware/admin.go @@ -0,0 +1,38 @@ +//go:build ignore +// +build ignore + +package middleware + +import ( + "net/http" + "strings" + + "github.com/gin-gonic/gin" + "stream.api/internal/database/model" + "stream.api/pkg/response" +) + +// RequireAdmin returns a Gin middleware that blocks non-admin users. +// Must be placed after the auth middleware so "user" is set in the context. +func RequireAdmin() gin.HandlerFunc { + return func(c *gin.Context) { + userValue, exists := c.Get("user") + if !exists { + response.Error(c, http.StatusUnauthorized, "Unauthorized") + return + } + + user, ok := userValue.(*model.User) + if !ok || user == nil { + response.Error(c, http.StatusUnauthorized, "Unauthorized") + return + } + + if user.Role == nil || strings.ToUpper(*user.Role) != "ADMIN" { + response.Error(c, http.StatusForbidden, "Admin access required") + return + } + + c.Next() + } +} diff --git a/internal/middleware/auth.go b/internal/middleware/auth.go index f3b0140..040a027 100644 --- a/internal/middleware/auth.go +++ b/internal/middleware/auth.go @@ -1,155 +1,52 @@ +//go:build ignore +// +build ignore + package middleware import ( "net/http" - "strings" - "time" "github.com/gin-gonic/gin" + "gorm.io/gorm" "stream.api/internal/config" - "stream.api/internal/database/query" "stream.api/pkg/cache" + "stream.api/pkg/logger" "stream.api/pkg/response" "stream.api/pkg/token" ) -const ( - CookieName = "auth_token" // Backward compatibility if needed, but we use access_token now -) - type AuthMiddleware struct { - cache cache.Cache - token token.Provider + authenticator *Authenticator } -func NewAuthMiddleware(c cache.Cache, t token.Provider, cfg *config.Config) *AuthMiddleware { +func NewAuthMiddleware(c cache.Cache, t token.Provider, _ *config.Config, db *gorm.DB, l logger.Logger) *AuthMiddleware { return &AuthMiddleware{ - cache: c, - token: t, + authenticator: NewAuthenticator(c, t, db, l), } } func (m *AuthMiddleware) Handle() gin.HandlerFunc { return func(c *gin.Context) { - ctx := c.Request.Context() - var userID string - var claims *token.Claims - var err error - - // 1. Try Access Token (Header or Cookie) - var accessToken string - authHeader := c.GetHeader("Authorization") - if len(authHeader) > 7 && strings.ToUpper(authHeader[0:7]) == "BEARER " { - accessToken = authHeader[7:] - } - - if accessToken == "" { - cookie, err := c.Cookie("access_token") - if err == nil { - accessToken = cookie - } - } - - if accessToken != "" { - claims, err = m.token.ParseToken(accessToken) - if err == nil { - userID = claims.UserID - } - } - - // 2. If Access Token invalid/missing, Try Refresh Token - if userID == "" { - refreshToken, errRefresh := c.Cookie("refresh_token") - if errRefresh != nil { - response.Error(c, http.StatusUnauthorized, "Unauthorized: No token") - return - } - - // Validate Refresh Token (JWT signature) - // Problem regarding pkg/token parsing: Generates MapClaims but ParseToken expects Claims struct. - // Let's rely on Redis mostly, or assume ParseToken *fails* if claims mismatch, which is fine. - // Better: Assume we can parse at least standard claims or check Redis directly? - // But we need the UUID inside the token to check Redis key `refresh_uuid:{uuid}`. - // WORKAROUND: In pkg/token/jwt.go we defined `rtClaims["refresh_uuid"]`. - // `ParseToken` attempts to map to `Claims`. It likely won't map unknown fields but `Valid` might be true if struct has RegisteredClaims? - // Actually `jwt.ParseWithClaims` will succeed if signature matches, even if some fields are missing in struct. - // But we need `refresh_uuid`! - // - // We MUST parse as MapClaims here or update pkg/token. - // For this execution, I will parse as MapClaims locally to unblock. - - // Validate Refresh Token (JWT signature) - // Parse using local helper to check refresh_uuid claim - // We can use m.token.ParseMapToken which we implemented - rtClaims, err := m.token.ParseMapToken(refreshToken) - if err != nil { - response.Error(c, http.StatusUnauthorized, "Unauthorized: Invalid refresh token") - return - } - - refreshUUID, ok := rtClaims["refresh_uuid"].(string) - if !ok { - response.Error(c, http.StatusUnauthorized, "Unauthorized: Invalid refresh token claim") - return - } - - // Check Redis - redisKey := "refresh_uuid:" + refreshUUID - userIDStr, err := m.cache.Get(ctx, redisKey) - if err != nil { - // Assuming Get returns error on miss or failure - response.Error(c, http.StatusUnauthorized, "Unauthorized: Session expired or invalid") - return - } - userID = userIDStr - - // Refresh Success: Generate NEW Pair (Rotational or just new Access?) - // Let's just issue new Access Token. Refresh Token rotation is safer but complex (need to update Redis key). - // We will just issue new Access Token. - - // Need User Role/Email for Access Token claims. - u := query.User - userObj, err := u.WithContext(ctx).Where(u.ID.Eq(userID)).First() - if err != nil { - response.Error(c, http.StatusUnauthorized, "User not found") - return - } - - // Calling pkg/token.GenerateTokenPair generates BOTH. - // If we want new Access we can regenerate pair and update tokens. - // Updating refresh token extends session (Slide expiration). - - newTd, err := m.token.GenerateTokenPair(userID, userObj.Email, *userObj.Role) - if err == nil { - // Delete old refresh token from Redis? - m.cache.Del(ctx, redisKey) - // Set new - m.cache.Set(ctx, "refresh_uuid:"+newTd.RefreshUUID, userID, time.Until(time.Unix(newTd.RtExpires, 0))) - - // Set Cookies - c.SetCookie("access_token", newTd.AccessToken, int(newTd.AtExpires-time.Now().Unix()), "/", "", false, true) - c.SetCookie("refresh_token", newTd.RefreshToken, int(newTd.RtExpires-time.Now().Unix()), "/", "", false, true) - } - } - - // 3. User Lookup / Block Check - u := query.User - user, err := u.WithContext(ctx).Where(u.ID.Eq(userID)).First() + result, err := m.authenticator.Authenticate(c.Request.Context(), AuthRequest{ + Authorization: c.GetHeader("Authorization"), + CookieHeader: c.GetHeader("Cookie"), + }) if err != nil { - response.Error(c, http.StatusUnauthorized, "Unauthorized: User not found") + authErr, ok := err.(*AuthError) + if !ok { + response.Error(c, http.StatusInternalServerError, "Authentication failed") + return + } + response.Error(c, authErr.StatusCode, authErr.Message) return } - if user.Role != nil && strings.ToLower(*user.Role) == "block" { - response.Error(c, http.StatusForbidden, "Forbidden: User is blocked") - return + for _, cookie := range result.SetCookies { + c.Header("Set-Cookie", cookie) } - c.Set("userID", user.ID) - c.Set("user", user) + c.Set("userID", result.UserID) + c.Set("user", result.User) c.Next() } } - -// Helper to parse generic claims -// Removed parseMapToken as it is now in TokenProvider interface diff --git a/internal/middleware/authenticator.go b/internal/middleware/authenticator.go new file mode 100644 index 0000000..b253f11 --- /dev/null +++ b/internal/middleware/authenticator.go @@ -0,0 +1,247 @@ +package middleware + +import ( + "context" + "encoding/json" + "errors" + "strings" + "time" + + "github.com/google/uuid" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" + "gorm.io/gorm" + "gorm.io/gorm/clause" + "stream.api/internal/database/model" + "stream.api/internal/database/query" + "stream.api/pkg/logger" +) + +const ( + ActorMarkerMetadataKey = "x-stream-internal-auth" + ActorIDMetadataKey = "x-stream-actor-id" + ActorEmailMetadataKey = "x-stream-actor-email" + ActorRoleMetadataKey = "x-stream-actor-role" +) + +type AuthResult struct { + UserID string + User *model.User +} + +type Actor struct { + UserID string + Email string + Role string +} + +type Authenticator struct { + db *gorm.DB + logger logger.Logger + trustedMarker string +} + +func NewAuthenticator(db *gorm.DB, l logger.Logger, trustedMarker string) *Authenticator { + return &Authenticator{ + db: db, + logger: l, + trustedMarker: strings.TrimSpace(trustedMarker), + } +} + +func (a *Authenticator) Authenticate(ctx context.Context) (*AuthResult, error) { + actor, err := a.RequireActor(ctx) + if err != nil { + return nil, err + } + + u := query.User + user, err := u.WithContext(ctx).Where(u.ID.Eq(actor.UserID)).First() + if err != nil { + return nil, status.Error(codes.Unauthenticated, "Unauthorized") + } + + user, err = a.syncSubscriptionState(ctx, user) + if err != nil { + a.logger.Error("Failed to sync subscription state", "error", err, "user_id", actor.UserID) + return nil, status.Error(codes.Internal, "Failed to load user subscription state") + } + + if user.Role != nil && strings.EqualFold(strings.TrimSpace(*user.Role), "block") { + return nil, status.Error(codes.PermissionDenied, "Forbidden: User is blocked") + } + + return &AuthResult{ + UserID: user.ID, + User: user, + }, nil +} + +func (a *Authenticator) RequireActor(ctx context.Context) (*Actor, error) { + md, err := a.requireTrustedMetadata(ctx) + if err != nil { + return nil, err + } + + userID := strings.TrimSpace(firstMetadataValue(md, ActorIDMetadataKey)) + role := strings.TrimSpace(firstMetadataValue(md, ActorRoleMetadataKey)) + if userID == "" || role == "" { + return nil, status.Error(codes.Unauthenticated, "Missing actor identity") + } + + return &Actor{ + UserID: userID, + Email: strings.TrimSpace(firstMetadataValue(md, ActorEmailMetadataKey)), + Role: role, + }, nil +} + +func (a *Authenticator) RequireInternalCall(ctx context.Context) error { + _, err := a.requireTrustedMetadata(ctx) + return err +} + +func (a *Authenticator) requireTrustedMetadata(ctx context.Context) (metadata.MD, error) { + md, ok := metadata.FromIncomingContext(ctx) + if !ok { + return nil, status.Error(codes.Unauthenticated, "Missing actor metadata") + } + + marker := firstMetadataValue(md, ActorMarkerMetadataKey) + if marker == "" || marker != a.trustedMarker { + return nil, status.Error(codes.Unauthenticated, "Invalid internal auth marker") + } + + return md, nil +} + +func firstMetadataValue(md metadata.MD, key string) string { + values := md.Get(key) + if len(values) == 0 { + return "" + } + return values[0] +} + +func (a *Authenticator) syncSubscriptionState(ctx context.Context, user *model.User) (*model.User, error) { + subscription, err := model.GetLatestPlanSubscription(ctx, a.db, user.ID) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return user, nil + } + return nil, err + } + + now := time.Now().UTC() + if err := a.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + var lockedSubscription model.PlanSubscription + if err := tx.WithContext(ctx). + Clauses(clause.Locking{Strength: "UPDATE"}). + Where("id = ?", subscription.ID). + First(&lockedSubscription).Error; err != nil { + return err + } + + if lockedSubscription.ExpiresAt.After(now) { + if user.PlanID == nil || strings.TrimSpace(*user.PlanID) != lockedSubscription.PlanID { + if err := tx.WithContext(ctx). + Model(&model.User{}). + Where("id = ?", user.ID). + Update("plan_id", lockedSubscription.PlanID).Error; err != nil { + return err + } + user.PlanID = &lockedSubscription.PlanID + } + + reminderDays, reminderField := reminderFieldForSubscription(&lockedSubscription, now) + if reminderField != "" { + sentAt := now + notification := &model.Notification{ + ID: uuidString(), + UserID: user.ID, + Type: "billing.subscription_expiring", + Title: "Plan expiring soon", + Message: reminderMessage(reminderDays), + ActionURL: model.StringPtr("/settings/billing"), + ActionLabel: model.StringPtr("Renew plan"), + Metadata: model.StringPtr(mustMarshalAuthJSON(map[string]interface{}{"plan_id": lockedSubscription.PlanID, "expires_at": lockedSubscription.ExpiresAt.UTC().Format(time.RFC3339), "reminder_days": reminderDays})), + } + if err := tx.WithContext(ctx).Create(notification).Error; err != nil { + return err + } + if err := tx.WithContext(ctx). + Model(&model.PlanSubscription{}). + Where("id = ?", lockedSubscription.ID). + Update(reminderField, sentAt).Error; err != nil { + return err + } + } + + return nil + } + + if user.PlanID != nil && strings.TrimSpace(*user.PlanID) != "" { + if err := tx.WithContext(ctx). + Model(&model.User{}). + Where("id = ?", user.ID). + Update("plan_id", nil).Error; err != nil { + return err + } + user.PlanID = nil + } + + return nil + }); err != nil { + return nil, err + } + + return user, nil +} + +func reminderFieldForSubscription(subscription *model.PlanSubscription, now time.Time) (int, string) { + if subscription == nil || !subscription.ExpiresAt.After(now) { + return 0, "" + } + + remaining := subscription.ExpiresAt.Sub(now) + switch { + case remaining <= 24*time.Hour: + if subscription.Reminder1DSentAt == nil { + return 1, "reminder_1d_sent_at" + } + case remaining <= 72*time.Hour: + if subscription.Reminder3DSentAt == nil { + return 3, "reminder_3d_sent_at" + } + case remaining <= 7*24*time.Hour: + if subscription.Reminder7DSentAt == nil { + return 7, "reminder_7d_sent_at" + } + } + + return 0, "" +} + +func reminderMessage(days int) string { + switch days { + case 1: + return "Your current plan expires in 1 day. Renew now to avoid interruption." + case 3: + return "Your current plan expires in 3 days. Renew now to keep access active." + default: + return "Your current plan expires in 7 days. Renew now to keep your plan active." + } +} + +func mustMarshalAuthJSON(value interface{}) string { + encoded, err := json.Marshal(value) + if err != nil { + return "{}" + } + return string(encoded) +} + +func uuidString() string { + return uuid.New().String() +} diff --git a/internal/middleware/error.go b/internal/middleware/error.go index 0a84dde..e164b15 100644 --- a/internal/middleware/error.go +++ b/internal/middleware/error.go @@ -1,3 +1,6 @@ +//go:build ignore +// +build ignore + package middleware import ( diff --git a/internal/rpc/app/register.go b/internal/rpc/app/register.go new file mode 100644 index 0000000..3f15370 --- /dev/null +++ b/internal/rpc/app/register.go @@ -0,0 +1,20 @@ +package app + +import ( + "google.golang.org/grpc" + appv1 "stream.api/internal/gen/proto/app/v1" +) + +func Register(server grpc.ServiceRegistrar, services *Services) { + appv1.RegisterAuthServiceServer(server, services.AuthServiceServer) + appv1.RegisterAccountServiceServer(server, services.AccountServiceServer) + appv1.RegisterPreferencesServiceServer(server, services.PreferencesServiceServer) + appv1.RegisterUsageServiceServer(server, services.UsageServiceServer) + appv1.RegisterNotificationsServiceServer(server, services.NotificationsServiceServer) + appv1.RegisterDomainsServiceServer(server, services.DomainsServiceServer) + appv1.RegisterAdTemplatesServiceServer(server, services.AdTemplatesServiceServer) + appv1.RegisterPlansServiceServer(server, services.PlansServiceServer) + appv1.RegisterPaymentsServiceServer(server, services.PaymentsServiceServer) + appv1.RegisterVideosServiceServer(server, services.VideosServiceServer) + appv1.RegisterAdminServiceServer(server, services.AdminServiceServer) +} diff --git a/internal/rpc/app/service_account.go b/internal/rpc/app/service_account.go new file mode 100644 index 0000000..5d8bfe0 --- /dev/null +++ b/internal/rpc/app/service_account.go @@ -0,0 +1,185 @@ +package app + +import ( + "context" + "errors" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "gorm.io/gorm" + authapi "stream.api/internal/api/auth" + preferencesapi "stream.api/internal/api/preferences" + usageapi "stream.api/internal/api/usage" + "stream.api/internal/database/model" + appv1 "stream.api/internal/gen/proto/app/v1" +) + +func (s *appServices) GetMe(ctx context.Context, _ *appv1.GetMeRequest) (*appv1.GetMeResponse, error) { + result, err := s.authenticate(ctx) + if err != nil { + return nil, err + } + payload, err := authapi.BuildUserPayload(ctx, s.db, result.User) + if err != nil { + return nil, status.Error(codes.Internal, "Failed to build user payload") + } + return &appv1.GetMeResponse{User: toProtoUserPayload(payload)}, nil +} +func (s *appServices) UpdateMe(ctx context.Context, req *appv1.UpdateMeRequest) (*appv1.UpdateMeResponse, error) { + result, err := s.authenticate(ctx) + if err != nil { + return nil, err + } + + updatedUser, err := authapi.UpdateUserProfile(ctx, s.db, s.logger, result.UserID, authapi.UpdateProfileInput{ + Username: req.Username, + Email: req.Email, + Language: req.Language, + Locale: req.Locale, + }) + if err != nil { + switch { + case errors.Is(err, authapi.ErrEmailRequired), errors.Is(err, authapi.ErrEmailAlreadyRegistered): + return nil, status.Error(codes.InvalidArgument, err.Error()) + default: + return nil, status.Error(codes.Internal, "Failed to update profile") + } + } + + payload, err := authapi.BuildUserPayload(ctx, s.db, updatedUser) + if err != nil { + return nil, status.Error(codes.Internal, "Failed to build user payload") + } + return &appv1.UpdateMeResponse{User: toProtoUser(payload)}, nil +} +func (s *appServices) DeleteMe(ctx context.Context, _ *appv1.DeleteMeRequest) (*appv1.MessageResponse, error) { + result, err := s.authenticate(ctx) + if err != nil { + return nil, err + } + + userID := result.UserID + if err := s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + if err := tx.Where("user_id = ?", userID).Delete(&model.Notification{}).Error; err != nil { + return err + } + if err := tx.Where("user_id = ?", userID).Delete(&model.Domain{}).Error; err != nil { + return err + } + if err := tx.Where("user_id = ?", userID).Delete(&model.AdTemplate{}).Error; err != nil { + return err + } + if err := tx.Where("user_id = ?", userID).Delete(&model.VideoAdConfig{}).Error; err != nil { + return err + } + if err := tx.Where("user_id = ?", userID).Delete(&model.WalletTransaction{}).Error; err != nil { + return err + } + if err := tx.Where("user_id = ?", userID).Delete(&model.PlanSubscription{}).Error; err != nil { + return err + } + if err := tx.Where("user_id = ?", userID).Delete(&model.UserPreference{}).Error; err != nil { + return err + } + if err := tx.Where("user_id = ?", userID).Delete(&model.Payment{}).Error; err != nil { + return err + } + if err := tx.Where("user_id = ?", userID).Delete(&model.Video{}).Error; err != nil { + return err + } + if err := tx.Where("id = ?", userID).Delete(&model.User{}).Error; err != nil { + return err + } + return nil + }); err != nil { + s.logger.Error("Failed to delete user", "error", err) + return nil, status.Error(codes.Internal, "Failed to delete account") + } + + return messageResponse("Account deleted successfully"), nil +} +func (s *appServices) ClearMyData(ctx context.Context, _ *appv1.ClearMyDataRequest) (*appv1.MessageResponse, error) { + result, err := s.authenticate(ctx) + if err != nil { + return nil, err + } + + userID := result.UserID + if err := s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + if err := tx.Where("user_id = ?", userID).Delete(&model.Notification{}).Error; err != nil { + return err + } + if err := tx.Where("user_id = ?", userID).Delete(&model.Domain{}).Error; err != nil { + return err + } + if err := tx.Where("user_id = ?", userID).Delete(&model.AdTemplate{}).Error; err != nil { + return err + } + if err := tx.Where("user_id = ?", userID).Delete(&model.VideoAdConfig{}).Error; err != nil { + return err + } + if err := tx.Where("user_id = ?", userID).Delete(&model.Video{}).Error; err != nil { + return err + } + if err := tx.Model(&model.User{}).Where("id = ?", userID).Updates(map[string]interface{}{"storage_used": 0}).Error; err != nil { + return err + } + return nil + }); err != nil { + s.logger.Error("Failed to clear user data", "error", err) + return nil, status.Error(codes.Internal, "Failed to clear data") + } + + return messageResponse("Data cleared successfully"), nil +} +func (s *appServices) GetPreferences(ctx context.Context, _ *appv1.GetPreferencesRequest) (*appv1.GetPreferencesResponse, error) { + result, err := s.authenticate(ctx) + if err != nil { + return nil, err + } + pref, err := preferencesapi.LoadUserPreferences(ctx, s.db, result.UserID) + if err != nil { + return nil, status.Error(codes.Internal, "Failed to load preferences") + } + return &appv1.GetPreferencesResponse{Preferences: toProtoPreferences(pref)}, nil +} +func (s *appServices) UpdatePreferences(ctx context.Context, req *appv1.UpdatePreferencesRequest) (*appv1.UpdatePreferencesResponse, error) { + result, err := s.authenticate(ctx) + if err != nil { + return nil, err + } + pref, err := preferencesapi.UpdateUserPreferences(ctx, s.db, s.logger, result.UserID, preferencesapi.UpdateInput{ + EmailNotifications: req.EmailNotifications, + PushNotifications: req.PushNotifications, + MarketingNotifications: req.MarketingNotifications, + TelegramNotifications: req.TelegramNotifications, + Autoplay: req.Autoplay, + Loop: req.Loop, + Muted: req.Muted, + ShowControls: req.ShowControls, + Pip: req.Pip, + Airplay: req.Airplay, + Chromecast: req.Chromecast, + Language: req.Language, + Locale: req.Locale, + }) + if err != nil { + return nil, status.Error(codes.Internal, "Failed to save preferences") + } + return &appv1.UpdatePreferencesResponse{Preferences: toProtoPreferences(pref)}, nil +} +func (s *appServices) GetUsage(ctx context.Context, _ *appv1.GetUsageRequest) (*appv1.GetUsageResponse, error) { + result, err := s.authenticate(ctx) + if err != nil { + return nil, err + } + payload, err := usageapi.LoadUsage(ctx, s.db, s.logger, result.User) + if err != nil { + return nil, status.Error(codes.Internal, "Failed to load usage") + } + return &appv1.GetUsageResponse{ + UserId: payload.UserID, + TotalVideos: payload.TotalVideos, + TotalStorage: payload.TotalStorage, + }, nil +} diff --git a/internal/rpc/app/service_admin_finance_catalog.go b/internal/rpc/app/service_admin_finance_catalog.go new file mode 100644 index 0000000..fe8cabd --- /dev/null +++ b/internal/rpc/app/service_admin_finance_catalog.go @@ -0,0 +1,672 @@ +package app + +import ( + "context" + "errors" + "fmt" + "net/http" + "strings" + "time" + + "github.com/google/uuid" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "gorm.io/gorm" + "stream.api/internal/database/model" + appv1 "stream.api/internal/gen/proto/app/v1" +) + +func (s *appServices) ListAdminPayments(ctx context.Context, req *appv1.ListAdminPaymentsRequest) (*appv1.ListAdminPaymentsResponse, error) { + if _, err := s.requireAdmin(ctx); err != nil { + return nil, err + } + + page, limit, offset := adminPageLimitOffset(req.GetPage(), req.GetLimit()) + limitInt := int(limit) + userID := strings.TrimSpace(req.GetUserId()) + statusFilter := strings.TrimSpace(req.GetStatus()) + + db := s.db.WithContext(ctx).Model(&model.Payment{}) + if userID != "" { + db = db.Where("user_id = ?", userID) + } + if statusFilter != "" { + db = db.Where("UPPER(status) = ?", strings.ToUpper(statusFilter)) + } + + var total int64 + if err := db.Count(&total).Error; err != nil { + return nil, status.Error(codes.Internal, "Failed to list payments") + } + + var payments []model.Payment + if err := db.Order("created_at DESC").Offset(offset).Limit(limitInt).Find(&payments).Error; err != nil { + return nil, status.Error(codes.Internal, "Failed to list payments") + } + + items := make([]*appv1.AdminPayment, 0, len(payments)) + for _, payment := range payments { + payload, err := s.buildAdminPayment(ctx, &payment) + if err != nil { + return nil, status.Error(codes.Internal, "Failed to list payments") + } + items = append(items, payload) + } + + return &appv1.ListAdminPaymentsResponse{Payments: items, Total: total, Page: page, Limit: limit}, nil +} +func (s *appServices) GetAdminPayment(ctx context.Context, req *appv1.GetAdminPaymentRequest) (*appv1.GetAdminPaymentResponse, error) { + if _, err := s.requireAdmin(ctx); err != nil { + return nil, err + } + + id := strings.TrimSpace(req.GetId()) + if id == "" { + return nil, status.Error(codes.NotFound, "Payment not found") + } + + var payment model.Payment + if err := s.db.WithContext(ctx).Where("id = ?", id).First(&payment).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, status.Error(codes.NotFound, "Payment not found") + } + return nil, status.Error(codes.Internal, "Failed to get payment") + } + + payload, err := s.buildAdminPayment(ctx, &payment) + if err != nil { + return nil, status.Error(codes.Internal, "Failed to get payment") + } + + return &appv1.GetAdminPaymentResponse{Payment: payload}, nil +} +func (s *appServices) CreateAdminPayment(ctx context.Context, req *appv1.CreateAdminPaymentRequest) (*appv1.CreateAdminPaymentResponse, error) { + if _, err := s.requireAdmin(ctx); err != nil { + return nil, err + } + + userID := strings.TrimSpace(req.GetUserId()) + planID := strings.TrimSpace(req.GetPlanId()) + if userID == "" || planID == "" { + return nil, status.Error(codes.InvalidArgument, "User ID and plan ID are required") + } + if !isAllowedTermMonths(req.GetTermMonths()) { + return nil, status.Error(codes.InvalidArgument, "Term months must be one of 1, 3, 6, or 12") + } + + paymentMethod := normalizePaymentMethod(req.GetPaymentMethod()) + if paymentMethod == "" { + return nil, status.Error(codes.InvalidArgument, "Payment method must be wallet or topup") + } + + var user model.User + if err := s.db.WithContext(ctx).Where("id = ?", userID).First(&user).Error; 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 create payment") + } + + var planRecord model.Plan + if err := s.db.WithContext(ctx).Where("id = ?", planID).First(&planRecord).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, status.Error(codes.InvalidArgument, "Plan not found") + } + return nil, status.Error(codes.Internal, "Failed to create payment") + } + if planRecord.IsActive == nil || !*planRecord.IsActive { + return nil, status.Error(codes.InvalidArgument, "Plan is not active") + } + + totalAmount := planRecord.Price * float64(req.GetTermMonths()) + statusValue := "SUCCESS" + provider := "INTERNAL" + currency := normalizeCurrency(nil) + transactionID := buildTransactionID("sub") + now := time.Now().UTC() + + paymentRecord := &model.Payment{ + ID: uuid.New().String(), + UserID: user.ID, + PlanID: &planRecord.ID, + Amount: totalAmount, + Currency: ¤cy, + Status: &statusValue, + Provider: &provider, + TransactionID: &transactionID, + } + invoiceID := buildInvoiceID(paymentRecord.ID) + var subscription *model.PlanSubscription + var walletBalance float64 + + err := s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + if _, err := lockUserForUpdate(ctx, tx, user.ID); err != nil { + return err + } + + currentSubscription, err := model.GetLatestPlanSubscription(ctx, tx, user.ID) + if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { + return err + } + + baseExpiry := now + if currentSubscription != nil && currentSubscription.ExpiresAt.After(baseExpiry) { + baseExpiry = currentSubscription.ExpiresAt.UTC() + } + newExpiry := baseExpiry.AddDate(0, int(req.GetTermMonths()), 0) + + currentWalletBalance, err := model.GetWalletBalance(ctx, tx, user.ID) + if err != nil { + return err + } + shortfall := maxFloat(totalAmount-currentWalletBalance, 0) + if paymentMethod == paymentMethodWallet && shortfall > 0 { + return statusErrorWithBody(ctx, codes.InvalidArgument, http.StatusBadRequest, "Insufficient wallet balance", map[string]interface{}{ + "payment_method": paymentMethod, + "wallet_balance": currentWalletBalance, + "total_amount": totalAmount, + "shortfall": shortfall, + }) + } + + topupAmount := 0.0 + if paymentMethod == paymentMethodTopup { + if req.TopupAmount == nil { + return statusErrorWithBody(ctx, codes.InvalidArgument, http.StatusBadRequest, "Top-up amount is required when payment method is topup", map[string]interface{}{ + "payment_method": paymentMethod, + "wallet_balance": currentWalletBalance, + "total_amount": totalAmount, + "shortfall": shortfall, + }) + } + topupAmount = maxFloat(req.GetTopupAmount(), 0) + if topupAmount <= 0 { + return statusErrorWithBody(ctx, codes.InvalidArgument, http.StatusBadRequest, "Top-up amount must be greater than 0", map[string]interface{}{ + "payment_method": paymentMethod, + "wallet_balance": currentWalletBalance, + "total_amount": totalAmount, + "shortfall": shortfall, + }) + } + if topupAmount < shortfall { + return statusErrorWithBody(ctx, codes.InvalidArgument, http.StatusBadRequest, "Top-up amount must be greater than or equal to the required shortfall", map[string]interface{}{ + "payment_method": paymentMethod, + "wallet_balance": currentWalletBalance, + "total_amount": totalAmount, + "shortfall": shortfall, + "topup_amount": topupAmount, + }) + } + } + + if err := tx.Create(paymentRecord).Error; err != nil { + return err + } + + walletUsedAmount := totalAmount + if paymentMethod == paymentMethodTopup { + topupTransaction := &model.WalletTransaction{ + ID: uuid.New().String(), + UserID: user.ID, + Type: walletTransactionTypeTopup, + Amount: maxFloat(req.GetTopupAmount(), 0), + Currency: model.StringPtr(currency), + Note: model.StringPtr(fmt.Sprintf("Wallet top-up for %s (%d months)", planRecord.Name, req.GetTermMonths())), + PaymentID: &paymentRecord.ID, + PlanID: &planRecord.ID, + TermMonths: int32Ptr(req.GetTermMonths()), + } + if err := tx.Create(topupTransaction).Error; err != nil { + return err + } + } + + debitTransaction := &model.WalletTransaction{ + ID: uuid.New().String(), + UserID: user.ID, + Type: walletTransactionTypeSubscriptionDebit, + Amount: -totalAmount, + Currency: model.StringPtr(currency), + Note: model.StringPtr(fmt.Sprintf("Subscription payment for %s (%d months)", planRecord.Name, req.GetTermMonths())), + PaymentID: &paymentRecord.ID, + PlanID: &planRecord.ID, + TermMonths: int32Ptr(req.GetTermMonths()), + } + if err := tx.Create(debitTransaction).Error; err != nil { + return err + } + + subscription = &model.PlanSubscription{ + ID: uuid.New().String(), + UserID: user.ID, + PaymentID: paymentRecord.ID, + PlanID: planRecord.ID, + TermMonths: req.GetTermMonths(), + PaymentMethod: paymentMethod, + WalletAmount: walletUsedAmount, + TopupAmount: maxFloat(req.GetTopupAmount(), 0), + StartedAt: now, + ExpiresAt: newExpiry, + } + if err := tx.Create(subscription).Error; err != nil { + return err + } + + if err := tx.Model(&model.User{}).Where("id = ?", user.ID).Update("plan_id", planRecord.ID).Error; err != nil { + return err + } + + notification := buildSubscriptionNotification(user.ID, paymentRecord.ID, invoiceID, &planRecord, subscription) + if err := tx.Create(notification).Error; err != nil { + return err + } + + walletBalance, err = model.GetWalletBalance(ctx, tx, user.ID) + if err != nil { + return err + } + return nil + }) + if err != nil { + if _, ok := status.FromError(err); ok { + return nil, err + } + return nil, status.Error(codes.Internal, "Failed to create payment") + } + + payload, err := s.buildAdminPayment(ctx, paymentRecord) + if err != nil { + return nil, status.Error(codes.Internal, "Failed to create payment") + } + return &appv1.CreateAdminPaymentResponse{ + Payment: payload, + Subscription: toProtoPlanSubscription(subscription), + WalletBalance: walletBalance, + InvoiceId: invoiceID, + }, nil +} +func (s *appServices) UpdateAdminPayment(ctx context.Context, req *appv1.UpdateAdminPaymentRequest) (*appv1.UpdateAdminPaymentResponse, error) { + if _, err := s.requireAdmin(ctx); err != nil { + return nil, err + } + + id := strings.TrimSpace(req.GetId()) + if id == "" { + return nil, status.Error(codes.NotFound, "Payment not found") + } + + newStatus := strings.ToUpper(strings.TrimSpace(req.GetStatus())) + if newStatus == "" { + newStatus = "SUCCESS" + } + if newStatus != "SUCCESS" && newStatus != "FAILED" && newStatus != "PENDING" { + return nil, status.Error(codes.InvalidArgument, "Invalid payment status") + } + + var payment model.Payment + if err := s.db.WithContext(ctx).Where("id = ?", id).First(&payment).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, status.Error(codes.NotFound, "Payment not found") + } + return nil, status.Error(codes.Internal, "Failed to update payment") + } + + currentStatus := strings.ToUpper(normalizePaymentStatus(payment.Status)) + if currentStatus != newStatus { + if (currentStatus == "FAILED" || currentStatus == "PENDING") && newStatus == "SUCCESS" { + return nil, status.Error(codes.InvalidArgument, "Cannot transition payment to SUCCESS from admin update; recreate through the payment flow instead") + } + payment.Status = model.StringPtr(newStatus) + if err := s.db.WithContext(ctx).Save(&payment).Error; err != nil { + return nil, status.Error(codes.Internal, "Failed to update payment") + } + } + + payload, err := s.buildAdminPayment(ctx, &payment) + if err != nil { + return nil, status.Error(codes.Internal, "Failed to update payment") + } + return &appv1.UpdateAdminPaymentResponse{Payment: payload}, nil +} +func (s *appServices) ListAdminPlans(ctx context.Context, _ *appv1.ListAdminPlansRequest) (*appv1.ListAdminPlansResponse, error) { + if _, err := s.requireAdmin(ctx); err != nil { + return nil, err + } + + var plans []model.Plan + if err := s.db.WithContext(ctx).Order("price ASC").Find(&plans).Error; err != nil { + return nil, status.Error(codes.Internal, "Failed to list plans") + } + + items := make([]*appv1.AdminPlan, 0, len(plans)) + for i := range plans { + payload, err := s.buildAdminPlan(ctx, &plans[i]) + if err != nil { + return nil, status.Error(codes.Internal, "Failed to list plans") + } + items = append(items, payload) + } + return &appv1.ListAdminPlansResponse{Plans: items}, nil +} +func (s *appServices) CreateAdminPlan(ctx context.Context, req *appv1.CreateAdminPlanRequest) (*appv1.CreateAdminPlanResponse, error) { + if _, err := s.requireAdmin(ctx); err != nil { + return nil, err + } + + if msg := validateAdminPlanInput(req.GetName(), req.GetCycle(), req.GetPrice(), req.GetStorageLimit(), req.GetUploadLimit()); msg != "" { + return nil, status.Error(codes.InvalidArgument, msg) + } + + plan := &model.Plan{ + ID: uuid.New().String(), + Name: strings.TrimSpace(req.GetName()), + Description: nullableTrimmedStringPtr(req.Description), + Features: append([]string(nil), req.GetFeatures()...), + Price: req.GetPrice(), + Cycle: strings.TrimSpace(req.GetCycle()), + StorageLimit: req.GetStorageLimit(), + UploadLimit: req.GetUploadLimit(), + DurationLimit: 0, + QualityLimit: "", + IsActive: model.BoolPtr(req.GetIsActive()), + } + + if err := s.db.WithContext(ctx).Create(plan).Error; err != nil { + return nil, status.Error(codes.Internal, "Failed to create plan") + } + + payload, err := s.buildAdminPlan(ctx, plan) + if err != nil { + return nil, status.Error(codes.Internal, "Failed to create plan") + } + return &appv1.CreateAdminPlanResponse{Plan: payload}, nil +} +func (s *appServices) UpdateAdminPlan(ctx context.Context, req *appv1.UpdateAdminPlanRequest) (*appv1.UpdateAdminPlanResponse, error) { + if _, err := s.requireAdmin(ctx); err != nil { + return nil, err + } + + id := strings.TrimSpace(req.GetId()) + if id == "" { + return nil, status.Error(codes.NotFound, "Plan not found") + } + if msg := validateAdminPlanInput(req.GetName(), req.GetCycle(), req.GetPrice(), req.GetStorageLimit(), req.GetUploadLimit()); msg != "" { + return nil, status.Error(codes.InvalidArgument, msg) + } + + var plan model.Plan + if err := s.db.WithContext(ctx).Where("id = ?", id).First(&plan).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, status.Error(codes.NotFound, "Plan not found") + } + return nil, status.Error(codes.Internal, "Failed to update plan") + } + + plan.Name = strings.TrimSpace(req.GetName()) + plan.Description = nullableTrimmedStringPtr(req.Description) + plan.Features = append([]string(nil), req.GetFeatures()...) + plan.Price = req.GetPrice() + plan.Cycle = strings.TrimSpace(req.GetCycle()) + plan.StorageLimit = req.GetStorageLimit() + plan.UploadLimit = req.GetUploadLimit() + plan.IsActive = model.BoolPtr(req.GetIsActive()) + + if err := s.db.WithContext(ctx).Save(&plan).Error; err != nil { + return nil, status.Error(codes.Internal, "Failed to update plan") + } + + payload, err := s.buildAdminPlan(ctx, &plan) + if err != nil { + return nil, status.Error(codes.Internal, "Failed to update plan") + } + return &appv1.UpdateAdminPlanResponse{Plan: payload}, nil +} +func (s *appServices) DeleteAdminPlan(ctx context.Context, req *appv1.DeleteAdminPlanRequest) (*appv1.DeleteAdminPlanResponse, error) { + if _, err := s.requireAdmin(ctx); err != nil { + return nil, err + } + + id := strings.TrimSpace(req.GetId()) + if id == "" { + return nil, status.Error(codes.NotFound, "Plan not found") + } + + var plan model.Plan + if err := s.db.WithContext(ctx).Where("id = ?", id).First(&plan).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, status.Error(codes.NotFound, "Plan not found") + } + return nil, status.Error(codes.Internal, "Failed to delete plan") + } + + var paymentCount int64 + if err := s.db.WithContext(ctx).Model(&model.Payment{}).Where("plan_id = ?", id).Count(&paymentCount).Error; err != nil { + return nil, status.Error(codes.Internal, "Failed to delete plan") + } + var subscriptionCount int64 + if err := s.db.WithContext(ctx).Model(&model.PlanSubscription{}).Where("plan_id = ?", id).Count(&subscriptionCount).Error; err != nil { + return nil, status.Error(codes.Internal, "Failed to delete plan") + } + + if paymentCount > 0 || subscriptionCount > 0 { + inactive := false + if err := s.db.WithContext(ctx).Model(&model.Plan{}).Where("id = ?", id).Update("is_active", inactive).Error; err != nil { + return nil, status.Error(codes.Internal, "Failed to deactivate plan") + } + return &appv1.DeleteAdminPlanResponse{Message: "Plan deactivated", Mode: "deactivated"}, nil + } + + if err := s.db.WithContext(ctx).Where("id = ?", id).Delete(&model.Plan{}).Error; err != nil { + return nil, status.Error(codes.Internal, "Failed to delete plan") + } + return &appv1.DeleteAdminPlanResponse{Message: "Plan deleted", Mode: "deleted"}, nil +} +func (s *appServices) ListAdminAdTemplates(ctx context.Context, req *appv1.ListAdminAdTemplatesRequest) (*appv1.ListAdminAdTemplatesResponse, error) { + if _, err := s.requireAdmin(ctx); err != nil { + return nil, err + } + + page, limit, offset := adminPageLimitOffset(req.GetPage(), req.GetLimit()) + limitInt := int(limit) + search := strings.TrimSpace(protoStringValue(req.Search)) + userID := strings.TrimSpace(protoStringValue(req.UserId)) + + db := s.db.WithContext(ctx).Model(&model.AdTemplate{}) + if search != "" { + like := "%" + search + "%" + db = db.Where("name ILIKE ?", like) + } + if userID != "" { + db = db.Where("user_id = ?", userID) + } + + var total int64 + if err := db.Count(&total).Error; err != nil { + return nil, status.Error(codes.Internal, "Failed to list ad templates") + } + + var templates []model.AdTemplate + if err := db.Order("created_at DESC").Offset(offset).Limit(limitInt).Find(&templates).Error; err != nil { + return nil, status.Error(codes.Internal, "Failed to list ad templates") + } + + items := make([]*appv1.AdminAdTemplate, 0, len(templates)) + for i := range templates { + payload, err := s.buildAdminAdTemplate(ctx, &templates[i]) + if err != nil { + return nil, status.Error(codes.Internal, "Failed to list ad templates") + } + items = append(items, payload) + } + + return &appv1.ListAdminAdTemplatesResponse{ + Templates: items, + Total: total, + Page: page, + Limit: limit, + }, nil +} +func (s *appServices) GetAdminAdTemplate(ctx context.Context, req *appv1.GetAdminAdTemplateRequest) (*appv1.GetAdminAdTemplateResponse, error) { + if _, err := s.requireAdmin(ctx); err != nil { + return nil, err + } + + id := strings.TrimSpace(req.GetId()) + if id == "" { + return nil, status.Error(codes.NotFound, "Ad template not found") + } + + var item model.AdTemplate + if err := s.db.WithContext(ctx).Where("id = ?", id).First(&item).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, status.Error(codes.NotFound, "Ad template not found") + } + return nil, status.Error(codes.Internal, "Failed to load ad template") + } + + payload, err := s.buildAdminAdTemplate(ctx, &item) + if err != nil { + return nil, status.Error(codes.Internal, "Failed to load ad template") + } + return &appv1.GetAdminAdTemplateResponse{Template: payload}, nil +} +func (s *appServices) CreateAdminAdTemplate(ctx context.Context, req *appv1.CreateAdminAdTemplateRequest) (*appv1.CreateAdminAdTemplateResponse, error) { + if _, err := s.requireAdmin(ctx); err != nil { + return nil, err + } + + duration := req.Duration + if msg := validateAdminAdTemplateInput(req.GetUserId(), req.GetName(), req.GetVastTagUrl(), req.GetAdFormat(), duration); msg != "" { + return nil, status.Error(codes.InvalidArgument, msg) + } + + var user model.User + if err := s.db.WithContext(ctx).Where("id = ?", strings.TrimSpace(req.GetUserId())).First(&user).Error; 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 ad template") + } + + item := &model.AdTemplate{ + ID: uuid.New().String(), + UserID: user.ID, + Name: strings.TrimSpace(req.GetName()), + Description: nullableTrimmedStringPtr(req.Description), + VastTagURL: strings.TrimSpace(req.GetVastTagUrl()), + AdFormat: model.StringPtr(normalizeAdminAdFormatValue(req.GetAdFormat())), + Duration: duration, + IsActive: model.BoolPtr(req.GetIsActive()), + IsDefault: req.GetIsDefault(), + } + if !boolValue(item.IsActive) { + item.IsDefault = false + } + + if err := s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + if item.IsDefault { + if err := s.unsetAdminDefaultTemplates(ctx, tx, item.UserID, ""); err != nil { + return err + } + } + return tx.Create(item).Error + }); err != nil { + return nil, status.Error(codes.Internal, "Failed to save ad template") + } + + payload, err := s.buildAdminAdTemplate(ctx, item) + if err != nil { + return nil, status.Error(codes.Internal, "Failed to save ad template") + } + return &appv1.CreateAdminAdTemplateResponse{Template: payload}, nil +} +func (s *appServices) UpdateAdminAdTemplate(ctx context.Context, req *appv1.UpdateAdminAdTemplateRequest) (*appv1.UpdateAdminAdTemplateResponse, error) { + if _, err := s.requireAdmin(ctx); err != nil { + return nil, err + } + + id := strings.TrimSpace(req.GetId()) + if id == "" { + return nil, status.Error(codes.NotFound, "Ad template not found") + } + duration := req.Duration + if msg := validateAdminAdTemplateInput(req.GetUserId(), req.GetName(), req.GetVastTagUrl(), req.GetAdFormat(), duration); msg != "" { + return nil, status.Error(codes.InvalidArgument, msg) + } + + var user model.User + if err := s.db.WithContext(ctx).Where("id = ?", strings.TrimSpace(req.GetUserId())).First(&user).Error; 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 ad template") + } + + var item model.AdTemplate + if err := s.db.WithContext(ctx).Where("id = ?", id).First(&item).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, status.Error(codes.NotFound, "Ad template not found") + } + return nil, status.Error(codes.Internal, "Failed to save ad template") + } + + item.UserID = user.ID + item.Name = strings.TrimSpace(req.GetName()) + item.Description = nullableTrimmedStringPtr(req.Description) + item.VastTagURL = strings.TrimSpace(req.GetVastTagUrl()) + item.AdFormat = model.StringPtr(normalizeAdminAdFormatValue(req.GetAdFormat())) + item.Duration = duration + item.IsActive = model.BoolPtr(req.GetIsActive()) + item.IsDefault = req.GetIsDefault() + if !boolValue(item.IsActive) { + item.IsDefault = false + } + + if err := s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + if item.IsDefault { + if err := s.unsetAdminDefaultTemplates(ctx, tx, item.UserID, item.ID); err != nil { + return err + } + } + return tx.Save(&item).Error + }); err != nil { + return nil, status.Error(codes.Internal, "Failed to save ad template") + } + + payload, err := s.buildAdminAdTemplate(ctx, &item) + if err != nil { + return nil, status.Error(codes.Internal, "Failed to save ad template") + } + return &appv1.UpdateAdminAdTemplateResponse{Template: payload}, nil +} +func (s *appServices) DeleteAdminAdTemplate(ctx context.Context, req *appv1.DeleteAdminAdTemplateRequest) (*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, "Ad template not found") + } + + err := s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + if err := tx.Where("ad_template_id = ?", id).Delete(&model.VideoAdConfig{}).Error; err != nil { + return err + } + res := tx.Where("id = ?", id).Delete(&model.AdTemplate{}) + if res.Error != nil { + return res.Error + } + if res.RowsAffected == 0 { + return gorm.ErrRecordNotFound + } + return nil + }) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, status.Error(codes.NotFound, "Ad template not found") + } + return nil, status.Error(codes.Internal, "Failed to delete ad template") + } + return &appv1.MessageResponse{Message: "Ad template deleted"}, nil +} diff --git a/internal/rpc/app/service_admin_jobs_agents.go b/internal/rpc/app/service_admin_jobs_agents.go new file mode 100644 index 0000000..7b4b34d --- /dev/null +++ b/internal/rpc/app/service_admin_jobs_agents.go @@ -0,0 +1,201 @@ +package app + +import ( + "context" + "encoding/json" + "errors" + "strings" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "gorm.io/gorm" + appv1 "stream.api/internal/gen/proto/app/v1" + "stream.api/internal/video/runtime/services" +) + +func (s *appServices) ListAdminJobs(ctx context.Context, req *appv1.ListAdminJobsRequest) (*appv1.ListAdminJobsResponse, error) { + if _, err := s.requireAdmin(ctx); err != nil { + return nil, err + } + if s.jobService == nil { + return nil, status.Error(codes.Unavailable, "Job service is unavailable") + } + + offset := int(req.GetOffset()) + limit := int(req.GetLimit()) + if offset < 0 { + offset = 0 + } + if limit <= 0 || limit > 100 { + limit = 20 + } + + var ( + result *services.PaginatedJobs + err error + ) + if agentID := strings.TrimSpace(req.GetAgentId()); agentID != "" { + result, err = s.jobService.ListJobsByAgent(ctx, agentID, offset, limit) + } else { + result, err = s.jobService.ListJobs(ctx, offset, limit) + } + if err != nil { + return nil, status.Error(codes.Internal, "Failed to list jobs") + } + + jobs := make([]*appv1.AdminJob, 0, len(result.Jobs)) + for _, job := range result.Jobs { + jobs = append(jobs, buildAdminJob(job)) + } + + return &appv1.ListAdminJobsResponse{ + Jobs: jobs, + Total: result.Total, + Offset: int32(result.Offset), + Limit: int32(result.Limit), + HasMore: result.HasMore, + }, nil +} +func (s *appServices) GetAdminJob(ctx context.Context, req *appv1.GetAdminJobRequest) (*appv1.GetAdminJobResponse, error) { + if _, err := s.requireAdmin(ctx); err != nil { + return nil, err + } + if s.jobService == nil { + return nil, status.Error(codes.Unavailable, "Job service is unavailable") + } + + id := strings.TrimSpace(req.GetId()) + if id == "" { + return nil, status.Error(codes.NotFound, "Job not found") + } + job, err := s.jobService.GetJob(ctx, id) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, status.Error(codes.NotFound, "Job not found") + } + return nil, status.Error(codes.Internal, "Failed to load job") + } + return &appv1.GetAdminJobResponse{Job: buildAdminJob(job)}, nil +} +func (s *appServices) GetAdminJobLogs(ctx context.Context, req *appv1.GetAdminJobLogsRequest) (*appv1.GetAdminJobLogsResponse, error) { + response, err := s.GetAdminJob(ctx, &appv1.GetAdminJobRequest{Id: req.GetId()}) + if err != nil { + return nil, err + } + return &appv1.GetAdminJobLogsResponse{Logs: response.GetJob().GetLogs()}, nil +} +func (s *appServices) CreateAdminJob(ctx context.Context, req *appv1.CreateAdminJobRequest) (*appv1.CreateAdminJobResponse, error) { + if _, err := s.requireAdmin(ctx); err != nil { + return nil, err + } + if s.jobService == nil { + return nil, status.Error(codes.Unavailable, "Job service is unavailable") + } + + command := strings.TrimSpace(req.GetCommand()) + if command == "" { + return nil, status.Error(codes.InvalidArgument, "Command is required") + } + image := strings.TrimSpace(req.GetImage()) + if image == "" { + image = "alpine" + } + name := strings.TrimSpace(req.GetName()) + if name == "" { + name = command + } + payload, err := json.Marshal(map[string]any{ + "image": image, + "commands": []string{command}, + "environment": req.GetEnv(), + }) + if err != nil { + return nil, status.Error(codes.Internal, "Failed to create job payload") + } + + job, err := s.jobService.CreateJob(ctx, strings.TrimSpace(req.GetUserId()), name, payload, int(req.GetPriority()), req.GetTimeLimit()) + if err != nil { + return nil, status.Error(codes.Internal, "Failed to create job") + } + return &appv1.CreateAdminJobResponse{Job: buildAdminJob(job)}, nil +} +func (s *appServices) CancelAdminJob(ctx context.Context, req *appv1.CancelAdminJobRequest) (*appv1.CancelAdminJobResponse, error) { + if _, err := s.requireAdmin(ctx); err != nil { + return nil, err + } + if s.jobService == nil { + return nil, status.Error(codes.Unavailable, "Job service is unavailable") + } + + id := strings.TrimSpace(req.GetId()) + if id == "" { + return nil, status.Error(codes.NotFound, "Job not found") + } + if err := s.jobService.CancelJob(ctx, id); err != nil { + if strings.Contains(strings.ToLower(err.Error()), "not found") { + return nil, status.Error(codes.NotFound, "Job not found") + } + return nil, status.Error(codes.FailedPrecondition, err.Error()) + } + return &appv1.CancelAdminJobResponse{Status: "cancelled", JobId: id}, nil +} +func (s *appServices) RetryAdminJob(ctx context.Context, req *appv1.RetryAdminJobRequest) (*appv1.RetryAdminJobResponse, error) { + if _, err := s.requireAdmin(ctx); err != nil { + return nil, err + } + if s.jobService == nil { + return nil, status.Error(codes.Unavailable, "Job service is unavailable") + } + + id := strings.TrimSpace(req.GetId()) + if id == "" { + return nil, status.Error(codes.NotFound, "Job not found") + } + job, err := s.jobService.RetryJob(ctx, id) + if err != nil { + if strings.Contains(strings.ToLower(err.Error()), "not found") { + return nil, status.Error(codes.NotFound, "Job not found") + } + return nil, status.Error(codes.FailedPrecondition, err.Error()) + } + return &appv1.RetryAdminJobResponse{Job: buildAdminJob(job)}, nil +} +func (s *appServices) ListAdminAgents(ctx context.Context, _ *appv1.ListAdminAgentsRequest) (*appv1.ListAdminAgentsResponse, error) { + if _, err := s.requireAdmin(ctx); err != nil { + return nil, err + } + if s.agentRuntime == nil { + return nil, status.Error(codes.Unavailable, "Agent runtime is unavailable") + } + + items := s.agentRuntime.ListAgentsWithStats() + agents := make([]*appv1.AdminAgent, 0, len(items)) + for _, item := range items { + agents = append(agents, buildAdminAgent(item)) + } + return &appv1.ListAdminAgentsResponse{Agents: agents}, nil +} +func (s *appServices) RestartAdminAgent(ctx context.Context, req *appv1.RestartAdminAgentRequest) (*appv1.AdminAgentCommandResponse, error) { + if _, err := s.requireAdmin(ctx); err != nil { + return nil, err + } + if s.agentRuntime == nil { + return nil, status.Error(codes.Unavailable, "Agent runtime is unavailable") + } + if !s.agentRuntime.SendCommand(strings.TrimSpace(req.GetId()), "restart") { + return nil, status.Error(codes.Unavailable, "Agent not active or command channel full") + } + return &appv1.AdminAgentCommandResponse{Status: "restart command sent"}, nil +} +func (s *appServices) UpdateAdminAgent(ctx context.Context, req *appv1.UpdateAdminAgentRequest) (*appv1.AdminAgentCommandResponse, error) { + if _, err := s.requireAdmin(ctx); err != nil { + return nil, err + } + if s.agentRuntime == nil { + return nil, status.Error(codes.Unavailable, "Agent runtime is unavailable") + } + if !s.agentRuntime.SendCommand(strings.TrimSpace(req.GetId()), "update") { + return nil, status.Error(codes.Unavailable, "Agent not active or command channel full") + } + return &appv1.AdminAgentCommandResponse{Status: "update command sent"}, nil +} diff --git a/internal/rpc/app/service_admin_users_videos.go b/internal/rpc/app/service_admin_users_videos.go new file mode 100644 index 0000000..608642e --- /dev/null +++ b/internal/rpc/app/service_admin_users_videos.go @@ -0,0 +1,579 @@ +package app + +import ( + "context" + "errors" + "strings" + "time" + + "github.com/google/uuid" + "golang.org/x/crypto/bcrypt" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "gorm.io/gorm" + "stream.api/internal/database/model" + appv1 "stream.api/internal/gen/proto/app/v1" +) + +func (s *appServices) GetAdminDashboard(ctx context.Context, _ *appv1.GetAdminDashboardRequest) (*appv1.GetAdminDashboardResponse, error) { + if _, err := s.requireAdmin(ctx); err != nil { + return nil, err + } + + dashboard := &appv1.AdminDashboard{} + db := s.db.WithContext(ctx) + + db.Model(&model.User{}).Count(&dashboard.TotalUsers) + db.Model(&model.Video{}).Count(&dashboard.TotalVideos) + db.Model(&model.User{}).Select("COALESCE(SUM(storage_used), 0)").Row().Scan(&dashboard.TotalStorageUsed) + db.Model(&model.Payment{}).Count(&dashboard.TotalPayments) + db.Model(&model.Payment{}).Where("status = ?", "SUCCESS").Select("COALESCE(SUM(amount), 0)").Row().Scan(&dashboard.TotalRevenue) + db.Model(&model.PlanSubscription{}).Where("expires_at > ?", time.Now()).Count(&dashboard.ActiveSubscriptions) + db.Model(&model.AdTemplate{}).Count(&dashboard.TotalAdTemplates) + + today := time.Now().Truncate(24 * time.Hour) + db.Model(&model.User{}).Where("created_at >= ?", today).Count(&dashboard.NewUsersToday) + db.Model(&model.Video{}).Where("created_at >= ?", today).Count(&dashboard.NewVideosToday) + + return &appv1.GetAdminDashboardResponse{Dashboard: dashboard}, nil +} +func (s *appServices) ListAdminUsers(ctx context.Context, req *appv1.ListAdminUsersRequest) (*appv1.ListAdminUsersResponse, error) { + if _, err := s.requireAdmin(ctx); err != nil { + return nil, err + } + + page, limit, offset := adminPageLimitOffset(req.GetPage(), req.GetLimit()) + limitInt := int(limit) + search := strings.TrimSpace(req.GetSearch()) + role := strings.TrimSpace(req.GetRole()) + + db := s.db.WithContext(ctx).Model(&model.User{}) + if search != "" { + like := "%" + search + "%" + db = db.Where("email ILIKE ? OR username ILIKE ?", like, like) + } + if role != "" { + db = db.Where("UPPER(role) = ?", strings.ToUpper(role)) + } + + var total int64 + if err := db.Count(&total).Error; err != nil { + return nil, status.Error(codes.Internal, "Failed to list users") + } + + var users []model.User + if err := db.Order("created_at DESC").Offset(offset).Limit(limitInt).Find(&users).Error; err != nil { + return nil, status.Error(codes.Internal, "Failed to list users") + } + + items := make([]*appv1.AdminUser, 0, len(users)) + for _, user := range users { + payload, err := s.buildAdminUser(ctx, &user) + if err != nil { + return nil, status.Error(codes.Internal, "Failed to list users") + } + items = append(items, payload) + } + + return &appv1.ListAdminUsersResponse{Users: items, Total: total, Page: page, Limit: limit}, nil +} +func (s *appServices) GetAdminUser(ctx context.Context, req *appv1.GetAdminUserRequest) (*appv1.GetAdminUserResponse, error) { + if _, err := s.requireAdmin(ctx); err != nil { + return nil, err + } + + id := strings.TrimSpace(req.GetId()) + if id == "" { + return nil, status.Error(codes.NotFound, "User not found") + } + + var user model.User + if err := s.db.WithContext(ctx).Where("id = ?", id).First(&user).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, status.Error(codes.NotFound, "User not found") + } + return nil, status.Error(codes.Internal, "Failed to get user") + } + + payload, err := s.buildAdminUser(ctx, &user) + if err != nil { + return nil, status.Error(codes.Internal, "Failed to get user") + } + + var subscription model.PlanSubscription + var subscriptionPayload *appv1.PlanSubscription + if err := s.db.WithContext(ctx).Where("user_id = ?", id).Order("created_at DESC").First(&subscription).Error; err == nil { + subscriptionPayload = toProtoPlanSubscription(&subscription) + } else if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { + return nil, status.Error(codes.Internal, "Failed to get user") + } + + return &appv1.GetAdminUserResponse{User: &appv1.AdminUserDetail{User: payload, Subscription: subscriptionPayload}}, nil +} +func (s *appServices) CreateAdminUser(ctx context.Context, req *appv1.CreateAdminUserRequest) (*appv1.CreateAdminUserResponse, error) { + if _, err := s.requireAdmin(ctx); err != nil { + return nil, err + } + + email := strings.TrimSpace(req.GetEmail()) + password := req.GetPassword() + if email == "" || password == "" { + return nil, status.Error(codes.InvalidArgument, "Email and password are required") + } + + role := normalizeAdminRoleValue(req.GetRole()) + if !isValidAdminRoleValue(role) { + return nil, status.Error(codes.InvalidArgument, "Invalid role. Must be USER, ADMIN, or BLOCK") + } + + planID := nullableTrimmedString(req.PlanId) + if err := s.ensurePlanExists(ctx, planID); err != nil { + return nil, err + } + + hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) + if err != nil { + return nil, status.Error(codes.Internal, "Failed to hash password") + } + + user := &model.User{ + ID: uuid.New().String(), + Email: email, + Password: model.StringPtr(string(hashedPassword)), + Username: nullableTrimmedString(req.Username), + Role: model.StringPtr(role), + PlanID: planID, + } + + if err := s.db.WithContext(ctx).Create(user).Error; err != nil { + if errors.Is(err, gorm.ErrDuplicatedKey) { + return nil, status.Error(codes.AlreadyExists, "Email already registered") + } + return nil, status.Error(codes.Internal, "Failed to create user") + } + + payload, err := s.buildAdminUser(ctx, user) + if err != nil { + return nil, status.Error(codes.Internal, "Failed to create user") + } + return &appv1.CreateAdminUserResponse{User: payload}, nil +} +func (s *appServices) UpdateAdminUser(ctx context.Context, req *appv1.UpdateAdminUserRequest) (*appv1.UpdateAdminUserResponse, error) { + adminResult, err := s.requireAdmin(ctx) + if err != nil { + return nil, err + } + + id := strings.TrimSpace(req.GetId()) + if id == "" { + return nil, status.Error(codes.NotFound, "User not found") + } + + updates := map[string]interface{}{} + if req.Email != nil { + email := strings.TrimSpace(req.GetEmail()) + if email == "" { + return nil, status.Error(codes.InvalidArgument, "Email is required") + } + updates["email"] = email + } + if req.Username != nil { + updates["username"] = nullableTrimmedString(req.Username) + } + if req.Role != nil { + role := normalizeAdminRoleValue(req.GetRole()) + if !isValidAdminRoleValue(role) { + return nil, status.Error(codes.InvalidArgument, "Invalid role. Must be USER, ADMIN, or BLOCK") + } + if id == adminResult.UserID && role != "ADMIN" { + return nil, status.Error(codes.InvalidArgument, "Cannot change your own role") + } + updates["role"] = role + } + if req.PlanId != nil { + planID := nullableTrimmedString(req.PlanId) + if err := s.ensurePlanExists(ctx, planID); err != nil { + return nil, err + } + updates["plan_id"] = planID + } + if req.Password != nil { + if strings.TrimSpace(req.GetPassword()) == "" { + return nil, status.Error(codes.InvalidArgument, "Password must not be empty") + } + hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.GetPassword()), bcrypt.DefaultCost) + if err != nil { + return nil, status.Error(codes.Internal, "Failed to hash password") + } + updates["password"] = string(hashedPassword) + } + if len(updates) == 0 { + var user model.User + if err := s.db.WithContext(ctx).Where("id = ?", id).First(&user).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, status.Error(codes.NotFound, "User not found") + } + return nil, status.Error(codes.Internal, "Failed to update user") + } + payload, err := s.buildAdminUser(ctx, &user) + if err != nil { + return nil, status.Error(codes.Internal, "Failed to update user") + } + return &appv1.UpdateAdminUserResponse{User: payload}, nil + } + + result := s.db.WithContext(ctx).Model(&model.User{}).Where("id = ?", id).Updates(updates) + if result.Error != nil { + if errors.Is(result.Error, gorm.ErrDuplicatedKey) { + return nil, status.Error(codes.AlreadyExists, "Email already registered") + } + return nil, status.Error(codes.Internal, "Failed to update user") + } + if result.RowsAffected == 0 { + return nil, status.Error(codes.NotFound, "User not found") + } + + var user model.User + if err := s.db.WithContext(ctx).Where("id = ?", id).First(&user).Error; err != nil { + return nil, status.Error(codes.Internal, "Failed to update user") + } + payload, err := s.buildAdminUser(ctx, &user) + if err != nil { + return nil, status.Error(codes.Internal, "Failed to update user") + } + return &appv1.UpdateAdminUserResponse{User: payload}, nil +} +func (s *appServices) UpdateAdminUserRole(ctx context.Context, req *appv1.UpdateAdminUserRoleRequest) (*appv1.UpdateAdminUserRoleResponse, error) { + adminResult, err := s.requireAdmin(ctx) + if err != nil { + return nil, err + } + + id := strings.TrimSpace(req.GetId()) + if id == "" { + return nil, status.Error(codes.NotFound, "User not found") + } + if id == adminResult.UserID { + return nil, status.Error(codes.InvalidArgument, "Cannot change your own role") + } + + role := normalizeAdminRoleValue(req.GetRole()) + if !isValidAdminRoleValue(role) { + return nil, status.Error(codes.InvalidArgument, "Invalid role. Must be USER, ADMIN, or BLOCK") + } + + result := s.db.WithContext(ctx).Model(&model.User{}).Where("id = ?", id).Update("role", role) + if result.Error != nil { + return nil, status.Error(codes.Internal, "Failed to update role") + } + if result.RowsAffected == 0 { + return nil, status.Error(codes.NotFound, "User not found") + } + + return &appv1.UpdateAdminUserRoleResponse{Message: "Role updated", Role: role}, nil +} +func (s *appServices) DeleteAdminUser(ctx context.Context, req *appv1.DeleteAdminUserRequest) (*appv1.MessageResponse, error) { + adminResult, err := s.requireAdmin(ctx) + if err != nil { + return nil, err + } + + id := strings.TrimSpace(req.GetId()) + if id == "" { + return nil, status.Error(codes.NotFound, "User not found") + } + if id == adminResult.UserID { + return nil, status.Error(codes.InvalidArgument, "Cannot delete your own account") + } + + var user model.User + if err := s.db.WithContext(ctx).Where("id = ?", id).First(&user).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, status.Error(codes.NotFound, "User not found") + } + return nil, status.Error(codes.Internal, "Failed to find user") + } + + err = s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + tables := []struct { + model interface{} + where string + }{ + {&model.VideoAdConfig{}, "user_id = ?"}, + {&model.AdTemplate{}, "user_id = ?"}, + {&model.Notification{}, "user_id = ?"}, + {&model.Domain{}, "user_id = ?"}, + {&model.WalletTransaction{}, "user_id = ?"}, + {&model.PlanSubscription{}, "user_id = ?"}, + {&model.UserPreference{}, "user_id = ?"}, + {&model.Video{}, "user_id = ?"}, + {&model.Payment{}, "user_id = ?"}, + } + for _, item := range tables { + if err := tx.Where(item.where, id).Delete(item.model).Error; err != nil { + return err + } + } + return tx.Where("id = ?", id).Delete(&model.User{}).Error + }) + if err != nil { + return nil, status.Error(codes.Internal, "Failed to delete user") + } + + return messageResponse("User deleted"), nil +} +func (s *appServices) ListAdminVideos(ctx context.Context, req *appv1.ListAdminVideosRequest) (*appv1.ListAdminVideosResponse, error) { + if _, err := s.requireAdmin(ctx); err != nil { + return nil, err + } + + page, limit, offset := adminPageLimitOffset(req.GetPage(), req.GetLimit()) + limitInt := int(limit) + search := strings.TrimSpace(req.GetSearch()) + userID := strings.TrimSpace(req.GetUserId()) + statusFilter := strings.TrimSpace(req.GetStatus()) + + db := s.db.WithContext(ctx).Model(&model.Video{}) + if search != "" { + like := "%" + search + "%" + db = db.Where("title ILIKE ?", like) + } + if userID != "" { + db = db.Where("user_id = ?", userID) + } + if statusFilter != "" && !strings.EqualFold(statusFilter, "all") { + db = db.Where("status = ?", normalizeVideoStatusValue(statusFilter)) + } + + var total int64 + if err := db.Count(&total).Error; err != nil { + return nil, status.Error(codes.Internal, "Failed to list videos") + } + + var videos []model.Video + if err := db.Order("created_at DESC").Offset(offset).Limit(limitInt).Find(&videos).Error; err != nil { + return nil, status.Error(codes.Internal, "Failed to list videos") + } + + items := make([]*appv1.AdminVideo, 0, len(videos)) + for _, video := range videos { + payload, err := s.buildAdminVideo(ctx, &video) + if err != nil { + return nil, status.Error(codes.Internal, "Failed to list videos") + } + items = append(items, payload) + } + + return &appv1.ListAdminVideosResponse{Videos: items, Total: total, Page: page, Limit: limit}, nil +} +func (s *appServices) GetAdminVideo(ctx context.Context, req *appv1.GetAdminVideoRequest) (*appv1.GetAdminVideoResponse, error) { + if _, err := s.requireAdmin(ctx); err != nil { + return nil, err + } + + id := strings.TrimSpace(req.GetId()) + if id == "" { + return nil, status.Error(codes.NotFound, "Video not found") + } + + var video model.Video + if err := s.db.WithContext(ctx).Where("id = ?", id).First(&video).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, status.Error(codes.NotFound, "Video not found") + } + return nil, status.Error(codes.Internal, "Failed to get video") + } + + payload, err := s.buildAdminVideo(ctx, &video) + if err != nil { + return nil, status.Error(codes.Internal, "Failed to get video") + } + + return &appv1.GetAdminVideoResponse{Video: payload}, nil +} +func (s *appServices) CreateAdminVideo(ctx context.Context, req *appv1.CreateAdminVideoRequest) (*appv1.CreateAdminVideoResponse, error) { + if _, err := s.requireAdmin(ctx); err != nil { + return nil, err + } + + userID := strings.TrimSpace(req.GetUserId()) + title := strings.TrimSpace(req.GetTitle()) + videoURL := strings.TrimSpace(req.GetUrl()) + if userID == "" || title == "" || videoURL == "" { + return nil, status.Error(codes.InvalidArgument, "User ID, title, and URL are required") + } + if req.GetSize() < 0 { + return nil, status.Error(codes.InvalidArgument, "Size must be greater than or equal to 0") + } + + var user model.User + if err := s.db.WithContext(ctx).Where("id = ?", userID).First(&user).Error; 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 create video") + } + + statusValue := normalizeVideoStatusValue(req.GetStatus()) + processingStatus := strings.ToUpper(statusValue) + storageType := detectStorageType(videoURL) + video := &model.Video{ + ID: uuid.New().String(), + UserID: user.ID, + Name: title, + Title: title, + Description: nullableTrimmedString(req.Description), + URL: videoURL, + Size: req.GetSize(), + Duration: req.GetDuration(), + Format: strings.TrimSpace(req.GetFormat()), + Status: model.StringPtr(statusValue), + ProcessingStatus: model.StringPtr(processingStatus), + StorageType: model.StringPtr(storageType), + } + + err := s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + if err := tx.Create(video).Error; err != nil { + return err + } + if err := tx.Model(&model.User{}).Where("id = ?", user.ID).UpdateColumn("storage_used", gorm.Expr("storage_used + ?", video.Size)).Error; err != nil { + return err + } + return s.saveAdminVideoAdConfig(ctx, tx, video.ID, user.ID, nullableTrimmedString(req.AdTemplateId)) + }) + if err != nil { + if strings.Contains(err.Error(), "Ad template not found") { + return nil, status.Error(codes.InvalidArgument, "Ad template not found") + } + return nil, status.Error(codes.Internal, "Failed to create video") + } + + payload, err := s.buildAdminVideo(ctx, video) + if err != nil { + return nil, status.Error(codes.Internal, "Failed to create video") + } + return &appv1.CreateAdminVideoResponse{Video: payload}, nil +} +func (s *appServices) UpdateAdminVideo(ctx context.Context, req *appv1.UpdateAdminVideoRequest) (*appv1.UpdateAdminVideoResponse, error) { + if _, err := s.requireAdmin(ctx); err != nil { + return nil, err + } + + id := strings.TrimSpace(req.GetId()) + userID := strings.TrimSpace(req.GetUserId()) + title := strings.TrimSpace(req.GetTitle()) + videoURL := strings.TrimSpace(req.GetUrl()) + if id == "" { + return nil, status.Error(codes.NotFound, "Video not found") + } + if userID == "" || title == "" || videoURL == "" { + return nil, status.Error(codes.InvalidArgument, "User ID, title, and URL are required") + } + if req.GetSize() < 0 { + return nil, status.Error(codes.InvalidArgument, "Size must be greater than or equal to 0") + } + + var video model.Video + if err := s.db.WithContext(ctx).Where("id = ?", id).First(&video).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, status.Error(codes.NotFound, "Video not found") + } + return nil, status.Error(codes.Internal, "Failed to update video") + } + + var user model.User + if err := s.db.WithContext(ctx).Where("id = ?", userID).First(&user).Error; 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 update video") + } + + oldSize := video.Size + oldUserID := video.UserID + statusValue := normalizeVideoStatusValue(req.GetStatus()) + processingStatus := strings.ToUpper(statusValue) + video.UserID = user.ID + video.Name = title + video.Title = title + video.Description = nullableTrimmedString(req.Description) + video.URL = videoURL + video.Size = req.GetSize() + video.Duration = req.GetDuration() + video.Format = strings.TrimSpace(req.GetFormat()) + video.Status = model.StringPtr(statusValue) + video.ProcessingStatus = model.StringPtr(processingStatus) + video.StorageType = model.StringPtr(detectStorageType(videoURL)) + + err := s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + if err := tx.Save(&video).Error; err != nil { + return err + } + if oldUserID == user.ID { + delta := video.Size - oldSize + if delta != 0 { + if err := tx.Model(&model.User{}).Where("id = ?", user.ID).UpdateColumn("storage_used", gorm.Expr("GREATEST(storage_used + ?, 0)", delta)).Error; err != nil { + return err + } + } + } else { + if err := tx.Model(&model.User{}).Where("id = ?", oldUserID).UpdateColumn("storage_used", gorm.Expr("GREATEST(storage_used - ?, 0)", oldSize)).Error; err != nil { + return err + } + if err := tx.Model(&model.User{}).Where("id = ?", user.ID).UpdateColumn("storage_used", gorm.Expr("storage_used + ?", video.Size)).Error; err != nil { + return err + } + } + if oldUserID != user.ID { + if err := tx.Model(&model.VideoAdConfig{}).Where("video_id = ?", video.ID).Update("user_id", user.ID).Error; err != nil { + return err + } + } + return s.saveAdminVideoAdConfig(ctx, tx, video.ID, user.ID, nullableTrimmedString(req.AdTemplateId)) + }) + if err != nil { + if strings.Contains(err.Error(), "Ad template not found") { + return nil, status.Error(codes.InvalidArgument, "Ad template not found") + } + return nil, status.Error(codes.Internal, "Failed to update video") + } + + payload, err := s.buildAdminVideo(ctx, &video) + if err != nil { + return nil, status.Error(codes.Internal, "Failed to update video") + } + return &appv1.UpdateAdminVideoResponse{Video: payload}, nil +} +func (s *appServices) DeleteAdminVideo(ctx context.Context, req *appv1.DeleteAdminVideoRequest) (*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, "Video not found") + } + + var video model.Video + if err := s.db.WithContext(ctx).Where("id = ?", id).First(&video).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, status.Error(codes.NotFound, "Video not found") + } + return nil, status.Error(codes.Internal, "Failed to find video") + } + + err := s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + if err := tx.Where("video_id = ?", video.ID).Delete(&model.VideoAdConfig{}).Error; err != nil { + return err + } + if err := tx.Where("id = ?", video.ID).Delete(&model.Video{}).Error; err != nil { + return err + } + return tx.Model(&model.User{}).Where("id = ?", video.UserID).UpdateColumn("storage_used", gorm.Expr("GREATEST(storage_used - ?, 0)", video.Size)).Error + }) + if err != nil { + return nil, status.Error(codes.Internal, "Failed to delete video") + } + + return messageResponse("Video deleted"), nil +} diff --git a/internal/rpc/app/service_auth.go b/internal/rpc/app/service_auth.go new file mode 100644 index 0000000..1bd9cf8 --- /dev/null +++ b/internal/rpc/app/service_auth.go @@ -0,0 +1,300 @@ +package app + +import ( + "context" + "encoding/json" + "errors" + "net/http" + "strings" + "time" + + "github.com/google/uuid" + "golang.org/x/crypto/bcrypt" + "golang.org/x/oauth2" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "gorm.io/gorm" + authapi "stream.api/internal/api/auth" + "stream.api/internal/database/model" + "stream.api/internal/database/query" + appv1 "stream.api/internal/gen/proto/app/v1" +) + +func (s *appServices) Login(ctx context.Context, req *appv1.LoginRequest) (*appv1.LoginResponse, error) { + email := strings.TrimSpace(req.GetEmail()) + password := req.GetPassword() + if email == "" || password == "" { + return nil, status.Error(codes.InvalidArgument, "Email and password are required") + } + + u := query.User + user, err := u.WithContext(ctx).Where(u.Email.Eq(email)).First() + if err != nil { + return nil, status.Error(codes.Unauthenticated, "Invalid credentials") + } + if user.Password == nil || strings.TrimSpace(*user.Password) == "" { + return nil, status.Error(codes.Unauthenticated, "Please login with Google") + } + if err := bcrypt.CompareHashAndPassword([]byte(*user.Password), []byte(password)); err != nil { + return nil, status.Error(codes.Unauthenticated, "Invalid credentials") + } + + if err := s.issueSessionCookies(ctx, user); err != nil { + return nil, err + } + + payload, err := authapi.BuildUserPayload(ctx, s.db, user) + if err != nil { + return nil, status.Error(codes.Internal, "Failed to build user payload") + } + return &appv1.LoginResponse{User: toProtoUser(payload)}, nil +} +func (s *appServices) Register(ctx context.Context, req *appv1.RegisterRequest) (*appv1.RegisterResponse, error) { + email := strings.TrimSpace(req.GetEmail()) + username := strings.TrimSpace(req.GetUsername()) + password := req.GetPassword() + if email == "" || username == "" || password == "" { + return nil, status.Error(codes.InvalidArgument, "Username, email and password are required") + } + + u := query.User + count, err := u.WithContext(ctx).Where(u.Email.Eq(email)).Count() + if err != nil { + s.logger.Error("Failed to check existing user", "error", err) + return nil, status.Error(codes.Internal, "Failed to register") + } + if count > 0 { + return nil, status.Error(codes.InvalidArgument, "Email already registered") + } + + hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) + if err != nil { + return nil, status.Error(codes.Internal, "Failed to register") + } + + role := "USER" + passwordHash := string(hashedPassword) + newUser := &model.User{ + ID: uuid.New().String(), + Email: email, + Password: &passwordHash, + Username: &username, + Role: &role, + } + if err := u.WithContext(ctx).Create(newUser); err != nil { + s.logger.Error("Failed to create user", "error", err) + return nil, status.Error(codes.Internal, "Failed to register") + } + + payload, err := authapi.BuildUserPayload(ctx, s.db, newUser) + if err != nil { + return nil, status.Error(codes.Internal, "Failed to build user payload") + } + return &appv1.RegisterResponse{User: toProtoUser(payload)}, nil +} +func (s *appServices) Logout(ctx context.Context, _ *appv1.LogoutRequest) (*appv1.MessageResponse, error) { + return messageResponse("Logged out"), nil +} +func (s *appServices) ChangePassword(ctx context.Context, req *appv1.ChangePasswordRequest) (*appv1.MessageResponse, error) { + result, err := s.authenticate(ctx) + if err != nil { + return nil, err + } + currentPassword := req.GetCurrentPassword() + newPassword := req.GetNewPassword() + if currentPassword == "" || newPassword == "" { + return nil, status.Error(codes.InvalidArgument, "Current password and new password are required") + } + if currentPassword == newPassword { + return nil, status.Error(codes.InvalidArgument, "New password must be different") + } + if result.User.Password == nil || strings.TrimSpace(*result.User.Password) == "" { + return nil, status.Error(codes.InvalidArgument, "This account does not have a local password") + } + if err := bcrypt.CompareHashAndPassword([]byte(*result.User.Password), []byte(currentPassword)); err != nil { + return nil, status.Error(codes.InvalidArgument, "Current password is incorrect") + } + newHash, err := bcrypt.GenerateFromPassword([]byte(newPassword), bcrypt.DefaultCost) + if err != nil { + return nil, status.Error(codes.Internal, "Failed to change password") + } + if _, err := query.User.WithContext(ctx). + Where(query.User.ID.Eq(result.UserID)). + Update(query.User.Password, string(newHash)); err != nil { + s.logger.Error("Failed to change password", "error", err) + return nil, status.Error(codes.Internal, "Failed to change password") + } + return messageResponse("Password changed successfully"), nil +} +func (s *appServices) ForgotPassword(ctx context.Context, req *appv1.ForgotPasswordRequest) (*appv1.MessageResponse, error) { + email := strings.TrimSpace(req.GetEmail()) + if email == "" { + return nil, status.Error(codes.InvalidArgument, "Email is required") + } + + u := query.User + user, err := u.WithContext(ctx).Where(u.Email.Eq(email)).First() + if err != nil { + return messageResponse("If email exists, a reset link has been sent"), nil + } + + tokenID := uuid.New().String() + if err := s.cache.Set(ctx, "reset_pw:"+tokenID, user.ID, 15*time.Minute); err != nil { + s.logger.Error("Failed to set reset token", "error", err) + return nil, status.Error(codes.Internal, "Try again later") + } + + s.logger.Info("Generated password reset token", "email", email, "token", tokenID) + return messageResponse("If email exists, a reset link has been sent"), nil +} +func (s *appServices) ResetPassword(ctx context.Context, req *appv1.ResetPasswordRequest) (*appv1.MessageResponse, error) { + resetToken := strings.TrimSpace(req.GetToken()) + newPassword := req.GetNewPassword() + if resetToken == "" || newPassword == "" { + return nil, status.Error(codes.InvalidArgument, "Token and new password are required") + } + + userID, err := s.cache.Get(ctx, "reset_pw:"+resetToken) + if err != nil || strings.TrimSpace(userID) == "" { + return nil, status.Error(codes.InvalidArgument, "Invalid or expired token") + } + + hashedPassword, err := bcrypt.GenerateFromPassword([]byte(newPassword), bcrypt.DefaultCost) + if err != nil { + return nil, status.Error(codes.Internal, "Internal error") + } + + if _, err := query.User.WithContext(ctx). + Where(query.User.ID.Eq(userID)). + Update(query.User.Password, string(hashedPassword)); err != nil { + s.logger.Error("Failed to update password", "error", err) + return nil, status.Error(codes.Internal, "Failed to update password") + } + + _ = s.cache.Del(ctx, "reset_pw:"+resetToken) + return messageResponse("Password reset successfully"), nil +} +func (s *appServices) GetGoogleLoginUrl(ctx context.Context, _ *appv1.GetGoogleLoginUrlRequest) (*appv1.GetGoogleLoginUrlResponse, error) { + if err := s.authenticator.RequireInternalCall(ctx); err != nil { + return nil, err + } + if s.googleOauth == nil || strings.TrimSpace(s.googleOauth.ClientID) == "" || strings.TrimSpace(s.googleOauth.RedirectURL) == "" { + return nil, status.Error(codes.FailedPrecondition, "Google OAuth is not configured") + } + + state, err := generateOAuthState() + if err != nil { + s.logger.Error("Failed to generate Google OAuth state", "error", err) + return nil, status.Error(codes.Internal, "Failed to start Google login") + } + + if err := s.cache.Set(ctx, googleOAuthStateCacheKey(state), "1", s.googleStateTTL); err != nil { + s.logger.Error("Failed to persist Google OAuth state", "error", err) + return nil, status.Error(codes.Internal, "Failed to start Google login") + } + + loginURL := s.googleOauth.AuthCodeURL(state, oauth2.AccessTypeOffline) + return &appv1.GetGoogleLoginUrlResponse{Url: loginURL}, nil +} +func (s *appServices) CompleteGoogleLogin(ctx context.Context, req *appv1.CompleteGoogleLoginRequest) (*appv1.CompleteGoogleLoginResponse, error) { + if err := s.authenticator.RequireInternalCall(ctx); err != nil { + return nil, err + } + if s.googleOauth == nil || strings.TrimSpace(s.googleOauth.ClientID) == "" || strings.TrimSpace(s.googleOauth.RedirectURL) == "" { + return nil, status.Error(codes.FailedPrecondition, "Google OAuth is not configured") + } + + code := strings.TrimSpace(req.GetCode()) + if code == "" { + return nil, status.Error(codes.InvalidArgument, "Code is required") + } + + tokenResp, err := s.googleOauth.Exchange(ctx, code) + if err != nil { + s.logger.Error("Failed to exchange Google OAuth token", "error", err) + return nil, status.Error(codes.Unauthenticated, "exchange_failed") + } + + client := s.googleOauth.Client(ctx, tokenResp) + resp, err := client.Get("https://www.googleapis.com/oauth2/v2/userinfo") + if err != nil { + s.logger.Error("Failed to fetch Google user info", "error", err) + return nil, status.Error(codes.Unauthenticated, "userinfo_failed") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + s.logger.Error("Google user info returned non-200", "status", resp.StatusCode) + return nil, status.Error(codes.Unauthenticated, "userinfo_failed") + } + + var googleUser struct { + ID string `json:"id"` + Email string `json:"email"` + Name string `json:"name"` + Picture string `json:"picture"` + } + if err := json.NewDecoder(resp.Body).Decode(&googleUser); err != nil { + s.logger.Error("Failed to decode Google user info", "error", err) + return nil, status.Error(codes.Internal, "userinfo_parse_failed") + } + + email := strings.TrimSpace(strings.ToLower(googleUser.Email)) + if email == "" { + return nil, status.Error(codes.InvalidArgument, "missing_email") + } + + u := query.User + user, err := u.WithContext(ctx).Where(u.Email.Eq(email)).First() + if err != nil { + if !errors.Is(err, gorm.ErrRecordNotFound) { + s.logger.Error("Failed to load Google user", "error", err) + return nil, status.Error(codes.Internal, "load_user_failed") + } + role := "USER" + user = &model.User{ + ID: uuid.New().String(), + Email: email, + Username: stringPointerOrNil(googleUser.Name), + GoogleID: stringPointerOrNil(googleUser.ID), + Avatar: stringPointerOrNil(googleUser.Picture), + Role: &role, + } + if err := u.WithContext(ctx).Create(user); err != nil { + s.logger.Error("Failed to create Google user", "error", err) + return nil, status.Error(codes.Internal, "create_user_failed") + } + } else { + updates := map[string]interface{}{} + if user.GoogleID == nil || strings.TrimSpace(*user.GoogleID) == "" { + updates["google_id"] = googleUser.ID + } + if user.Avatar == nil || strings.TrimSpace(*user.Avatar) == "" { + updates["avatar"] = googleUser.Picture + } + if user.Username == nil || strings.TrimSpace(*user.Username) == "" { + updates["username"] = googleUser.Name + } + if len(updates) > 0 { + if err := s.db.WithContext(ctx).Model(&model.User{}).Where("id = ?", user.ID).Updates(updates).Error; err != nil { + s.logger.Error("Failed to update Google user", "error", err) + return nil, status.Error(codes.Internal, "update_user_failed") + } + user, err = u.WithContext(ctx).Where(u.ID.Eq(user.ID)).First() + if err != nil { + s.logger.Error("Failed to reload Google user", "error", err) + return nil, status.Error(codes.Internal, "reload_user_failed") + } + } + } + + if err := s.issueSessionCookies(ctx, user); err != nil { + return nil, status.Error(codes.Internal, "session_failed") + } + + payload, err := authapi.BuildUserPayload(ctx, s.db, user) + if err != nil { + return nil, status.Error(codes.Internal, "Failed to build user payload") + } + return &appv1.CompleteGoogleLoginResponse{User: toProtoUser(payload)}, nil +} diff --git a/internal/rpc/app/service_core.go b/internal/rpc/app/service_core.go new file mode 100644 index 0000000..7002500 --- /dev/null +++ b/internal/rpc/app/service_core.go @@ -0,0 +1,203 @@ +package app + +import ( + "time" + + "golang.org/x/oauth2" + "golang.org/x/oauth2/google" + "gorm.io/gorm" + "stream.api/internal/config" + appv1 "stream.api/internal/gen/proto/app/v1" + "stream.api/internal/middleware" + videogrpc "stream.api/internal/video/runtime/grpc" + "stream.api/internal/video/runtime/services" + "stream.api/pkg/cache" + "stream.api/pkg/logger" + "stream.api/pkg/storage" + "stream.api/pkg/token" +) + +const adTemplateUpgradeRequiredMessage = "Upgrade required to manage Ads & VAST" + +const ( + walletTransactionTypeTopup = "topup" + walletTransactionTypeSubscriptionDebit = "subscription_debit" + paymentMethodWallet = "wallet" + paymentMethodTopup = "topup" + paymentKindSubscription = "subscription" + paymentKindWalletTopup = "wallet_topup" +) + +var allowedTermMonths = map[int32]struct{}{ + 1: {}, + 3: {}, + 6: {}, + 12: {}, +} + +type Services struct { + AuthServiceServer + AccountServiceServer + PreferencesServiceServer + UsageServiceServer + NotificationsServiceServer + DomainsServiceServer + AdTemplatesServiceServer + PlansServiceServer + PaymentsServiceServer + VideosServiceServer + AdminServiceServer +} + +type appServices struct { + appv1.UnimplementedAuthServiceServer + appv1.UnimplementedAccountServiceServer + appv1.UnimplementedPreferencesServiceServer + appv1.UnimplementedUsageServiceServer + appv1.UnimplementedNotificationsServiceServer + appv1.UnimplementedDomainsServiceServer + appv1.UnimplementedAdTemplatesServiceServer + appv1.UnimplementedPlansServiceServer + appv1.UnimplementedPaymentsServiceServer + appv1.UnimplementedVideosServiceServer + appv1.UnimplementedAdminServiceServer + + db *gorm.DB + logger logger.Logger + authenticator *middleware.Authenticator + tokenProvider token.Provider + cache cache.Cache + storageProvider storage.Provider + jobService *services.JobService + agentRuntime *videogrpc.Server + googleOauth *oauth2.Config + googleStateTTL time.Duration +} + +type paymentRow struct { + ID string `gorm:"column:id"` + Amount float64 `gorm:"column:amount"` + Currency *string `gorm:"column:currency"` + Status *string `gorm:"column:status"` + PlanID *string `gorm:"column:plan_id"` + PlanName *string `gorm:"column:plan_name"` + TermMonths *int32 `gorm:"column:term_months"` + PaymentMethod *string `gorm:"column:payment_method"` + ExpiresAt *time.Time `gorm:"column:expires_at"` + CreatedAt *time.Time `gorm:"column:created_at"` +} + +type paymentInvoiceDetails struct { + PlanName string + TermMonths *int32 + PaymentMethod string + ExpiresAt *time.Time + WalletAmount float64 + TopupAmount float64 +} + +type apiErrorBody struct { + Code int `json:"code"` + Message string `json:"message"` + Data interface{} `json:"data,omitempty"` +} + +func NewServices(c cache.Cache, t token.Provider, db *gorm.DB, l logger.Logger, cfg *config.Config, jobService *services.JobService, agentRuntime *videogrpc.Server) *Services { + var storageProvider storage.Provider + if cfg != nil { + provider, err := storage.NewS3Provider(cfg) + if err != nil { + l.Error("Failed to initialize S3 provider for gRPC app services", "error", err) + } else { + storageProvider = provider + } + } + + googleStateTTL := 10 * time.Minute + googleOauth := &oauth2.Config{} + if cfg != nil { + if cfg.Google.StateTTLMinute > 0 { + googleStateTTL = time.Duration(cfg.Google.StateTTLMinute) * time.Minute + } + googleOauth = &oauth2.Config{ + ClientID: cfg.Google.ClientID, + ClientSecret: cfg.Google.ClientSecret, + RedirectURL: cfg.Google.RedirectURL, + Scopes: []string{ + "https://www.googleapis.com/auth/userinfo.email", + "https://www.googleapis.com/auth/userinfo.profile", + }, + Endpoint: google.Endpoint, + } + } + + service := &appServices{ + db: db, + logger: l, + authenticator: middleware.NewAuthenticator(db, l, cfg.Internal.Marker), + tokenProvider: t, + cache: c, + storageProvider: storageProvider, + jobService: jobService, + agentRuntime: agentRuntime, + googleOauth: googleOauth, + googleStateTTL: googleStateTTL, + } + return &Services{ + AuthServiceServer: service, + AccountServiceServer: service, + PreferencesServiceServer: service, + UsageServiceServer: service, + NotificationsServiceServer: service, + DomainsServiceServer: service, + AdTemplatesServiceServer: service, + PlansServiceServer: service, + PaymentsServiceServer: service, + VideosServiceServer: service, + AdminServiceServer: service, + } +} + +type AuthServiceServer interface { + appv1.AuthServiceServer +} + +type AccountServiceServer interface { + appv1.AccountServiceServer +} + +type PreferencesServiceServer interface { + appv1.PreferencesServiceServer +} + +type UsageServiceServer interface { + appv1.UsageServiceServer +} + +type NotificationsServiceServer interface { + appv1.NotificationsServiceServer +} + +type DomainsServiceServer interface { + appv1.DomainsServiceServer +} + +type AdTemplatesServiceServer interface { + appv1.AdTemplatesServiceServer +} + +type PlansServiceServer interface { + appv1.PlansServiceServer +} + +type PaymentsServiceServer interface { + appv1.PaymentsServiceServer +} + +type VideosServiceServer interface { + appv1.VideosServiceServer +} + +type AdminServiceServer interface { + appv1.AdminServiceServer +} diff --git a/internal/rpc/app/service_helpers.go b/internal/rpc/app/service_helpers.go new file mode 100644 index 0000000..0962275 --- /dev/null +++ b/internal/rpc/app/service_helpers.go @@ -0,0 +1,1100 @@ +package app + +import ( + "context" + "crypto/rand" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "net/http" + "net/url" + "strings" + "time" + + "github.com/google/uuid" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/types/known/timestamppb" + "gorm.io/gorm" + "gorm.io/gorm/clause" + authapi "stream.api/internal/api/auth" + paymentapi "stream.api/internal/api/payment" + "stream.api/internal/database/model" + appv1 "stream.api/internal/gen/proto/app/v1" + "stream.api/internal/middleware" + "stream.api/internal/video/runtime/domain" + "stream.api/internal/video/runtime/services" +) + +func (s *appServices) requireAdmin(ctx context.Context) (*middleware.AuthResult, error) { + result, err := s.authenticate(ctx) + if err != nil { + return nil, err + } + if result.User == nil || result.User.Role == nil || strings.ToUpper(strings.TrimSpace(*result.User.Role)) != "ADMIN" { + return nil, status.Error(codes.PermissionDenied, "Admin access required") + } + return result, nil +} +func adminPageLimitOffset(pageValue int32, limitValue int32) (int32, int32, int) { + page := pageValue + if page < 1 { + page = 1 + } + limit := limitValue + if limit <= 0 { + limit = 20 + } + if limit > 100 { + limit = 100 + } + offset := int((page - 1) * limit) + return page, limit, offset +} +func buildAdminJob(job *domain.Job) *appv1.AdminJob { + if job == nil { + return nil + } + return &appv1.AdminJob{ + Id: job.ID, + Status: string(job.Status), + Priority: int32(job.Priority), + UserId: job.UserID, + Name: job.Name, + TimeLimit: job.TimeLimit, + InputUrl: job.InputURL, + OutputUrl: job.OutputURL, + TotalDuration: job.TotalDuration, + CurrentTime: job.CurrentTime, + Progress: job.Progress, + AgentId: job.AgentID, + Logs: job.Logs, + Config: job.Config, + Cancelled: job.Cancelled, + RetryCount: int32(job.RetryCount), + MaxRetries: int32(job.MaxRetries), + CreatedAt: timestamppb.New(job.CreatedAt), + UpdatedAt: timestamppb.New(job.UpdatedAt), + } +} +func buildAdminAgent(agent *services.AgentWithStats) *appv1.AdminAgent { + if agent == nil || agent.Agent == nil { + return nil + } + return &appv1.AdminAgent{ + Id: agent.ID, + Name: agent.Name, + Platform: agent.Platform, + Backend: agent.Backend, + Version: agent.Version, + Capacity: agent.Capacity, + Status: string(agent.Status), + Cpu: agent.CPU, + Ram: agent.RAM, + LastHeartbeat: timestamppb.New(agent.LastHeartbeat), + CreatedAt: timestamppb.New(agent.CreatedAt), + UpdatedAt: timestamppb.New(agent.UpdatedAt), + } +} +func normalizeAdminRoleValue(value string) string { + role := strings.ToUpper(strings.TrimSpace(value)) + if role == "" { + return "USER" + } + return role +} +func isValidAdminRoleValue(role string) bool { + switch normalizeAdminRoleValue(role) { + case "USER", "ADMIN", "BLOCK": + return true + default: + return false + } +} +func (s *appServices) ensurePlanExists(ctx context.Context, planID *string) error { + if planID == nil { + return nil + } + trimmed := strings.TrimSpace(*planID) + if trimmed == "" { + return nil + } + var count int64 + if err := s.db.WithContext(ctx).Model(&model.Plan{}).Where("id = ?", trimmed).Count(&count).Error; err != nil { + return status.Error(codes.Internal, "Failed to validate plan") + } + if count == 0 { + return status.Error(codes.InvalidArgument, "Plan not found") + } + return nil +} +func (s *appServices) saveAdminVideoAdConfig(ctx context.Context, tx *gorm.DB, videoID, userID string, adTemplateID *string) error { + if adTemplateID == nil { + return nil + } + trimmed := strings.TrimSpace(*adTemplateID) + if trimmed == "" { + return tx.Where("video_id = ? AND user_id = ?", videoID, userID).Delete(&model.VideoAdConfig{}).Error + } + + var template model.AdTemplate + if err := tx.WithContext(ctx).Where("id = ? AND user_id = ?", trimmed, userID).First(&template).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return errors.New("Ad template not found") + } + return err + } + + var existing model.VideoAdConfig + if err := tx.WithContext(ctx).Where("video_id = ? AND user_id = ?", videoID, userID).First(&existing).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return tx.Create(&model.VideoAdConfig{ + VideoID: videoID, + UserID: userID, + AdTemplateID: template.ID, + VastTagURL: template.VastTagURL, + AdFormat: template.AdFormat, + Duration: template.Duration, + }).Error + } + return err + } + + existing.AdTemplateID = template.ID + existing.VastTagURL = template.VastTagURL + existing.AdFormat = template.AdFormat + existing.Duration = template.Duration + return tx.Save(&existing).Error +} +func (s *appServices) buildAdminUser(ctx context.Context, user *model.User) (*appv1.AdminUser, error) { + if user == nil { + return nil, nil + } + + payload := &appv1.AdminUser{ + Id: user.ID, + Email: user.Email, + Username: nullableTrimmedString(user.Username), + Avatar: nullableTrimmedString(user.Avatar), + Role: nullableTrimmedString(user.Role), + PlanId: nullableTrimmedString(user.PlanID), + StorageUsed: user.StorageUsed, + CreatedAt: timeToProto(user.CreatedAt), + UpdatedAt: timestamppb.New(user.UpdatedAt.UTC()), + WalletBalance: 0, + } + + var videoCount int64 + if err := s.db.WithContext(ctx).Model(&model.Video{}).Where("user_id = ?", user.ID).Count(&videoCount).Error; err != nil { + return nil, err + } + payload.VideoCount = videoCount + + walletBalance, err := model.GetWalletBalance(ctx, s.db, user.ID) + if err != nil { + return nil, err + } + payload.WalletBalance = walletBalance + + if user.PlanID != nil && strings.TrimSpace(*user.PlanID) != "" { + var plan model.Plan + if err := s.db.WithContext(ctx).Select("id, name").Where("id = ?", *user.PlanID).First(&plan).Error; err == nil { + payload.PlanName = nullableTrimmedString(&plan.Name) + } else if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { + return nil, err + } + } + + return payload, nil +} +func (s *appServices) buildAdminVideo(ctx context.Context, video *model.Video) (*appv1.AdminVideo, error) { + if video == nil { + return nil, nil + } + + statusValue := stringValue(video.Status) + if statusValue == "" { + statusValue = "ready" + } + + payload := &appv1.AdminVideo{ + Id: video.ID, + UserId: video.UserID, + Title: video.Title, + Description: nullableTrimmedString(video.Description), + Url: video.URL, + Status: strings.ToLower(statusValue), + Size: video.Size, + Duration: video.Duration, + Format: video.Format, + CreatedAt: timeToProto(video.CreatedAt), + UpdatedAt: timestamppb.New(video.UpdatedAt.UTC()), + } + + var user model.User + if err := s.db.WithContext(ctx).Select("id, email").Where("id = ?", video.UserID).First(&user).Error; err == nil { + payload.OwnerEmail = nullableTrimmedString(&user.Email) + } else if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { + return nil, err + } + + var adConfig model.VideoAdConfig + if err := s.db.WithContext(ctx).Where("video_id = ?", video.ID).First(&adConfig).Error; err == nil { + payload.AdTemplateId = nullableTrimmedString(&adConfig.AdTemplateID) + var template model.AdTemplate + if err := s.db.WithContext(ctx).Select("id, name").Where("id = ?", adConfig.AdTemplateID).First(&template).Error; err == nil { + payload.AdTemplateName = nullableTrimmedString(&template.Name) + } else if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { + return nil, err + } + } else if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { + return nil, err + } + + return payload, nil +} +func (s *appServices) buildAdminPayment(ctx context.Context, payment *model.Payment) (*appv1.AdminPayment, error) { + if payment == nil { + return nil, nil + } + + currency := normalizeCurrency(payment.Currency) + if strings.TrimSpace(currency) == "" { + currency = "USD" + } + + payload := &appv1.AdminPayment{ + Id: payment.ID, + UserId: payment.UserID, + PlanId: nullableTrimmedString(payment.PlanID), + Amount: payment.Amount, + Currency: currency, + Status: normalizePaymentStatus(payment.Status), + Provider: strings.ToUpper(stringValue(payment.Provider)), + TransactionId: nullableTrimmedString(payment.TransactionID), + InvoiceId: payment.ID, + CreatedAt: timeToProto(payment.CreatedAt), + UpdatedAt: timestamppb.New(payment.UpdatedAt.UTC()), + } + + var user model.User + if err := s.db.WithContext(ctx).Select("id, email").Where("id = ?", payment.UserID).First(&user).Error; err == nil { + payload.UserEmail = nullableTrimmedString(&user.Email) + } else if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { + return nil, err + } + + if payment.PlanID != nil && strings.TrimSpace(*payment.PlanID) != "" { + var plan model.Plan + if err := s.db.WithContext(ctx).Select("id, name").Where("id = ?", *payment.PlanID).First(&plan).Error; err == nil { + payload.PlanName = nullableTrimmedString(&plan.Name) + } else if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { + return nil, err + } + } + + var subscription model.PlanSubscription + if err := s.db.WithContext(ctx).Where("payment_id = ?", payment.ID).Order("created_at DESC").First(&subscription).Error; err == nil { + payload.TermMonths = &subscription.TermMonths + payload.PaymentMethod = nullableTrimmedString(&subscription.PaymentMethod) + expiresAt := subscription.ExpiresAt.UTC().Format(time.RFC3339) + payload.ExpiresAt = nullableTrimmedString(&expiresAt) + payload.WalletAmount = &subscription.WalletAmount + payload.TopupAmount = &subscription.TopupAmount + } else if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { + return nil, err + } + + return payload, nil +} +func (s *appServices) loadPlanUsageCounts(ctx context.Context, planID string) (int64, int64, int64, error) { + var userCount int64 + if err := s.db.WithContext(ctx).Model(&model.User{}).Where("plan_id = ?", planID).Count(&userCount).Error; err != nil { + return 0, 0, 0, err + } + + var paymentCount int64 + if err := s.db.WithContext(ctx).Model(&model.Payment{}).Where("plan_id = ?", planID).Count(&paymentCount).Error; err != nil { + return 0, 0, 0, err + } + + var subscriptionCount int64 + if err := s.db.WithContext(ctx).Model(&model.PlanSubscription{}).Where("plan_id = ?", planID).Count(&subscriptionCount).Error; err != nil { + return 0, 0, 0, err + } + + return userCount, paymentCount, subscriptionCount, nil +} +func validateAdminPlanInput(name, cycle string, price float64, storageLimit int64, uploadLimit int32) string { + if strings.TrimSpace(name) == "" { + return "Name is required" + } + if strings.TrimSpace(cycle) == "" { + return "Cycle is required" + } + if price < 0 { + return "Price must be greater than or equal to 0" + } + if storageLimit <= 0 { + return "Storage limit must be greater than 0" + } + if uploadLimit <= 0 { + return "Upload limit must be greater than 0" + } + return "" +} +func normalizeAdminAdFormatValue(value string) string { + switch strings.TrimSpace(strings.ToLower(value)) { + case "mid-roll", "post-roll": + return strings.TrimSpace(strings.ToLower(value)) + default: + return "pre-roll" + } +} +func validateAdminAdTemplateInput(userID, name, vastTagURL, adFormat string, duration *int64) string { + if strings.TrimSpace(userID) == "" { + return "User ID is required" + } + if strings.TrimSpace(name) == "" || strings.TrimSpace(vastTagURL) == "" { + return "Name and VAST URL are required" + } + format := normalizeAdminAdFormatValue(adFormat) + if format == "mid-roll" && (duration == nil || *duration <= 0) { + return "Duration is required for mid-roll templates" + } + return "" +} +func (s *appServices) unsetAdminDefaultTemplates(ctx context.Context, tx *gorm.DB, userID, excludeID string) error { + query := tx.WithContext(ctx).Model(&model.AdTemplate{}).Where("user_id = ?", userID) + if excludeID != "" { + query = query.Where("id <> ?", excludeID) + } + return query.Update("is_default", false).Error +} +func (s *appServices) buildAdminPlan(ctx context.Context, plan *model.Plan) (*appv1.AdminPlan, error) { + if plan == nil { + return nil, nil + } + + userCount, paymentCount, subscriptionCount, err := s.loadPlanUsageCounts(ctx, plan.ID) + if err != nil { + return nil, err + } + + payload := &appv1.AdminPlan{ + Id: plan.ID, + Name: plan.Name, + Description: nullableTrimmedString(plan.Description), + Features: append([]string(nil), plan.Features...), + Price: plan.Price, + Cycle: plan.Cycle, + StorageLimit: plan.StorageLimit, + UploadLimit: plan.UploadLimit, + DurationLimit: plan.DurationLimit, + QualityLimit: plan.QualityLimit, + IsActive: boolValue(plan.IsActive), + UserCount: userCount, + PaymentCount: paymentCount, + SubscriptionCount: subscriptionCount, + } + return payload, nil +} +func (s *appServices) buildAdminAdTemplate(ctx context.Context, item *model.AdTemplate) (*appv1.AdminAdTemplate, error) { + if item == nil { + return nil, nil + } + + payload := &appv1.AdminAdTemplate{ + Id: item.ID, + UserId: item.UserID, + Name: item.Name, + Description: nullableTrimmedString(item.Description), + VastTagUrl: item.VastTagURL, + AdFormat: stringValue(item.AdFormat), + Duration: item.Duration, + IsActive: boolValue(item.IsActive), + IsDefault: item.IsDefault, + CreatedAt: timeToProto(item.CreatedAt), + UpdatedAt: timeToProto(item.UpdatedAt), + } + + var user model.User + if err := s.db.WithContext(ctx).Select("id, email").Where("id = ?", item.UserID).First(&user).Error; err == nil { + payload.OwnerEmail = nullableTrimmedString(&user.Email) + } else if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { + return nil, err + } + + return payload, nil +} +func (s *appServices) authenticate(ctx context.Context) (*middleware.AuthResult, error) { + return s.authenticator.Authenticate(ctx) +} +func statusErrorWithBody(ctx context.Context, grpcCode codes.Code, httpCode int, message string, data interface{}) error { + body := apiErrorBody{ + Code: httpCode, + Message: message, + Data: data, + } + encoded, err := json.Marshal(body) + if err == nil { + _ = grpc.SetTrailer(ctx, metadata.Pairs("x-error-body", string(encoded))) + } + return status.Error(grpcCode, message) +} +func (s *appServices) issueSessionCookies(ctx context.Context, user *model.User) error { + if user == nil { + return status.Error(codes.Unauthenticated, "Unauthorized") + } + tokenPair, err := s.tokenProvider.GenerateTokenPair(user.ID, user.Email, safeRole(user.Role)) + if err != nil { + s.logger.Error("Token generation failed", "error", err) + return status.Error(codes.Internal, "Error generating tokens") + } + + if err := s.cache.Set(ctx, "refresh_uuid:"+tokenPair.RefreshUUID, user.ID, time.Until(time.Unix(tokenPair.RtExpires, 0))); err != nil { + s.logger.Error("Session storage failed", "error", err) + return status.Error(codes.Internal, "Error storing session") + } + + if err := grpc.SetHeader(ctx, metadata.Pairs( + "set-cookie", buildTokenCookie("access_token", tokenPair.AccessToken, int(tokenPair.AtExpires-time.Now().Unix())), + "set-cookie", buildTokenCookie("refresh_token", tokenPair.RefreshToken, int(tokenPair.RtExpires-time.Now().Unix())), + )); err != nil { + s.logger.Error("Failed to set gRPC auth headers", "error", err) + } + + return nil +} +func cookieValueFromHeader(cookieHeader string, name string) string { + cookieHeader = strings.TrimSpace(cookieHeader) + if cookieHeader == "" { + return "" + } + request := &http.Request{Header: http.Header{"Cookie": []string{cookieHeader}}} + cookie, err := request.Cookie(name) + if err != nil { + return "" + } + return cookie.Value +} +func buildTokenCookie(name string, value string, maxAge int) string { + return (&http.Cookie{ + Name: name, + Value: value, + Path: "/", + MaxAge: maxAge, + HttpOnly: true, + }).String() +} +func clearCookieHeader(name string) string { + return (&http.Cookie{Name: name, Value: "", Path: "/", MaxAge: -1, HttpOnly: true}).String() +} +func messageResponse(message string) *appv1.MessageResponse { + return &appv1.MessageResponse{Message: message} +} +func ensurePaidPlan(user *model.User) error { + if user == nil { + return status.Error(codes.Unauthenticated, "Unauthorized") + } + if user.PlanID == nil || strings.TrimSpace(*user.PlanID) == "" { + return status.Error(codes.PermissionDenied, adTemplateUpgradeRequiredMessage) + } + return nil +} +func safeRole(role *string) string { + if role == nil || strings.TrimSpace(*role) == "" { + return "USER" + } + return *role +} +func generateOAuthState() (string, error) { + buffer := make([]byte, 32) + if _, err := rand.Read(buffer); err != nil { + return "", err + } + return base64.RawURLEncoding.EncodeToString(buffer), nil +} +func googleOAuthStateCacheKey(state string) string { + return "google_oauth_state:" + state +} +func stringPointerOrNil(value string) *string { + trimmed := strings.TrimSpace(value) + if trimmed == "" { + return nil + } + return &trimmed +} +func toProtoVideo(item *model.Video) *appv1.Video { + if item == nil { + return nil + } + statusValue := stringValue(item.Status) + if statusValue == "" { + statusValue = "ready" + } + return &appv1.Video{ + Id: item.ID, + UserId: item.UserID, + Title: item.Title, + Description: item.Description, + Url: item.URL, + Status: strings.ToLower(statusValue), + Size: item.Size, + Duration: item.Duration, + Format: item.Format, + Thumbnail: item.Thumbnail, + ProcessingStatus: item.ProcessingStatus, + StorageType: item.StorageType, + CreatedAt: timeToProto(item.CreatedAt), + UpdatedAt: timestamppb.New(item.UpdatedAt.UTC()), + } +} +func normalizeVideoStatusValue(value string) string { + switch strings.ToLower(strings.TrimSpace(value)) { + case "processing", "pending": + return "processing" + case "failed", "error": + return "failed" + default: + return "ready" + } +} +func detectStorageType(rawURL string) string { + if shouldDeleteStoredObject(rawURL) { + return "S3" + } + return "WORKER" +} +func shouldDeleteStoredObject(rawURL string) bool { + trimmed := strings.TrimSpace(rawURL) + if trimmed == "" { + return false + } + parsed, err := url.Parse(trimmed) + if err != nil { + return !strings.HasPrefix(trimmed, "/") + } + return parsed.Scheme == "" && parsed.Host == "" && !strings.HasPrefix(trimmed, "/") +} +func extractObjectKey(rawURL string) string { + trimmed := strings.TrimSpace(rawURL) + if trimmed == "" { + return "" + } + parsed, err := url.Parse(trimmed) + if err != nil { + return trimmed + } + return strings.TrimPrefix(parsed.Path, "/") +} +func toProtoUserPayload(user *authapi.UserPayload) *appv1.User { + if user == nil { + return nil + } + return &appv1.User{ + Id: user.ID, + Email: user.Email, + Username: user.Username, + Avatar: user.Avatar, + Role: user.Role, + GoogleId: user.GoogleID, + StorageUsed: user.StorageUsed, + PlanId: user.PlanID, + PlanStartedAt: timeToProto(user.PlanStartedAt), + PlanExpiresAt: timeToProto(user.PlanExpiresAt), + PlanTermMonths: user.PlanTermMonths, + PlanPaymentMethod: user.PlanPaymentMethod, + PlanExpiringSoon: user.PlanExpiringSoon, + WalletBalance: user.WalletBalance, + Language: user.Language, + Locale: user.Locale, + CreatedAt: timeToProto(user.CreatedAt), + UpdatedAt: timestamppb.New(user.UpdatedAt), + } +} +func toProtoUser(user *authapi.UserPayload) *appv1.User { + if user == nil { + return nil + } + return &appv1.User{ + Id: user.ID, + Email: user.Email, + Username: user.Username, + Avatar: user.Avatar, + Role: user.Role, + GoogleId: user.GoogleID, + StorageUsed: user.StorageUsed, + PlanId: user.PlanID, + PlanStartedAt: timeToProto(user.PlanStartedAt), + PlanExpiresAt: timeToProto(user.PlanExpiresAt), + PlanTermMonths: user.PlanTermMonths, + PlanPaymentMethod: user.PlanPaymentMethod, + PlanExpiringSoon: user.PlanExpiringSoon, + WalletBalance: user.WalletBalance, + Language: user.Language, + Locale: user.Locale, + CreatedAt: timeToProto(user.CreatedAt), + UpdatedAt: timestamppb.New(user.UpdatedAt), + } +} +func toProtoPreferences(pref *model.UserPreference) *appv1.Preferences { + if pref == nil { + return nil + } + return &appv1.Preferences{ + EmailNotifications: boolValue(pref.EmailNotifications), + PushNotifications: boolValue(pref.PushNotifications), + MarketingNotifications: pref.MarketingNotifications, + TelegramNotifications: pref.TelegramNotifications, + Autoplay: pref.Autoplay, + Loop: pref.Loop, + Muted: pref.Muted, + ShowControls: boolValue(pref.ShowControls), + Pip: boolValue(pref.Pip), + Airplay: boolValue(pref.Airplay), + Chromecast: boolValue(pref.Chromecast), + Language: model.StringValue(pref.Language), + Locale: model.StringValue(pref.Locale), + } +} +func toProtoNotification(item model.Notification) *appv1.Notification { + return &appv1.Notification{ + Id: item.ID, + Type: normalizeNotificationType(item.Type), + Title: item.Title, + Message: item.Message, + Read: item.IsRead, + ActionUrl: item.ActionURL, + ActionLabel: item.ActionLabel, + CreatedAt: timeToProto(item.CreatedAt), + } +} +func toProtoDomain(item *model.Domain) *appv1.Domain { + if item == nil { + return nil + } + return &appv1.Domain{ + Id: item.ID, + Name: item.Name, + CreatedAt: timeToProto(item.CreatedAt), + UpdatedAt: timeToProto(item.UpdatedAt), + } +} +func toProtoAdTemplate(item *model.AdTemplate) *appv1.AdTemplate { + if item == nil { + return nil + } + return &appv1.AdTemplate{ + Id: item.ID, + Name: item.Name, + Description: item.Description, + VastTagUrl: item.VastTagURL, + AdFormat: model.StringValue(item.AdFormat), + Duration: int64PtrToInt32Ptr(item.Duration), + IsActive: boolValue(item.IsActive), + IsDefault: item.IsDefault, + CreatedAt: timeToProto(item.CreatedAt), + UpdatedAt: timeToProto(item.UpdatedAt), + } +} +func toProtoPlan(item *model.Plan) *appv1.Plan { + if item == nil { + return nil + } + return &appv1.Plan{ + Id: item.ID, + Name: item.Name, + Description: item.Description, + Price: item.Price, + Cycle: item.Cycle, + StorageLimit: item.StorageLimit, + UploadLimit: item.UploadLimit, + DurationLimit: item.DurationLimit, + QualityLimit: item.QualityLimit, + Features: item.Features, + IsActive: boolValue(item.IsActive), + } +} +func toProtoPayment(item *model.Payment) *appv1.Payment { + if item == nil { + return nil + } + return &appv1.Payment{ + Id: item.ID, + UserId: item.UserID, + PlanId: item.PlanID, + Amount: item.Amount, + Currency: normalizeCurrency(item.Currency), + Status: normalizePaymentStatus(item.Status), + Provider: strings.ToUpper(stringValue(item.Provider)), + TransactionId: item.TransactionID, + CreatedAt: timeToProto(item.CreatedAt), + UpdatedAt: timestamppb.New(item.UpdatedAt.UTC()), + } +} +func toProtoPlanSubscription(item *model.PlanSubscription) *appv1.PlanSubscription { + if item == nil { + return nil + } + return &appv1.PlanSubscription{ + Id: item.ID, + UserId: item.UserID, + PaymentId: item.PaymentID, + PlanId: item.PlanID, + TermMonths: item.TermMonths, + PaymentMethod: item.PaymentMethod, + WalletAmount: item.WalletAmount, + TopupAmount: item.TopupAmount, + StartedAt: timestamppb.New(item.StartedAt.UTC()), + ExpiresAt: timestamppb.New(item.ExpiresAt.UTC()), + CreatedAt: timeToProto(item.CreatedAt), + UpdatedAt: timeToProto(item.UpdatedAt), + } +} +func toProtoWalletTransaction(item *model.WalletTransaction) *appv1.WalletTransaction { + if item == nil { + return nil + } + return &appv1.WalletTransaction{ + Id: item.ID, + UserId: item.UserID, + Type: item.Type, + Amount: item.Amount, + Currency: normalizeCurrency(item.Currency), + Note: item.Note, + PaymentId: item.PaymentID, + PlanId: item.PlanID, + TermMonths: item.TermMonths, + CreatedAt: timeToProto(item.CreatedAt), + UpdatedAt: timeToProto(item.UpdatedAt), + } +} +func toProtoPaymentHistoryItem(item *paymentapi.PaymentHistoryItem) *appv1.PaymentHistoryItem { + if item == nil { + return nil + } + return &appv1.PaymentHistoryItem{ + Id: item.ID, + Amount: item.Amount, + Currency: item.Currency, + Status: item.Status, + PlanId: item.PlanID, + PlanName: item.PlanName, + InvoiceId: item.InvoiceID, + Kind: item.Kind, + TermMonths: item.TermMonths, + PaymentMethod: item.PaymentMethod, + ExpiresAt: timeToProto(item.ExpiresAt), + CreatedAt: timeToProto(item.CreatedAt), + } +} +func timeToProto(value *time.Time) *timestamppb.Timestamp { + if value == nil { + return nil + } + return timestamppb.New(value.UTC()) +} +func boolValue(value *bool) bool { + return value != nil && *value +} +func stringValue(value *string) string { + if value == nil { + return "" + } + return *value +} +func int32PtrToInt64Ptr(value *int32) *int64 { + if value == nil { + return nil + } + converted := int64(*value) + return &converted +} +func int64PtrToInt32Ptr(value *int64) *int32 { + if value == nil { + return nil + } + converted := int32(*value) + return &converted +} +func int32Ptr(value int32) *int32 { + return &value +} +func nullableInt64(value *int64) int64 { + if value == nil { + return 0 + } + return *value +} +func nullableInt64Ptr(value int64) *int64 { + return &value +} +func protoStringValue(value *string) string { + if value == nil { + return "" + } + return strings.TrimSpace(*value) +} +func nullableTrimmedStringPtr(value *string) *string { + if value == nil { + return nil + } + trimmed := strings.TrimSpace(*value) + if trimmed == "" { + return nil + } + return &trimmed +} +func nullableTrimmedString(value *string) *string { + if value == nil { + return nil + } + trimmed := strings.TrimSpace(*value) + if trimmed == "" { + return nil + } + return &trimmed +} +func normalizeNotificationType(value string) string { + lower := strings.ToLower(strings.TrimSpace(value)) + switch { + case strings.Contains(lower, "video"): + return "video" + case strings.Contains(lower, "payment"), strings.Contains(lower, "billing"): + return "payment" + case strings.Contains(lower, "warning"): + return "warning" + case strings.Contains(lower, "error"): + return "error" + case strings.Contains(lower, "success"): + return "success" + case strings.Contains(lower, "system"): + return "system" + default: + return "info" + } +} +func normalizeDomain(value string) string { + normalized := strings.TrimSpace(strings.ToLower(value)) + normalized = strings.TrimPrefix(normalized, "https://") + normalized = strings.TrimPrefix(normalized, "http://") + normalized = strings.TrimPrefix(normalized, "www.") + normalized = strings.TrimSuffix(normalized, "/") + return normalized +} +func normalizeAdFormat(value string) string { + switch strings.TrimSpace(strings.ToLower(value)) { + case "mid-roll", "post-roll": + return strings.TrimSpace(strings.ToLower(value)) + default: + return "pre-roll" + } +} +func adTemplateIsActive(value *bool) bool { + return value == nil || *value +} +func unsetDefaultTemplates(tx *gorm.DB, userID, excludeID string) error { + query := tx.Model(&model.AdTemplate{}).Where("user_id = ?", userID) + if excludeID != "" { + query = query.Where("id <> ?", excludeID) + } + return query.Update("is_default", false).Error +} +func normalizePaymentStatus(status *string) string { + value := strings.ToLower(strings.TrimSpace(stringValue(status))) + switch value { + case "success", "succeeded", "paid": + return "success" + case "failed", "error", "canceled", "cancelled": + return "failed" + case "pending", "processing": + return "pending" + default: + if value == "" { + return "success" + } + return value + } +} +func normalizeCurrency(currency *string) string { + value := strings.ToUpper(strings.TrimSpace(stringValue(currency))) + if value == "" { + return "USD" + } + return value +} +func normalizePaymentMethod(value string) string { + switch strings.ToLower(strings.TrimSpace(value)) { + case paymentMethodWallet: + return paymentMethodWallet + case paymentMethodTopup: + return paymentMethodTopup + default: + return "" + } +} +func normalizeOptionalPaymentMethod(value *string) *string { + normalized := normalizePaymentMethod(stringValue(value)) + if normalized == "" { + return nil + } + return &normalized +} +func buildInvoiceID(id string) string { + trimmed := strings.ReplaceAll(strings.TrimSpace(id), "-", "") + if len(trimmed) > 12 { + trimmed = trimmed[:12] + } + return "INV-" + strings.ToUpper(trimmed) +} +func buildTransactionID(prefix string) string { + return fmt.Sprintf("%s_%d", prefix, time.Now().UnixNano()) +} +func buildInvoiceFilename(id string) string { + return fmt.Sprintf("invoice-%s.txt", id) +} +func (s *appServices) buildPaymentInvoice(ctx context.Context, paymentRecord *model.Payment) (string, string, error) { + details, err := s.loadPaymentInvoiceDetails(ctx, paymentRecord) + if err != nil { + return "", "", err + } + + createdAt := formatOptionalTimestamp(paymentRecord.CreatedAt) + lines := []string{ + "Stream API Invoice", + fmt.Sprintf("Invoice ID: %s", buildInvoiceID(paymentRecord.ID)), + fmt.Sprintf("Payment ID: %s", paymentRecord.ID), + fmt.Sprintf("User ID: %s", paymentRecord.UserID), + fmt.Sprintf("Plan: %s", details.PlanName), + fmt.Sprintf("Amount: %.2f %s", paymentRecord.Amount, normalizeCurrency(paymentRecord.Currency)), + fmt.Sprintf("Status: %s", strings.ToUpper(normalizePaymentStatus(paymentRecord.Status))), + fmt.Sprintf("Provider: %s", strings.ToUpper(stringValue(paymentRecord.Provider))), + fmt.Sprintf("Payment Method: %s", strings.ToUpper(details.PaymentMethod)), + fmt.Sprintf("Transaction ID: %s", stringValue(paymentRecord.TransactionID)), + } + + if details.TermMonths != nil { + lines = append(lines, fmt.Sprintf("Term: %d month(s)", *details.TermMonths)) + } + if details.ExpiresAt != nil { + lines = append(lines, fmt.Sprintf("Valid Until: %s", details.ExpiresAt.UTC().Format(time.RFC3339))) + } + if details.WalletAmount > 0 { + lines = append(lines, fmt.Sprintf("Wallet Applied: %.2f %s", details.WalletAmount, normalizeCurrency(paymentRecord.Currency))) + } + if details.TopupAmount > 0 { + lines = append(lines, fmt.Sprintf("Top-up Added: %.2f %s", details.TopupAmount, normalizeCurrency(paymentRecord.Currency))) + } + lines = append(lines, fmt.Sprintf("Created At: %s", createdAt)) + + return strings.Join(lines, "\n"), buildInvoiceFilename(paymentRecord.ID), nil +} +func buildTopupInvoice(transaction *model.WalletTransaction) string { + createdAt := formatOptionalTimestamp(transaction.CreatedAt) + return strings.Join([]string{ + "Stream API Wallet Top-up Invoice", + fmt.Sprintf("Invoice ID: %s", buildInvoiceID(transaction.ID)), + fmt.Sprintf("Wallet Transaction ID: %s", transaction.ID), + fmt.Sprintf("User ID: %s", transaction.UserID), + fmt.Sprintf("Amount: %.2f %s", transaction.Amount, normalizeCurrency(transaction.Currency)), + "Status: SUCCESS", + fmt.Sprintf("Type: %s", strings.ToUpper(transaction.Type)), + fmt.Sprintf("Note: %s", model.StringValue(transaction.Note)), + fmt.Sprintf("Created At: %s", createdAt), + }, "\n") +} +func (s *appServices) loadPaymentInvoiceDetails(ctx context.Context, paymentRecord *model.Payment) (*paymentInvoiceDetails, error) { + details := &paymentInvoiceDetails{ + PlanName: "Unknown plan", + PaymentMethod: paymentMethodWallet, + } + + if paymentRecord.PlanID != nil && strings.TrimSpace(*paymentRecord.PlanID) != "" { + var planRecord model.Plan + if err := s.db.WithContext(ctx).Where("id = ?", *paymentRecord.PlanID).First(&planRecord).Error; err != nil { + if !errors.Is(err, gorm.ErrRecordNotFound) { + return nil, err + } + } else { + details.PlanName = planRecord.Name + } + } + + var subscription model.PlanSubscription + if err := s.db.WithContext(ctx). + Where("payment_id = ?", paymentRecord.ID). + Order("created_at DESC"). + First(&subscription).Error; err != nil { + if !errors.Is(err, gorm.ErrRecordNotFound) { + return nil, err + } + return details, nil + } + + details.TermMonths = &subscription.TermMonths + details.PaymentMethod = normalizePaymentMethod(subscription.PaymentMethod) + if details.PaymentMethod == "" { + details.PaymentMethod = paymentMethodWallet + } + details.ExpiresAt = &subscription.ExpiresAt + details.WalletAmount = subscription.WalletAmount + details.TopupAmount = subscription.TopupAmount + + return details, nil +} +func buildSubscriptionNotification(userID, paymentID, invoiceID string, planRecord *model.Plan, subscription *model.PlanSubscription) *model.Notification { + return &model.Notification{ + ID: uuid.New().String(), + UserID: userID, + Type: "billing.subscription", + Title: "Subscription activated", + Message: fmt.Sprintf("Your subscription to %s is active until %s.", planRecord.Name, subscription.ExpiresAt.UTC().Format("2006-01-02")), + Metadata: model.StringPtr(mustMarshalJSON(map[string]interface{}{ + "payment_id": paymentID, + "invoice_id": invoiceID, + "plan_id": planRecord.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), + })), + } +} +func isAllowedTermMonths(value int32) bool { + _, ok := allowedTermMonths[value] + return ok +} +func lockUserForUpdate(ctx context.Context, tx *gorm.DB, userID string) (*model.User, error) { + var user model.User + if err := tx.WithContext(ctx). + Clauses(clause.Locking{Strength: "UPDATE"}). + Where("id = ?", userID). + First(&user).Error; err != nil { + return nil, err + } + return &user, nil +} +func maxFloat(left, right float64) float64 { + if left > right { + return left + } + return right +} +func formatOptionalTimestamp(value *time.Time) string { + if value == nil { + return "" + } + return value.UTC().Format(time.RFC3339) +} +func mustMarshalJSON(value interface{}) string { + encoded, err := json.Marshal(value) + if err != nil { + return "{}" + } + return string(encoded) +} diff --git a/internal/rpc/app/service_payments.go b/internal/rpc/app/service_payments.go new file mode 100644 index 0000000..1b22fad --- /dev/null +++ b/internal/rpc/app/service_payments.go @@ -0,0 +1,414 @@ +package app + +import ( + "context" + "errors" + "fmt" + "net/http" + "sort" + "strings" + "time" + + "github.com/google/uuid" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "gorm.io/gorm" + paymentapi "stream.api/internal/api/payment" + "stream.api/internal/database/model" + "stream.api/internal/database/query" + appv1 "stream.api/internal/gen/proto/app/v1" +) + +func (s *appServices) CreatePayment(ctx context.Context, req *appv1.CreatePaymentRequest) (*appv1.CreatePaymentResponse, error) { + result, err := s.authenticate(ctx) + if err != nil { + return nil, err + } + + planID := strings.TrimSpace(req.GetPlanId()) + if planID == "" { + return nil, status.Error(codes.InvalidArgument, "Plan ID is required") + } + if !isAllowedTermMonths(req.GetTermMonths()) { + return nil, status.Error(codes.InvalidArgument, "Term months must be one of 1, 3, 6, or 12") + } + + paymentMethod := normalizePaymentMethod(req.GetPaymentMethod()) + if paymentMethod == "" { + return nil, status.Error(codes.InvalidArgument, "Payment method must be wallet or topup") + } + + var planRecord model.Plan + if err := s.db.WithContext(ctx).Where("id = ?", planID).First(&planRecord).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, status.Error(codes.NotFound, "Plan not found") + } + s.logger.Error("Failed to load plan", "error", err) + return nil, status.Error(codes.Internal, "Failed to create payment") + } + if planRecord.IsActive == nil || !*planRecord.IsActive { + return nil, status.Error(codes.InvalidArgument, "Plan is not active") + } + + totalAmount := planRecord.Price * float64(req.GetTermMonths()) + if totalAmount < 0 { + return nil, status.Error(codes.InvalidArgument, "Amount must be greater than or equal to 0") + } + + statusValue := "SUCCESS" + provider := "INTERNAL" + currency := normalizeCurrency(nil) + transactionID := buildTransactionID("sub") + now := time.Now().UTC() + + paymentRecord := &model.Payment{ + ID: uuid.New().String(), + UserID: result.UserID, + PlanID: &planRecord.ID, + Amount: totalAmount, + Currency: ¤cy, + Status: &statusValue, + Provider: &provider, + TransactionID: &transactionID, + } + + invoiceID := buildInvoiceID(paymentRecord.ID) + var subscription *model.PlanSubscription + var walletBalance float64 + + err = s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + if _, err := lockUserForUpdate(ctx, tx, result.UserID); err != nil { + return err + } + + currentSubscription, err := model.GetLatestPlanSubscription(ctx, tx, result.UserID) + if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { + return err + } + + baseExpiry := now + if currentSubscription != nil && currentSubscription.ExpiresAt.After(baseExpiry) { + baseExpiry = currentSubscription.ExpiresAt.UTC() + } + newExpiry := baseExpiry.AddDate(0, int(req.GetTermMonths()), 0) + + currentWalletBalance, err := model.GetWalletBalance(ctx, tx, result.UserID) + if err != nil { + return err + } + shortfall := maxFloat(totalAmount-currentWalletBalance, 0) + + if paymentMethod == paymentMethodWallet && shortfall > 0 { + return statusErrorWithBody(ctx, codes.InvalidArgument, http.StatusBadRequest, "Insufficient wallet balance", map[string]interface{}{ + "payment_method": paymentMethod, + "wallet_balance": currentWalletBalance, + "total_amount": totalAmount, + "shortfall": shortfall, + }) + } + + topupAmount := 0.0 + if paymentMethod == paymentMethodTopup { + if req.TopupAmount == nil { + return statusErrorWithBody(ctx, codes.InvalidArgument, http.StatusBadRequest, "Top-up amount is required when payment method is topup", map[string]interface{}{ + "payment_method": paymentMethod, + "wallet_balance": currentWalletBalance, + "total_amount": totalAmount, + "shortfall": shortfall, + }) + } + + topupAmount = maxFloat(req.GetTopupAmount(), 0) + if topupAmount <= 0 { + return statusErrorWithBody(ctx, codes.InvalidArgument, http.StatusBadRequest, "Top-up amount must be greater than 0", map[string]interface{}{ + "payment_method": paymentMethod, + "wallet_balance": currentWalletBalance, + "total_amount": totalAmount, + "shortfall": shortfall, + }) + } + if topupAmount < shortfall { + return statusErrorWithBody(ctx, codes.InvalidArgument, http.StatusBadRequest, "Top-up amount must be greater than or equal to the required shortfall", map[string]interface{}{ + "payment_method": paymentMethod, + "wallet_balance": currentWalletBalance, + "total_amount": totalAmount, + "shortfall": shortfall, + "topup_amount": topupAmount, + }) + } + } + + if err := tx.Create(paymentRecord).Error; err != nil { + return err + } + + walletUsedAmount := totalAmount + + if paymentMethod == paymentMethodTopup { + topupTransaction := &model.WalletTransaction{ + ID: uuid.New().String(), + UserID: result.UserID, + Type: walletTransactionTypeTopup, + Amount: topupAmount, + Currency: model.StringPtr(currency), + Note: model.StringPtr(fmt.Sprintf("Wallet top-up for %s (%d months)", planRecord.Name, req.GetTermMonths())), + PaymentID: &paymentRecord.ID, + PlanID: &planRecord.ID, + TermMonths: int32Ptr(req.GetTermMonths()), + } + if err := tx.Create(topupTransaction).Error; err != nil { + return err + } + } + + debitTransaction := &model.WalletTransaction{ + ID: uuid.New().String(), + UserID: result.UserID, + Type: walletTransactionTypeSubscriptionDebit, + Amount: -totalAmount, + Currency: model.StringPtr(currency), + Note: model.StringPtr(fmt.Sprintf("Subscription payment for %s (%d months)", planRecord.Name, req.GetTermMonths())), + PaymentID: &paymentRecord.ID, + PlanID: &planRecord.ID, + TermMonths: int32Ptr(req.GetTermMonths()), + } + if err := tx.Create(debitTransaction).Error; err != nil { + return err + } + + subscription = &model.PlanSubscription{ + ID: uuid.New().String(), + UserID: result.UserID, + PaymentID: paymentRecord.ID, + PlanID: planRecord.ID, + TermMonths: req.GetTermMonths(), + PaymentMethod: paymentMethod, + WalletAmount: walletUsedAmount, + TopupAmount: topupAmount, + StartedAt: now, + ExpiresAt: newExpiry, + } + if err := tx.Create(subscription).Error; err != nil { + return err + } + + if err := tx.Model(&model.User{}). + Where("id = ?", result.UserID). + Update("plan_id", planRecord.ID).Error; err != nil { + return err + } + + notification := buildSubscriptionNotification(result.UserID, paymentRecord.ID, invoiceID, &planRecord, subscription) + if err := tx.Create(notification).Error; err != nil { + return err + } + + walletBalance, err = model.GetWalletBalance(ctx, tx, result.UserID) + if err != nil { + return err + } + + return nil + }) + if err != nil { + if _, ok := status.FromError(err); ok { + return nil, err + } + s.logger.Error("Failed to create payment", "error", err) + return nil, status.Error(codes.Internal, "Failed to create payment") + } + + return &appv1.CreatePaymentResponse{ + Payment: toProtoPayment(paymentRecord), + Subscription: toProtoPlanSubscription(subscription), + WalletBalance: walletBalance, + InvoiceId: invoiceID, + Message: "Payment completed successfully", + }, nil +} +func (s *appServices) ListPaymentHistory(ctx context.Context, _ *appv1.ListPaymentHistoryRequest) (*appv1.ListPaymentHistoryResponse, error) { + result, err := s.authenticate(ctx) + if err != nil { + return nil, err + } + + var rows []paymentRow + if err := s.db.WithContext(ctx). + Table("payment AS p"). + Select("p.id, p.amount, p.currency, p.status, p.plan_id, pl.name AS plan_name, ps.term_months, ps.payment_method, ps.expires_at, p.created_at"). + Joins("LEFT JOIN plan AS pl ON pl.id = p.plan_id"). + Joins("LEFT JOIN plan_subscriptions AS ps ON ps.payment_id = p.id"). + Where("p.user_id = ?", result.UserID). + Order("p.created_at DESC"). + Scan(&rows).Error; err != nil { + s.logger.Error("Failed to fetch payment history", "error", err) + return nil, status.Error(codes.Internal, "Failed to fetch payment history") + } + + items := make([]paymentapi.PaymentHistoryItem, 0, len(rows)) + for _, row := range rows { + items = append(items, paymentapi.PaymentHistoryItem{ + ID: row.ID, + Amount: row.Amount, + Currency: normalizeCurrency(row.Currency), + Status: normalizePaymentStatus(row.Status), + PlanID: row.PlanID, + PlanName: row.PlanName, + InvoiceID: buildInvoiceID(row.ID), + Kind: paymentKindSubscription, + TermMonths: row.TermMonths, + PaymentMethod: normalizeOptionalPaymentMethod(row.PaymentMethod), + ExpiresAt: row.ExpiresAt, + CreatedAt: row.CreatedAt, + }) + } + + var topups []model.WalletTransaction + if err := s.db.WithContext(ctx). + Where("user_id = ? AND type = ? AND payment_id IS NULL", result.UserID, walletTransactionTypeTopup). + Order("created_at DESC"). + Find(&topups).Error; err != nil { + s.logger.Error("Failed to fetch wallet topups", "error", err) + return nil, status.Error(codes.Internal, "Failed to fetch payment history") + } + + for _, topup := range topups { + createdAt := topup.CreatedAt + items = append(items, paymentapi.PaymentHistoryItem{ + ID: topup.ID, + Amount: topup.Amount, + Currency: normalizeCurrency(topup.Currency), + Status: "success", + InvoiceID: buildInvoiceID(topup.ID), + Kind: paymentKindWalletTopup, + CreatedAt: createdAt, + }) + } + + sort.Slice(items, func(i, j int) bool { + left := time.Time{} + right := time.Time{} + if items[i].CreatedAt != nil { + left = *items[i].CreatedAt + } + if items[j].CreatedAt != nil { + right = *items[j].CreatedAt + } + return right.After(left) + }) + + payload := make([]*appv1.PaymentHistoryItem, 0, len(items)) + for _, item := range items { + copyItem := item + payload = append(payload, toProtoPaymentHistoryItem(©Item)) + } + + return &appv1.ListPaymentHistoryResponse{Payments: payload}, nil +} +func (s *appServices) TopupWallet(ctx context.Context, req *appv1.TopupWalletRequest) (*appv1.TopupWalletResponse, error) { + result, err := s.authenticate(ctx) + if err != nil { + return nil, err + } + + amount := req.GetAmount() + if amount < 1 { + return nil, status.Error(codes.InvalidArgument, "Amount must be at least 1") + } + + transaction := &model.WalletTransaction{ + ID: uuid.New().String(), + UserID: result.UserID, + Type: walletTransactionTypeTopup, + Amount: amount, + Currency: model.StringPtr("USD"), + Note: model.StringPtr(fmt.Sprintf("Wallet top-up of %.2f USD", amount)), + } + + notification := &model.Notification{ + ID: uuid.New().String(), + UserID: result.UserID, + Type: "billing.topup", + Title: "Wallet credited", + Message: fmt.Sprintf("Your wallet has been credited with %.2f USD.", amount), + Metadata: model.StringPtr(mustMarshalJSON(map[string]interface{}{ + "wallet_transaction_id": transaction.ID, + "invoice_id": buildInvoiceID(transaction.ID), + })), + } + + if err := s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + if _, err := lockUserForUpdate(ctx, tx, result.UserID); err != nil { + return err + } + if err := tx.Create(transaction).Error; err != nil { + return err + } + if err := tx.Create(notification).Error; err != nil { + return err + } + return nil + }); err != nil { + s.logger.Error("Failed to top up wallet", "error", err) + return nil, status.Error(codes.Internal, "Failed to top up wallet") + } + + balance, err := model.GetWalletBalance(ctx, s.db, result.UserID) + if err != nil { + s.logger.Error("Failed to calculate wallet balance", "error", err) + return nil, status.Error(codes.Internal, "Failed to top up wallet") + } + + return &appv1.TopupWalletResponse{ + WalletTransaction: toProtoWalletTransaction(transaction), + WalletBalance: balance, + InvoiceId: buildInvoiceID(transaction.ID), + }, nil +} +func (s *appServices) DownloadInvoice(ctx context.Context, req *appv1.DownloadInvoiceRequest) (*appv1.DownloadInvoiceResponse, 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, "Invoice not found") + } + + paymentRecord, err := query.Payment.WithContext(ctx). + Where(query.Payment.ID.Eq(id), query.Payment.UserID.Eq(result.UserID)). + First() + if err == nil { + invoiceText, filename, buildErr := s.buildPaymentInvoice(ctx, paymentRecord) + if buildErr != nil { + s.logger.Error("Failed to build payment invoice", "error", buildErr) + return nil, status.Error(codes.Internal, "Failed to download invoice") + } + return &appv1.DownloadInvoiceResponse{ + Filename: filename, + ContentType: "text/plain; charset=utf-8", + Content: invoiceText, + }, nil + } + if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { + s.logger.Error("Failed to load payment invoice", "error", err) + return nil, status.Error(codes.Internal, "Failed to download invoice") + } + + var topup model.WalletTransaction + if err := s.db.WithContext(ctx). + Where("id = ? AND user_id = ? AND type = ? AND payment_id IS NULL", id, result.UserID, walletTransactionTypeTopup). + First(&topup).Error; err == nil { + return &appv1.DownloadInvoiceResponse{ + Filename: buildInvoiceFilename(topup.ID), + ContentType: "text/plain; charset=utf-8", + Content: buildTopupInvoice(&topup), + }, nil + } else if !errors.Is(err, gorm.ErrRecordNotFound) { + s.logger.Error("Failed to load topup invoice", "error", err) + return nil, status.Error(codes.Internal, "Failed to download invoice") + } + + return nil, status.Error(codes.NotFound, "Invoice not found") +} diff --git a/internal/rpc/app/service_user_features.go b/internal/rpc/app/service_user_features.go new file mode 100644 index 0000000..2a0e6a3 --- /dev/null +++ b/internal/rpc/app/service_user_features.go @@ -0,0 +1,390 @@ +package app + +import ( + "context" + "errors" + "strings" + + "github.com/google/uuid" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "gorm.io/gorm" + "stream.api/internal/database/model" + appv1 "stream.api/internal/gen/proto/app/v1" +) + +func (s *appServices) ListNotifications(ctx context.Context, _ *appv1.ListNotificationsRequest) (*appv1.ListNotificationsResponse, error) { + result, err := s.authenticate(ctx) + if err != nil { + return nil, err + } + + var rows []model.Notification + if err := s.db.WithContext(ctx). + Where("user_id = ?", result.UserID). + Order("created_at DESC"). + Find(&rows).Error; err != nil { + s.logger.Error("Failed to list notifications", "error", err) + return nil, status.Error(codes.Internal, "Failed to load notifications") + } + + items := make([]*appv1.Notification, 0, len(rows)) + for _, row := range rows { + items = append(items, toProtoNotification(row)) + } + + return &appv1.ListNotificationsResponse{Notifications: items}, nil +} +func (s *appServices) MarkNotificationRead(ctx context.Context, req *appv1.MarkNotificationReadRequest) (*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, "Notification not found") + } + + res := s.db.WithContext(ctx). + Model(&model.Notification{}). + Where("id = ? AND user_id = ?", id, result.UserID). + Update("is_read", true) + if res.Error != nil { + s.logger.Error("Failed to update notification", "error", res.Error) + return nil, status.Error(codes.Internal, "Failed to update notification") + } + if res.RowsAffected == 0 { + return nil, status.Error(codes.NotFound, "Notification not found") + } + + return messageResponse("Notification updated"), nil +} +func (s *appServices) MarkAllNotificationsRead(ctx context.Context, _ *appv1.MarkAllNotificationsReadRequest) (*appv1.MessageResponse, error) { + result, err := s.authenticate(ctx) + if err != nil { + return nil, err + } + + if err := s.db.WithContext(ctx). + Model(&model.Notification{}). + Where("user_id = ? AND is_read = ?", result.UserID, false). + Update("is_read", true).Error; err != nil { + s.logger.Error("Failed to mark all notifications as read", "error", err) + return nil, status.Error(codes.Internal, "Failed to update notifications") + } + + return messageResponse("All notifications marked as read"), nil +} +func (s *appServices) DeleteNotification(ctx context.Context, req *appv1.DeleteNotificationRequest) (*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, "Notification not found") + } + + res := s.db.WithContext(ctx). + Where("id = ? AND user_id = ?", id, result.UserID). + Delete(&model.Notification{}) + if res.Error != nil { + s.logger.Error("Failed to delete notification", "error", res.Error) + return nil, status.Error(codes.Internal, "Failed to delete notification") + } + if res.RowsAffected == 0 { + return nil, status.Error(codes.NotFound, "Notification not found") + } + + return messageResponse("Notification deleted"), nil +} +func (s *appServices) ClearNotifications(ctx context.Context, _ *appv1.ClearNotificationsRequest) (*appv1.MessageResponse, error) { + result, err := s.authenticate(ctx) + if err != nil { + return nil, err + } + + if err := s.db.WithContext(ctx).Where("user_id = ?", result.UserID).Delete(&model.Notification{}).Error; err != nil { + s.logger.Error("Failed to clear notifications", "error", err) + return nil, status.Error(codes.Internal, "Failed to clear notifications") + } + + return messageResponse("All notifications deleted"), nil +} +func (s *appServices) ListDomains(ctx context.Context, _ *appv1.ListDomainsRequest) (*appv1.ListDomainsResponse, error) { + result, err := s.authenticate(ctx) + if err != nil { + return nil, err + } + + var rows []model.Domain + if err := s.db.WithContext(ctx). + Where("user_id = ?", result.UserID). + Order("created_at DESC"). + Find(&rows).Error; err != nil { + s.logger.Error("Failed to list domains", "error", err) + return nil, status.Error(codes.Internal, "Failed to load domains") + } + + items := make([]*appv1.Domain, 0, len(rows)) + for _, row := range rows { + item := row + items = append(items, toProtoDomain(&item)) + } + + return &appv1.ListDomainsResponse{Domains: items}, nil +} +func (s *appServices) CreateDomain(ctx context.Context, req *appv1.CreateDomainRequest) (*appv1.CreateDomainResponse, error) { + result, err := s.authenticate(ctx) + if err != nil { + return nil, err + } + + name := normalizeDomain(req.GetName()) + if name == "" || !strings.Contains(name, ".") || strings.ContainsAny(name, "/ ") { + return nil, status.Error(codes.InvalidArgument, "Invalid domain") + } + + var count int64 + if err := s.db.WithContext(ctx). + Model(&model.Domain{}). + Where("user_id = ? AND name = ?", result.UserID, name). + Count(&count).Error; err != nil { + s.logger.Error("Failed to validate domain", "error", err) + return nil, status.Error(codes.Internal, "Failed to create domain") + } + if count > 0 { + return nil, status.Error(codes.InvalidArgument, "Domain already exists") + } + + item := &model.Domain{ + ID: uuid.New().String(), + UserID: result.UserID, + Name: name, + } + if err := s.db.WithContext(ctx).Create(item).Error; err != nil { + s.logger.Error("Failed to create domain", "error", err) + return nil, status.Error(codes.Internal, "Failed to create domain") + } + + return &appv1.CreateDomainResponse{Domain: toProtoDomain(item)}, nil +} +func (s *appServices) DeleteDomain(ctx context.Context, req *appv1.DeleteDomainRequest) (*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, "Domain not found") + } + + res := s.db.WithContext(ctx). + Where("id = ? AND user_id = ?", id, result.UserID). + Delete(&model.Domain{}) + if res.Error != nil { + s.logger.Error("Failed to delete domain", "error", res.Error) + return nil, status.Error(codes.Internal, "Failed to delete domain") + } + if res.RowsAffected == 0 { + return nil, status.Error(codes.NotFound, "Domain not found") + } + + return messageResponse("Domain deleted"), nil +} +func (s *appServices) ListAdTemplates(ctx context.Context, _ *appv1.ListAdTemplatesRequest) (*appv1.ListAdTemplatesResponse, error) { + result, err := s.authenticate(ctx) + if err != nil { + return nil, err + } + + var items []model.AdTemplate + if err := s.db.WithContext(ctx). + Where("user_id = ?", result.UserID). + Order("is_default DESC"). + Order("created_at DESC"). + Find(&items).Error; err != nil { + s.logger.Error("Failed to list ad templates", "error", err) + return nil, status.Error(codes.Internal, "Failed to load ad templates") + } + + payload := make([]*appv1.AdTemplate, 0, len(items)) + for _, item := range items { + copyItem := item + payload = append(payload, toProtoAdTemplate(©Item)) + } + + return &appv1.ListAdTemplatesResponse{Templates: payload}, nil +} +func (s *appServices) CreateAdTemplate(ctx context.Context, req *appv1.CreateAdTemplateRequest) (*appv1.CreateAdTemplateResponse, error) { + result, err := s.authenticate(ctx) + if err != nil { + return nil, err + } + if err := ensurePaidPlan(result.User); err != nil { + return nil, err + } + + name := strings.TrimSpace(req.GetName()) + vastURL := strings.TrimSpace(req.GetVastTagUrl()) + if name == "" || vastURL == "" { + return nil, status.Error(codes.InvalidArgument, "Name and VAST URL are required") + } + + format := normalizeAdFormat(req.GetAdFormat()) + if format == "mid-roll" && (req.Duration == nil || *req.Duration <= 0) { + return nil, status.Error(codes.InvalidArgument, "Duration is required for mid-roll templates") + } + + item := &model.AdTemplate{ + ID: uuid.New().String(), + UserID: result.UserID, + Name: name, + Description: nullableTrimmedString(req.Description), + VastTagURL: vastURL, + AdFormat: model.StringPtr(format), + Duration: int32PtrToInt64Ptr(req.Duration), + IsActive: model.BoolPtr(req.IsActive == nil || *req.IsActive), + IsDefault: req.IsDefault != nil && *req.IsDefault, + } + if !adTemplateIsActive(item.IsActive) { + item.IsDefault = false + } + + if err := s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + if item.IsDefault { + if err := unsetDefaultTemplates(tx, result.UserID, ""); err != nil { + return err + } + } + return tx.Create(item).Error + }); err != nil { + s.logger.Error("Failed to create ad template", "error", err) + return nil, status.Error(codes.Internal, "Failed to save ad template") + } + + return &appv1.CreateAdTemplateResponse{Template: toProtoAdTemplate(item)}, nil +} +func (s *appServices) UpdateAdTemplate(ctx context.Context, req *appv1.UpdateAdTemplateRequest) (*appv1.UpdateAdTemplateResponse, error) { + result, err := s.authenticate(ctx) + if err != nil { + return nil, err + } + if err := ensurePaidPlan(result.User); err != nil { + return nil, err + } + + id := strings.TrimSpace(req.GetId()) + if id == "" { + return nil, status.Error(codes.NotFound, "Ad template not found") + } + + name := strings.TrimSpace(req.GetName()) + vastURL := strings.TrimSpace(req.GetVastTagUrl()) + if name == "" || vastURL == "" { + return nil, status.Error(codes.InvalidArgument, "Name and VAST URL are required") + } + + format := normalizeAdFormat(req.GetAdFormat()) + if format == "mid-roll" && (req.Duration == nil || *req.Duration <= 0) { + return nil, status.Error(codes.InvalidArgument, "Duration is required for mid-roll templates") + } + + var item model.AdTemplate + if err := s.db.WithContext(ctx).Where("id = ? AND user_id = ?", id, result.UserID).First(&item).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, status.Error(codes.NotFound, "Ad template not found") + } + s.logger.Error("Failed to load ad template", "error", err) + return nil, status.Error(codes.Internal, "Failed to save ad template") + } + + item.Name = name + item.Description = nullableTrimmedString(req.Description) + item.VastTagURL = vastURL + item.AdFormat = model.StringPtr(format) + item.Duration = int32PtrToInt64Ptr(req.Duration) + if req.IsActive != nil { + item.IsActive = model.BoolPtr(*req.IsActive) + } + if req.IsDefault != nil { + item.IsDefault = *req.IsDefault + } + if !adTemplateIsActive(item.IsActive) { + item.IsDefault = false + } + + if err := s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + if item.IsDefault { + if err := unsetDefaultTemplates(tx, result.UserID, item.ID); err != nil { + return err + } + } + return tx.Save(&item).Error + }); err != nil { + s.logger.Error("Failed to update ad template", "error", err) + return nil, status.Error(codes.Internal, "Failed to save ad template") + } + + return &appv1.UpdateAdTemplateResponse{Template: toProtoAdTemplate(&item)}, nil +} +func (s *appServices) DeleteAdTemplate(ctx context.Context, req *appv1.DeleteAdTemplateRequest) (*appv1.MessageResponse, error) { + result, err := s.authenticate(ctx) + if err != nil { + return nil, err + } + if err := ensurePaidPlan(result.User); err != nil { + return nil, err + } + + id := strings.TrimSpace(req.GetId()) + if id == "" { + return nil, status.Error(codes.NotFound, "Ad template not found") + } + + if err := s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + if err := tx.Where("ad_template_id = ? AND user_id = ?", id, result.UserID). + Delete(&model.VideoAdConfig{}).Error; err != nil { + return err + } + + res := tx.Where("id = ? AND user_id = ?", id, result.UserID).Delete(&model.AdTemplate{}) + if res.Error != nil { + return res.Error + } + if res.RowsAffected == 0 { + return gorm.ErrRecordNotFound + } + return nil + }); err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, status.Error(codes.NotFound, "Ad template not found") + } + s.logger.Error("Failed to delete ad template", "error", err) + return nil, status.Error(codes.Internal, "Failed to delete ad template") + } + + return messageResponse("Ad template deleted"), nil +} +func (s *appServices) ListPlans(ctx context.Context, _ *appv1.ListPlansRequest) (*appv1.ListPlansResponse, error) { + if _, err := s.authenticate(ctx); err != nil { + return nil, err + } + + var plans []model.Plan + if err := s.db.WithContext(ctx).Where("is_active = ?", true).Find(&plans).Error; err != nil { + s.logger.Error("Failed to fetch plans", "error", err) + return nil, status.Error(codes.Internal, "Failed to fetch plans") + } + + items := make([]*appv1.Plan, 0, len(plans)) + for _, plan := range plans { + copyPlan := plan + items = append(items, toProtoPlan(©Plan)) + } + + return &appv1.ListPlansResponse{Plans: items}, nil +} diff --git a/internal/rpc/app/service_videos.go b/internal/rpc/app/service_videos.go new file mode 100644 index 0000000..a2fcb30 --- /dev/null +++ b/internal/rpc/app/service_videos.go @@ -0,0 +1,274 @@ +package app + +import ( + "context" + "errors" + "fmt" + "strings" + "time" + + "github.com/google/uuid" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "gorm.io/gorm" + "stream.api/internal/database/model" + appv1 "stream.api/internal/gen/proto/app/v1" +) + +func (s *appServices) GetUploadUrl(ctx context.Context, req *appv1.GetUploadUrlRequest) (*appv1.GetUploadUrlResponse, error) { + result, err := s.authenticate(ctx) + if err != nil { + return nil, err + } + if s.storageProvider == nil { + return nil, status.Error(codes.FailedPrecondition, "Storage provider is not configured") + } + + filename := strings.TrimSpace(req.GetFilename()) + if filename == "" { + return nil, status.Error(codes.InvalidArgument, "Filename is required") + } + + fileID := uuid.New().String() + key := fmt.Sprintf("videos/%s/%s-%s", result.UserID, fileID, filename) + uploadURL, err := s.storageProvider.GeneratePresignedURL(key, 15*time.Minute) + if err != nil { + s.logger.Error("Failed to generate upload URL", "error", err) + return nil, status.Error(codes.Internal, "Storage error") + } + + return &appv1.GetUploadUrlResponse{UploadUrl: uploadURL, Key: key, FileId: fileID}, nil +} +func (s *appServices) CreateVideo(ctx context.Context, req *appv1.CreateVideoRequest) (*appv1.CreateVideoResponse, error) { + result, err := s.authenticate(ctx) + if err != nil { + return nil, err + } + + title := strings.TrimSpace(req.GetTitle()) + if title == "" { + return nil, status.Error(codes.InvalidArgument, "Title is required") + } + videoURL := strings.TrimSpace(req.GetUrl()) + if videoURL == "" { + return nil, status.Error(codes.InvalidArgument, "URL is required") + } + + statusValue := "ready" + processingStatus := "READY" + storageType := detectStorageType(videoURL) + description := strings.TrimSpace(req.GetDescription()) + format := strings.TrimSpace(req.GetFormat()) + + video := &model.Video{ + ID: uuid.New().String(), + UserID: result.UserID, + Name: title, + Title: title, + Description: nullableTrimmedString(&description), + URL: videoURL, + Size: req.GetSize(), + Duration: req.GetDuration(), + Format: format, + Status: &statusValue, + ProcessingStatus: &processingStatus, + StorageType: &storageType, + } + + if err := s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + if err := tx.Create(video).Error; err != nil { + return err + } + return tx.Model(&model.User{}). + Where("id = ?", result.UserID). + UpdateColumn("storage_used", gorm.Expr("storage_used + ?", video.Size)).Error + }); err != nil { + s.logger.Error("Failed to create video", "error", err) + return nil, status.Error(codes.Internal, "Failed to create video") + } + + return &appv1.CreateVideoResponse{Video: toProtoVideo(video)}, nil +} +func (s *appServices) ListVideos(ctx context.Context, req *appv1.ListVideosRequest) (*appv1.ListVideosResponse, error) { + result, err := s.authenticate(ctx) + if err != nil { + return nil, err + } + + page := req.GetPage() + if page < 1 { + page = 1 + } + limit := req.GetLimit() + if limit <= 0 { + limit = 10 + } + if limit > 100 { + limit = 100 + } + offset := int((page - 1) * limit) + + db := s.db.WithContext(ctx).Model(&model.Video{}).Where("user_id = ?", result.UserID) + if search := strings.TrimSpace(req.GetSearch()); search != "" { + like := "%" + search + "%" + db = db.Where("title ILIKE ? OR description ILIKE ?", like, like) + } + if st := strings.TrimSpace(req.GetStatus()); st != "" && !strings.EqualFold(st, "all") { + db = db.Where("status = ?", normalizeVideoStatusValue(st)) + } + + var total int64 + if err := db.Count(&total).Error; err != nil { + s.logger.Error("Failed to count videos", "error", err) + return nil, status.Error(codes.Internal, "Failed to fetch videos") + } + + var videos []model.Video + if err := db.Order("created_at DESC").Offset(offset).Limit(int(limit)).Find(&videos).Error; err != nil { + s.logger.Error("Failed to list videos", "error", err) + return nil, status.Error(codes.Internal, "Failed to fetch videos") + } + + items := make([]*appv1.Video, 0, len(videos)) + for i := range videos { + items = append(items, toProtoVideo(&videos[i])) + } + + return &appv1.ListVideosResponse{Videos: items, Total: total, Page: page, Limit: limit}, nil +} +func (s *appServices) GetVideo(ctx context.Context, req *appv1.GetVideoRequest) (*appv1.GetVideoResponse, 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, "Video not found") + } + + _ = s.db.WithContext(ctx).Model(&model.Video{}). + Where("id = ? AND user_id = ?", id, result.UserID). + UpdateColumn("views", gorm.Expr("views + ?", 1)).Error + + var video model.Video + if err := s.db.WithContext(ctx).Where("id = ? AND user_id = ?", id, result.UserID).First(&video).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, status.Error(codes.NotFound, "Video not found") + } + s.logger.Error("Failed to fetch video", "error", err) + return nil, status.Error(codes.Internal, "Failed to fetch video") + } + + return &appv1.GetVideoResponse{Video: toProtoVideo(&video)}, nil +} +func (s *appServices) UpdateVideo(ctx context.Context, req *appv1.UpdateVideoRequest) (*appv1.UpdateVideoResponse, 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, "Video not found") + } + + updates := map[string]any{} + if title := strings.TrimSpace(req.GetTitle()); title != "" { + updates["name"] = title + updates["title"] = title + } + if req.Description != nil { + desc := strings.TrimSpace(req.GetDescription()) + updates["description"] = nullableTrimmedString(&desc) + } + if urlValue := strings.TrimSpace(req.GetUrl()); urlValue != "" { + updates["url"] = urlValue + } + if req.Size > 0 { + updates["size"] = req.GetSize() + } + if req.Duration > 0 { + updates["duration"] = req.GetDuration() + } + if req.Format != nil { + updates["format"] = strings.TrimSpace(req.GetFormat()) + } + if req.Status != nil { + updates["status"] = normalizeVideoStatusValue(req.GetStatus()) + } + if len(updates) == 0 { + return nil, status.Error(codes.InvalidArgument, "No changes provided") + } + + res := s.db.WithContext(ctx). + Model(&model.Video{}). + Where("id = ? AND user_id = ?", id, result.UserID). + Updates(updates) + if res.Error != nil { + s.logger.Error("Failed to update video", "error", res.Error) + return nil, status.Error(codes.Internal, "Failed to update video") + } + if res.RowsAffected == 0 { + return nil, status.Error(codes.NotFound, "Video not found") + } + + var video model.Video + if err := s.db.WithContext(ctx).Where("id = ? AND user_id = ?", id, result.UserID).First(&video).Error; err != nil { + s.logger.Error("Failed to reload video", "error", err) + return nil, status.Error(codes.Internal, "Failed to update video") + } + + return &appv1.UpdateVideoResponse{Video: toProtoVideo(&video)}, nil +} +func (s *appServices) DeleteVideo(ctx context.Context, req *appv1.DeleteVideoRequest) (*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, "Video not found") + } + + var video model.Video + if err := s.db.WithContext(ctx).Where("id = ? AND user_id = ?", id, result.UserID).First(&video).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, status.Error(codes.NotFound, "Video not found") + } + s.logger.Error("Failed to load video", "error", err) + return nil, status.Error(codes.Internal, "Failed to delete video") + } + + if s.storageProvider != nil && shouldDeleteStoredObject(video.URL) { + if err := s.storageProvider.Delete(video.URL); err != nil { + if parsedKey := extractObjectKey(video.URL); parsedKey != "" && parsedKey != video.URL { + if deleteErr := s.storageProvider.Delete(parsedKey); deleteErr != nil { + s.logger.Error("Failed to delete video object", "error", deleteErr, "video_id", video.ID) + return nil, status.Error(codes.Internal, "Failed to delete video") + } + } else { + s.logger.Error("Failed to delete video object", "error", err, "video_id", video.ID) + return nil, status.Error(codes.Internal, "Failed to delete video") + } + } + } + + if err := s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + if err := tx.Where("video_id = ? AND user_id = ?", video.ID, result.UserID).Delete(&model.VideoAdConfig{}).Error; err != nil { + return err + } + if err := tx.Where("id = ? AND user_id = ?", video.ID, result.UserID).Delete(&model.Video{}).Error; err != nil { + return err + } + return tx.Model(&model.User{}). + Where("id = ?", result.UserID). + UpdateColumn("storage_used", gorm.Expr("storage_used - ?", video.Size)).Error + }); err != nil { + s.logger.Error("Failed to delete video", "error", err) + return nil, status.Error(codes.Internal, "Failed to delete video") + } + + return messageResponse("Video deleted successfully"), nil +} diff --git a/internal/video/runtime/adapters/queue/redis/adapter.go b/internal/video/runtime/adapters/queue/redis/adapter.go new file mode 100644 index 0000000..79dfb2e --- /dev/null +++ b/internal/video/runtime/adapters/queue/redis/adapter.go @@ -0,0 +1,174 @@ +package redis + +import ( + "context" + "encoding/json" + "fmt" + "time" + + goredis "github.com/redis/go-redis/v9" + "stream.api/internal/video/runtime/domain" +) + +const ( + JobQueueKey = "render:jobs:queue" + LogChannel = "render:jobs:logs" + ResourceChannel = "render:agents:resources" + JobUpdateChannel = "render:jobs:updates" +) + +type Adapter struct{ client *goredis.Client } + +func NewAdapter(addr, password string, db int) (*Adapter, error) { + client := goredis.NewClient(&goredis.Options{Addr: addr, Password: password, DB: db}) + if err := client.Ping(context.Background()).Err(); err != nil { + return nil, err + } + return &Adapter{client: client}, nil +} + +func (r *Adapter) Client() *goredis.Client { return r.client } + +func (r *Adapter) Enqueue(ctx context.Context, job *domain.Job) error { + data, err := json.Marshal(job) + if err != nil { + return err + } + timestamp := time.Now().UnixNano() + score := float64(-(int64(job.Priority) * 1000000000) - timestamp) + return r.client.ZAdd(ctx, JobQueueKey, goredis.Z{Score: score, Member: data}).Err() +} + +func (r *Adapter) Dequeue(ctx context.Context) (*domain.Job, error) { + for { + if ctx.Err() != nil { + return nil, ctx.Err() + } + res, err := r.client.ZPopMin(ctx, JobQueueKey, 1).Result() + if err != nil { + if ctx.Err() != nil { + return nil, ctx.Err() + } + return nil, err + } + if len(res) == 0 { + select { + case <-ctx.Done(): + return nil, ctx.Err() + case <-time.After(time.Second): + continue + } + } + var raw []byte + switch member := res[0].Member.(type) { + case string: + raw = []byte(member) + case []byte: + raw = member + default: + return nil, fmt.Errorf("unexpected redis queue payload type %T", member) + } + var job domain.Job + if err := json.Unmarshal(raw, &job); err != nil { + return nil, err + } + return &job, nil + } +} + +func (r *Adapter) Publish(ctx context.Context, jobID string, logLine string, progress float64) error { + payload, err := json.Marshal(domain.LogEntry{JobID: jobID, Line: logLine, Progress: progress}) + if err != nil { + return err + } + return r.client.Publish(ctx, LogChannel, payload).Err() +} + +func (r *Adapter) Subscribe(ctx context.Context, jobID string) (<-chan domain.LogEntry, error) { + pubsub := r.client.Subscribe(ctx, LogChannel) + ch := make(chan domain.LogEntry) + go func() { + defer close(ch) + defer pubsub.Close() + for msg := range pubsub.Channel() { + var entry domain.LogEntry + if err := json.Unmarshal([]byte(msg.Payload), &entry); err != nil { + continue + } + if jobID == "" || entry.JobID == jobID { + ch <- entry + } + } + }() + return ch, nil +} + +func (r *Adapter) PublishResource(ctx context.Context, agentID string, data []byte) error { + var decoded struct { + CPU float64 `json:"cpu"` + RAM float64 `json:"ram"` + } + if err := json.Unmarshal(data, &decoded); err != nil { + return err + } + payload, err := json.Marshal(domain.SystemResource{AgentID: agentID, CPU: decoded.CPU, RAM: decoded.RAM}) + if err != nil { + return err + } + return r.client.Publish(ctx, ResourceChannel, payload).Err() +} + +func (r *Adapter) SubscribeResources(ctx context.Context) (<-chan domain.SystemResource, error) { + pubsub := r.client.Subscribe(ctx, ResourceChannel) + ch := make(chan domain.SystemResource) + go func() { + defer close(ch) + defer pubsub.Close() + for msg := range pubsub.Channel() { + var entry domain.SystemResource + if err := json.Unmarshal([]byte(msg.Payload), &entry); err != nil { + continue + } + ch <- entry + } + }() + return ch, nil +} + +func (r *Adapter) PublishCancel(ctx context.Context, agentID string, jobID string) error { + return r.client.Publish(ctx, fmt.Sprintf("render:agents:%s:cancel", agentID), jobID).Err() +} + +func (r *Adapter) SubscribeCancel(ctx context.Context, agentID string) (<-chan string, error) { + pubsub := r.client.Subscribe(ctx, fmt.Sprintf("render:agents:%s:cancel", agentID)) + ch := make(chan string) + go func() { + defer close(ch) + defer pubsub.Close() + for msg := range pubsub.Channel() { + ch <- msg.Payload + } + }() + return ch, nil +} + +func (r *Adapter) PublishJobUpdate(ctx context.Context, jobID string, status string) error { + payload, err := json.Marshal(map[string]string{"job_id": jobID, "status": status}) + if err != nil { + return err + } + return r.client.Publish(ctx, JobUpdateChannel, payload).Err() +} + +func (r *Adapter) SubscribeJobUpdates(ctx context.Context) (<-chan string, error) { + pubsub := r.client.Subscribe(ctx, JobUpdateChannel) + ch := make(chan string) + go func() { + defer close(ch) + defer pubsub.Close() + for msg := range pubsub.Channel() { + ch <- msg.Payload + } + }() + return ch, nil +} diff --git a/internal/video/runtime/domain/agent.go b/internal/video/runtime/domain/agent.go new file mode 100644 index 0000000..8c850f9 --- /dev/null +++ b/internal/video/runtime/domain/agent.go @@ -0,0 +1,26 @@ +package domain + +import "time" + +type AgentStatus string + +const ( + AgentStatusOnline AgentStatus = "online" + AgentStatusOffline AgentStatus = "offline" + AgentStatusBusy AgentStatus = "busy" +) + +type Agent struct { + ID string `json:"id"` + Name string `json:"name"` + Platform string `json:"platform"` + Backend string `json:"backend"` + Version string `json:"version"` + Capacity int32 `json:"capacity"` + Status AgentStatus `json:"status"` + CPU float64 `json:"cpu"` + RAM float64 `json:"ram"` + LastHeartbeat time.Time `json:"last_heartbeat"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} diff --git a/internal/video/runtime/domain/job.go b/internal/video/runtime/domain/job.go new file mode 100644 index 0000000..a357e1e --- /dev/null +++ b/internal/video/runtime/domain/job.go @@ -0,0 +1,37 @@ +package domain + +import "time" + +type JobStatus string + +const ( + JobStatusPending JobStatus = "pending" + JobStatusRunning JobStatus = "running" + JobStatusSuccess JobStatus = "success" + JobStatusFailure JobStatus = "failure" + JobStatusCancelled JobStatus = "cancelled" +) + +type Job struct { + ID string `json:"id" gorm:"primaryKey;type:uuid;default:gen_random_uuid()"` + Status JobStatus `json:"status" gorm:"type:varchar(32);index"` + Priority int `json:"priority" gorm:"default:0;index"` + UserID string `json:"user_id" gorm:"index"` + Name string `json:"name"` + TimeLimit int64 `json:"time_limit"` + InputURL string `json:"input_url"` + OutputURL string `json:"output_url"` + TotalDuration int64 `json:"total_duration"` + CurrentTime int64 `json:"current_time"` + Progress float64 `json:"progress"` + AgentID *string `json:"agent_id" gorm:"type:uuid;index"` + Logs string `json:"logs" gorm:"type:text"` + Config string `json:"config" gorm:"type:text"` + Cancelled bool `json:"cancelled" gorm:"default:false"` + RetryCount int `json:"retry_count" gorm:"default:0"` + MaxRetries int `json:"max_retries" gorm:"default:3"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +func (Job) TableName() string { return "render_jobs" } diff --git a/internal/video/runtime/domain/log.go b/internal/video/runtime/domain/log.go new file mode 100644 index 0000000..7c032ab --- /dev/null +++ b/internal/video/runtime/domain/log.go @@ -0,0 +1,13 @@ +package domain + +type LogEntry struct { + JobID string `json:"job_id"` + Line string `json:"line"` + Progress float64 `json:"progress"` +} + +type SystemResource struct { + AgentID string `json:"agent_id"` + CPU float64 `json:"cpu"` + RAM float64 `json:"ram"` +} diff --git a/internal/video/runtime/grpc/agent_manager.go b/internal/video/runtime/grpc/agent_manager.go new file mode 100644 index 0000000..d2a83de --- /dev/null +++ b/internal/video/runtime/grpc/agent_manager.go @@ -0,0 +1,111 @@ +package grpc + +import ( + "sync" + "time" + + "stream.api/internal/video/runtime/domain" +) + +type AgentInfo struct { + ID string + Name string + Platform string + Backend string + Version string + Capacity int32 + CPU float64 + RAM float64 + LastHeartbeat time.Time + ConnectedAt time.Time + CommandCh chan string +} + +type AgentManager struct { + mu sync.RWMutex + agents map[string]*AgentInfo +} + +func NewAgentManager() *AgentManager { + return &AgentManager{agents: make(map[string]*AgentInfo)} +} + +func (am *AgentManager) Register(id string, name, platform, backend, version string, capacity int32) { + am.mu.Lock() + defer am.mu.Unlock() + now := time.Now() + if existing, ok := am.agents[id]; ok { + existing.Name = name + existing.Platform = platform + existing.Backend = backend + existing.Version = version + existing.Capacity = capacity + existing.LastHeartbeat = now + return + } + am.agents[id] = &AgentInfo{ID: id, Name: name, Platform: platform, Backend: backend, Version: version, Capacity: capacity, LastHeartbeat: now, ConnectedAt: now, CommandCh: make(chan string, 10)} +} + +func (am *AgentManager) GetCommandChannel(id string) (chan string, bool) { + am.mu.RLock() + defer am.mu.RUnlock() + agent, ok := am.agents[id] + if !ok { + return nil, false + } + return agent.CommandCh, true +} + +func (am *AgentManager) SendCommand(id string, cmd string) bool { + am.mu.RLock() + defer am.mu.RUnlock() + agent, ok := am.agents[id] + if !ok { + return false + } + select { + case agent.CommandCh <- cmd: + return true + default: + return false + } +} + +func (am *AgentManager) UpdateHeartbeat(id string) { + am.mu.Lock() + defer am.mu.Unlock() + if agent, ok := am.agents[id]; ok { + agent.LastHeartbeat = time.Now() + } +} + +func (am *AgentManager) UpdateResources(id string, cpu, ram float64) { + am.mu.Lock() + defer am.mu.Unlock() + if agent, ok := am.agents[id]; ok { + agent.CPU = cpu + agent.RAM = ram + agent.LastHeartbeat = time.Now() + } +} + +func (am *AgentManager) Unregister(id string) { + am.mu.Lock() + defer am.mu.Unlock() + delete(am.agents, id) +} + +func (am *AgentManager) ListAll() []*domain.Agent { + am.mu.RLock() + defer am.mu.RUnlock() + now := time.Now() + all := make([]*domain.Agent, 0, len(am.agents)) + for _, info := range am.agents { + status := domain.AgentStatusOnline + if now.Sub(info.LastHeartbeat) >= 60*time.Second { + status = domain.AgentStatusOffline + } + all = append(all, &domain.Agent{ID: info.ID, Name: info.Name, Platform: info.Platform, Backend: info.Backend, Version: info.Version, Capacity: info.Capacity, Status: status, CPU: info.CPU, RAM: info.RAM, LastHeartbeat: info.LastHeartbeat, CreatedAt: info.ConnectedAt, UpdatedAt: info.LastHeartbeat}) + } + return all +} diff --git a/internal/video/runtime/grpc/server.go b/internal/video/runtime/grpc/server.go new file mode 100644 index 0000000..00ea39b --- /dev/null +++ b/internal/video/runtime/grpc/server.go @@ -0,0 +1,361 @@ +package grpc + +import ( + "context" + "crypto/rand" + "encoding/hex" + "encoding/json" + "fmt" + "strconv" + "sync" + "time" + + grpcpkg "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" + "stream.api/internal/video/runtime/domain" + "stream.api/internal/video/runtime/proto" + "stream.api/internal/video/runtime/services" +) + +type Server struct { + proto.UnimplementedWoodpeckerServer + proto.UnimplementedWoodpeckerAuthServer + jobService *services.JobService + agentManager *AgentManager + agentSecret string + sessions sync.Map + agentJobs sync.Map + onAgentEvent func(string, *services.AgentWithStats) +} + +func NewServer(jobService *services.JobService, agentSecret string) *Server { + return &Server{jobService: jobService, agentManager: NewAgentManager(), agentSecret: agentSecret} +} + +func (s *Server) SetAgentEventHandler(handler func(string, *services.AgentWithStats)) { + s.onAgentEvent = handler +} + +func (s *Server) Register(grpcServer grpcpkg.ServiceRegistrar) { + proto.RegisterWoodpeckerServer(grpcServer, s) + proto.RegisterWoodpeckerAuthServer(grpcServer, s) +} + +func (s *Server) SendCommand(agentID string, cmd string) bool { + return s.agentManager.SendCommand(agentID, cmd) +} + +func (s *Server) ListAgents() []*domain.Agent { return s.agentManager.ListAll() } + +func (s *Server) ListAgentsWithStats() []*services.AgentWithStats { + agents := s.agentManager.ListAll() + result := make([]*services.AgentWithStats, 0, len(agents)) + for _, agent := range agents { + result = append(result, &services.AgentWithStats{Agent: agent, ActiveJobCount: int64(len(s.getAgentJobs(agent.ID)))}) + } + return result +} + +func (s *Server) getAgentWithStats(agentID string) *services.AgentWithStats { + for _, agent := range s.ListAgentsWithStats() { + if agent != nil && agent.Agent != nil && agent.ID == agentID { + return agent + } + } + return nil +} + +func (s *Server) Version(context.Context, *proto.Empty) (*proto.VersionResponse, error) { + return &proto.VersionResponse{GrpcVersion: 15, ServerVersion: "stream.api"}, nil +} + +func generateToken() string { + b := make([]byte, 16) + _, _ = rand.Read(b) + return hex.EncodeToString(b) +} + +func generateAgentID() string { + return strconv.FormatInt(time.Now().UnixNano(), 10) +} + +func (s *Server) getAgentIDFromContext(ctx context.Context) (string, string, bool) { + md, ok := metadata.FromIncomingContext(ctx) + if !ok { + return "", "", false + } + tokens := md.Get("token") + if len(tokens) == 0 { + return "", "", false + } + token := tokens[0] + if id, ok := s.sessions.Load(token); ok { + return id.(string), token, true + } + return "", "", false +} + +func (s *Server) Next(context.Context, *proto.NextRequest) (*proto.NextResponse, error) { + return nil, status.Error(codes.Unimplemented, "use StreamJobs") +} + +func (s *Server) StreamJobs(_ *proto.StreamOptions, stream grpcpkg.ServerStreamingServer[proto.Workflow]) error { + ctx := stream.Context() + agentID, _, ok := s.getAgentIDFromContext(ctx) + if !ok { + return status.Error(codes.Unauthenticated, "invalid or missing token") + } + s.agentManager.UpdateHeartbeat(agentID) + cancelCh, _ := s.jobService.SubscribeCancel(ctx, agentID) + commandCh, _ := s.agentManager.GetCommandChannel(agentID) + ticker := time.NewTicker(2 * time.Second) + defer ticker.Stop() + for { + select { + case cmd := <-commandCh: + payload, _ := json.Marshal(map[string]any{"image": "alpine", "commands": []string{"echo 'System Command'"}, "environment": map[string]string{}, "action": cmd}) + if err := stream.Send(&proto.Workflow{Id: fmt.Sprintf("cmd-%s-%d", agentID, time.Now().UnixNano()), Timeout: 300, Payload: payload}); err != nil { + return err + } + case jobID := <-cancelCh: + if s.isJobAssigned(agentID, jobID) { + if err := stream.Send(&proto.Workflow{Id: jobID, Cancel: true}); err != nil { + return err + } + } + case <-ctx.Done(): + return nil + case <-ticker.C: + s.agentManager.UpdateHeartbeat(agentID) + jobCtx, cancel := context.WithTimeout(ctx, time.Second) + job, err := s.jobService.GetNextJob(jobCtx) + cancel() + if err != nil || job == nil { + continue + } + s.trackJobAssignment(agentID, job.ID) + if err := s.jobService.AssignJob(ctx, job.ID, agentID); err != nil { + s.untrackJobAssignment(agentID, job.ID) + continue + } + var config map[string]any + if err := json.Unmarshal([]byte(job.Config), &config); err != nil { + _ = s.jobService.UpdateJobStatus(ctx, job.ID, domain.JobStatusFailure) + s.untrackJobAssignment(agentID, job.ID) + continue + } + image, _ := config["image"].(string) + if image == "" { + image = "alpine" + } + commands := []string{"echo 'No commands specified'"} + if raw, ok := config["commands"].([]any); ok && len(raw) > 0 { + commands = commands[:0] + for _, item := range raw { + if text, ok := item.(string); ok { + commands = append(commands, text) + } + } + if len(commands) == 0 { + commands = []string{"echo 'No commands specified'"} + } + } + payload, _ := json.Marshal(map[string]any{"image": image, "commands": commands, "environment": map[string]string{}}) + if err := stream.Send(&proto.Workflow{Id: job.ID, Timeout: job.TimeLimit, Payload: payload}); err != nil { + _ = s.jobService.UpdateJobStatus(ctx, job.ID, domain.JobStatusPending) + s.untrackJobAssignment(agentID, job.ID) + return err + } + } + } +} + +func (s *Server) SubmitStatus(stream grpcpkg.ClientStreamingServer[proto.StatusUpdate, proto.Empty]) error { + ctx := stream.Context() + agentID, _, ok := s.getAgentIDFromContext(ctx) + if !ok { + return status.Error(codes.Unauthenticated, "invalid or missing token") + } + for { + update, err := stream.Recv() + if err != nil { + return stream.SendAndClose(&proto.Empty{}) + } + switch update.Type { + case 0, 1: + _ = s.jobService.ProcessLog(ctx, update.StepUuid, update.Data) + case 4: + var progress float64 + fmt.Sscanf(string(update.Data), "%f", &progress) + _ = s.jobService.UpdateJobProgress(ctx, update.StepUuid, progress) + case 5: + var stats struct { + CPU float64 `json:"cpu"` + RAM float64 `json:"ram"` + } + if json.Unmarshal(update.Data, &stats) == nil { + s.agentManager.UpdateResources(agentID, stats.CPU, stats.RAM) + if s.onAgentEvent != nil { + s.onAgentEvent("agent_update", s.getAgentWithStats(agentID)) + } + } + _ = s.jobService.PublishSystemResources(ctx, agentID, update.Data) + } + } +} + +func (s *Server) Init(ctx context.Context, req *proto.InitRequest) (*proto.Empty, error) { + if err := s.jobService.UpdateJobStatus(ctx, req.Id, domain.JobStatusRunning); err != nil { + return nil, status.Error(codes.Internal, "failed to update job status") + } + return &proto.Empty{}, nil +} + +func (s *Server) Wait(context.Context, *proto.WaitRequest) (*proto.WaitResponse, error) { + return &proto.WaitResponse{Canceled: false}, nil +} + +func (s *Server) Done(ctx context.Context, req *proto.DoneRequest) (*proto.Empty, error) { + agentID, _, ok := s.getAgentIDFromContext(ctx) + if !ok { + return nil, status.Error(codes.Unauthenticated, "invalid session") + } + jobStatus := domain.JobStatusSuccess + if req.State != nil && req.State.Error != "" { + jobStatus = domain.JobStatusFailure + } + if err := s.jobService.UpdateJobStatus(ctx, req.Id, jobStatus); err != nil { + return nil, status.Error(codes.Internal, "failed to update job status") + } + s.untrackJobAssignment(agentID, req.Id) + return &proto.Empty{}, nil +} + +func (s *Server) Update(context.Context, *proto.UpdateRequest) (*proto.Empty, error) { + return &proto.Empty{}, nil +} + +func (s *Server) Log(ctx context.Context, req *proto.LogRequest) (*proto.Empty, error) { + if _, _, ok := s.getAgentIDFromContext(ctx); !ok { + return nil, status.Error(codes.Unauthenticated, "invalid session") + } + for _, entry := range req.LogEntries { + if entry.StepUuid != "" { + _ = s.jobService.ProcessLog(ctx, entry.StepUuid, entry.Data) + } + } + return &proto.Empty{}, nil +} + +func (s *Server) Extend(context.Context, *proto.ExtendRequest) (*proto.Empty, error) { + return &proto.Empty{}, nil +} + +func (s *Server) RegisterAgent(ctx context.Context, req *proto.RegisterAgentRequest) (*proto.RegisterAgentResponse, error) { + if req.Info == nil { + return nil, status.Error(codes.InvalidArgument, "connection info is required") + } + id, _, ok := s.getAgentIDFromContext(ctx) + if !ok { + return nil, status.Error(codes.Unauthenticated, "invalid session") + } + hostname := "" + if req.Info.CustomLabels != nil { + hostname = req.Info.CustomLabels["hostname"] + } + name := hostname + if name == "" { + name = fmt.Sprintf("agent-%s", id) + } + s.agentManager.Register(id, name, req.Info.Platform, req.Info.Backend, req.Info.Version, req.Info.Capacity) + if s.onAgentEvent != nil { + s.onAgentEvent("agent_update", s.getAgentWithStats(id)) + } + return &proto.RegisterAgentResponse{AgentId: id}, nil +} + +func (s *Server) UnregisterAgent(ctx context.Context, _ *proto.Empty) (*proto.Empty, error) { + agentID, token, ok := s.getAgentIDFromContext(ctx) + if !ok { + return nil, status.Error(codes.Unauthenticated, "invalid session") + } + for _, jobID := range s.getAgentJobs(agentID) { + _ = s.jobService.UpdateJobStatus(ctx, jobID, domain.JobStatusFailure) + s.untrackJobAssignment(agentID, jobID) + } + s.sessions.Delete(token) + s.agentJobs.Delete(agentID) + agent := s.getAgentWithStats(agentID) + s.agentManager.Unregister(agentID) + if s.onAgentEvent != nil { + s.onAgentEvent("agent_update", agent) + } + return &proto.Empty{}, nil +} + +func (s *Server) ReportHealth(ctx context.Context, _ *proto.ReportHealthRequest) (*proto.Empty, error) { + agentID, _, ok := s.getAgentIDFromContext(ctx) + if !ok { + return nil, status.Error(codes.Unauthenticated, "invalid session") + } + s.agentManager.UpdateHeartbeat(agentID) + return &proto.Empty{}, nil +} + +func (s *Server) Auth(ctx context.Context, req *proto.AuthRequest) (*proto.AuthResponse, error) { + if s.agentSecret != "" && req.AgentToken != s.agentSecret { + return nil, status.Error(codes.Unauthenticated, "invalid agent secret") + } + agentID := req.AgentId + if len(agentID) > 6 && agentID[:6] == "agent-" { + agentID = agentID[6:] + } + if agentID == "" { + agentID = generateAgentID() + } + accessToken := generateToken() + s.sessions.Store(accessToken, agentID) + return &proto.AuthResponse{Status: "ok", AgentId: agentID, AccessToken: accessToken}, nil +} + +func (s *Server) trackJobAssignment(agentID, jobID string) { + jobSetInterface, _ := s.agentJobs.LoadOrStore(agentID, &sync.Map{}) + if jobSet, ok := jobSetInterface.(*sync.Map); ok { + jobSet.Store(jobID, true) + } +} + +func (s *Server) untrackJobAssignment(agentID, jobID string) { + if jobSetInterface, ok := s.agentJobs.Load(agentID); ok { + if jobSet, ok := jobSetInterface.(*sync.Map); ok { + jobSet.Delete(jobID) + } + } +} + +func (s *Server) isJobAssigned(agentID, jobID string) bool { + if jobSetInterface, ok := s.agentJobs.Load(agentID); ok { + if jobSet, ok := jobSetInterface.(*sync.Map); ok { + _, found := jobSet.Load(jobID) + return found + } + } + return false +} + +func (s *Server) getAgentJobs(agentID string) []string { + jobs := []string{} + if jobSetInterface, ok := s.agentJobs.Load(agentID); ok { + if jobSet, ok := jobSetInterface.(*sync.Map); ok { + jobSet.Range(func(key, _ any) bool { + if jobID, ok := key.(string); ok { + jobs = append(jobs, jobID) + } + return true + }) + } + } + return jobs +} diff --git a/internal/video/runtime/http.go b/internal/video/runtime/http.go new file mode 100644 index 0000000..38bbed6 --- /dev/null +++ b/internal/video/runtime/http.go @@ -0,0 +1,76 @@ +//go:build ignore +// +build ignore + +package runtime + +import ( + "net/http" + "time" + + "github.com/gin-gonic/gin" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" +) + +func (m *Module) MetricsHandler() gin.HandlerFunc { + return gin.WrapH(promhttp.Handler()) +} + +// HandleLive godoc +// @Summary Liveness health check +// @Description Returns liveness status for the API and render module +// @Tags health +// @Produce json +// @Success 200 {object} map[string]string +// @Failure 503 {object} map[string]string +// @Router /health/live [get] +func (m *Module) HandleLive(c *gin.Context) { + status, code := m.healthService.SimpleHealthCheck(c.Request.Context()) + c.JSON(code, gin.H{"status": status}) +} + +// HandleReady godoc +// @Summary Readiness health check +// @Description Returns readiness status including render gRPC availability flag +// @Tags health +// @Produce json +// @Success 200 {object} map[string]interface{} +// @Failure 503 {object} map[string]interface{} +// @Router /health/ready [get] +func (m *Module) HandleReady(c *gin.Context) { + status, code := m.healthService.SimpleHealthCheck(c.Request.Context()) + c.JSON(code, gin.H{"status": status, "grpc_enabled": m.grpcServer != nil}) +} + +// HandleDetailed godoc +// @Summary Detailed health check +// @Description Returns detailed health state for database, redis, and render dependencies +// @Tags health +// @Produce json +// @Success 200 {object} services.HealthReport +// @Router /health/detailed [get] +func (m *Module) HandleDetailed(c *gin.Context) { + c.JSON(http.StatusOK, m.healthService.CheckHealth(c.Request.Context())) +} + +var ( + httpRequests = prometheus.NewCounterVec(prometheus.CounterOpts{Name: "stream_api_http_requests_total", Help: "Total HTTP requests."}, []string{"method", "path", "status"}) + httpDuration = prometheus.NewHistogramVec(prometheus.HistogramOpts{Name: "stream_api_http_request_duration_seconds", Help: "HTTP request duration."}, []string{"method", "path"}) +) + +func init() { + prometheus.MustRegister(httpRequests, httpDuration) +} + +func MetricsMiddleware() gin.HandlerFunc { + return func(c *gin.Context) { + start := time.Now() + c.Next() + path := c.FullPath() + if path == "" { + path = c.Request.URL.Path + } + httpRequests.WithLabelValues(c.Request.Method, path, http.StatusText(c.Writer.Status())).Inc() + httpDuration.WithLabelValues(c.Request.Method, path).Observe(time.Since(start).Seconds()) + } +} diff --git a/internal/video/runtime/module.go b/internal/video/runtime/module.go new file mode 100644 index 0000000..302cf49 --- /dev/null +++ b/internal/video/runtime/module.go @@ -0,0 +1,73 @@ +package runtime + +import ( + "context" + "fmt" + "net" + + grpcpkg "google.golang.org/grpc" + "gorm.io/gorm" + "stream.api/internal/config" + apprpc "stream.api/internal/rpc/app" + redisadapter "stream.api/internal/video/runtime/adapters/queue/redis" + runtimegrpc "stream.api/internal/video/runtime/grpc" + "stream.api/internal/video/runtime/services" + "stream.api/pkg/cache" + "stream.api/pkg/logger" + "stream.api/pkg/token" +) + +type Module struct { + ctx context.Context + cfg *config.Config + jobService *services.JobService + healthService *services.HealthService + grpcServer *runtimegrpc.Server + mqttPublisher *mqttPublisher + grpcRaw *grpcpkg.Server +} + +func NewModule(ctx context.Context, cfg *config.Config, db *gorm.DB, cacheClient cache.Cache, tokenProvider token.Provider, appLogger logger.Logger) (*Module, error) { + adapter, err := redisadapter.NewAdapter(cfg.Redis.Addr, cfg.Redis.Password, cfg.Redis.DB) + if err != nil { + return nil, err + } + jobService := services.NewJobService(adapter, adapter) + healthService := services.NewHealthService(db, adapter.Client(), cfg.Render.ServiceName) + grpcServer := runtimegrpc.NewServer(jobService, cfg.Render.AgentSecret) + module := &Module{ctx: ctx, cfg: cfg, jobService: jobService, healthService: healthService, grpcServer: grpcServer, grpcRaw: grpcpkg.NewServer()} + if publisher, err := newMQTTPublisher(jobService, grpcServer, appLogger); err != nil { + appLogger.Error("Failed to initialize MQTT publisher", "error", err) + } else { + module.mqttPublisher = publisher + grpcServer.SetAgentEventHandler(func(eventType string, agent *services.AgentWithStats) { + publishAgentEvent(publisher.client, appLogger, eventType, agent) + }) + } + grpcServer.Register(module.grpcRaw) + apprpc.Register(module.grpcRaw, apprpc.NewServices(cacheClient, tokenProvider, db, appLogger, cfg, jobService, grpcServer)) + if module.mqttPublisher != nil { + module.mqttPublisher.start(ctx) + } + return module, nil +} + +func (m *Module) JobService() *services.JobService { return m.jobService } + +func (m *Module) AgentRuntime() *runtimegrpc.Server { return m.grpcServer } + +func (m *Module) GRPCServer() *grpcpkg.Server { return m.grpcRaw } + +func (m *Module) GRPCAddress() string { + return fmt.Sprintf(":%s", m.cfg.Server.GRPCPort) +} + +func (m *Module) ServeGRPC(listener net.Listener) error { + return m.grpcRaw.Serve(listener) +} + +func (m *Module) Shutdown() { + if m.grpcRaw != nil { + m.grpcRaw.GracefulStop() + } +} diff --git a/internal/video/runtime/mqtt_publisher.go b/internal/video/runtime/mqtt_publisher.go new file mode 100644 index 0000000..166a35b --- /dev/null +++ b/internal/video/runtime/mqtt_publisher.go @@ -0,0 +1,206 @@ +package runtime + +import ( + "context" + "encoding/json" + "fmt" + "time" + + mqtt "github.com/eclipse/paho.mqtt.golang" + "stream.api/internal/video/runtime/domain" + "stream.api/internal/video/runtime/services" + "stream.api/pkg/logger" +) + +const ( + defaultMQTTBrokerURL = "tcp://broker.mqtt-dashboard.com:1883" + defaultMQTTPrefix = "picpic" +) + +type mqttPublisher struct { + client mqtt.Client + jobService *services.JobService + agentRT interface{ ListAgentsWithStats() []*services.AgentWithStats } + logger logger.Logger + prefix string +} + +func newMQTTPublisher(jobService *services.JobService, agentRT interface{ ListAgentsWithStats() []*services.AgentWithStats }, appLogger logger.Logger) (*mqttPublisher, error) { + opts := mqtt.NewClientOptions() + opts.AddBroker(defaultMQTTBrokerURL) + opts.SetClientID(fmt.Sprintf("stream-api-%d", time.Now().UnixNano())) + opts.SetKeepAlive(60 * time.Second) + opts.SetPingTimeout(10 * time.Second) + opts.SetAutoReconnect(true) + + client := mqtt.NewClient(opts) + token := client.Connect() + if token.Wait() && token.Error() != nil { + return nil, token.Error() + } + + return &mqttPublisher{ + client: client, + jobService: jobService, + agentRT: agentRT, + logger: appLogger, + prefix: defaultMQTTPrefix, + }, nil +} + +func (p *mqttPublisher) start(ctx context.Context) { + if p == nil || p.jobService == nil { + return + } + go p.consumeLogs(ctx) + go p.consumeJobUpdates(ctx) + go p.consumeResources(ctx) +} + +func (p *mqttPublisher) 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) + return + } + for { + select { + case <-ctx.Done(): + return + case entry, ok := <-ch: + if !ok { + return + } + payload, _ := json.Marshal(entry) + p.publish(fmt.Sprintf("%s/logs/%s", p.prefix, entry.JobID), payload) + } + } +} + +func (p *mqttPublisher) 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) + return + } + for { + select { + case <-ctx.Done(): + return + case msg, ok := <-ch: + if !ok { + return + } + var inner map[string]any + if err := json.Unmarshal([]byte(msg), &inner); err != nil { + continue + } + jobID, _ := inner["job_id"].(string) + eventPayload, _ := json.Marshal(map[string]any{ + "type": "job_update", + "payload": inner, + }) + if jobID != "" { + p.publish(fmt.Sprintf("%s/job/%s", p.prefix, jobID), eventPayload) + } + p.publish(fmt.Sprintf("%s/events", p.prefix), eventPayload) + } + } +} + +func (p *mqttPublisher) consumeResources(ctx context.Context) { + ch, err := p.jobService.SubscribeSystemResources(ctx) + if err != nil { + p.logger.Error("Failed to subscribe resources for MQTT", "error", err) + return + } + for { + select { + case <-ctx.Done(): + return + case entry, ok := <-ch: + if !ok { + return + } + resourcePayload, _ := json.Marshal(map[string]any{ + "type": "resource_update", + "payload": entry, + }) + p.publish(fmt.Sprintf("%s/events", p.prefix), resourcePayload) + if p.agentRT != nil { + for _, agent := range p.agentRT.ListAgentsWithStats() { + if agent == nil || agent.Agent == nil || agent.ID != entry.AgentID { + continue + } + agentPayload, _ := json.Marshal(map[string]any{ + "type": "agent_update", + "payload": mapAgentPayload(agent), + }) + p.publish(fmt.Sprintf("%s/events", p.prefix), agentPayload) + break + } + } + } + } +} + +func (p *mqttPublisher) publish(topic string, payload []byte) { + if p == nil || p.client == nil { + return + } + token := p.client.Publish(topic, 0, false, payload) + token.WaitTimeout(5 * time.Second) +} + +func mapAgentPayload(agent *services.AgentWithStats) map[string]any { + if agent == nil || agent.Agent == nil { + return map[string]any{} + } + return map[string]any{ + "id": agent.ID, + "name": agent.Name, + "platform": agent.Platform, + "backend": agent.Backend, + "version": agent.Version, + "capacity": agent.Capacity, + "status": string(agent.Status), + "cpu": agent.CPU, + "ram": agent.RAM, + "last_heartbeat": agent.LastHeartbeat, + "created_at": agent.CreatedAt, + "updated_at": agent.UpdatedAt, + "active_job_count": agent.ActiveJobCount, + } +} + +func publishAgentEvent(client mqtt.Client, appLogger logger.Logger, eventType string, agent *services.AgentWithStats) { + if client == nil || agent == nil || agent.Agent == nil { + return + } + payload, err := json.Marshal(map[string]any{ + "type": eventType, + "payload": mapAgentPayload(agent), + }) + if err != nil { + appLogger.Error("Failed to marshal agent MQTT event", "error", err) + return + } + token := client.Publish(fmt.Sprintf("%s/events", defaultMQTTPrefix), 0, false, payload) + token.WaitTimeout(5 * time.Second) +} + +func publishResourceEvent(client mqtt.Client, appLogger logger.Logger, entry domain.SystemResource) { + if client == nil { + return + } + payload, err := json.Marshal(map[string]any{ + "type": "resource_update", + "payload": entry, + }) + if err != nil { + appLogger.Error("Failed to marshal resource MQTT event", "error", err) + return + } + token := client.Publish(fmt.Sprintf("%s/events", defaultMQTTPrefix), 0, false, payload) + token.WaitTimeout(5 * time.Second) +} diff --git a/internal/video/runtime/proto/woodpecker.pb.go b/internal/video/runtime/proto/woodpecker.pb.go new file mode 100644 index 0000000..e4e8d76 --- /dev/null +++ b/internal/video/runtime/proto/woodpecker.pb.go @@ -0,0 +1,1556 @@ +// Copyright 2021 Woodpecker Authors +// Copyright 2011 Drone.IO Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc v3.21.12 +// source: proto/woodpecker.proto + +package proto + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type StepState struct { + state protoimpl.MessageState `protogen:"open.v1"` + StepUuid string `protobuf:"bytes,1,opt,name=step_uuid,json=stepUuid,proto3" json:"step_uuid,omitempty"` + Started int64 `protobuf:"varint,2,opt,name=started,proto3" json:"started,omitempty"` + Finished int64 `protobuf:"varint,3,opt,name=finished,proto3" json:"finished,omitempty"` + Exited bool `protobuf:"varint,4,opt,name=exited,proto3" json:"exited,omitempty"` + ExitCode int32 `protobuf:"varint,5,opt,name=exit_code,json=exitCode,proto3" json:"exit_code,omitempty"` + Error string `protobuf:"bytes,6,opt,name=error,proto3" json:"error,omitempty"` + Canceled bool `protobuf:"varint,7,opt,name=canceled,proto3" json:"canceled,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *StepState) Reset() { + *x = StepState{} + mi := &file_proto_woodpecker_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *StepState) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StepState) ProtoMessage() {} + +func (x *StepState) ProtoReflect() protoreflect.Message { + mi := &file_proto_woodpecker_proto_msgTypes[0] + 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 StepState.ProtoReflect.Descriptor instead. +func (*StepState) Descriptor() ([]byte, []int) { + return file_proto_woodpecker_proto_rawDescGZIP(), []int{0} +} + +func (x *StepState) GetStepUuid() string { + if x != nil { + return x.StepUuid + } + return "" +} + +func (x *StepState) GetStarted() int64 { + if x != nil { + return x.Started + } + return 0 +} + +func (x *StepState) GetFinished() int64 { + if x != nil { + return x.Finished + } + return 0 +} + +func (x *StepState) GetExited() bool { + if x != nil { + return x.Exited + } + return false +} + +func (x *StepState) GetExitCode() int32 { + if x != nil { + return x.ExitCode + } + return 0 +} + +func (x *StepState) GetError() string { + if x != nil { + return x.Error + } + return "" +} + +func (x *StepState) GetCanceled() bool { + if x != nil { + return x.Canceled + } + return false +} + +type WorkflowState struct { + state protoimpl.MessageState `protogen:"open.v1"` + Started int64 `protobuf:"varint,1,opt,name=started,proto3" json:"started,omitempty"` + Finished int64 `protobuf:"varint,2,opt,name=finished,proto3" json:"finished,omitempty"` + Error string `protobuf:"bytes,3,opt,name=error,proto3" json:"error,omitempty"` + Canceled bool `protobuf:"varint,4,opt,name=canceled,proto3" json:"canceled,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WorkflowState) Reset() { + *x = WorkflowState{} + mi := &file_proto_woodpecker_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WorkflowState) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WorkflowState) ProtoMessage() {} + +func (x *WorkflowState) ProtoReflect() protoreflect.Message { + mi := &file_proto_woodpecker_proto_msgTypes[1] + 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 WorkflowState.ProtoReflect.Descriptor instead. +func (*WorkflowState) Descriptor() ([]byte, []int) { + return file_proto_woodpecker_proto_rawDescGZIP(), []int{1} +} + +func (x *WorkflowState) GetStarted() int64 { + if x != nil { + return x.Started + } + return 0 +} + +func (x *WorkflowState) GetFinished() int64 { + if x != nil { + return x.Finished + } + return 0 +} + +func (x *WorkflowState) GetError() string { + if x != nil { + return x.Error + } + return "" +} + +func (x *WorkflowState) GetCanceled() bool { + if x != nil { + return x.Canceled + } + return false +} + +type LogEntry struct { + state protoimpl.MessageState `protogen:"open.v1"` + StepUuid string `protobuf:"bytes,1,opt,name=step_uuid,json=stepUuid,proto3" json:"step_uuid,omitempty"` + Time int64 `protobuf:"varint,2,opt,name=time,proto3" json:"time,omitempty"` + Line int32 `protobuf:"varint,3,opt,name=line,proto3" json:"line,omitempty"` + Type int32 `protobuf:"varint,4,opt,name=type,proto3" json:"type,omitempty"` // 0 = stdout, 1 = stderr, 2 = exit-code, 3 = metadata, 4 = progress + Data []byte `protobuf:"bytes,5,opt,name=data,proto3" json:"data,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *LogEntry) Reset() { + *x = LogEntry{} + mi := &file_proto_woodpecker_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *LogEntry) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LogEntry) ProtoMessage() {} + +func (x *LogEntry) ProtoReflect() protoreflect.Message { + mi := &file_proto_woodpecker_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LogEntry.ProtoReflect.Descriptor instead. +func (*LogEntry) Descriptor() ([]byte, []int) { + return file_proto_woodpecker_proto_rawDescGZIP(), []int{2} +} + +func (x *LogEntry) GetStepUuid() string { + if x != nil { + return x.StepUuid + } + return "" +} + +func (x *LogEntry) GetTime() int64 { + if x != nil { + return x.Time + } + return 0 +} + +func (x *LogEntry) GetLine() int32 { + if x != nil { + return x.Line + } + return 0 +} + +func (x *LogEntry) GetType() int32 { + if x != nil { + return x.Type + } + return 0 +} + +func (x *LogEntry) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +type Filter struct { + state protoimpl.MessageState `protogen:"open.v1"` + Labels map[string]string `protobuf:"bytes,1,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Filter) Reset() { + *x = Filter{} + mi := &file_proto_woodpecker_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Filter) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Filter) ProtoMessage() {} + +func (x *Filter) ProtoReflect() protoreflect.Message { + mi := &file_proto_woodpecker_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Filter.ProtoReflect.Descriptor instead. +func (*Filter) Descriptor() ([]byte, []int) { + return file_proto_woodpecker_proto_rawDescGZIP(), []int{3} +} + +func (x *Filter) GetLabels() map[string]string { + if x != nil { + return x.Labels + } + return nil +} + +type Workflow struct { + state protoimpl.MessageState `protogen:"open.v1"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Timeout int64 `protobuf:"varint,2,opt,name=timeout,proto3" json:"timeout,omitempty"` + Payload []byte `protobuf:"bytes,3,opt,name=payload,proto3" json:"payload,omitempty"` + Cancel bool `protobuf:"varint,4,opt,name=cancel,proto3" json:"cancel,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Workflow) Reset() { + *x = Workflow{} + mi := &file_proto_woodpecker_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Workflow) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Workflow) ProtoMessage() {} + +func (x *Workflow) ProtoReflect() protoreflect.Message { + mi := &file_proto_woodpecker_proto_msgTypes[4] + 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 Workflow.ProtoReflect.Descriptor instead. +func (*Workflow) Descriptor() ([]byte, []int) { + return file_proto_woodpecker_proto_rawDescGZIP(), []int{4} +} + +func (x *Workflow) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *Workflow) GetTimeout() int64 { + if x != nil { + return x.Timeout + } + return 0 +} + +func (x *Workflow) GetPayload() []byte { + if x != nil { + return x.Payload + } + return nil +} + +func (x *Workflow) GetCancel() bool { + if x != nil { + return x.Cancel + } + return false +} + +type NextRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Filter *Filter `protobuf:"bytes,1,opt,name=filter,proto3" json:"filter,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *NextRequest) Reset() { + *x = NextRequest{} + mi := &file_proto_woodpecker_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *NextRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NextRequest) ProtoMessage() {} + +func (x *NextRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_woodpecker_proto_msgTypes[5] + 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 NextRequest.ProtoReflect.Descriptor instead. +func (*NextRequest) Descriptor() ([]byte, []int) { + return file_proto_woodpecker_proto_rawDescGZIP(), []int{5} +} + +func (x *NextRequest) GetFilter() *Filter { + if x != nil { + return x.Filter + } + return nil +} + +type InitRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + State *WorkflowState `protobuf:"bytes,2,opt,name=state,proto3" json:"state,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *InitRequest) Reset() { + *x = InitRequest{} + mi := &file_proto_woodpecker_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *InitRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InitRequest) ProtoMessage() {} + +func (x *InitRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_woodpecker_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 InitRequest.ProtoReflect.Descriptor instead. +func (*InitRequest) Descriptor() ([]byte, []int) { + return file_proto_woodpecker_proto_rawDescGZIP(), []int{6} +} + +func (x *InitRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *InitRequest) GetState() *WorkflowState { + if x != nil { + return x.State + } + return nil +} + +type WaitRequest 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 *WaitRequest) Reset() { + *x = WaitRequest{} + mi := &file_proto_woodpecker_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WaitRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WaitRequest) ProtoMessage() {} + +func (x *WaitRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_woodpecker_proto_msgTypes[7] + 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 WaitRequest.ProtoReflect.Descriptor instead. +func (*WaitRequest) Descriptor() ([]byte, []int) { + return file_proto_woodpecker_proto_rawDescGZIP(), []int{7} +} + +func (x *WaitRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type DoneRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + State *WorkflowState `protobuf:"bytes,2,opt,name=state,proto3" json:"state,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DoneRequest) Reset() { + *x = DoneRequest{} + mi := &file_proto_woodpecker_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DoneRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DoneRequest) ProtoMessage() {} + +func (x *DoneRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_woodpecker_proto_msgTypes[8] + 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 DoneRequest.ProtoReflect.Descriptor instead. +func (*DoneRequest) Descriptor() ([]byte, []int) { + return file_proto_woodpecker_proto_rawDescGZIP(), []int{8} +} + +func (x *DoneRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *DoneRequest) GetState() *WorkflowState { + if x != nil { + return x.State + } + return nil +} + +type ExtendRequest 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 *ExtendRequest) Reset() { + *x = ExtendRequest{} + mi := &file_proto_woodpecker_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ExtendRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ExtendRequest) ProtoMessage() {} + +func (x *ExtendRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_woodpecker_proto_msgTypes[9] + 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 ExtendRequest.ProtoReflect.Descriptor instead. +func (*ExtendRequest) Descriptor() ([]byte, []int) { + return file_proto_woodpecker_proto_rawDescGZIP(), []int{9} +} + +func (x *ExtendRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type UpdateRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + State *StepState `protobuf:"bytes,2,opt,name=state,proto3" json:"state,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UpdateRequest) Reset() { + *x = UpdateRequest{} + mi := &file_proto_woodpecker_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdateRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateRequest) ProtoMessage() {} + +func (x *UpdateRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_woodpecker_proto_msgTypes[10] + 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 UpdateRequest.ProtoReflect.Descriptor instead. +func (*UpdateRequest) Descriptor() ([]byte, []int) { + return file_proto_woodpecker_proto_rawDescGZIP(), []int{10} +} + +func (x *UpdateRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *UpdateRequest) GetState() *StepState { + if x != nil { + return x.State + } + return nil +} + +type LogRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + LogEntries []*LogEntry `protobuf:"bytes,1,rep,name=logEntries,proto3" json:"logEntries,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *LogRequest) Reset() { + *x = LogRequest{} + mi := &file_proto_woodpecker_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *LogRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LogRequest) ProtoMessage() {} + +func (x *LogRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_woodpecker_proto_msgTypes[11] + 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 LogRequest.ProtoReflect.Descriptor instead. +func (*LogRequest) Descriptor() ([]byte, []int) { + return file_proto_woodpecker_proto_rawDescGZIP(), []int{11} +} + +func (x *LogRequest) GetLogEntries() []*LogEntry { + if x != nil { + return x.LogEntries + } + return nil +} + +type Empty struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Empty) Reset() { + *x = Empty{} + mi := &file_proto_woodpecker_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Empty) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Empty) ProtoMessage() {} + +func (x *Empty) ProtoReflect() protoreflect.Message { + mi := &file_proto_woodpecker_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 Empty.ProtoReflect.Descriptor instead. +func (*Empty) Descriptor() ([]byte, []int) { + return file_proto_woodpecker_proto_rawDescGZIP(), []int{12} +} + +type ReportHealthRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ReportHealthRequest) Reset() { + *x = ReportHealthRequest{} + mi := &file_proto_woodpecker_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ReportHealthRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReportHealthRequest) ProtoMessage() {} + +func (x *ReportHealthRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_woodpecker_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 ReportHealthRequest.ProtoReflect.Descriptor instead. +func (*ReportHealthRequest) Descriptor() ([]byte, []int) { + return file_proto_woodpecker_proto_rawDescGZIP(), []int{13} +} + +func (x *ReportHealthRequest) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +type AgentInfo struct { + state protoimpl.MessageState `protogen:"open.v1"` + Platform string `protobuf:"bytes,1,opt,name=platform,proto3" json:"platform,omitempty"` + Capacity int32 `protobuf:"varint,2,opt,name=capacity,proto3" json:"capacity,omitempty"` + Backend string `protobuf:"bytes,3,opt,name=backend,proto3" json:"backend,omitempty"` + Version string `protobuf:"bytes,4,opt,name=version,proto3" json:"version,omitempty"` + CustomLabels map[string]string `protobuf:"bytes,5,rep,name=customLabels,proto3" json:"customLabels,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AgentInfo) Reset() { + *x = AgentInfo{} + mi := &file_proto_woodpecker_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AgentInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AgentInfo) ProtoMessage() {} + +func (x *AgentInfo) ProtoReflect() protoreflect.Message { + mi := &file_proto_woodpecker_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 AgentInfo.ProtoReflect.Descriptor instead. +func (*AgentInfo) Descriptor() ([]byte, []int) { + return file_proto_woodpecker_proto_rawDescGZIP(), []int{14} +} + +func (x *AgentInfo) GetPlatform() string { + if x != nil { + return x.Platform + } + return "" +} + +func (x *AgentInfo) GetCapacity() int32 { + if x != nil { + return x.Capacity + } + return 0 +} + +func (x *AgentInfo) GetBackend() string { + if x != nil { + return x.Backend + } + return "" +} + +func (x *AgentInfo) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +func (x *AgentInfo) GetCustomLabels() map[string]string { + if x != nil { + return x.CustomLabels + } + return nil +} + +type RegisterAgentRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Info *AgentInfo `protobuf:"bytes,1,opt,name=info,proto3" json:"info,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RegisterAgentRequest) Reset() { + *x = RegisterAgentRequest{} + mi := &file_proto_woodpecker_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RegisterAgentRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegisterAgentRequest) ProtoMessage() {} + +func (x *RegisterAgentRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_woodpecker_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 RegisterAgentRequest.ProtoReflect.Descriptor instead. +func (*RegisterAgentRequest) Descriptor() ([]byte, []int) { + return file_proto_woodpecker_proto_rawDescGZIP(), []int{15} +} + +func (x *RegisterAgentRequest) GetInfo() *AgentInfo { + if x != nil { + return x.Info + } + return nil +} + +type VersionResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + GrpcVersion int32 `protobuf:"varint,1,opt,name=grpc_version,json=grpcVersion,proto3" json:"grpc_version,omitempty"` + ServerVersion string `protobuf:"bytes,2,opt,name=server_version,json=serverVersion,proto3" json:"server_version,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *VersionResponse) Reset() { + *x = VersionResponse{} + mi := &file_proto_woodpecker_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *VersionResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*VersionResponse) ProtoMessage() {} + +func (x *VersionResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_woodpecker_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 VersionResponse.ProtoReflect.Descriptor instead. +func (*VersionResponse) Descriptor() ([]byte, []int) { + return file_proto_woodpecker_proto_rawDescGZIP(), []int{16} +} + +func (x *VersionResponse) GetGrpcVersion() int32 { + if x != nil { + return x.GrpcVersion + } + return 0 +} + +func (x *VersionResponse) GetServerVersion() string { + if x != nil { + return x.ServerVersion + } + return "" +} + +type NextResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Workflow *Workflow `protobuf:"bytes,1,opt,name=workflow,proto3" json:"workflow,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *NextResponse) Reset() { + *x = NextResponse{} + mi := &file_proto_woodpecker_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *NextResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NextResponse) ProtoMessage() {} + +func (x *NextResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_woodpecker_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 NextResponse.ProtoReflect.Descriptor instead. +func (*NextResponse) Descriptor() ([]byte, []int) { + return file_proto_woodpecker_proto_rawDescGZIP(), []int{17} +} + +func (x *NextResponse) GetWorkflow() *Workflow { + if x != nil { + return x.Workflow + } + return nil +} + +type RegisterAgentResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + AgentId string `protobuf:"bytes,1,opt,name=agent_id,json=agentId,proto3" json:"agent_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RegisterAgentResponse) Reset() { + *x = RegisterAgentResponse{} + mi := &file_proto_woodpecker_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RegisterAgentResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegisterAgentResponse) ProtoMessage() {} + +func (x *RegisterAgentResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_woodpecker_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 RegisterAgentResponse.ProtoReflect.Descriptor instead. +func (*RegisterAgentResponse) Descriptor() ([]byte, []int) { + return file_proto_woodpecker_proto_rawDescGZIP(), []int{18} +} + +func (x *RegisterAgentResponse) GetAgentId() string { + if x != nil { + return x.AgentId + } + return "" +} + +type WaitResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Canceled bool `protobuf:"varint,1,opt,name=canceled,proto3" json:"canceled,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WaitResponse) Reset() { + *x = WaitResponse{} + mi := &file_proto_woodpecker_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WaitResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WaitResponse) ProtoMessage() {} + +func (x *WaitResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_woodpecker_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 WaitResponse.ProtoReflect.Descriptor instead. +func (*WaitResponse) Descriptor() ([]byte, []int) { + return file_proto_woodpecker_proto_rawDescGZIP(), []int{19} +} + +func (x *WaitResponse) GetCanceled() bool { + if x != nil { + return x.Canceled + } + return false +} + +type AuthRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + AgentToken string `protobuf:"bytes,1,opt,name=agent_token,json=agentToken,proto3" json:"agent_token,omitempty"` + AgentId string `protobuf:"bytes,2,opt,name=agent_id,json=agentId,proto3" json:"agent_id,omitempty"` + Hostname string `protobuf:"bytes,3,opt,name=hostname,proto3" json:"hostname,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AuthRequest) Reset() { + *x = AuthRequest{} + mi := &file_proto_woodpecker_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AuthRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AuthRequest) ProtoMessage() {} + +func (x *AuthRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_woodpecker_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 AuthRequest.ProtoReflect.Descriptor instead. +func (*AuthRequest) Descriptor() ([]byte, []int) { + return file_proto_woodpecker_proto_rawDescGZIP(), []int{20} +} + +func (x *AuthRequest) GetAgentToken() string { + if x != nil { + return x.AgentToken + } + return "" +} + +func (x *AuthRequest) GetAgentId() string { + if x != nil { + return x.AgentId + } + return "" +} + +func (x *AuthRequest) GetHostname() string { + if x != nil { + return x.Hostname + } + return "" +} + +type AuthResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` + AgentId string `protobuf:"bytes,2,opt,name=agent_id,json=agentId,proto3" json:"agent_id,omitempty"` + AccessToken string `protobuf:"bytes,3,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AuthResponse) Reset() { + *x = AuthResponse{} + mi := &file_proto_woodpecker_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AuthResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AuthResponse) ProtoMessage() {} + +func (x *AuthResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_woodpecker_proto_msgTypes[21] + 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 AuthResponse.ProtoReflect.Descriptor instead. +func (*AuthResponse) Descriptor() ([]byte, []int) { + return file_proto_woodpecker_proto_rawDescGZIP(), []int{21} +} + +func (x *AuthResponse) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *AuthResponse) GetAgentId() string { + if x != nil { + return x.AgentId + } + return "" +} + +func (x *AuthResponse) GetAccessToken() string { + if x != nil { + return x.AccessToken + } + return "" +} + +type StreamOptions struct { + state protoimpl.MessageState `protogen:"open.v1"` + Filter *Filter `protobuf:"bytes,1,opt,name=filter,proto3" json:"filter,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *StreamOptions) Reset() { + *x = StreamOptions{} + mi := &file_proto_woodpecker_proto_msgTypes[22] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *StreamOptions) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StreamOptions) ProtoMessage() {} + +func (x *StreamOptions) ProtoReflect() protoreflect.Message { + mi := &file_proto_woodpecker_proto_msgTypes[22] + 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 StreamOptions.ProtoReflect.Descriptor instead. +func (*StreamOptions) Descriptor() ([]byte, []int) { + return file_proto_woodpecker_proto_rawDescGZIP(), []int{22} +} + +func (x *StreamOptions) GetFilter() *Filter { + if x != nil { + return x.Filter + } + return nil +} + +type StatusUpdate struct { + state protoimpl.MessageState `protogen:"open.v1"` + StepUuid string `protobuf:"bytes,1,opt,name=step_uuid,json=stepUuid,proto3" json:"step_uuid,omitempty"` + Time int64 `protobuf:"varint,2,opt,name=time,proto3" json:"time,omitempty"` + Type int32 `protobuf:"varint,3,opt,name=type,proto3" json:"type,omitempty"` // 0=stdout, 1=stderr, 2=exit-code, 3=metadata, 4=progress, 5=system-resource + Data []byte `protobuf:"bytes,4,opt,name=data,proto3" json:"data,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *StatusUpdate) Reset() { + *x = StatusUpdate{} + mi := &file_proto_woodpecker_proto_msgTypes[23] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *StatusUpdate) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StatusUpdate) ProtoMessage() {} + +func (x *StatusUpdate) ProtoReflect() protoreflect.Message { + mi := &file_proto_woodpecker_proto_msgTypes[23] + 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 StatusUpdate.ProtoReflect.Descriptor instead. +func (*StatusUpdate) Descriptor() ([]byte, []int) { + return file_proto_woodpecker_proto_rawDescGZIP(), []int{23} +} + +func (x *StatusUpdate) GetStepUuid() string { + if x != nil { + return x.StepUuid + } + return "" +} + +func (x *StatusUpdate) GetTime() int64 { + if x != nil { + return x.Time + } + return 0 +} + +func (x *StatusUpdate) GetType() int32 { + if x != nil { + return x.Type + } + return 0 +} + +func (x *StatusUpdate) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +var File_proto_woodpecker_proto protoreflect.FileDescriptor + +const file_proto_woodpecker_proto_rawDesc = "" + + "\n" + + "\x16proto/woodpecker.proto\x12\x05proto\"\xc5\x01\n" + + "\tStepState\x12\x1b\n" + + "\tstep_uuid\x18\x01 \x01(\tR\bstepUuid\x12\x18\n" + + "\astarted\x18\x02 \x01(\x03R\astarted\x12\x1a\n" + + "\bfinished\x18\x03 \x01(\x03R\bfinished\x12\x16\n" + + "\x06exited\x18\x04 \x01(\bR\x06exited\x12\x1b\n" + + "\texit_code\x18\x05 \x01(\x05R\bexitCode\x12\x14\n" + + "\x05error\x18\x06 \x01(\tR\x05error\x12\x1a\n" + + "\bcanceled\x18\a \x01(\bR\bcanceled\"w\n" + + "\rWorkflowState\x12\x18\n" + + "\astarted\x18\x01 \x01(\x03R\astarted\x12\x1a\n" + + "\bfinished\x18\x02 \x01(\x03R\bfinished\x12\x14\n" + + "\x05error\x18\x03 \x01(\tR\x05error\x12\x1a\n" + + "\bcanceled\x18\x04 \x01(\bR\bcanceled\"w\n" + + "\bLogEntry\x12\x1b\n" + + "\tstep_uuid\x18\x01 \x01(\tR\bstepUuid\x12\x12\n" + + "\x04time\x18\x02 \x01(\x03R\x04time\x12\x12\n" + + "\x04line\x18\x03 \x01(\x05R\x04line\x12\x12\n" + + "\x04type\x18\x04 \x01(\x05R\x04type\x12\x12\n" + + "\x04data\x18\x05 \x01(\fR\x04data\"v\n" + + "\x06Filter\x121\n" + + "\x06labels\x18\x01 \x03(\v2\x19.proto.Filter.LabelsEntryR\x06labels\x1a9\n" + + "\vLabelsEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"f\n" + + "\bWorkflow\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\x12\x18\n" + + "\atimeout\x18\x02 \x01(\x03R\atimeout\x12\x18\n" + + "\apayload\x18\x03 \x01(\fR\apayload\x12\x16\n" + + "\x06cancel\x18\x04 \x01(\bR\x06cancel\"4\n" + + "\vNextRequest\x12%\n" + + "\x06filter\x18\x01 \x01(\v2\r.proto.FilterR\x06filter\"I\n" + + "\vInitRequest\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\x12*\n" + + "\x05state\x18\x02 \x01(\v2\x14.proto.WorkflowStateR\x05state\"\x1d\n" + + "\vWaitRequest\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\"I\n" + + "\vDoneRequest\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\x12*\n" + + "\x05state\x18\x02 \x01(\v2\x14.proto.WorkflowStateR\x05state\"\x1f\n" + + "\rExtendRequest\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\"G\n" + + "\rUpdateRequest\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\x12&\n" + + "\x05state\x18\x02 \x01(\v2\x10.proto.StepStateR\x05state\"=\n" + + "\n" + + "LogRequest\x12/\n" + + "\n" + + "logEntries\x18\x01 \x03(\v2\x0f.proto.LogEntryR\n" + + "logEntries\"\a\n" + + "\x05Empty\"-\n" + + "\x13ReportHealthRequest\x12\x16\n" + + "\x06status\x18\x01 \x01(\tR\x06status\"\x80\x02\n" + + "\tAgentInfo\x12\x1a\n" + + "\bplatform\x18\x01 \x01(\tR\bplatform\x12\x1a\n" + + "\bcapacity\x18\x02 \x01(\x05R\bcapacity\x12\x18\n" + + "\abackend\x18\x03 \x01(\tR\abackend\x12\x18\n" + + "\aversion\x18\x04 \x01(\tR\aversion\x12F\n" + + "\fcustomLabels\x18\x05 \x03(\v2\".proto.AgentInfo.CustomLabelsEntryR\fcustomLabels\x1a?\n" + + "\x11CustomLabelsEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"<\n" + + "\x14RegisterAgentRequest\x12$\n" + + "\x04info\x18\x01 \x01(\v2\x10.proto.AgentInfoR\x04info\"[\n" + + "\x0fVersionResponse\x12!\n" + + "\fgrpc_version\x18\x01 \x01(\x05R\vgrpcVersion\x12%\n" + + "\x0eserver_version\x18\x02 \x01(\tR\rserverVersion\";\n" + + "\fNextResponse\x12+\n" + + "\bworkflow\x18\x01 \x01(\v2\x0f.proto.WorkflowR\bworkflow\"2\n" + + "\x15RegisterAgentResponse\x12\x19\n" + + "\bagent_id\x18\x01 \x01(\tR\aagentId\"*\n" + + "\fWaitResponse\x12\x1a\n" + + "\bcanceled\x18\x01 \x01(\bR\bcanceled\"e\n" + + "\vAuthRequest\x12\x1f\n" + + "\vagent_token\x18\x01 \x01(\tR\n" + + "agentToken\x12\x19\n" + + "\bagent_id\x18\x02 \x01(\tR\aagentId\x12\x1a\n" + + "\bhostname\x18\x03 \x01(\tR\bhostname\"d\n" + + "\fAuthResponse\x12\x16\n" + + "\x06status\x18\x01 \x01(\tR\x06status\x12\x19\n" + + "\bagent_id\x18\x02 \x01(\tR\aagentId\x12!\n" + + "\faccess_token\x18\x03 \x01(\tR\vaccessToken\"6\n" + + "\rStreamOptions\x12%\n" + + "\x06filter\x18\x01 \x01(\v2\r.proto.FilterR\x06filter\"g\n" + + "\fStatusUpdate\x12\x1b\n" + + "\tstep_uuid\x18\x01 \x01(\tR\bstepUuid\x12\x12\n" + + "\x04time\x18\x02 \x01(\x03R\x04time\x12\x12\n" + + "\x04type\x18\x03 \x01(\x05R\x04type\x12\x12\n" + + "\x04data\x18\x04 \x01(\fR\x04data2\xb2\x05\n" + + "\n" + + "Woodpecker\x121\n" + + "\aVersion\x12\f.proto.Empty\x1a\x16.proto.VersionResponse\"\x00\x121\n" + + "\x04Next\x12\x12.proto.NextRequest\x1a\x13.proto.NextResponse\"\x00\x12*\n" + + "\x04Init\x12\x12.proto.InitRequest\x1a\f.proto.Empty\"\x00\x121\n" + + "\x04Wait\x12\x12.proto.WaitRequest\x1a\x13.proto.WaitResponse\"\x00\x12*\n" + + "\x04Done\x12\x12.proto.DoneRequest\x1a\f.proto.Empty\"\x00\x12.\n" + + "\x06Extend\x12\x14.proto.ExtendRequest\x1a\f.proto.Empty\"\x00\x12.\n" + + "\x06Update\x12\x14.proto.UpdateRequest\x1a\f.proto.Empty\"\x00\x12(\n" + + "\x03Log\x12\x11.proto.LogRequest\x1a\f.proto.Empty\"\x00\x12L\n" + + "\rRegisterAgent\x12\x1b.proto.RegisterAgentRequest\x1a\x1c.proto.RegisterAgentResponse\"\x00\x12/\n" + + "\x0fUnregisterAgent\x12\f.proto.Empty\x1a\f.proto.Empty\"\x00\x12:\n" + + "\fReportHealth\x12\x1a.proto.ReportHealthRequest\x1a\f.proto.Empty\"\x00\x127\n" + + "\n" + + "StreamJobs\x12\x14.proto.StreamOptions\x1a\x0f.proto.Workflow\"\x000\x01\x125\n" + + "\fSubmitStatus\x12\x13.proto.StatusUpdate\x1a\f.proto.Empty\"\x00(\x012C\n" + + "\x0eWoodpeckerAuth\x121\n" + + "\x04Auth\x12\x12.proto.AuthRequest\x1a\x13.proto.AuthResponse\"\x00B.Z,go.woodpecker-ci.org/woodpecker/v3/rpc/protob\x06proto3" + +var ( + file_proto_woodpecker_proto_rawDescOnce sync.Once + file_proto_woodpecker_proto_rawDescData []byte +) + +func file_proto_woodpecker_proto_rawDescGZIP() []byte { + file_proto_woodpecker_proto_rawDescOnce.Do(func() { + file_proto_woodpecker_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_proto_woodpecker_proto_rawDesc), len(file_proto_woodpecker_proto_rawDesc))) + }) + return file_proto_woodpecker_proto_rawDescData +} + +var file_proto_woodpecker_proto_msgTypes = make([]protoimpl.MessageInfo, 26) +var file_proto_woodpecker_proto_goTypes = []any{ + (*StepState)(nil), // 0: proto.StepState + (*WorkflowState)(nil), // 1: proto.WorkflowState + (*LogEntry)(nil), // 2: proto.LogEntry + (*Filter)(nil), // 3: proto.Filter + (*Workflow)(nil), // 4: proto.Workflow + (*NextRequest)(nil), // 5: proto.NextRequest + (*InitRequest)(nil), // 6: proto.InitRequest + (*WaitRequest)(nil), // 7: proto.WaitRequest + (*DoneRequest)(nil), // 8: proto.DoneRequest + (*ExtendRequest)(nil), // 9: proto.ExtendRequest + (*UpdateRequest)(nil), // 10: proto.UpdateRequest + (*LogRequest)(nil), // 11: proto.LogRequest + (*Empty)(nil), // 12: proto.Empty + (*ReportHealthRequest)(nil), // 13: proto.ReportHealthRequest + (*AgentInfo)(nil), // 14: proto.AgentInfo + (*RegisterAgentRequest)(nil), // 15: proto.RegisterAgentRequest + (*VersionResponse)(nil), // 16: proto.VersionResponse + (*NextResponse)(nil), // 17: proto.NextResponse + (*RegisterAgentResponse)(nil), // 18: proto.RegisterAgentResponse + (*WaitResponse)(nil), // 19: proto.WaitResponse + (*AuthRequest)(nil), // 20: proto.AuthRequest + (*AuthResponse)(nil), // 21: proto.AuthResponse + (*StreamOptions)(nil), // 22: proto.StreamOptions + (*StatusUpdate)(nil), // 23: proto.StatusUpdate + nil, // 24: proto.Filter.LabelsEntry + nil, // 25: proto.AgentInfo.CustomLabelsEntry +} +var file_proto_woodpecker_proto_depIdxs = []int32{ + 24, // 0: proto.Filter.labels:type_name -> proto.Filter.LabelsEntry + 3, // 1: proto.NextRequest.filter:type_name -> proto.Filter + 1, // 2: proto.InitRequest.state:type_name -> proto.WorkflowState + 1, // 3: proto.DoneRequest.state:type_name -> proto.WorkflowState + 0, // 4: proto.UpdateRequest.state:type_name -> proto.StepState + 2, // 5: proto.LogRequest.logEntries:type_name -> proto.LogEntry + 25, // 6: proto.AgentInfo.customLabels:type_name -> proto.AgentInfo.CustomLabelsEntry + 14, // 7: proto.RegisterAgentRequest.info:type_name -> proto.AgentInfo + 4, // 8: proto.NextResponse.workflow:type_name -> proto.Workflow + 3, // 9: proto.StreamOptions.filter:type_name -> proto.Filter + 12, // 10: proto.Woodpecker.Version:input_type -> proto.Empty + 5, // 11: proto.Woodpecker.Next:input_type -> proto.NextRequest + 6, // 12: proto.Woodpecker.Init:input_type -> proto.InitRequest + 7, // 13: proto.Woodpecker.Wait:input_type -> proto.WaitRequest + 8, // 14: proto.Woodpecker.Done:input_type -> proto.DoneRequest + 9, // 15: proto.Woodpecker.Extend:input_type -> proto.ExtendRequest + 10, // 16: proto.Woodpecker.Update:input_type -> proto.UpdateRequest + 11, // 17: proto.Woodpecker.Log:input_type -> proto.LogRequest + 15, // 18: proto.Woodpecker.RegisterAgent:input_type -> proto.RegisterAgentRequest + 12, // 19: proto.Woodpecker.UnregisterAgent:input_type -> proto.Empty + 13, // 20: proto.Woodpecker.ReportHealth:input_type -> proto.ReportHealthRequest + 22, // 21: proto.Woodpecker.StreamJobs:input_type -> proto.StreamOptions + 23, // 22: proto.Woodpecker.SubmitStatus:input_type -> proto.StatusUpdate + 20, // 23: proto.WoodpeckerAuth.Auth:input_type -> proto.AuthRequest + 16, // 24: proto.Woodpecker.Version:output_type -> proto.VersionResponse + 17, // 25: proto.Woodpecker.Next:output_type -> proto.NextResponse + 12, // 26: proto.Woodpecker.Init:output_type -> proto.Empty + 19, // 27: proto.Woodpecker.Wait:output_type -> proto.WaitResponse + 12, // 28: proto.Woodpecker.Done:output_type -> proto.Empty + 12, // 29: proto.Woodpecker.Extend:output_type -> proto.Empty + 12, // 30: proto.Woodpecker.Update:output_type -> proto.Empty + 12, // 31: proto.Woodpecker.Log:output_type -> proto.Empty + 18, // 32: proto.Woodpecker.RegisterAgent:output_type -> proto.RegisterAgentResponse + 12, // 33: proto.Woodpecker.UnregisterAgent:output_type -> proto.Empty + 12, // 34: proto.Woodpecker.ReportHealth:output_type -> proto.Empty + 4, // 35: proto.Woodpecker.StreamJobs:output_type -> proto.Workflow + 12, // 36: proto.Woodpecker.SubmitStatus:output_type -> proto.Empty + 21, // 37: proto.WoodpeckerAuth.Auth:output_type -> proto.AuthResponse + 24, // [24:38] is the sub-list for method output_type + 10, // [10:24] is the sub-list for method input_type + 10, // [10:10] is the sub-list for extension type_name + 10, // [10:10] is the sub-list for extension extendee + 0, // [0:10] is the sub-list for field type_name +} + +func init() { file_proto_woodpecker_proto_init() } +func file_proto_woodpecker_proto_init() { + if File_proto_woodpecker_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_proto_woodpecker_proto_rawDesc), len(file_proto_woodpecker_proto_rawDesc)), + NumEnums: 0, + NumMessages: 26, + NumExtensions: 0, + NumServices: 2, + }, + GoTypes: file_proto_woodpecker_proto_goTypes, + DependencyIndexes: file_proto_woodpecker_proto_depIdxs, + MessageInfos: file_proto_woodpecker_proto_msgTypes, + }.Build() + File_proto_woodpecker_proto = out.File + file_proto_woodpecker_proto_goTypes = nil + file_proto_woodpecker_proto_depIdxs = nil +} diff --git a/internal/video/runtime/proto/woodpecker_grpc.pb.go b/internal/video/runtime/proto/woodpecker_grpc.pb.go new file mode 100644 index 0000000..b26b66b --- /dev/null +++ b/internal/video/runtime/proto/woodpecker_grpc.pb.go @@ -0,0 +1,697 @@ +// Copyright 2021 Woodpecker Authors +// Copyright 2011 Drone.IO Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.6.1 +// - protoc v3.21.12 +// source: proto/woodpecker.proto + +package proto + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + Woodpecker_Version_FullMethodName = "/proto.Woodpecker/Version" + Woodpecker_Next_FullMethodName = "/proto.Woodpecker/Next" + Woodpecker_Init_FullMethodName = "/proto.Woodpecker/Init" + Woodpecker_Wait_FullMethodName = "/proto.Woodpecker/Wait" + Woodpecker_Done_FullMethodName = "/proto.Woodpecker/Done" + Woodpecker_Extend_FullMethodName = "/proto.Woodpecker/Extend" + Woodpecker_Update_FullMethodName = "/proto.Woodpecker/Update" + Woodpecker_Log_FullMethodName = "/proto.Woodpecker/Log" + Woodpecker_RegisterAgent_FullMethodName = "/proto.Woodpecker/RegisterAgent" + Woodpecker_UnregisterAgent_FullMethodName = "/proto.Woodpecker/UnregisterAgent" + Woodpecker_ReportHealth_FullMethodName = "/proto.Woodpecker/ReportHealth" + Woodpecker_StreamJobs_FullMethodName = "/proto.Woodpecker/StreamJobs" + Woodpecker_SubmitStatus_FullMethodName = "/proto.Woodpecker/SubmitStatus" +) + +// WoodpeckerClient is the client API for Woodpecker 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. +// +// Woodpecker Server Service +type WoodpeckerClient interface { + Version(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*VersionResponse, error) + Next(ctx context.Context, in *NextRequest, opts ...grpc.CallOption) (*NextResponse, error) + Init(ctx context.Context, in *InitRequest, opts ...grpc.CallOption) (*Empty, error) + Wait(ctx context.Context, in *WaitRequest, opts ...grpc.CallOption) (*WaitResponse, error) + Done(ctx context.Context, in *DoneRequest, opts ...grpc.CallOption) (*Empty, error) + Extend(ctx context.Context, in *ExtendRequest, opts ...grpc.CallOption) (*Empty, error) + Update(ctx context.Context, in *UpdateRequest, opts ...grpc.CallOption) (*Empty, error) + Log(ctx context.Context, in *LogRequest, opts ...grpc.CallOption) (*Empty, error) + RegisterAgent(ctx context.Context, in *RegisterAgentRequest, opts ...grpc.CallOption) (*RegisterAgentResponse, error) + UnregisterAgent(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) + ReportHealth(ctx context.Context, in *ReportHealthRequest, opts ...grpc.CallOption) (*Empty, error) + // New Streaming RPCs + StreamJobs(ctx context.Context, in *StreamOptions, opts ...grpc.CallOption) (grpc.ServerStreamingClient[Workflow], error) + SubmitStatus(ctx context.Context, opts ...grpc.CallOption) (grpc.ClientStreamingClient[StatusUpdate, Empty], error) +} + +type woodpeckerClient struct { + cc grpc.ClientConnInterface +} + +func NewWoodpeckerClient(cc grpc.ClientConnInterface) WoodpeckerClient { + return &woodpeckerClient{cc} +} + +func (c *woodpeckerClient) Version(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*VersionResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(VersionResponse) + err := c.cc.Invoke(ctx, Woodpecker_Version_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *woodpeckerClient) Next(ctx context.Context, in *NextRequest, opts ...grpc.CallOption) (*NextResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(NextResponse) + err := c.cc.Invoke(ctx, Woodpecker_Next_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *woodpeckerClient) Init(ctx context.Context, in *InitRequest, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, Woodpecker_Init_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *woodpeckerClient) Wait(ctx context.Context, in *WaitRequest, opts ...grpc.CallOption) (*WaitResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(WaitResponse) + err := c.cc.Invoke(ctx, Woodpecker_Wait_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *woodpeckerClient) Done(ctx context.Context, in *DoneRequest, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, Woodpecker_Done_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *woodpeckerClient) Extend(ctx context.Context, in *ExtendRequest, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, Woodpecker_Extend_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *woodpeckerClient) Update(ctx context.Context, in *UpdateRequest, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, Woodpecker_Update_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *woodpeckerClient) Log(ctx context.Context, in *LogRequest, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, Woodpecker_Log_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *woodpeckerClient) RegisterAgent(ctx context.Context, in *RegisterAgentRequest, opts ...grpc.CallOption) (*RegisterAgentResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(RegisterAgentResponse) + err := c.cc.Invoke(ctx, Woodpecker_RegisterAgent_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *woodpeckerClient) UnregisterAgent(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, Woodpecker_UnregisterAgent_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *woodpeckerClient) ReportHealth(ctx context.Context, in *ReportHealthRequest, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, Woodpecker_ReportHealth_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *woodpeckerClient) StreamJobs(ctx context.Context, in *StreamOptions, opts ...grpc.CallOption) (grpc.ServerStreamingClient[Workflow], error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + stream, err := c.cc.NewStream(ctx, &Woodpecker_ServiceDesc.Streams[0], Woodpecker_StreamJobs_FullMethodName, cOpts...) + if err != nil { + return nil, err + } + x := &grpc.GenericClientStream[StreamOptions, Workflow]{ClientStream: stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type Woodpecker_StreamJobsClient = grpc.ServerStreamingClient[Workflow] + +func (c *woodpeckerClient) SubmitStatus(ctx context.Context, opts ...grpc.CallOption) (grpc.ClientStreamingClient[StatusUpdate, Empty], error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + stream, err := c.cc.NewStream(ctx, &Woodpecker_ServiceDesc.Streams[1], Woodpecker_SubmitStatus_FullMethodName, cOpts...) + if err != nil { + return nil, err + } + x := &grpc.GenericClientStream[StatusUpdate, Empty]{ClientStream: stream} + return x, nil +} + +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type Woodpecker_SubmitStatusClient = grpc.ClientStreamingClient[StatusUpdate, Empty] + +// WoodpeckerServer is the server API for Woodpecker service. +// All implementations must embed UnimplementedWoodpeckerServer +// for forward compatibility. +// +// Woodpecker Server Service +type WoodpeckerServer interface { + Version(context.Context, *Empty) (*VersionResponse, error) + Next(context.Context, *NextRequest) (*NextResponse, error) + Init(context.Context, *InitRequest) (*Empty, error) + Wait(context.Context, *WaitRequest) (*WaitResponse, error) + Done(context.Context, *DoneRequest) (*Empty, error) + Extend(context.Context, *ExtendRequest) (*Empty, error) + Update(context.Context, *UpdateRequest) (*Empty, error) + Log(context.Context, *LogRequest) (*Empty, error) + RegisterAgent(context.Context, *RegisterAgentRequest) (*RegisterAgentResponse, error) + UnregisterAgent(context.Context, *Empty) (*Empty, error) + ReportHealth(context.Context, *ReportHealthRequest) (*Empty, error) + // New Streaming RPCs + StreamJobs(*StreamOptions, grpc.ServerStreamingServer[Workflow]) error + SubmitStatus(grpc.ClientStreamingServer[StatusUpdate, Empty]) error + mustEmbedUnimplementedWoodpeckerServer() +} + +// UnimplementedWoodpeckerServer 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 UnimplementedWoodpeckerServer struct{} + +func (UnimplementedWoodpeckerServer) Version(context.Context, *Empty) (*VersionResponse, error) { + return nil, status.Error(codes.Unimplemented, "method Version not implemented") +} +func (UnimplementedWoodpeckerServer) Next(context.Context, *NextRequest) (*NextResponse, error) { + return nil, status.Error(codes.Unimplemented, "method Next not implemented") +} +func (UnimplementedWoodpeckerServer) Init(context.Context, *InitRequest) (*Empty, error) { + return nil, status.Error(codes.Unimplemented, "method Init not implemented") +} +func (UnimplementedWoodpeckerServer) Wait(context.Context, *WaitRequest) (*WaitResponse, error) { + return nil, status.Error(codes.Unimplemented, "method Wait not implemented") +} +func (UnimplementedWoodpeckerServer) Done(context.Context, *DoneRequest) (*Empty, error) { + return nil, status.Error(codes.Unimplemented, "method Done not implemented") +} +func (UnimplementedWoodpeckerServer) Extend(context.Context, *ExtendRequest) (*Empty, error) { + return nil, status.Error(codes.Unimplemented, "method Extend not implemented") +} +func (UnimplementedWoodpeckerServer) Update(context.Context, *UpdateRequest) (*Empty, error) { + return nil, status.Error(codes.Unimplemented, "method Update not implemented") +} +func (UnimplementedWoodpeckerServer) Log(context.Context, *LogRequest) (*Empty, error) { + return nil, status.Error(codes.Unimplemented, "method Log not implemented") +} +func (UnimplementedWoodpeckerServer) RegisterAgent(context.Context, *RegisterAgentRequest) (*RegisterAgentResponse, error) { + return nil, status.Error(codes.Unimplemented, "method RegisterAgent not implemented") +} +func (UnimplementedWoodpeckerServer) UnregisterAgent(context.Context, *Empty) (*Empty, error) { + return nil, status.Error(codes.Unimplemented, "method UnregisterAgent not implemented") +} +func (UnimplementedWoodpeckerServer) ReportHealth(context.Context, *ReportHealthRequest) (*Empty, error) { + return nil, status.Error(codes.Unimplemented, "method ReportHealth not implemented") +} +func (UnimplementedWoodpeckerServer) StreamJobs(*StreamOptions, grpc.ServerStreamingServer[Workflow]) error { + return status.Error(codes.Unimplemented, "method StreamJobs not implemented") +} +func (UnimplementedWoodpeckerServer) SubmitStatus(grpc.ClientStreamingServer[StatusUpdate, Empty]) error { + return status.Error(codes.Unimplemented, "method SubmitStatus not implemented") +} +func (UnimplementedWoodpeckerServer) mustEmbedUnimplementedWoodpeckerServer() {} +func (UnimplementedWoodpeckerServer) testEmbeddedByValue() {} + +// UnsafeWoodpeckerServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to WoodpeckerServer will +// result in compilation errors. +type UnsafeWoodpeckerServer interface { + mustEmbedUnimplementedWoodpeckerServer() +} + +func RegisterWoodpeckerServer(s grpc.ServiceRegistrar, srv WoodpeckerServer) { + // If the following call panics, it indicates UnimplementedWoodpeckerServer 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(&Woodpecker_ServiceDesc, srv) +} + +func _Woodpecker_Version_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WoodpeckerServer).Version(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Woodpecker_Version_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WoodpeckerServer).Version(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _Woodpecker_Next_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(NextRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WoodpeckerServer).Next(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Woodpecker_Next_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WoodpeckerServer).Next(ctx, req.(*NextRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Woodpecker_Init_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(InitRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WoodpeckerServer).Init(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Woodpecker_Init_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WoodpeckerServer).Init(ctx, req.(*InitRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Woodpecker_Wait_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(WaitRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WoodpeckerServer).Wait(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Woodpecker_Wait_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WoodpeckerServer).Wait(ctx, req.(*WaitRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Woodpecker_Done_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DoneRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WoodpeckerServer).Done(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Woodpecker_Done_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WoodpeckerServer).Done(ctx, req.(*DoneRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Woodpecker_Extend_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ExtendRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WoodpeckerServer).Extend(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Woodpecker_Extend_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WoodpeckerServer).Extend(ctx, req.(*ExtendRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Woodpecker_Update_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WoodpeckerServer).Update(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Woodpecker_Update_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WoodpeckerServer).Update(ctx, req.(*UpdateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Woodpecker_Log_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(LogRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WoodpeckerServer).Log(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Woodpecker_Log_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WoodpeckerServer).Log(ctx, req.(*LogRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Woodpecker_RegisterAgent_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RegisterAgentRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WoodpeckerServer).RegisterAgent(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Woodpecker_RegisterAgent_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WoodpeckerServer).RegisterAgent(ctx, req.(*RegisterAgentRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Woodpecker_UnregisterAgent_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WoodpeckerServer).UnregisterAgent(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Woodpecker_UnregisterAgent_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WoodpeckerServer).UnregisterAgent(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _Woodpecker_ReportHealth_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ReportHealthRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WoodpeckerServer).ReportHealth(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Woodpecker_ReportHealth_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WoodpeckerServer).ReportHealth(ctx, req.(*ReportHealthRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Woodpecker_StreamJobs_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(StreamOptions) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(WoodpeckerServer).StreamJobs(m, &grpc.GenericServerStream[StreamOptions, Workflow]{ServerStream: stream}) +} + +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type Woodpecker_StreamJobsServer = grpc.ServerStreamingServer[Workflow] + +func _Woodpecker_SubmitStatus_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(WoodpeckerServer).SubmitStatus(&grpc.GenericServerStream[StatusUpdate, Empty]{ServerStream: stream}) +} + +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type Woodpecker_SubmitStatusServer = grpc.ClientStreamingServer[StatusUpdate, Empty] + +// Woodpecker_ServiceDesc is the grpc.ServiceDesc for Woodpecker service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Woodpecker_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "proto.Woodpecker", + HandlerType: (*WoodpeckerServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Version", + Handler: _Woodpecker_Version_Handler, + }, + { + MethodName: "Next", + Handler: _Woodpecker_Next_Handler, + }, + { + MethodName: "Init", + Handler: _Woodpecker_Init_Handler, + }, + { + MethodName: "Wait", + Handler: _Woodpecker_Wait_Handler, + }, + { + MethodName: "Done", + Handler: _Woodpecker_Done_Handler, + }, + { + MethodName: "Extend", + Handler: _Woodpecker_Extend_Handler, + }, + { + MethodName: "Update", + Handler: _Woodpecker_Update_Handler, + }, + { + MethodName: "Log", + Handler: _Woodpecker_Log_Handler, + }, + { + MethodName: "RegisterAgent", + Handler: _Woodpecker_RegisterAgent_Handler, + }, + { + MethodName: "UnregisterAgent", + Handler: _Woodpecker_UnregisterAgent_Handler, + }, + { + MethodName: "ReportHealth", + Handler: _Woodpecker_ReportHealth_Handler, + }, + }, + Streams: []grpc.StreamDesc{ + { + StreamName: "StreamJobs", + Handler: _Woodpecker_StreamJobs_Handler, + ServerStreams: true, + }, + { + StreamName: "SubmitStatus", + Handler: _Woodpecker_SubmitStatus_Handler, + ClientStreams: true, + }, + }, + Metadata: "proto/woodpecker.proto", +} + +const ( + WoodpeckerAuth_Auth_FullMethodName = "/proto.WoodpeckerAuth/Auth" +) + +// WoodpeckerAuthClient is the client API for WoodpeckerAuth 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 WoodpeckerAuthClient interface { + Auth(ctx context.Context, in *AuthRequest, opts ...grpc.CallOption) (*AuthResponse, error) +} + +type woodpeckerAuthClient struct { + cc grpc.ClientConnInterface +} + +func NewWoodpeckerAuthClient(cc grpc.ClientConnInterface) WoodpeckerAuthClient { + return &woodpeckerAuthClient{cc} +} + +func (c *woodpeckerAuthClient) Auth(ctx context.Context, in *AuthRequest, opts ...grpc.CallOption) (*AuthResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(AuthResponse) + err := c.cc.Invoke(ctx, WoodpeckerAuth_Auth_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// WoodpeckerAuthServer is the server API for WoodpeckerAuth service. +// All implementations must embed UnimplementedWoodpeckerAuthServer +// for forward compatibility. +type WoodpeckerAuthServer interface { + Auth(context.Context, *AuthRequest) (*AuthResponse, error) + mustEmbedUnimplementedWoodpeckerAuthServer() +} + +// UnimplementedWoodpeckerAuthServer 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 UnimplementedWoodpeckerAuthServer struct{} + +func (UnimplementedWoodpeckerAuthServer) Auth(context.Context, *AuthRequest) (*AuthResponse, error) { + return nil, status.Error(codes.Unimplemented, "method Auth not implemented") +} +func (UnimplementedWoodpeckerAuthServer) mustEmbedUnimplementedWoodpeckerAuthServer() {} +func (UnimplementedWoodpeckerAuthServer) testEmbeddedByValue() {} + +// UnsafeWoodpeckerAuthServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to WoodpeckerAuthServer will +// result in compilation errors. +type UnsafeWoodpeckerAuthServer interface { + mustEmbedUnimplementedWoodpeckerAuthServer() +} + +func RegisterWoodpeckerAuthServer(s grpc.ServiceRegistrar, srv WoodpeckerAuthServer) { + // If the following call panics, it indicates UnimplementedWoodpeckerAuthServer 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(&WoodpeckerAuth_ServiceDesc, srv) +} + +func _WoodpeckerAuth_Auth_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AuthRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WoodpeckerAuthServer).Auth(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: WoodpeckerAuth_Auth_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WoodpeckerAuthServer).Auth(ctx, req.(*AuthRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// WoodpeckerAuth_ServiceDesc is the grpc.ServiceDesc for WoodpeckerAuth service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var WoodpeckerAuth_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "proto.WoodpeckerAuth", + HandlerType: (*WoodpeckerAuthServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Auth", + Handler: _WoodpeckerAuth_Auth_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "proto/woodpecker.proto", +} diff --git a/internal/video/runtime/services/agent_stats.go b/internal/video/runtime/services/agent_stats.go new file mode 100644 index 0000000..28e57ad --- /dev/null +++ b/internal/video/runtime/services/agent_stats.go @@ -0,0 +1,8 @@ +package services + +import "stream.api/internal/video/runtime/domain" + +type AgentWithStats struct { + *domain.Agent + ActiveJobCount int64 `json:"active_job_count"` +} diff --git a/internal/video/runtime/services/health_service.go b/internal/video/runtime/services/health_service.go new file mode 100644 index 0000000..fe462bc --- /dev/null +++ b/internal/video/runtime/services/health_service.go @@ -0,0 +1,103 @@ +package services + +import ( + "context" + "fmt" + "time" + + "github.com/redis/go-redis/v9" + "gorm.io/gorm" +) + +type HealthStatus string + +const ( + HealthStatusHealthy HealthStatus = "healthy" + HealthStatusUnhealthy HealthStatus = "unhealthy" + HealthStatusDegraded HealthStatus = "degraded" +) + +type ComponentHealth struct { + Status HealthStatus `json:"status"` + Message string `json:"message,omitempty"` + Latency string `json:"latency,omitempty"` + CheckedAt time.Time `json:"checked_at"` +} + +type HealthReport struct { + Status HealthStatus `json:"status"` + Version string `json:"version"` + CheckedAt time.Time `json:"checked_at"` + Components map[string]ComponentHealth `json:"components"` +} + +type HealthService struct { + db *gorm.DB + redis *redis.Client + version string +} + +func NewHealthService(db *gorm.DB, redisClient *redis.Client, version string) *HealthService { + if version == "" { + version = "0.0.1" + } + return &HealthService{db: db, redis: redisClient, version: version} +} + +func (s *HealthService) CheckHealth(ctx context.Context) *HealthReport { + report := &HealthReport{Status: HealthStatusHealthy, Version: s.version, CheckedAt: time.Now(), Components: map[string]ComponentHealth{}} + dbHealth := s.checkDatabase(ctx) + report.Components["database"] = dbHealth + if dbHealth.Status != HealthStatusHealthy { + report.Status = HealthStatusUnhealthy + } + redisHealth := s.checkRedis(ctx) + report.Components["redis"] = redisHealth + if redisHealth.Status != HealthStatusHealthy && report.Status == HealthStatusHealthy { + report.Status = HealthStatusDegraded + } + return report +} + +func (s *HealthService) SimpleHealthCheck(ctx context.Context) (string, int) { + report := s.CheckHealth(ctx) + switch report.Status { + case HealthStatusHealthy: + return "ok", 200 + case HealthStatusDegraded: + return "degraded", 200 + default: + return "unhealthy", 503 + } +} + +func (s *HealthService) checkDatabase(ctx context.Context) ComponentHealth { + start := time.Now() + sqlDB, err := s.db.DB() + if err != nil { + return ComponentHealth{Status: HealthStatusUnhealthy, Message: fmt.Sprintf("failed to get database instance: %v", err), CheckedAt: time.Now()} + } + ctx, cancel := context.WithTimeout(ctx, 5*time.Second) + defer cancel() + if err := sqlDB.PingContext(ctx); err != nil { + return ComponentHealth{Status: HealthStatusUnhealthy, Message: fmt.Sprintf("database ping failed: %v", err), Latency: time.Since(start).String(), CheckedAt: time.Now()} + } + var result int + if err := s.db.WithContext(ctx).Raw("SELECT 1").Scan(&result).Error; err != nil { + return ComponentHealth{Status: HealthStatusUnhealthy, Message: fmt.Sprintf("database query failed: %v", err), Latency: time.Since(start).String(), CheckedAt: time.Now()} + } + return ComponentHealth{Status: HealthStatusHealthy, Latency: time.Since(start).String(), CheckedAt: time.Now()} +} + +func (s *HealthService) checkRedis(ctx context.Context) ComponentHealth { + start := time.Now() + if s.redis == nil { + return ComponentHealth{Status: HealthStatusUnhealthy, Message: "redis client not initialized", CheckedAt: time.Now()} + } + ctx, cancel := context.WithTimeout(ctx, 5*time.Second) + defer cancel() + if err := s.redis.Ping(ctx).Err(); err != nil { + return ComponentHealth{Status: HealthStatusUnhealthy, Message: fmt.Sprintf("redis ping failed: %v", err), Latency: time.Since(start).String(), CheckedAt: time.Now()} + } + return ComponentHealth{Status: HealthStatusHealthy, Latency: time.Since(start).String(), CheckedAt: time.Now()} +} diff --git a/internal/video/runtime/services/job_service.go b/internal/video/runtime/services/job_service.go new file mode 100644 index 0000000..686def1 --- /dev/null +++ b/internal/video/runtime/services/job_service.go @@ -0,0 +1,394 @@ +package services + +import ( + "context" + "encoding/json" + "fmt" + "regexp" + "strconv" + "strings" + "time" + + "stream.api/internal/database/model" + "stream.api/internal/database/query" + "stream.api/internal/video/runtime/domain" +) + +type JobQueue interface { + Enqueue(ctx context.Context, job *domain.Job) error + Dequeue(ctx context.Context) (*domain.Job, error) +} + +type LogPubSub interface { + Publish(ctx context.Context, jobID string, logLine string, progress float64) error + PublishResource(ctx context.Context, agentID string, data []byte) error + PublishCancel(ctx context.Context, agentID string, jobID string) error + PublishJobUpdate(ctx context.Context, jobID string, status string) error + Subscribe(ctx context.Context, jobID string) (<-chan domain.LogEntry, error) + SubscribeResources(ctx context.Context) (<-chan domain.SystemResource, error) + SubscribeCancel(ctx context.Context, agentID string) (<-chan string, error) + SubscribeJobUpdates(ctx context.Context) (<-chan string, error) +} + +type JobService struct { + queue JobQueue + pubsub LogPubSub +} + +func NewJobService(queue JobQueue, pubsub LogPubSub) *JobService { + return &JobService{queue: queue, pubsub: pubsub} +} + +type PaginatedJobs struct { + Jobs []*domain.Job `json:"jobs"` + Total int64 `json:"total"` + Offset int `json:"offset"` + Limit int `json:"limit"` + HasMore bool `json:"has_more"` +} + +type jobConfigEnvelope struct { + Image string `json:"image,omitempty"` + Commands []string `json:"commands,omitempty"` + Environment map[string]string `json:"environment,omitempty"` + Name string `json:"name,omitempty"` + UserID string `json:"user_id,omitempty"` + TimeLimit int64 `json:"time_limit,omitempty"` +} + +func strPtr(v string) *string { return &v } +func int64Ptr(v int64) *int64 { return &v } +func boolPtr(v bool) *bool { return &v } +func float64Ptr(v float64) *float64 { return &v } +func int32Ptr(v int32) *int32 { return &v } +func timePtr(v time.Time) *time.Time { return &v } + +func parseJobConfig(raw *string) jobConfigEnvelope { + if raw == nil || strings.TrimSpace(*raw) == "" { + return jobConfigEnvelope{} + } + var cfg jobConfigEnvelope + _ = json.Unmarshal([]byte(*raw), &cfg) + return cfg +} + +func encodeJobConfig(raw []byte, name, userID string, timeLimit int64) string { + cfg := parseJobConfig(strPtr(string(raw))) + if name != "" { + cfg.Name = name + } + if userID != "" { + cfg.UserID = userID + } + if timeLimit > 0 { + cfg.TimeLimit = timeLimit + } + encoded, _ := json.Marshal(cfg) + return string(encoded) +} + +func toDomainJob(job *model.Job) *domain.Job { + if job == nil { + return nil + } + cfg := parseJobConfig(job.Config) + result := &domain.Job{ID: job.ID, Name: cfg.Name, UserID: cfg.UserID, TimeLimit: cfg.TimeLimit} + if job.Status != nil { + result.Status = domain.JobStatus(*job.Status) + } + if job.Priority != nil { + result.Priority = int(*job.Priority) + } + if job.InputURL != nil { + result.InputURL = *job.InputURL + } + if job.OutputURL != nil { + result.OutputURL = *job.OutputURL + } + if job.TotalDuration != nil { + result.TotalDuration = *job.TotalDuration + } + if job.CurrentTime != nil { + result.CurrentTime = *job.CurrentTime + } + if job.Progress != nil { + result.Progress = *job.Progress + } + if job.AgentID != nil { + agentID := strconv.FormatInt(*job.AgentID, 10) + result.AgentID = &agentID + } + if job.Logs != nil { + result.Logs = *job.Logs + } + if job.Config != nil { + result.Config = *job.Config + } + if job.Cancelled != nil { + result.Cancelled = *job.Cancelled + } + if job.RetryCount != nil { + result.RetryCount = int(*job.RetryCount) + } + if job.MaxRetries != nil { + result.MaxRetries = int(*job.MaxRetries) + } + if job.CreatedAt != nil { + result.CreatedAt = *job.CreatedAt + } + if job.UpdatedAt != nil { + result.UpdatedAt = *job.UpdatedAt + } + return result +} + +func (s *JobService) CreateJob(ctx context.Context, userID string, name string, config []byte, priority int, timeLimit int64) (*domain.Job, error) { + status := string(domain.JobStatusPending) + now := time.Now() + job := &model.Job{ + ID: fmt.Sprintf("job-%d", now.UnixNano()), + Status: strPtr(status), + Priority: int64Ptr(int64(priority)), + Config: strPtr(encodeJobConfig(config, name, userID, timeLimit)), + Cancelled: boolPtr(false), + RetryCount: int64Ptr(0), + MaxRetries: int64Ptr(3), + CreatedAt: timePtr(now), + UpdatedAt: timePtr(now), + } + if err := query.Job.WithContext(ctx).Create(job); err != nil { + return nil, err + } + domainJob := toDomainJob(job) + if err := s.queue.Enqueue(ctx, domainJob); err != nil { + return nil, err + } + return domainJob, nil +} + +func (s *JobService) ListJobs(ctx context.Context, offset, limit int) (*PaginatedJobs, error) { + if offset < 0 { + offset = 0 + } + if limit <= 0 || limit > 100 { + limit = 20 + } + jobs, total, err := query.Job.WithContext(ctx).Order(query.Job.CreatedAt.Desc()).FindByPage(offset, limit) + if err != nil { + return nil, err + } + items := make([]*domain.Job, 0, len(jobs)) + for _, job := range jobs { + items = append(items, toDomainJob(job)) + } + return &PaginatedJobs{Jobs: items, Total: total, Offset: offset, Limit: limit, HasMore: offset+len(items) < int(total)}, nil +} + +func (s *JobService) ListJobsByAgent(ctx context.Context, agentID string, offset, limit int) (*PaginatedJobs, error) { + if offset < 0 { + offset = 0 + } + if limit <= 0 || limit > 100 { + limit = 20 + } + agentNumeric, err := strconv.ParseInt(agentID, 10, 64) + if err != nil { + return &PaginatedJobs{Jobs: []*domain.Job{}, Total: 0, Offset: offset, Limit: limit, HasMore: false}, nil + } + q := query.Job.WithContext(ctx).Where(query.Job.AgentID.Eq(agentNumeric)).Order(query.Job.CreatedAt.Desc()) + jobs, total, err := q.FindByPage(offset, limit) + if err != nil { + return nil, err + } + items := make([]*domain.Job, 0, len(jobs)) + for _, job := range jobs { + items = append(items, toDomainJob(job)) + } + return &PaginatedJobs{Jobs: items, Total: total, Offset: offset, Limit: limit, HasMore: offset+len(items) < int(total)}, nil +} + +func (s *JobService) GetJob(ctx context.Context, id string) (*domain.Job, error) { + job, err := query.Job.WithContext(ctx).Where(query.Job.ID.Eq(id)).First() + if err != nil { + return nil, err + } + return toDomainJob(job), nil +} + +func (s *JobService) GetNextJob(ctx context.Context) (*domain.Job, error) { + return s.queue.Dequeue(ctx) +} +func (s *JobService) SubscribeSystemResources(ctx context.Context) (<-chan domain.SystemResource, error) { + return s.pubsub.SubscribeResources(ctx) +} +func (s *JobService) SubscribeJobLogs(ctx context.Context, jobID string) (<-chan domain.LogEntry, error) { + return s.pubsub.Subscribe(ctx, jobID) +} +func (s *JobService) SubscribeCancel(ctx context.Context, agentID string) (<-chan string, error) { + return s.pubsub.SubscribeCancel(ctx, agentID) +} +func (s *JobService) SubscribeJobUpdates(ctx context.Context) (<-chan string, error) { + return s.pubsub.SubscribeJobUpdates(ctx) +} + +func (s *JobService) UpdateJobStatus(ctx context.Context, jobID string, status domain.JobStatus) error { + job, err := query.Job.WithContext(ctx).Where(query.Job.ID.Eq(jobID)).First() + if err != nil { + return err + } + now := time.Now() + job.Status = strPtr(string(status)) + job.UpdatedAt = &now + if err := query.Job.WithContext(ctx).Save(job); err != nil { + return err + } + return s.pubsub.PublishJobUpdate(ctx, jobID, string(status)) +} + +func (s *JobService) AssignJob(ctx context.Context, jobID string, agentID string) error { + job, err := query.Job.WithContext(ctx).Where(query.Job.ID.Eq(jobID)).First() + if err != nil { + return err + } + agentNumeric, err := strconv.ParseInt(agentID, 10, 64) + if err != nil { + return err + } + now := time.Now() + status := string(domain.JobStatusRunning) + job.AgentID = &agentNumeric + job.Status = &status + job.UpdatedAt = &now + if err := query.Job.WithContext(ctx).Save(job); err != nil { + return err + } + return s.pubsub.PublishJobUpdate(ctx, jobID, status) +} + +func (s *JobService) CancelJob(ctx context.Context, jobID string) error { + job, err := query.Job.WithContext(ctx).Where(query.Job.ID.Eq(jobID)).First() + if err != nil { + return fmt.Errorf("job not found: %w", err) + } + currentStatus := "" + if job.Status != nil { + currentStatus = *job.Status + } + if currentStatus != string(domain.JobStatusPending) && currentStatus != string(domain.JobStatusRunning) { + return fmt.Errorf("cannot cancel job with status %s", currentStatus) + } + cancelled := true + status := string(domain.JobStatusCancelled) + now := time.Now() + job.Cancelled = &cancelled + job.Status = &status + job.UpdatedAt = &now + if err := query.Job.WithContext(ctx).Save(job); err != nil { + return err + } + _ = s.pubsub.PublishJobUpdate(ctx, jobID, status) + if job.AgentID != nil { + _ = s.pubsub.PublishCancel(ctx, strconv.FormatInt(*job.AgentID, 10), job.ID) + } + return s.pubsub.Publish(ctx, jobID, "[SYSTEM] Job cancelled by admin", -1) +} + +func (s *JobService) RetryJob(ctx context.Context, jobID string) (*domain.Job, error) { + job, err := query.Job.WithContext(ctx).Where(query.Job.ID.Eq(jobID)).First() + if err != nil { + return nil, fmt.Errorf("job not found: %w", err) + } + currentStatus := "" + if job.Status != nil { + currentStatus = *job.Status + } + if currentStatus != string(domain.JobStatusFailure) && currentStatus != string(domain.JobStatusCancelled) { + return nil, fmt.Errorf("cannot retry job with status %s", currentStatus) + } + currentRetry := int64(0) + if job.RetryCount != nil { + currentRetry = *job.RetryCount + } + maxRetries := int64(3) + if job.MaxRetries != nil { + maxRetries = *job.MaxRetries + } + if currentRetry >= maxRetries { + return nil, fmt.Errorf("max retries (%d) exceeded", maxRetries) + } + pending := string(domain.JobStatusPending) + cancelled := false + progress := 0.0 + now := time.Now() + job.Status = &pending + job.Cancelled = &cancelled + job.RetryCount = int64Ptr(currentRetry + 1) + job.Progress = &progress + job.AgentID = nil + job.UpdatedAt = &now + if err := query.Job.WithContext(ctx).Save(job); err != nil { + return nil, err + } + domainJob := toDomainJob(job) + if err := s.queue.Enqueue(ctx, domainJob); err != nil { + return nil, err + } + return domainJob, nil +} + +func (s *JobService) UpdateJobProgress(ctx context.Context, jobID string, progress float64) error { + job, err := query.Job.WithContext(ctx).Where(query.Job.ID.Eq(jobID)).First() + if err != nil { + return err + } + now := time.Now() + job.Progress = float64Ptr(progress) + job.UpdatedAt = &now + if err := query.Job.WithContext(ctx).Save(job); err != nil { + return err + } + return s.pubsub.Publish(ctx, jobID, "", progress) +} + +func (s *JobService) ProcessLog(ctx context.Context, jobID string, logData []byte) error { + line := string(logData) + re := regexp.MustCompile(`out_time_us=(\d+)`) + matches := re.FindStringSubmatch(line) + var progress float64 + if len(matches) > 1 { + us, _ := strconv.ParseInt(matches[1], 10, 64) + if us > 0 { + progress = float64(us) / 1000000.0 + } + } + job, err := query.Job.WithContext(ctx).Where(query.Job.ID.Eq(jobID)).First() + if err != nil { + return err + } + existingLogs := "" + if job.Logs != nil { + existingLogs = *job.Logs + } + newLog := line + if !strings.HasSuffix(newLog, "\n") { + newLog += "\n" + } + existingLogs += newLog + if len(existingLogs) > 10*1024*1024 { + existingLogs = existingLogs[len(existingLogs)-8*1024*1024:] + } + now := time.Now() + job.Logs = &existingLogs + if progress > 0 { + job.Progress = float64Ptr(progress) + } + job.UpdatedAt = &now + if err := query.Job.WithContext(ctx).Save(job); err != nil { + return err + } + return s.pubsub.Publish(ctx, jobID, line, progress) +} + +func (s *JobService) PublishSystemResources(ctx context.Context, agentID string, data []byte) error { + return s.pubsub.PublishResource(ctx, agentID, data) +} diff --git a/pkg/response/response.go b/pkg/response/response.go index 7edb8d9..ad6b4fe 100644 --- a/pkg/response/response.go +++ b/pkg/response/response.go @@ -1,3 +1,6 @@ +//go:build ignore +// +build ignore + package response import ( diff --git a/proto/app/v1/account.proto b/proto/app/v1/account.proto new file mode 100644 index 0000000..6b5b2c0 --- /dev/null +++ b/proto/app/v1/account.proto @@ -0,0 +1,104 @@ +syntax = "proto3"; + +package stream.app.v1; + +option go_package = "stream.api/internal/gen/proto/app/v1;appv1"; + +import "app/v1/common.proto"; + +service AccountService { + rpc GetMe(GetMeRequest) returns (GetMeResponse); + rpc UpdateMe(UpdateMeRequest) returns (UpdateMeResponse); + rpc DeleteMe(DeleteMeRequest) returns (MessageResponse); + rpc ClearMyData(ClearMyDataRequest) returns (MessageResponse); +} + +service PreferencesService { + rpc GetPreferences(GetPreferencesRequest) returns (GetPreferencesResponse); + rpc UpdatePreferences(UpdatePreferencesRequest) returns (UpdatePreferencesResponse); +} + +service UsageService { + rpc GetUsage(GetUsageRequest) returns (GetUsageResponse); +} + +service NotificationsService { + rpc ListNotifications(ListNotificationsRequest) returns (ListNotificationsResponse); + rpc MarkNotificationRead(MarkNotificationReadRequest) returns (MessageResponse); + rpc MarkAllNotificationsRead(MarkAllNotificationsReadRequest) returns (MessageResponse); + rpc DeleteNotification(DeleteNotificationRequest) returns (MessageResponse); + rpc ClearNotifications(ClearNotificationsRequest) returns (MessageResponse); +} + +message GetMeRequest {} + +message GetMeResponse { + User user = 1; +} + +message UpdateMeRequest { + optional string username = 1; + optional string email = 2; + optional string language = 3; + optional string locale = 4; +} + +message UpdateMeResponse { + User user = 1; +} + +message DeleteMeRequest {} + +message ClearMyDataRequest {} + +message GetPreferencesRequest {} + +message GetPreferencesResponse { + Preferences preferences = 1; +} + +message UpdatePreferencesRequest { + optional bool email_notifications = 1; + optional bool push_notifications = 2; + optional bool marketing_notifications = 3; + optional bool telegram_notifications = 4; + optional bool autoplay = 5; + optional bool loop = 6; + optional bool muted = 7; + optional bool show_controls = 8; + optional bool pip = 9; + optional bool airplay = 10; + optional bool chromecast = 11; + optional string language = 12; + optional string locale = 13; +} + +message UpdatePreferencesResponse { + Preferences preferences = 1; +} + +message GetUsageRequest {} + +message GetUsageResponse { + string user_id = 1; + int64 total_videos = 2; + int64 total_storage = 3; +} + +message ListNotificationsRequest {} + +message ListNotificationsResponse { + repeated Notification notifications = 1; +} + +message MarkNotificationReadRequest { + string id = 1; +} + +message MarkAllNotificationsReadRequest {} + +message DeleteNotificationRequest { + string id = 1; +} + +message ClearNotificationsRequest {} diff --git a/proto/app/v1/admin.proto b/proto/app/v1/admin.proto new file mode 100644 index 0000000..9f4cc12 --- /dev/null +++ b/proto/app/v1/admin.proto @@ -0,0 +1,399 @@ +syntax = "proto3"; + +package stream.app.v1; + +option go_package = "stream.api/internal/gen/proto/app/v1;appv1"; + +import "app/v1/common.proto"; + +service AdminService { + rpc GetAdminDashboard(GetAdminDashboardRequest) returns (GetAdminDashboardResponse); + rpc ListAdminUsers(ListAdminUsersRequest) returns (ListAdminUsersResponse); + rpc GetAdminUser(GetAdminUserRequest) returns (GetAdminUserResponse); + rpc CreateAdminUser(CreateAdminUserRequest) returns (CreateAdminUserResponse); + rpc UpdateAdminUser(UpdateAdminUserRequest) returns (UpdateAdminUserResponse); + rpc UpdateAdminUserRole(UpdateAdminUserRoleRequest) returns (UpdateAdminUserRoleResponse); + rpc DeleteAdminUser(DeleteAdminUserRequest) returns (MessageResponse); + rpc ListAdminVideos(ListAdminVideosRequest) returns (ListAdminVideosResponse); + rpc GetAdminVideo(GetAdminVideoRequest) returns (GetAdminVideoResponse); + rpc CreateAdminVideo(CreateAdminVideoRequest) returns (CreateAdminVideoResponse); + rpc UpdateAdminVideo(UpdateAdminVideoRequest) returns (UpdateAdminVideoResponse); + rpc DeleteAdminVideo(DeleteAdminVideoRequest) returns (MessageResponse); + rpc ListAdminPayments(ListAdminPaymentsRequest) returns (ListAdminPaymentsResponse); + rpc GetAdminPayment(GetAdminPaymentRequest) returns (GetAdminPaymentResponse); + rpc CreateAdminPayment(CreateAdminPaymentRequest) returns (CreateAdminPaymentResponse); + rpc UpdateAdminPayment(UpdateAdminPaymentRequest) returns (UpdateAdminPaymentResponse); + rpc ListAdminPlans(ListAdminPlansRequest) returns (ListAdminPlansResponse); + rpc CreateAdminPlan(CreateAdminPlanRequest) returns (CreateAdminPlanResponse); + rpc UpdateAdminPlan(UpdateAdminPlanRequest) returns (UpdateAdminPlanResponse); + rpc DeleteAdminPlan(DeleteAdminPlanRequest) returns (DeleteAdminPlanResponse); + rpc ListAdminAdTemplates(ListAdminAdTemplatesRequest) returns (ListAdminAdTemplatesResponse); + rpc GetAdminAdTemplate(GetAdminAdTemplateRequest) returns (GetAdminAdTemplateResponse); + rpc CreateAdminAdTemplate(CreateAdminAdTemplateRequest) returns (CreateAdminAdTemplateResponse); + rpc UpdateAdminAdTemplate(UpdateAdminAdTemplateRequest) returns (UpdateAdminAdTemplateResponse); + rpc DeleteAdminAdTemplate(DeleteAdminAdTemplateRequest) returns (MessageResponse); + rpc ListAdminJobs(ListAdminJobsRequest) returns (ListAdminJobsResponse); + rpc GetAdminJob(GetAdminJobRequest) returns (GetAdminJobResponse); + rpc GetAdminJobLogs(GetAdminJobLogsRequest) returns (GetAdminJobLogsResponse); + rpc CreateAdminJob(CreateAdminJobRequest) returns (CreateAdminJobResponse); + rpc CancelAdminJob(CancelAdminJobRequest) returns (CancelAdminJobResponse); + rpc RetryAdminJob(RetryAdminJobRequest) returns (RetryAdminJobResponse); + rpc ListAdminAgents(ListAdminAgentsRequest) returns (ListAdminAgentsResponse); + rpc RestartAdminAgent(RestartAdminAgentRequest) returns (AdminAgentCommandResponse); + rpc UpdateAdminAgent(UpdateAdminAgentRequest) returns (AdminAgentCommandResponse); +} + +message GetAdminDashboardRequest {} + +message GetAdminDashboardResponse { + AdminDashboard dashboard = 1; +} + +message ListAdminUsersRequest { + int32 page = 1; + int32 limit = 2; + optional string search = 3; + optional string role = 4; +} + +message ListAdminUsersResponse { + repeated AdminUser users = 1; + int64 total = 2; + int32 page = 3; + int32 limit = 4; +} + +message GetAdminUserRequest { + string id = 1; +} + +message GetAdminUserResponse { + AdminUserDetail user = 1; +} + +message CreateAdminUserRequest { + string email = 1; + optional string username = 2; + string password = 3; + optional string role = 4; + optional string plan_id = 5; +} + +message CreateAdminUserResponse { + AdminUser user = 1; +} + +message UpdateAdminUserRequest { + string id = 1; + optional string email = 2; + optional string username = 3; + optional string password = 4; + optional string role = 5; + optional string plan_id = 6; +} + +message UpdateAdminUserResponse { + AdminUser user = 1; +} + +message UpdateAdminUserRoleRequest { + string id = 1; + string role = 2; +} + +message UpdateAdminUserRoleResponse { + string message = 1; + string role = 2; +} + +message DeleteAdminUserRequest { + string id = 1; +} + +message ListAdminVideosRequest { + int32 page = 1; + int32 limit = 2; + optional string search = 3; + optional string user_id = 4; + optional string status = 5; +} + +message ListAdminVideosResponse { + repeated AdminVideo videos = 1; + int64 total = 2; + int32 page = 3; + int32 limit = 4; +} + +message GetAdminVideoRequest { + string id = 1; +} + +message GetAdminVideoResponse { + AdminVideo video = 1; +} + +message CreateAdminVideoRequest { + string user_id = 1; + string title = 2; + optional string description = 3; + string url = 4; + int64 size = 5; + int32 duration = 6; + optional string format = 7; + optional string status = 8; + optional string ad_template_id = 9; +} + +message CreateAdminVideoResponse { + AdminVideo video = 1; +} + +message UpdateAdminVideoRequest { + string id = 1; + string user_id = 2; + string title = 3; + optional string description = 4; + string url = 5; + int64 size = 6; + int32 duration = 7; + optional string format = 8; + optional string status = 9; + optional string ad_template_id = 10; +} + +message UpdateAdminVideoResponse { + AdminVideo video = 1; +} + +message DeleteAdminVideoRequest { + string id = 1; +} + +message ListAdminPaymentsRequest { + int32 page = 1; + int32 limit = 2; + optional string user_id = 3; + optional string status = 4; +} + +message ListAdminPaymentsResponse { + repeated AdminPayment payments = 1; + int64 total = 2; + int32 page = 3; + int32 limit = 4; +} + +message GetAdminPaymentRequest { + string id = 1; +} + +message GetAdminPaymentResponse { + AdminPayment payment = 1; +} + +message CreateAdminPaymentRequest { + string user_id = 1; + string plan_id = 2; + int32 term_months = 3; + string payment_method = 4; + optional double topup_amount = 5; +} + +message CreateAdminPaymentResponse { + AdminPayment payment = 1; + PlanSubscription subscription = 2; + double wallet_balance = 3; + string invoice_id = 4; +} + +message UpdateAdminPaymentRequest { + string id = 1; + string status = 2; +} + +message UpdateAdminPaymentResponse { + AdminPayment payment = 1; +} + +message ListAdminPlansRequest {} + +message ListAdminPlansResponse { + repeated AdminPlan plans = 1; +} + +message CreateAdminPlanRequest { + string name = 1; + optional string description = 2; + repeated string features = 3; + double price = 4; + string cycle = 5; + int64 storage_limit = 6; + int32 upload_limit = 7; + optional bool is_active = 8; +} + +message CreateAdminPlanResponse { + AdminPlan plan = 1; +} + +message UpdateAdminPlanRequest { + string id = 1; + string name = 2; + optional string description = 3; + repeated string features = 4; + double price = 5; + string cycle = 6; + int64 storage_limit = 7; + int32 upload_limit = 8; + optional bool is_active = 9; +} + +message UpdateAdminPlanResponse { + AdminPlan plan = 1; +} + +message DeleteAdminPlanRequest { + string id = 1; +} + +message DeleteAdminPlanResponse { + string message = 1; + string mode = 2; +} + +message ListAdminAdTemplatesRequest { + int32 page = 1; + int32 limit = 2; + optional string user_id = 3; + optional string search = 4; +} + +message ListAdminAdTemplatesResponse { + repeated AdminAdTemplate templates = 1; + int64 total = 2; + int32 page = 3; + int32 limit = 4; +} + +message GetAdminAdTemplateRequest { + string id = 1; +} + +message GetAdminAdTemplateResponse { + AdminAdTemplate template = 1; +} + +message CreateAdminAdTemplateRequest { + string user_id = 1; + string name = 2; + optional string description = 3; + string vast_tag_url = 4; + optional string ad_format = 5; + optional int64 duration = 6; + optional bool is_active = 7; + optional bool is_default = 8; +} + +message CreateAdminAdTemplateResponse { + AdminAdTemplate template = 1; +} + +message UpdateAdminAdTemplateRequest { + string id = 1; + string user_id = 2; + string name = 3; + optional string description = 4; + string vast_tag_url = 5; + optional string ad_format = 6; + optional int64 duration = 7; + optional bool is_active = 8; + optional bool is_default = 9; +} + +message UpdateAdminAdTemplateResponse { + AdminAdTemplate template = 1; +} + +message DeleteAdminAdTemplateRequest { + string id = 1; +} + +message ListAdminJobsRequest { + int32 offset = 1; + int32 limit = 2; + optional string agent_id = 3; +} + +message ListAdminJobsResponse { + repeated AdminJob jobs = 1; + int64 total = 2; + int32 offset = 3; + int32 limit = 4; + bool has_more = 5; +} + +message GetAdminJobRequest { + string id = 1; +} + +message GetAdminJobResponse { + AdminJob job = 1; +} + +message GetAdminJobLogsRequest { + string id = 1; +} + +message GetAdminJobLogsResponse { + string logs = 1; +} + +message CreateAdminJobRequest { + string command = 1; + optional string image = 2; + map env = 3; + int32 priority = 4; + optional string user_id = 5; + optional string name = 6; + int64 time_limit = 7; +} + +message CreateAdminJobResponse { + AdminJob job = 1; +} + +message CancelAdminJobRequest { + string id = 1; +} + +message CancelAdminJobResponse { + string status = 1; + string job_id = 2; +} + +message RetryAdminJobRequest { + string id = 1; +} + +message RetryAdminJobResponse { + AdminJob job = 1; +} + +message ListAdminAgentsRequest {} + +message ListAdminAgentsResponse { + repeated AdminAgent agents = 1; +} + +message RestartAdminAgentRequest { + string id = 1; +} + +message UpdateAdminAgentRequest { + string id = 1; +} + +message AdminAgentCommandResponse { + string status = 1; +} diff --git a/proto/app/v1/auth.proto b/proto/app/v1/auth.proto new file mode 100644 index 0000000..9aaa9f8 --- /dev/null +++ b/proto/app/v1/auth.proto @@ -0,0 +1,67 @@ +syntax = "proto3"; + +package stream.app.v1; + +option go_package = "stream.api/internal/gen/proto/app/v1;appv1"; + +import "app/v1/common.proto"; + +service AuthService { + rpc Login(LoginRequest) returns (LoginResponse); + rpc Register(RegisterRequest) returns (RegisterResponse); + rpc Logout(LogoutRequest) returns (MessageResponse); + rpc ChangePassword(ChangePasswordRequest) returns (MessageResponse); + rpc ForgotPassword(ForgotPasswordRequest) returns (MessageResponse); + rpc ResetPassword(ResetPasswordRequest) returns (MessageResponse); + rpc GetGoogleLoginUrl(GetGoogleLoginUrlRequest) returns (GetGoogleLoginUrlResponse); + rpc CompleteGoogleLogin(CompleteGoogleLoginRequest) returns (CompleteGoogleLoginResponse); +} + +message LoginRequest { + string email = 1; + string password = 2; +} + +message LoginResponse { + User user = 1; +} + +message RegisterRequest { + string username = 1; + string email = 2; + string password = 3; +} + +message RegisterResponse { + User user = 1; +} + +message LogoutRequest {} + +message ChangePasswordRequest { + string current_password = 1; + string new_password = 2; +} + +message ForgotPasswordRequest { + string email = 1; +} + +message ResetPasswordRequest { + string token = 1; + string new_password = 2; +} + +message GetGoogleLoginUrlRequest {} + +message GetGoogleLoginUrlResponse { + string url = 1; +} + +message CompleteGoogleLoginRequest { + string code = 1; +} + +message CompleteGoogleLoginResponse { + User user = 1; +} diff --git a/proto/app/v1/catalog.proto b/proto/app/v1/catalog.proto new file mode 100644 index 0000000..356deed --- /dev/null +++ b/proto/app/v1/catalog.proto @@ -0,0 +1,87 @@ +syntax = "proto3"; + +package stream.app.v1; + +option go_package = "stream.api/internal/gen/proto/app/v1;appv1"; + +import "app/v1/common.proto"; + +service DomainsService { + rpc ListDomains(ListDomainsRequest) returns (ListDomainsResponse); + rpc CreateDomain(CreateDomainRequest) returns (CreateDomainResponse); + rpc DeleteDomain(DeleteDomainRequest) returns (MessageResponse); +} + +service AdTemplatesService { + rpc ListAdTemplates(ListAdTemplatesRequest) returns (ListAdTemplatesResponse); + rpc CreateAdTemplate(CreateAdTemplateRequest) returns (CreateAdTemplateResponse); + rpc UpdateAdTemplate(UpdateAdTemplateRequest) returns (UpdateAdTemplateResponse); + rpc DeleteAdTemplate(DeleteAdTemplateRequest) returns (MessageResponse); +} + +service PlansService { + rpc ListPlans(ListPlansRequest) returns (ListPlansResponse); +} + +message ListDomainsRequest {} + +message ListDomainsResponse { + repeated Domain domains = 1; +} + +message CreateDomainRequest { + string name = 1; +} + +message CreateDomainResponse { + Domain domain = 1; +} + +message DeleteDomainRequest { + string id = 1; +} + +message ListAdTemplatesRequest {} + +message ListAdTemplatesResponse { + repeated AdTemplate templates = 1; +} + +message CreateAdTemplateRequest { + string name = 1; + optional string description = 2; + string vast_tag_url = 3; + optional string ad_format = 4; + optional int32 duration = 5; + optional bool is_active = 6; + optional bool is_default = 7; +} + +message CreateAdTemplateResponse { + AdTemplate template = 1; +} + +message UpdateAdTemplateRequest { + string id = 1; + string name = 2; + optional string description = 3; + string vast_tag_url = 4; + optional string ad_format = 5; + optional int32 duration = 6; + optional bool is_active = 7; + optional bool is_default = 8; +} + +message UpdateAdTemplateResponse { + AdTemplate template = 1; +} + +message DeleteAdTemplateRequest { + string id = 1; +} + +message ListPlansRequest {} + +message ListPlansResponse { + repeated Plan plans = 1; +} diff --git a/proto/app/v1/common.proto b/proto/app/v1/common.proto new file mode 100644 index 0000000..1ffa13a --- /dev/null +++ b/proto/app/v1/common.proto @@ -0,0 +1,306 @@ +syntax = "proto3"; + +package stream.app.v1; + +option go_package = "stream.api/internal/gen/proto/app/v1;appv1"; + +import "google/protobuf/timestamp.proto"; + +message MessageResponse { + string message = 1; +} + +message User { + string id = 1; + string email = 2; + optional string username = 3; + optional string avatar = 4; + optional string role = 5; + optional string google_id = 6; + int64 storage_used = 7; + optional string plan_id = 8; + google.protobuf.Timestamp plan_started_at = 9; + google.protobuf.Timestamp plan_expires_at = 10; + optional int32 plan_term_months = 11; + optional string plan_payment_method = 12; + bool plan_expiring_soon = 13; + double wallet_balance = 14; + string language = 15; + string locale = 16; + google.protobuf.Timestamp created_at = 17; + google.protobuf.Timestamp updated_at = 18; +} + +message Preferences { + bool email_notifications = 1; + bool push_notifications = 2; + bool marketing_notifications = 3; + bool telegram_notifications = 4; + bool autoplay = 5; + bool loop = 6; + bool muted = 7; + bool show_controls = 8; + bool pip = 9; + bool airplay = 10; + bool chromecast = 11; + string language = 12; + string locale = 13; +} + +message Notification { + string id = 1; + string type = 2; + string title = 3; + string message = 4; + bool read = 5; + optional string action_url = 6; + optional string action_label = 7; + google.protobuf.Timestamp created_at = 8; +} + +message Domain { + string id = 1; + string name = 2; + google.protobuf.Timestamp created_at = 3; + google.protobuf.Timestamp updated_at = 4; +} + +message AdTemplate { + string id = 1; + string name = 2; + optional string description = 3; + string vast_tag_url = 4; + string ad_format = 5; + optional int32 duration = 6; + bool is_active = 7; + bool is_default = 8; + google.protobuf.Timestamp created_at = 9; + google.protobuf.Timestamp updated_at = 10; +} + +message Plan { + string id = 1; + string name = 2; + optional string description = 3; + double price = 4; + string cycle = 5; + int64 storage_limit = 6; + int32 upload_limit = 7; + int32 duration_limit = 8; + string quality_limit = 9; + repeated string features = 10; + bool is_active = 11; +} + +message Payment { + string id = 1; + string user_id = 2; + optional string plan_id = 3; + double amount = 4; + string currency = 5; + string status = 6; + string provider = 7; + optional string transaction_id = 8; + google.protobuf.Timestamp created_at = 9; + google.protobuf.Timestamp updated_at = 10; +} + +message PlanSubscription { + string id = 1; + string user_id = 2; + string payment_id = 3; + string plan_id = 4; + int32 term_months = 5; + string payment_method = 6; + double wallet_amount = 7; + double topup_amount = 8; + google.protobuf.Timestamp started_at = 9; + google.protobuf.Timestamp expires_at = 10; + google.protobuf.Timestamp created_at = 11; + google.protobuf.Timestamp updated_at = 12; +} + +message WalletTransaction { + string id = 1; + string user_id = 2; + string type = 3; + double amount = 4; + string currency = 5; + optional string note = 6; + optional string payment_id = 7; + optional string plan_id = 8; + optional int32 term_months = 9; + google.protobuf.Timestamp created_at = 10; + google.protobuf.Timestamp updated_at = 11; +} + +message PaymentHistoryItem { + string id = 1; + double amount = 2; + string currency = 3; + string status = 4; + optional string plan_id = 5; + optional string plan_name = 6; + string invoice_id = 7; + string kind = 8; + optional int32 term_months = 9; + optional string payment_method = 10; + google.protobuf.Timestamp expires_at = 11; + google.protobuf.Timestamp created_at = 12; +} + +message Video { + string id = 1; + string user_id = 2; + string title = 3; + optional string description = 4; + string url = 5; + string status = 6; + int64 size = 7; + int32 duration = 8; + string format = 9; + optional string thumbnail = 10; + optional string processing_status = 11; + optional string storage_type = 12; + google.protobuf.Timestamp created_at = 13; + google.protobuf.Timestamp updated_at = 14; +} + +message AdminDashboard { + int64 total_users = 1; + int64 total_videos = 2; + int64 total_storage_used = 3; + int64 total_payments = 4; + double total_revenue = 5; + int64 active_subscriptions = 6; + int64 total_ad_templates = 7; + int64 new_users_today = 8; + int64 new_videos_today = 9; +} + +message AdminUser { + string id = 1; + string email = 2; + optional string username = 3; + optional string avatar = 4; + optional string role = 5; + optional string plan_id = 6; + optional string plan_name = 7; + int64 storage_used = 8; + int64 video_count = 9; + double wallet_balance = 10; + google.protobuf.Timestamp created_at = 11; + google.protobuf.Timestamp updated_at = 12; +} + +message AdminUserDetail { + AdminUser user = 1; + optional PlanSubscription subscription = 2; +} + +message AdminVideo { + string id = 1; + string user_id = 2; + string title = 3; + optional string description = 4; + string url = 5; + string status = 6; + int64 size = 7; + int32 duration = 8; + string format = 9; + optional string owner_email = 10; + optional string ad_template_id = 11; + optional string ad_template_name = 12; + google.protobuf.Timestamp created_at = 13; + google.protobuf.Timestamp updated_at = 14; +} + +message AdminPayment { + string id = 1; + string user_id = 2; + optional string plan_id = 3; + double amount = 4; + string currency = 5; + string status = 6; + string provider = 7; + optional string transaction_id = 8; + optional string user_email = 9; + optional string plan_name = 10; + string invoice_id = 11; + optional int32 term_months = 12; + optional string payment_method = 13; + optional string expires_at = 14; + optional double wallet_amount = 15; + optional double topup_amount = 16; + google.protobuf.Timestamp created_at = 17; + google.protobuf.Timestamp updated_at = 18; +} + +message AdminPlan { + string id = 1; + string name = 2; + optional string description = 3; + repeated string features = 4; + double price = 5; + string cycle = 6; + int64 storage_limit = 7; + int32 upload_limit = 8; + int32 duration_limit = 9; + string quality_limit = 10; + bool is_active = 11; + int64 user_count = 12; + int64 payment_count = 13; + int64 subscription_count = 14; +} + +message AdminAdTemplate { + string id = 1; + string user_id = 2; + string name = 3; + optional string description = 4; + string vast_tag_url = 5; + string ad_format = 6; + optional int64 duration = 7; + bool is_active = 8; + bool is_default = 9; + optional string owner_email = 10; + google.protobuf.Timestamp created_at = 11; + google.protobuf.Timestamp updated_at = 12; +} + +message AdminJob { + string id = 1; + string status = 2; + int32 priority = 3; + string user_id = 4; + string name = 5; + int64 time_limit = 6; + string input_url = 7; + string output_url = 8; + int64 total_duration = 9; + int64 current_time = 10; + double progress = 11; + optional string agent_id = 12; + string logs = 13; + string config = 14; + bool cancelled = 15; + int32 retry_count = 16; + int32 max_retries = 17; + google.protobuf.Timestamp created_at = 18; + google.protobuf.Timestamp updated_at = 19; +} + +message AdminAgent { + string id = 1; + string name = 2; + string platform = 3; + string backend = 4; + string version = 5; + int32 capacity = 6; + string status = 7; + double cpu = 8; + double ram = 9; + google.protobuf.Timestamp last_heartbeat = 10; + google.protobuf.Timestamp created_at = 11; + google.protobuf.Timestamp updated_at = 12; +} diff --git a/proto/app/v1/payments.proto b/proto/app/v1/payments.proto new file mode 100644 index 0000000..7985411 --- /dev/null +++ b/proto/app/v1/payments.proto @@ -0,0 +1,55 @@ +syntax = "proto3"; + +package stream.app.v1; + +option go_package = "stream.api/internal/gen/proto/app/v1;appv1"; + +import "app/v1/common.proto"; + +service PaymentsService { + rpc CreatePayment(CreatePaymentRequest) returns (CreatePaymentResponse); + rpc ListPaymentHistory(ListPaymentHistoryRequest) returns (ListPaymentHistoryResponse); + rpc TopupWallet(TopupWalletRequest) returns (TopupWalletResponse); + rpc DownloadInvoice(DownloadInvoiceRequest) returns (DownloadInvoiceResponse); +} + +message CreatePaymentRequest { + string plan_id = 1; + int32 term_months = 2; + string payment_method = 3; + optional double topup_amount = 4; +} + +message CreatePaymentResponse { + Payment payment = 1; + PlanSubscription subscription = 2; + double wallet_balance = 3; + string invoice_id = 4; + string message = 5; +} + +message ListPaymentHistoryRequest {} + +message ListPaymentHistoryResponse { + repeated PaymentHistoryItem payments = 1; +} + +message TopupWalletRequest { + double amount = 1; +} + +message TopupWalletResponse { + WalletTransaction wallet_transaction = 1; + double wallet_balance = 2; + string invoice_id = 3; +} + +message DownloadInvoiceRequest { + string id = 1; +} + +message DownloadInvoiceResponse { + string filename = 1; + string content_type = 2; + string content = 3; +} diff --git a/proto/app/v1/videos.proto b/proto/app/v1/videos.proto new file mode 100644 index 0000000..c03ce52 --- /dev/null +++ b/proto/app/v1/videos.proto @@ -0,0 +1,80 @@ +syntax = "proto3"; + +package stream.app.v1; + +option go_package = "stream.api/internal/gen/proto/app/v1;appv1"; + +import "app/v1/common.proto"; + +service VideosService { + rpc GetUploadUrl(GetUploadUrlRequest) returns (GetUploadUrlResponse); + rpc CreateVideo(CreateVideoRequest) returns (CreateVideoResponse); + rpc ListVideos(ListVideosRequest) returns (ListVideosResponse); + rpc GetVideo(GetVideoRequest) returns (GetVideoResponse); + rpc UpdateVideo(UpdateVideoRequest) returns (UpdateVideoResponse); + rpc DeleteVideo(DeleteVideoRequest) returns (MessageResponse); +} + +message GetUploadUrlRequest { + string filename = 1; +} + +message GetUploadUrlResponse { + string upload_url = 1; + string key = 2; + string file_id = 3; +} + +message CreateVideoRequest { + string title = 1; + optional string description = 2; + string url = 3; + int64 size = 4; + int32 duration = 5; + optional string format = 6; +} + +message CreateVideoResponse { + Video video = 1; +} + +message ListVideosRequest { + int32 page = 1; + int32 limit = 2; + optional string search = 3; + optional string status = 4; +} + +message ListVideosResponse { + repeated Video videos = 1; + int64 total = 2; + int32 page = 3; + int32 limit = 4; +} + +message GetVideoRequest { + string id = 1; +} + +message GetVideoResponse { + Video video = 1; +} + +message UpdateVideoRequest { + string id = 1; + string title = 2; + optional string description = 3; + string url = 4; + int64 size = 5; + int32 duration = 6; + optional string format = 7; + optional string status = 8; +} + +message UpdateVideoResponse { + Video video = 1; +} + +message DeleteVideoRequest { + string id = 1; +}