implement remote debug api

This commit is contained in:
aliamerj
2025-08-20 19:50:15 +03:00
parent 9685411246
commit f4757165c1
9 changed files with 514 additions and 16 deletions

View File

@@ -0,0 +1,174 @@
package types
import (
"encoding/json"
"fmt"
"time"
"github.com/google/uuid"
)
type JobStatus string
const (
JobStatusPending JobStatus = "pending"
JobStatusSucceeded JobStatus = "succeeded"
JobStatusFailed JobStatus = "failed"
)
type JobType string
const (
JobTypeBundle JobType = "bundle"
// add more job types here
)
type Job struct {
// ID is the primary identifier
ID string `gorm:"primaryKey"`
// CreatedAt when job was created (UTC)
CreatedAt time.Time `gorm:"autoCreateTime"`
// CompletedAt when job finished, null if still running
CompletedAt *time.Time
// TriggeredBy user that triggered this job
TriggeredBy string `gorm:"index"`
PeerID string `gorm:"index"`
AccountID string `gorm:"index"`
// Type of the job, e.g. "bundle"
Type JobType `gorm:"index;type:varchar(50)"`
// Status of the job: pending, succeeded, failed
Status JobStatus `gorm:"index;type:varchar(50)"`
// FailedReason describes why the job failed (if failed)
FailedReason string
// Result can contain job output (JSON, URL, etc.)
Result string
// Parameters is a JSON blob storing job configuration (untyped)
Parameters json.RawMessage `gorm:"type:json"`
}
// JobParametersBundle represents parameters for bundle/debug jobs
type JobParametersBundle struct {
BundleFor bool `json:"bundle_for"`
BundleForTime int `json:"bundle_for_time"` // minutes
LogFileCount int `json:"log_file_count"`
Anonymize bool `json:"anonymize"`
}
// NewJob creates a new job with default fields and validation
func NewJob(triggeredBy, accountID, peerID string, jobType JobType, parameters map[string]any) (*Job, error) {
job := &Job{
ID: uuid.New().String(),
TriggeredBy: triggeredBy,
PeerID: peerID,
AccountID: accountID,
Type: jobType,
Status: JobStatusPending,
CreatedAt: time.Now().UTC(),
}
// Encode parameters
if err := job.encodeParameters(parameters); err != nil {
return nil, fmt.Errorf("failed to encode job parameters: %w", err)
}
// Validate job
if err := job.ValidateJobRequest(); err != nil {
return nil, err
}
return job, nil
}
// DecodeParameters decodes raw parameters into a target struct
func (j *Job) DecodeParameters(target any) error {
if len(j.Parameters) == 0 {
return nil
}
return json.Unmarshal(j.Parameters, target)
}
// EncodeParameters replaces raw parameters with marshaled JSON
func (j *Job) encodeParameters(params map[string]any) error {
if params == nil {
return fmt.Errorf("parameters cannot be empty")
}
data, err := json.Marshal(params)
if err != nil {
return err
}
j.Parameters = data
return nil
}
// IsPending returns true if job is pending
func (j *Job) IsPending() bool {
return j.Status == JobStatusPending
}
// MarkSucceeded sets job as completed successfully
func (j *Job) MarkSucceeded(result string) {
now := time.Now().UTC()
j.Status = JobStatusSucceeded
j.Result = result
j.CompletedAt = &now
}
// MarkFailed sets job as failed with reason
func (j *Job) MarkFailed(reason string) {
now := time.Now().UTC()
j.Status = JobStatusFailed
j.FailedReason = reason
j.CompletedAt = &now
}
// HasCompleted checks if job is completed (success or fail)
func (j *Job) HasCompleted() bool {
return j.Status == JobStatusSucceeded || j.Status == JobStatusFailed
}
func (j *Job) ValidateJobRequest() error {
if j == nil {
return fmt.Errorf("job cannot be nil")
}
if j.Type == "" {
return fmt.Errorf("job type must be specified")
}
if len(j.Parameters) == 0 {
return fmt.Errorf("job parameters must be provided")
}
switch j.Type {
case JobTypeBundle:
var params JobParametersBundle
if err := json.Unmarshal(j.Parameters, &params); err != nil {
return fmt.Errorf("invalid parameters for bundle job: %w", err)
}
// validate bundle_for_time <= 5 minutes
if params.BundleForTime < 0 || params.BundleForTime > 5 {
return fmt.Errorf("bundle_for_time must be between 0 and 5, got %d", params.BundleForTime)
}
// validate log-file-count ≥ 1 and ≤ 1000
if params.LogFileCount < 1 || params.LogFileCount > 1000 {
return fmt.Errorf("log-file-count must be between 1 and 1000, got %d", params.LogFileCount)
}
default:
return fmt.Errorf("unsupported job type: %s", j.Type)
}
return nil
}