diff --git a/internal/certificate/service.go b/internal/certificate/service.go index e2bdb0f1..a4558e74 100644 --- a/internal/certificate/service.go +++ b/internal/certificate/service.go @@ -17,21 +17,21 @@ const ( defaultExpireMessage = "有 ${COUNT} 张证书即将过期,域名分别为 ${DOMAINS},请保持关注!" ) -type CertificateRepository interface { - ListExpireSoon(ctx context.Context) ([]domain.Certificate, error) +type certificateRepository interface { + ListExpireSoon(ctx context.Context) ([]*domain.Certificate, error) } -type certificateService struct { - repo CertificateRepository +type CertificateService struct { + repo certificateRepository } -func NewCertificateService(repo CertificateRepository) *certificateService { - return &certificateService{ +func NewCertificateService(repo certificateRepository) *CertificateService { + return &CertificateService{ repo: repo, } } -func (s *certificateService) InitSchedule(ctx context.Context) error { +func (s *CertificateService) InitSchedule(ctx context.Context) error { scheduler := app.GetScheduler() err := scheduler.Add("certificate", "0 0 * * *", func() { certs, err := s.repo.ListExpireSoon(context.Background()) @@ -58,13 +58,11 @@ func (s *certificateService) InitSchedule(ctx context.Context) error { return nil } -type certificateNotification struct { - Subject string `json:"subject"` - Message string `json:"message"` -} - -func buildExpireSoonNotification(records []domain.Certificate) *certificateNotification { - if len(records) == 0 { +func buildExpireSoonNotification(certificates []*domain.Certificate) *struct { + Subject string + Message string +} { + if len(certificates) == 0 { return nil } @@ -85,9 +83,9 @@ func buildExpireSoonNotification(records []domain.Certificate) *certificateNotif } // 替换变量 - count := len(records) + count := len(certificates) domains := make([]string, count) - for i, record := range records { + for i, record := range certificates { domains[i] = record.SubjectAltNames } countStr := strconv.Itoa(count) @@ -98,8 +96,8 @@ func buildExpireSoonNotification(records []domain.Certificate) *certificateNotif message = strings.ReplaceAll(message, "${DOMAINS}", domainStr) // 返回消息 - return &certificateNotification{ - Subject: subject, - Message: message, - } + return &struct { + Subject string + Message string + }{Subject: subject, Message: message} } diff --git a/internal/domain/access.go b/internal/domain/access.go index c9b1d2a9..c7cf08fd 100644 --- a/internal/domain/access.go +++ b/internal/domain/access.go @@ -7,11 +7,11 @@ import ( type Access struct { Meta - Name string `json:"name" db:"name"` - Provider string `json:"provider" db:"provider"` - Config string `json:"config" db:"config"` - Usage string `json:"usage" db:"usage"` - DeletedAt time.Time `json:"deleted" db:"deleted"` + Name string `json:"name" db:"name"` + Provider string `json:"provider" db:"provider"` + Config string `json:"config" db:"config"` + Usage string `json:"usage" db:"usage"` + DeletedAt *time.Time `json:"deleted" db:"deleted"` } func (a *Access) UnmarshalConfigToMap() (map[string]any, error) { diff --git a/internal/domain/certificate.go b/internal/domain/certificate.go index 2fe0c131..84179c3a 100644 --- a/internal/domain/certificate.go +++ b/internal/domain/certificate.go @@ -23,4 +23,5 @@ type Certificate struct { WorkflowId string `json:"workflowId" db:"workflowId"` WorkflowNodeId string `json:"workflowNodeId" db:"workflowNodeId"` WorkflowOutputId string `json:"workflowOutputId" db:"workflowOutputId"` + DeletedAt *time.Time `json:"deleted" db:"deleted"` } diff --git a/internal/domain/err.go b/internal/domain/err.go deleted file mode 100644 index 15d43186..00000000 --- a/internal/domain/err.go +++ /dev/null @@ -1,33 +0,0 @@ -package domain - -var ( - ErrInvalidParams = NewXError(400, "invalid params") - ErrRecordNotFound = NewXError(404, "record not found") -) - -func IsRecordNotFound(err error) bool { - if e, ok := err.(*XError); ok { - return e.GetCode() == ErrRecordNotFound.GetCode() - } - return false -} - -type XError struct { - Code int `json:"code"` - Msg string `json:"msg"` -} - -func NewXError(code int, msg string) *XError { - return &XError{code, msg} -} - -func (e *XError) Error() string { - return e.Msg -} - -func (e *XError) GetCode() int { - if e.Code == 0 { - return 100 - } - return e.Code -} diff --git a/internal/domain/error.go b/internal/domain/error.go new file mode 100644 index 00000000..39dd64b9 --- /dev/null +++ b/internal/domain/error.go @@ -0,0 +1,30 @@ +package domain + +var ( + ErrInvalidParams = NewError(400, "invalid params") + ErrRecordNotFound = NewError(404, "record not found") +) + +type Error struct { + Code int `json:"code"` + Msg string `json:"msg"` +} + +func NewError(code int, msg string) *Error { + if code == 0 { + code = -1 + } + + return &Error{code, msg} +} + +func (e *Error) Error() string { + return e.Msg +} + +func IsRecordNotFoundError(err error) bool { + if e, ok := err.(*Error); ok { + return e.Code == ErrRecordNotFound.Code + } + return false +} diff --git a/internal/notify/service.go b/internal/notify/service.go index 8c99d824..0e414950 100644 --- a/internal/notify/service.go +++ b/internal/notify/service.go @@ -12,15 +12,15 @@ const ( notifyTestBody = "欢迎使用 Certimate ,这是一条测试通知。" ) -type SettingsRepository interface { +type settingsRepository interface { GetByName(ctx context.Context, name string) (*domain.Settings, error) } type NotifyService struct { - settingRepo SettingsRepository + settingRepo settingsRepository } -func NewNotifyService(settingRepo SettingsRepository) *NotifyService { +func NewNotifyService(settingRepo settingsRepository) *NotifyService { return &NotifyService{ settingRepo: settingRepo, } diff --git a/internal/pkg/utils/types/types.go b/internal/pkg/utils/types/types.go new file mode 100644 index 00000000..b88467b1 --- /dev/null +++ b/internal/pkg/utils/types/types.go @@ -0,0 +1,25 @@ +package types + +import "reflect" + +// 判断对象是否为 nil。 +// +// 入参: +// - value:待判断的对象。 +// +// 出参: +// - 如果对象值为 nil,则返回 true;否则返回 false。 +func IsNil(obj any) bool { + if obj == nil { + return true + } + + v := reflect.ValueOf(obj) + if v.Kind() == reflect.Ptr { + return v.IsNil() + } else if v.Kind() == reflect.Interface { + return v.Elem().IsNil() + } + + return false +} diff --git a/internal/repository/access.go b/internal/repository/access.go index 9646dcc9..9612c123 100644 --- a/internal/repository/access.go +++ b/internal/repository/access.go @@ -4,9 +4,12 @@ import ( "context" "database/sql" "errors" + "fmt" + "github.com/pocketbase/pocketbase/models" "github.com/usual2970/certimate/internal/app" "github.com/usual2970/certimate/internal/domain" + "github.com/usual2970/certimate/internal/pkg/utils/types" ) type AccessRepository struct{} @@ -15,7 +18,7 @@ func NewAccessRepository() *AccessRepository { return &AccessRepository{} } -func (a *AccessRepository) GetById(ctx context.Context, id string) (*domain.Access, error) { +func (r *AccessRepository) GetById(ctx context.Context, id string) (*domain.Access, error) { record, err := app.GetApp().Dao().FindRecordById("access", id) if err != nil { if errors.Is(err, sql.ErrNoRows) { @@ -24,6 +27,18 @@ func (a *AccessRepository) GetById(ctx context.Context, id string) (*domain.Acce return nil, err } + if !types.IsNil(record.Get("deleted")) { + return nil, domain.ErrRecordNotFound + } + + return r.castRecordToModel(record) +} + +func (r *AccessRepository) castRecordToModel(record *models.Record) (*domain.Access, error) { + if record == nil { + return nil, fmt.Errorf("record is nil") + } + access := &domain.Access{ Meta: domain.Meta{ Id: record.GetId(), diff --git a/internal/repository/acme_account.go b/internal/repository/acme_account.go index 3289ced4..f5996fe9 100644 --- a/internal/repository/acme_account.go +++ b/internal/repository/acme_account.go @@ -22,7 +22,11 @@ var g singleflight.Group func (r *AcmeAccountRepository) GetByCAAndEmail(ca, email string) (*domain.AcmeAccount, error) { resp, err, _ := g.Do(fmt.Sprintf("acme_account_%s_%s", ca, email), func() (interface{}, error) { - resp, err := app.GetApp().Dao().FindFirstRecordByFilter("acme_accounts", "ca={:ca} && email={:email}", dbx.Params{"ca": ca, "email": email}) + resp, err := app.GetApp().Dao().FindFirstRecordByFilter( + "acme_accounts", + "ca={:ca} && email={:email}", + dbx.Params{"ca": ca, "email": email}, + ) if err != nil { return nil, err } @@ -33,30 +37,15 @@ func (r *AcmeAccountRepository) GetByCAAndEmail(ca, email string) (*domain.AcmeA } if resp == nil { - return nil, fmt.Errorf("acme account not found") + return nil, domain.ErrRecordNotFound } record, ok := resp.(*models.Record) if !ok { - return nil, fmt.Errorf("acme account not found") + return nil, domain.ErrRecordNotFound } - resource := ®istration.Resource{} - if err := record.UnmarshalJSONField("resource", resource); err != nil { - return nil, err - } - - return &domain.AcmeAccount{ - Meta: domain.Meta{ - Id: record.GetId(), - CreatedAt: record.GetCreated().Time(), - UpdatedAt: record.GetUpdated().Time(), - }, - CA: record.GetString("ca"), - Email: record.GetString("email"), - Key: record.GetString("key"), - Resource: resource, - }, nil + return r.castRecordToModel(record) } func (r *AcmeAccountRepository) Save(ca, email, key string, resource *registration.Resource) error { @@ -72,3 +61,27 @@ func (r *AcmeAccountRepository) Save(ca, email, key string, resource *registrati record.Set("resource", resource) return app.GetApp().Dao().Save(record) } + +func (r *AcmeAccountRepository) castRecordToModel(record *models.Record) (*domain.AcmeAccount, error) { + if record == nil { + return nil, fmt.Errorf("record is nil") + } + + resource := ®istration.Resource{} + if err := record.UnmarshalJSONField("resource", resource); err != nil { + return nil, err + } + + acmeAccount := &domain.AcmeAccount{ + Meta: domain.Meta{ + Id: record.GetId(), + CreatedAt: record.GetCreated().Time(), + UpdatedAt: record.GetUpdated().Time(), + }, + CA: record.GetString("ca"), + Email: record.GetString("email"), + Key: record.GetString("key"), + Resource: resource, + } + return acmeAccount, nil +} diff --git a/internal/repository/certificate.go b/internal/repository/certificate.go index 9bce7a9e..1ff31ac0 100644 --- a/internal/repository/certificate.go +++ b/internal/repository/certificate.go @@ -2,9 +2,15 @@ package repository import ( "context" + "database/sql" + "errors" + "fmt" + "github.com/pocketbase/dbx" + "github.com/pocketbase/pocketbase/models" "github.com/usual2970/certimate/internal/app" "github.com/usual2970/certimate/internal/domain" + "github.com/usual2970/certimate/internal/pkg/utils/types" ) type CertificateRepository struct{} @@ -13,14 +19,89 @@ func NewCertificateRepository() *CertificateRepository { return &CertificateRepository{} } -func (c *CertificateRepository) ListExpireSoon(ctx context.Context) ([]domain.Certificate, error) { - certificates := []domain.Certificate{} - err := app.GetApp().Dao().DB(). - NewQuery("SELECT * FROM certificate WHERE expireAt > DATETIME('now') AND expireAt < DATETIME('now', '+20 days')"). - All(&certificates) +func (r *CertificateRepository) ListExpireSoon(ctx context.Context) ([]*domain.Certificate, error) { + records, err := app.GetApp().Dao().FindRecordsByFilter( + "certificate", + "expireAt>DATETIME('now') && expireAt validityDuration { a.AddOutput(ctx, a.node.Name, "已申请过证书,且证书在有效期内") diff --git a/internal/workflow/node-processor/condition_node.go b/internal/workflow/node-processor/condition_node.go index 742b3be9..cd3ab07f 100644 --- a/internal/workflow/node-processor/condition_node.go +++ b/internal/workflow/node-processor/condition_node.go @@ -8,13 +8,13 @@ import ( type conditionNode struct { node *domain.WorkflowNode - *Logger + *nodeLogger } func NewConditionNode(node *domain.WorkflowNode) *conditionNode { return &conditionNode{ - node: node, - Logger: NewLogger(node), + node: node, + nodeLogger: NewNodeLogger(node), } } diff --git a/internal/workflow/node-processor/deploy_node.go b/internal/workflow/node-processor/deploy_node.go index 1623e84c..135e8cfd 100644 --- a/internal/workflow/node-processor/deploy_node.go +++ b/internal/workflow/node-processor/deploy_node.go @@ -12,15 +12,17 @@ import ( type deployNode struct { node *domain.WorkflowNode - outputRepo WorkflowOutputRepository - *Logger + certRepo certificateRepository + outputRepo workflowOutputRepository + *nodeLogger } func NewDeployNode(node *domain.WorkflowNode) *deployNode { return &deployNode{ node: node, - Logger: NewLogger(node), + nodeLogger: NewNodeLogger(node), outputRepo: repository.NewWorkflowOutputRepository(), + certRepo: repository.NewCertificateRepository(), } } @@ -28,7 +30,7 @@ func (d *deployNode) Run(ctx context.Context) error { d.AddOutput(ctx, d.node.Name, "开始执行") // 检查是否部署过(部署过则直接返回,和 v0.2 暂时保持一致) output, err := d.outputRepo.GetByNodeId(ctx, d.node.Id) - if err != nil && !domain.IsRecordNotFound(err) { + if err != nil && !domain.IsRecordNotFoundError(err) { d.AddOutput(ctx, d.node.Name, "查询部署记录失败", err.Error()) return err } @@ -42,7 +44,7 @@ func (d *deployNode) Run(ctx context.Context) error { return fmt.Errorf("证书来源配置错误: %s", certSource) } - cert, err := d.outputRepo.GetCertificateByNodeId(ctx, certSourceSlice[0]) + cert, err := d.certRepo.GetByWorkflowNodeId(ctx, certSourceSlice[0]) if err != nil { d.AddOutput(ctx, d.node.Name, "获取证书失败", err.Error()) return err diff --git a/internal/workflow/node-processor/notify_node.go b/internal/workflow/node-processor/notify_node.go index 42d071b0..ffb40566 100644 --- a/internal/workflow/node-processor/notify_node.go +++ b/internal/workflow/node-processor/notify_node.go @@ -8,20 +8,17 @@ import ( "github.com/usual2970/certimate/internal/repository" ) -type SettingRepository interface { - GetByName(ctx context.Context, name string) (*domain.Settings, error) -} type notifyNode struct { - node *domain.WorkflowNode - settingRepo SettingRepository - *Logger + node *domain.WorkflowNode + settingsRepo settingRepository + *nodeLogger } func NewNotifyNode(node *domain.WorkflowNode) *notifyNode { return ¬ifyNode{ - node: node, - Logger: NewLogger(node), - settingRepo: repository.NewSettingsRepository(), + node: node, + nodeLogger: NewNodeLogger(node), + settingsRepo: repository.NewSettingsRepository(), } } @@ -29,7 +26,7 @@ func (n *notifyNode) Run(ctx context.Context) error { n.AddOutput(ctx, n.node.Name, "开始执行") // 获取通知配置 - setting, err := n.settingRepo.GetByName(ctx, "notifyChannels") + setting, err := n.settingsRepo.GetByName(ctx, "notifyChannels") if err != nil { n.AddOutput(ctx, n.node.Name, "获取通知配置失败", err.Error()) return err diff --git a/internal/workflow/node-processor/processor.go b/internal/workflow/node-processor/processor.go index fca0ee0a..128022ac 100644 --- a/internal/workflow/node-processor/processor.go +++ b/internal/workflow/node-processor/processor.go @@ -8,18 +8,18 @@ import ( "github.com/usual2970/certimate/internal/domain" ) -type NodeProcessor interface { +type nodeProcessor interface { Run(ctx context.Context) error Log(ctx context.Context) *domain.WorkflowRunLog AddOutput(ctx context.Context, title, content string, err ...string) } -type Logger struct { +type nodeLogger struct { log *domain.WorkflowRunLog } -func NewLogger(node *domain.WorkflowNode) *Logger { - return &Logger{ +func NewNodeLogger(node *domain.WorkflowNode) *nodeLogger { + return &nodeLogger{ log: &domain.WorkflowRunLog{ NodeId: node.Id, NodeName: node.Name, @@ -28,11 +28,11 @@ func NewLogger(node *domain.WorkflowNode) *Logger { } } -func (l *Logger) Log(ctx context.Context) *domain.WorkflowRunLog { +func (l *nodeLogger) Log(ctx context.Context) *domain.WorkflowRunLog { return l.log } -func (l *Logger) AddOutput(ctx context.Context, title, content string, err ...string) { +func (l *nodeLogger) AddOutput(ctx context.Context, title, content string, err ...string) { output := domain.WorkflowRunLogOutput{ Time: time.Now().UTC().Format(time.RFC3339), Title: title, @@ -45,7 +45,7 @@ func (l *Logger) AddOutput(ctx context.Context, title, content string, err ...st l.log.Outputs = append(l.log.Outputs, output) } -func GetProcessor(node *domain.WorkflowNode) (NodeProcessor, error) { +func GetProcessor(node *domain.WorkflowNode) (nodeProcessor, error) { switch node.Type { case domain.WorkflowNodeTypeStart: return NewStartNode(node), nil @@ -60,3 +60,16 @@ func GetProcessor(node *domain.WorkflowNode) (NodeProcessor, error) { } return nil, errors.New("not implemented") } + +type certificateRepository interface { + GetByWorkflowNodeId(ctx context.Context, workflowNodeId string) (*domain.Certificate, error) +} + +type workflowOutputRepository interface { + GetByNodeId(ctx context.Context, nodeId string) (*domain.WorkflowOutput, error) + Save(ctx context.Context, output *domain.WorkflowOutput, certificate *domain.Certificate, cb func(id string) error) error +} + +type settingRepository interface { + GetByName(ctx context.Context, name string) (*domain.Settings, error) +} diff --git a/internal/workflow/node-processor/start_node.go b/internal/workflow/node-processor/start_node.go index 682b77d6..6dc641ab 100644 --- a/internal/workflow/node-processor/start_node.go +++ b/internal/workflow/node-processor/start_node.go @@ -8,13 +8,13 @@ import ( type startNode struct { node *domain.WorkflowNode - *Logger + *nodeLogger } func NewStartNode(node *domain.WorkflowNode) *startNode { return &startNode{ - node: node, - Logger: NewLogger(node), + node: node, + nodeLogger: NewNodeLogger(node), } } diff --git a/internal/workflow/service.go b/internal/workflow/service.go index 83afa94b..93b4c51a 100644 --- a/internal/workflow/service.go +++ b/internal/workflow/service.go @@ -19,21 +19,21 @@ type workflowRunData struct { Options *domain.WorkflowRunReq } -type WorkflowRepository interface { +type workflowRepository interface { + ListEnabledAuto(ctx context.Context) ([]*domain.Workflow, error) GetById(ctx context.Context, id string) (*domain.Workflow, error) - SaveRun(ctx context.Context, run *domain.WorkflowRun) error Save(ctx context.Context, workflow *domain.Workflow) error - ListEnabledAuto(ctx context.Context) ([]domain.Workflow, error) + SaveRun(ctx context.Context, run *domain.WorkflowRun) error } type WorkflowService struct { ch chan *workflowRunData - repo WorkflowRepository + repo workflowRepository wg sync.WaitGroup cancel context.CancelFunc } -func NewWorkflowService(repo WorkflowRepository) *WorkflowService { +func NewWorkflowService(repo workflowRepository) *WorkflowService { rs := &WorkflowService{ repo: repo, ch: make(chan *workflowRunData, 1), diff --git a/migrations/1737019549_updated_certificate.go b/migrations/1737019549_updated_certificate.go new file mode 100644 index 00000000..cef2a645 --- /dev/null +++ b/migrations/1737019549_updated_certificate.go @@ -0,0 +1,54 @@ +package migrations + +import ( + "encoding/json" + + "github.com/pocketbase/dbx" + "github.com/pocketbase/pocketbase/daos" + m "github.com/pocketbase/pocketbase/migrations" + "github.com/pocketbase/pocketbase/models/schema" +) + +func init() { + m.Register(func(db dbx.Builder) error { + dao := daos.New(db); + + collection, err := dao.FindCollectionByNameOrId("4szxr9x43tpj6np") + if err != nil { + return err + } + + // add + new_deleted := &schema.SchemaField{} + if err := json.Unmarshal([]byte(`{ + "system": false, + "id": "klyf4nlq", + "name": "deleted", + "type": "date", + "required": false, + "presentable": false, + "unique": false, + "options": { + "min": "", + "max": "" + } + }`), new_deleted); err != nil { + return err + } + collection.Schema.AddField(new_deleted) + + return dao.SaveCollection(collection) + }, func(db dbx.Builder) error { + dao := daos.New(db); + + collection, err := dao.FindCollectionByNameOrId("4szxr9x43tpj6np") + if err != nil { + return err + } + + // remove + collection.Schema.RemoveField("klyf4nlq") + + return dao.SaveCollection(collection) + }) +} diff --git a/ui/src/api/notify.ts b/ui/src/api/notify.ts index 6eb078ab..e0167175 100644 --- a/ui/src/api/notify.ts +++ b/ui/src/api/notify.ts @@ -5,7 +5,7 @@ import { getPocketBase } from "@/repository/pocketbase"; export const notifyTest = async (channel: string) => { const pb = getPocketBase(); - const resp = await pb.send("/api/notify/test", { + const resp = await pb.send("/api/notify/test", { method: "POST", headers: { "Content-Type": "application/json", diff --git a/ui/src/api/statistics.ts b/ui/src/api/statistics.ts index 6c1d422c..f05dda0c 100644 --- a/ui/src/api/statistics.ts +++ b/ui/src/api/statistics.ts @@ -6,7 +6,7 @@ import { getPocketBase } from "@/repository/pocketbase"; export const get = async () => { const pb = getPocketBase(); - const resp = await pb.send("/api/statistics/get", { + const resp = await pb.send>("/api/statistics/get", { method: "GET", }); @@ -14,5 +14,5 @@ export const get = async () => { throw new ClientResponseError({ status: resp.code, response: resp, data: {} }); } - return resp.data as Statistics; + return resp; }; diff --git a/ui/src/api/workflow.ts b/ui/src/api/workflow.ts index c40fcce8..afead94e 100644 --- a/ui/src/api/workflow.ts +++ b/ui/src/api/workflow.ts @@ -1,12 +1,12 @@ -import { ClientResponseError, type RecordSubscription } from "pocketbase"; +import { ClientResponseError } from "pocketbase"; -import { WORKFLOW_TRIGGERS, type WorkflowModel } from "@/domain/workflow"; +import { WORKFLOW_TRIGGERS } from "@/domain/workflow"; import { getPocketBase } from "@/repository/pocketbase"; export const run = async (id: string) => { const pb = getPocketBase(); - const resp = await pb.send("/api/workflow/run", { + const resp = await pb.send("/api/workflow/run", { method: "POST", headers: { "Content-Type": "application/json", @@ -23,15 +23,3 @@ export const run = async (id: string) => { return resp; }; - -export const subscribe = async (id: string, cb: (e: RecordSubscription) => void) => { - const pb = getPocketBase(); - - return pb.collection("workflow").subscribe(id, cb); -}; - -export const unsubscribe = async (id: string) => { - const pb = getPocketBase(); - - return pb.collection("workflow").unsubscribe(id); -}; diff --git a/ui/src/components/workflow/WorkflowRuns.tsx b/ui/src/components/workflow/WorkflowRuns.tsx index 6686d906..df5a93dc 100644 --- a/ui/src/components/workflow/WorkflowRuns.tsx +++ b/ui/src/components/workflow/WorkflowRuns.tsx @@ -139,9 +139,9 @@ const WorkflowRuns = ({ className, style, workflowId }: WorkflowRunsProps) => { }, { refreshDeps: [workflowId, page, pageSize], - onSuccess: (data) => { - setTableData(data.items); - setTableTotal(data.totalItems); + onSuccess: (res) => { + setTableData(res.items); + setTableTotal(res.totalItems); }, onError: (err) => { if (err instanceof ClientResponseError && err.isAbort) { diff --git a/ui/src/domain/certificate.ts b/ui/src/domain/certificate.ts index 33d6571e..00c0d7d5 100644 --- a/ui/src/domain/certificate.ts +++ b/ui/src/domain/certificate.ts @@ -8,7 +8,7 @@ export interface CertificateModel extends BaseModel { effectAt: ISO8601String; expireAt: ISO8601String; workflowId: string; - expand: { + expand?: { workflowId?: WorkflowModel; // TODO: ugly, maybe to use an alias? }; } diff --git a/ui/src/pages/accesses/AccessList.tsx b/ui/src/pages/accesses/AccessList.tsx index 6cd1c128..ba7d59d8 100644 --- a/ui/src/pages/accesses/AccessList.tsx +++ b/ui/src/pages/accesses/AccessList.tsx @@ -142,9 +142,9 @@ const AccessList = () => { }, { refreshDeps: [accesses, page, pageSize], - onSuccess: (data) => { - setTableData(data.items); - setTableTotal(data.totalItems); + onSuccess: (res) => { + setTableData(res.items); + setTableTotal(res.totalItems); }, } ); diff --git a/ui/src/pages/certificates/CertificateList.tsx b/ui/src/pages/certificates/CertificateList.tsx index 0a845faf..7ee33b7c 100644 --- a/ui/src/pages/certificates/CertificateList.tsx +++ b/ui/src/pages/certificates/CertificateList.tsx @@ -204,9 +204,9 @@ const CertificateList = () => { }, { refreshDeps: [filters, page, pageSize], - onSuccess: (data) => { - setTableData(data.items); - setTableTotal(data.totalItems); + onSuccess: (res) => { + setTableData(res.items); + setTableTotal(res.totalItems); }, onError: (err) => { if (err instanceof ClientResponseError && err.isAbort) { diff --git a/ui/src/pages/dashboard/Dashboard.tsx b/ui/src/pages/dashboard/Dashboard.tsx index 82e0acea..b9870850 100644 --- a/ui/src/pages/dashboard/Dashboard.tsx +++ b/ui/src/pages/dashboard/Dashboard.tsx @@ -164,9 +164,9 @@ const Dashboard = () => { }, { refreshDeps: [page, pageSize], - onSuccess: (data) => { - setTableData(data.items); - setTableTotal(data.totalItems > 3 ? 3 : data.totalItems); + onSuccess: (res) => { + setTableData(res.items); + setTableTotal(res.totalItems > 3 ? 3 : res.totalItems); }, onError: (err) => { if (err instanceof ClientResponseError && err.isAbort) { @@ -193,8 +193,8 @@ const Dashboard = () => { return getStatistics(); }, { - onSuccess: (data) => { - setStatistics(data); + onSuccess: (res) => { + setStatistics(res.data); }, onError: (err) => { if (err instanceof ClientResponseError && err.isAbort) { diff --git a/ui/src/pages/workflows/WorkflowDetail.tsx b/ui/src/pages/workflows/WorkflowDetail.tsx index 543bf244..826703b3 100644 --- a/ui/src/pages/workflows/WorkflowDetail.tsx +++ b/ui/src/pages/workflows/WorkflowDetail.tsx @@ -16,7 +16,7 @@ import { createSchemaFieldRule } from "antd-zod"; import { isEqual } from "radash"; import { z } from "zod"; -import { run as runWorkflow, subscribe as subscribeWorkflow, unsubscribe as unsubscribeWorkflow } from "@/api/workflow"; +import { run as runWorkflow } from "@/api/workflow"; import ModalForm from "@/components/ModalForm"; import Show from "@/components/Show"; import WorkflowElements from "@/components/workflow/WorkflowElements"; @@ -24,7 +24,7 @@ import WorkflowRuns from "@/components/workflow/WorkflowRuns"; import { isAllNodesValidated } from "@/domain/workflow"; import { WORKFLOW_RUN_STATUSES } from "@/domain/workflowRun"; import { useAntdForm, useZustandShallowSelector } from "@/hooks"; -import { remove as removeWorkflow } from "@/repository/workflow"; +import { remove as removeWorkflow, subscribe as subscribeWorkflow, unsubscribe as unsubscribeWorkflow } from "@/repository/workflow"; import { useWorkflowStore } from "@/stores/workflow"; import { getErrMsg } from "@/utils/error"; diff --git a/ui/src/pages/workflows/WorkflowList.tsx b/ui/src/pages/workflows/WorkflowList.tsx index 967d8410..fa3f9154 100644 --- a/ui/src/pages/workflows/WorkflowList.tsx +++ b/ui/src/pages/workflows/WorkflowList.tsx @@ -250,9 +250,9 @@ const WorkflowList = () => { }, { refreshDeps: [filters, page, pageSize], - onSuccess: (data) => { - setTableData(data.items); - setTableTotal(data.totalItems); + onSuccess: (res) => { + setTableData(res.items); + setTableTotal(res.totalItems); }, onError: (err) => { if (err instanceof ClientResponseError && err.isAbort) { diff --git a/ui/src/repository/certificate.ts b/ui/src/repository/certificate.ts index 0589c116..0654dc28 100644 --- a/ui/src/repository/certificate.ts +++ b/ui/src/repository/certificate.ts @@ -19,17 +19,18 @@ export const list = async (request: ListCertificateRequest) => { const perPage = request.perPage || 10; const options: RecordListOptions = { - sort: "-created", expand: "workflowId", + filter: "deleted=null", + sort: "-created", requestKey: null, }; if (request.state === "expireSoon") { - options.filter = pb.filter("expireAt<{:expiredAt}", { - expiredAt: dayjs().add(15, "d").toDate(), + options.filter = pb.filter("expireAt<{:expiredAt} && deleted=null", { + expiredAt: dayjs().add(20, "d").toDate(), }); } else if (request.state === "expired") { - options.filter = pb.filter("expireAt<={:expiredAt}", { + options.filter = pb.filter("expireAt<={:expiredAt} && deleted=null", { expiredAt: new Date(), }); } diff --git a/ui/src/repository/workflow.ts b/ui/src/repository/workflow.ts index dc16b2ea..4410aa52 100644 --- a/ui/src/repository/workflow.ts +++ b/ui/src/repository/workflow.ts @@ -1,4 +1,4 @@ -import { type RecordListOptions } from "pocketbase"; +import { type RecordListOptions, type RecordSubscription } from "pocketbase"; import { type WorkflowModel } from "@/domain/workflow"; import { getPocketBase } from "./pocketbase"; @@ -48,3 +48,15 @@ export const save = async (record: MaybeModelRecord) => { export const remove = async (record: MaybeModelRecordWithId) => { return await getPocketBase().collection(COLLECTION_NAME).delete(record.id); }; + +export const subscribe = async (id: string, cb: (e: RecordSubscription) => void) => { + const pb = getPocketBase(); + + return pb.collection("workflow").subscribe(id, cb); +}; + +export const unsubscribe = async (id: string) => { + const pb = getPocketBase(); + + return pb.collection("workflow").unsubscribe(id); +}; diff --git a/ui/types/global.d.ts b/ui/types/global.d.ts index 9c4f16f7..3334b33f 100644 --- a/ui/types/global.d.ts +++ b/ui/types/global.d.ts @@ -12,6 +12,12 @@ declare global { declare type MaybeModelRecord = T | Omit; declare type MaybeModelRecordWithId = T | Pick; + + declare interface BaseResponse { + code: number; + msg: string; + data: T; + } } export {};