refactor: change database schemas

This commit is contained in:
Fu Diwei
2025-07-24 22:35:35 +08:00
parent c494b1869d
commit 3cc4de8abc
39 changed files with 638 additions and 227 deletions
+1 -1
View File
@@ -54,7 +54,7 @@ func (s *CertificateService) InitSchedule(ctx context.Context) error {
if settingsContent != nil && settingsContent.ExpiredCertificatesMaxDaysRetention != 0 {
ret, err := s.certificateRepo.DeleteWhere(
context.Background(),
dbx.NewExp(fmt.Sprintf("expireAt<DATETIME('now', '-%d days')", settingsContent.ExpiredCertificatesMaxDaysRetention)),
dbx.NewExp(fmt.Sprintf("validityNotAfter<DATETIME('now', '-%d days')", settingsContent.ExpiredCertificatesMaxDaysRetention)),
)
if err != nil {
app.GetLogger().Error("failed to delete expired certificates", "err", err)
+9 -9
View File
@@ -23,16 +23,16 @@ type Certificate struct {
IssuerOrg string `json:"issuerOrg" db:"issuerOrg"`
IssuerCertificate string `json:"issuerCertificate" db:"issuerCertificate"`
KeyAlgorithm CertificateKeyAlgorithmType `json:"keyAlgorithm" db:"keyAlgorithm"`
EffectAt time.Time `json:"effectAt" db:"effectAt"`
ExpireAt time.Time `json:"expireAt" db:"expireAt"`
ValidityNotBefore time.Time `json:"validityNotBefore" db:"validityNotBefore"`
ValidityNotAfter time.Time `json:"validityNotAfter" db:"validityNotAfter"`
ACMEAccountUrl string `json:"acmeAccountUrl" db:"acmeAccountUrl"`
ACMECertUrl string `json:"acmeCertUrl" db:"acmeCertUrl"`
ACMECertStableUrl string `json:"acmeCertStableUrl" db:"acmeCertStableUrl"`
ACMERenewed bool `json:"acmeRenewed" db:"acmeRenewed"`
WorkflowId string `json:"workflowId" db:"workflowId"`
WorkflowId string `json:"workflowId" db:"workflowRef"`
WorkflowRunId string `json:"workflowRunId" db:"workflowRunRef"`
WorkflowOutputId string `json:"workflowOutputId" db:"workflowOutputRef"`
WorkflowNodeId string `json:"workflowNodeId" db:"workflowNodeId"`
WorkflowRunId string `json:"workflowRunId" db:"workflowRunId"`
WorkflowOutputId string `json:"workflowOutputId" db:"workflowOutputId"`
DeletedAt *time.Time `json:"deleted" db:"deleted"`
}
@@ -40,8 +40,8 @@ func (c *Certificate) PopulateFromX509(certX509 *x509.Certificate) *Certificate
c.SubjectAltNames = strings.Join(certX509.DNSNames, ";")
c.SerialNumber = strings.ToUpper(certX509.SerialNumber.Text(16))
c.IssuerOrg = strings.Join(certX509.Issuer.Organization, ";")
c.EffectAt = certX509.NotBefore
c.ExpireAt = certX509.NotAfter
c.ValidityNotBefore = certX509.NotBefore
c.ValidityNotAfter = certX509.NotAfter
switch certX509.PublicKeyAlgorithm {
case x509.RSA:
@@ -120,8 +120,8 @@ func (c *Certificate) PopulateFromPEM(certPEM, privkeyPEM string) *Certificate {
type CertificateSourceType string
const (
CertificateSourceTypeWorkflow = CertificateSourceType("workflow")
CertificateSourceTypeUpload = CertificateSourceType("upload")
CertificateSourceTypeRequest = CertificateSourceType("request")
CertificateSourceTypeUpload = CertificateSourceType("upload")
)
type CertificateKeyAlgorithmType string
+5 -5
View File
@@ -20,7 +20,7 @@ type Workflow struct {
Content *WorkflowNode `json:"content" db:"content"`
Draft *WorkflowNode `json:"draft" db:"draft"`
HasDraft bool `json:"hasDraft" db:"hasDraft"`
LastRunId string `json:"lastRunId" db:"lastRunId"`
LastRunId string `json:"lastRunId" db:"lastRunRef"`
LastRunStatus WorkflowRunStatusType `json:"lastRunStatus" db:"lastRunStatus"`
LastRunTime time.Time `json:"lastRunTime" db:"lastRunTime"`
}
@@ -45,8 +45,8 @@ const (
type WorkflowTriggerType string
const (
WorkflowTriggerTypeAuto = WorkflowTriggerType("auto")
WorkflowTriggerTypeManual = WorkflowTriggerType("manual")
WorkflowTriggerTypeScheduled = WorkflowTriggerType("scheduled")
WorkflowTriggerTypeManual = WorkflowTriggerType("manual")
)
type WorkflowNode struct {
@@ -55,8 +55,8 @@ type WorkflowNode struct {
Name string `json:"name"`
Config map[string]any `json:"config"`
Inputs []WorkflowNodeIO `json:"inputs"`
Outputs []WorkflowNodeIO `json:"outputs"`
Inputs []WorkflowNodeIO `json:"inputs,omitempty"`
Outputs []WorkflowNodeIO `json:"outputs,omitempty"`
Next *WorkflowNode `json:"next,omitempty"`
Branches []WorkflowNode `json:"branches,omitempty"`
+2 -2
View File
@@ -9,8 +9,8 @@ const CollectionNameWorkflowLog = "workflow_logs"
type WorkflowLog struct {
Meta
WorkflowId string `json:"workflowId" db:"workflowId"`
RunId string `json:"workflorunIdwId" db:"runId"`
WorkflowId string `json:"workflowId" db:"workflowRef"`
RunId string `json:"runId" db:"runRef"`
NodeId string `json:"nodeId" db:"nodeId"`
NodeName string `json:"nodeName" db:"nodeName"`
Timestamp int64 `json:"timestamp" db:"timestamp"` // 毫秒级时间戳
+2 -2
View File
@@ -4,8 +4,8 @@ const CollectionNameWorkflowOutput = "workflow_output"
type WorkflowOutput struct {
Meta
WorkflowId string `json:"workflowId" db:"workflow"`
RunId string `json:"runId" db:"runId"`
WorkflowId string `json:"workflowId" db:"workflowRef"`
RunId string `json:"runId" db:"runRef"`
NodeId string `json:"nodeId" db:"nodeId"`
Node *WorkflowNode `json:"node" db:"node"`
Outputs []WorkflowNodeIO `json:"outputs" db:"outputs"`
+1 -1
View File
@@ -8,7 +8,7 @@ const CollectionNameWorkflowRun = "workflow_run"
type WorkflowRun struct {
Meta
WorkflowId string `json:"workflowId" db:"workflowId"`
WorkflowId string `json:"workflowId" db:"workflowRef"`
Status WorkflowRunStatusType `json:"status" db:"status"`
Trigger WorkflowTriggerType `json:"trigger" db:"trigger"`
StartedAt time.Time `json:"startedAt" db:"startedAt"`
+13 -13
View File
@@ -21,8 +21,8 @@ func NewCertificateRepository() *CertificateRepository {
func (r *CertificateRepository) ListExpireSoon(ctx context.Context) ([]*domain.Certificate, error) {
records, err := app.GetApp().FindAllRecords(
domain.CollectionNameCertificate,
dbx.NewExp("expireAt>DATETIME('now')"),
dbx.NewExp("expireAt<DATETIME('now', '+20 days')"),
dbx.NewExp("validityNotAfter>DATETIME('now')"),
dbx.NewExp("validityNotAfter<DATETIME('now', '+20 days')"),
dbx.NewExp("deleted=null"),
)
if err != nil {
@@ -80,7 +80,7 @@ func (r *CertificateRepository) GetByWorkflowNodeId(ctx context.Context, workflo
func (r *CertificateRepository) GetByWorkflowRunIdAndNodeId(ctx context.Context, workflowRunId string, workflowNodeId string) (*domain.Certificate, error) {
records, err := app.GetApp().FindRecordsByFilter(
domain.CollectionNameCertificate,
"workflowRunId={:workflowRunId} && workflowNodeId={:workflowNodeId} && deleted=null",
"workflowRunRef={:workflowRunId} && workflowNodeId={:workflowNodeId} && deleted=null",
"-created",
1, 0,
dbx.Params{"workflowRunId": workflowRunId},
@@ -124,16 +124,16 @@ func (r *CertificateRepository) Save(ctx context.Context, certificate *domain.Ce
record.Set("issuerOrg", certificate.IssuerOrg)
record.Set("issuerCertificate", certificate.IssuerCertificate)
record.Set("keyAlgorithm", string(certificate.KeyAlgorithm))
record.Set("effectAt", certificate.EffectAt)
record.Set("expireAt", certificate.ExpireAt)
record.Set("validityNotBefore", certificate.ValidityNotBefore)
record.Set("validityNotAfter", certificate.ValidityNotAfter)
record.Set("acmeAccountUrl", certificate.ACMEAccountUrl)
record.Set("acmeCertUrl", certificate.ACMECertUrl)
record.Set("acmeCertStableUrl", certificate.ACMECertStableUrl)
record.Set("acmeRenewed", certificate.ACMERenewed)
record.Set("workflowId", certificate.WorkflowId)
record.Set("workflowRunId", certificate.WorkflowRunId)
record.Set("workflowRef", certificate.WorkflowId)
record.Set("workflowRunRef", certificate.WorkflowRunId)
record.Set("workflowOutputRef", certificate.WorkflowOutputId)
record.Set("workflowNodeId", certificate.WorkflowNodeId)
record.Set("workflowOutputId", certificate.WorkflowOutputId)
if err := app.GetApp().Save(record); err != nil {
return certificate, err
}
@@ -186,16 +186,16 @@ func (r *CertificateRepository) castRecordToModel(record *core.Record) (*domain.
IssuerOrg: record.GetString("issuerOrg"),
IssuerCertificate: record.GetString("issuerCertificate"),
KeyAlgorithm: domain.CertificateKeyAlgorithmType(record.GetString("keyAlgorithm")),
EffectAt: record.GetDateTime("effectAt").Time(),
ExpireAt: record.GetDateTime("expireAt").Time(),
ValidityNotBefore: record.GetDateTime("validityNotBefore").Time(),
ValidityNotAfter: record.GetDateTime("validityNotAfter").Time(),
ACMEAccountUrl: record.GetString("acmeAccountUrl"),
ACMECertUrl: record.GetString("acmeCertUrl"),
ACMECertStableUrl: record.GetString("acmeCertStableUrl"),
ACMERenewed: record.GetBool("acmeRenewed"),
WorkflowId: record.GetString("workflowId"),
WorkflowRunId: record.GetString("workflowRunId"),
WorkflowId: record.GetString("workflowRef"),
WorkflowRunId: record.GetString("workflowRunRef"),
WorkflowOutputId: record.GetString("workflowOutputRef"),
WorkflowNodeId: record.GetString("workflowNodeId"),
WorkflowOutputId: record.GetString("workflowOutputId"),
}
return certificate, nil
}
+2 -2
View File
@@ -32,7 +32,7 @@ func (r *StatisticsRepository) Get(ctx context.Context) (*domain.Statistics, err
Total int `db:"total"`
}{}
if err := app.GetDB().
NewQuery("SELECT COUNT(*) AS total FROM certificate WHERE expireAt > DATETIME('now') and expireAt < DATETIME('now', '+20 days') AND deleted = ''").
NewQuery("SELECT COUNT(*) AS total FROM certificate WHERE validityNotAfter > DATETIME('now') and validityNotAfter < DATETIME('now', '+20 days') AND deleted = ''").
One(&certExpireSoonTotal); err != nil {
return nil, err
}
@@ -43,7 +43,7 @@ func (r *StatisticsRepository) Get(ctx context.Context) (*domain.Statistics, err
Total int `db:"total"`
}{}
if err := app.GetDB().
NewQuery("SELECT COUNT(*) AS total FROM certificate WHERE expireAt < DATETIME('now') AND deleted = ''").
NewQuery("SELECT COUNT(*) AS total FROM certificate WHERE validityNotAfter < DATETIME('now') AND deleted = ''").
One(&certExpiredTotal); err != nil {
return nil, err
}
+4 -4
View File
@@ -18,13 +18,13 @@ func NewWorkflowRepository() *WorkflowRepository {
return &WorkflowRepository{}
}
func (r *WorkflowRepository) ListEnabledAuto(ctx context.Context) ([]*domain.Workflow, error) {
func (r *WorkflowRepository) ListEnabledScheduled(ctx context.Context) ([]*domain.Workflow, error) {
records, err := app.GetApp().FindRecordsByFilter(
domain.CollectionNameWorkflow,
"enabled={:enabled} && trigger={:trigger}",
"-created",
0, 0,
dbx.Params{"enabled": true, "trigger": string(domain.WorkflowTriggerTypeAuto)},
dbx.Params{"enabled": true, "trigger": string(domain.WorkflowTriggerTypeScheduled)},
)
if err != nil {
return nil, err
@@ -82,7 +82,7 @@ func (r *WorkflowRepository) Save(ctx context.Context, workflow *domain.Workflow
record.Set("content", workflow.Content)
record.Set("draft", workflow.Draft)
record.Set("hasDraft", workflow.HasDraft)
record.Set("lastRunId", workflow.LastRunId)
record.Set("lastRunRef", workflow.LastRunId)
record.Set("lastRunStatus", string(workflow.LastRunStatus))
record.Set("lastRunTime", workflow.LastRunTime)
if err := app.GetApp().Save(record); err != nil {
@@ -124,7 +124,7 @@ func (r *WorkflowRepository) castRecordToModel(record *core.Record) (*domain.Wor
Content: content,
Draft: draft,
HasDraft: record.GetBool("hasDraft"),
LastRunId: record.GetString("lastRunId"),
LastRunId: record.GetString("lastRunRef"),
LastRunStatus: domain.WorkflowRunStatusType(record.GetString("lastRunStatus")),
LastRunTime: record.GetDateTime("lastRunTime").Time(),
}
+5 -5
View File
@@ -21,7 +21,7 @@ func NewWorkflowLogRepository() *WorkflowLogRepository {
func (r *WorkflowLogRepository) ListByWorkflowRunId(ctx context.Context, workflowRunId string) ([]*domain.WorkflowLog, error) {
records, err := app.GetApp().FindRecordsByFilter(
domain.CollectionNameWorkflowLog,
"runId={:runId}",
"runRef={:runId}",
"timestamp",
0, 0,
dbx.Params{"runId": workflowRunId},
@@ -62,8 +62,8 @@ func (r *WorkflowLogRepository) Save(ctx context.Context, workflowLog *domain.Wo
}
}
record.Set("workflowId", workflowLog.WorkflowId)
record.Set("runId", workflowLog.RunId)
record.Set("workflowRef", workflowLog.WorkflowId)
record.Set("runRef", workflowLog.RunId)
record.Set("nodeId", workflowLog.NodeId)
record.Set("nodeName", workflowLog.NodeName)
record.Set("timestamp", workflowLog.Timestamp)
@@ -99,8 +99,8 @@ func (r *WorkflowLogRepository) castRecordToModel(record *core.Record) (*domain.
CreatedAt: record.GetDateTime("created").Time(),
UpdatedAt: record.GetDateTime("updated").Time(),
},
WorkflowId: record.GetString("workflowId"),
RunId: record.GetString("runId"),
WorkflowId: record.GetString("workflowRef"),
RunId: record.GetString("runRef"),
NodeId: record.GetString("nodeId"),
NodeName: record.GetString("nodeName"),
Timestamp: int64(record.GetInt("timestamp")),
+4 -4
View File
@@ -123,8 +123,8 @@ func (r *WorkflowOutputRepository) castRecordToModel(record *core.Record) (*doma
CreatedAt: record.GetDateTime("created").Time(),
UpdatedAt: record.GetDateTime("updated").Time(),
},
WorkflowId: record.GetString("workflowId"),
RunId: record.GetString("runId"),
WorkflowId: record.GetString("workflowRef"),
RunId: record.GetString("runRef"),
NodeId: record.GetString("nodeId"),
Node: node,
Outputs: outputs,
@@ -148,8 +148,8 @@ func (r *WorkflowOutputRepository) saveRecord(workflowOutput *domain.WorkflowOut
return record, err
}
}
record.Set("workflowId", workflowOutput.WorkflowId)
record.Set("runId", workflowOutput.RunId)
record.Set("workflowRef", workflowOutput.WorkflowId)
record.Set("runRef", workflowOutput.RunId)
record.Set("nodeId", workflowOutput.NodeId)
record.Set("node", workflowOutput.Node)
record.Set("outputs", workflowOutput.Outputs)
+4 -4
View File
@@ -50,7 +50,7 @@ func (r *WorkflowRunRepository) Save(ctx context.Context, workflowRun *domain.Wo
}
err = app.GetApp().RunInTransaction(func(txApp core.App) error {
record.Set("workflowId", workflowRun.WorkflowId)
record.Set("workflowRef", workflowRun.WorkflowId)
record.Set("trigger", string(workflowRun.Trigger))
record.Set("status", string(workflowRun.Status))
record.Set("startedAt", workflowRun.StartedAt)
@@ -70,7 +70,7 @@ func (r *WorkflowRunRepository) Save(ctx context.Context, workflowRun *domain.Wo
workflowRecord, err := txApp.FindRecordById(domain.CollectionNameWorkflow, workflowRun.WorkflowId)
if err != nil {
return err
} else if workflowRun.Id == workflowRecord.GetString("lastRunId") {
} else if workflowRun.Id == workflowRecord.GetString("lastRunRef") {
workflowRecord.IgnoreUnchangedFields(true)
workflowRecord.Set("lastRunStatus", record.GetString("status"))
err = txApp.Save(workflowRecord)
@@ -79,7 +79,7 @@ func (r *WorkflowRunRepository) Save(ctx context.Context, workflowRun *domain.Wo
}
} else if workflowRecord.GetDateTime("lastRunTime").Time().IsZero() || workflowRun.StartedAt.After(workflowRecord.GetDateTime("lastRunTime").Time()) {
workflowRecord.IgnoreUnchangedFields(true)
workflowRecord.Set("lastRunId", record.Id)
workflowRecord.Set("lastRunRef", record.Id)
workflowRecord.Set("lastRunStatus", record.GetString("status"))
workflowRecord.Set("lastRunTime", record.GetString("startedAt"))
err = txApp.Save(workflowRecord)
@@ -136,7 +136,7 @@ func (r *WorkflowRunRepository) castRecordToModel(record *core.Record) (*domain.
CreatedAt: record.GetDateTime("created").Time(),
UpdatedAt: record.GetDateTime("updated").Time(),
},
WorkflowId: record.GetString("workflowId"),
WorkflowId: record.GetString("workflowRef"),
Status: domain.WorkflowRunStatusType(record.GetString("status")),
Trigger: domain.WorkflowTriggerType(record.GetString("trigger")),
StartedAt: record.GetDateTime("startedAt").Time(),
+3 -3
View File
@@ -57,8 +57,8 @@ func onWorkflowRecordCreateOrUpdate(ctx context.Context, record *core.Record) er
enabled := record.GetBool("enabled")
trigger := record.GetString("trigger")
// 如果是手动触发或未启用,移除定时任务
if !enabled || trigger == string(domain.WorkflowTriggerTypeManual) {
// 如果非定时触发或未启用,移除定时任务
if !enabled || trigger != string(domain.WorkflowTriggerTypeScheduled) {
scheduler.Remove(fmt.Sprintf("workflow#%s", workflowId))
return nil
}
@@ -68,7 +68,7 @@ func onWorkflowRecordCreateOrUpdate(ctx context.Context, record *core.Record) er
workflowSrv := NewWorkflowService(repository.NewWorkflowRepository(), repository.NewWorkflowRunRepository(), repository.NewSettingsRepository())
workflowSrv.StartRun(ctx, &dtos.WorkflowStartRunReq{
WorkflowId: workflowId,
RunTrigger: domain.WorkflowTriggerTypeAuto,
RunTrigger: domain.WorkflowTriggerTypeScheduled,
})
})
if err != nil {
@@ -79,7 +79,7 @@ func (n *applyNode) Process(ctx context.Context) error {
}
certificate := &domain.Certificate{
Source: domain.CertificateSourceTypeWorkflow,
Source: domain.CertificateSourceTypeRequest,
Certificate: applyResult.FullChainCertificate,
PrivateKey: applyResult.PrivateKey,
IssuerCertificate: applyResult.IssuerCertificate,
@@ -115,7 +115,7 @@ func (n *applyNode) Process(ctx context.Context) error {
// 记录中间结果
n.outputs[outputKeyForNodeSkipped] = strconv.FormatBool(false)
n.outputs[outputKeyForCertificateValidity] = strconv.FormatBool(true)
n.outputs[outputKeyForCertificateDaysLeft] = strconv.FormatInt(int64(time.Until(certificate.ExpireAt).Hours()/24), 10)
n.outputs[outputKeyForCertificateDaysLeft] = strconv.FormatInt(int64(time.Until(certificate.ValidityNotAfter).Hours()/24), 10)
n.logger.Info("application completed")
return nil
@@ -158,7 +158,7 @@ func (n *applyNode) checkCanSkip(ctx context.Context, lastOutput *domain.Workflo
lastCertificate, _ := n.certRepo.GetByWorkflowRunIdAndNodeId(ctx, lastOutput.RunId, lastOutput.NodeId)
if lastCertificate != nil {
renewalInterval := time.Duration(thisNodeCfg.SkipBeforeExpiryDays) * time.Hour * 24
expirationTime := time.Until(lastCertificate.ExpireAt)
expirationTime := time.Until(lastCertificate.ValidityNotAfter)
if expirationTime > renewalInterval {
daysLeft := int(expirationTime.Hours() / 24)
// TODO: 优化此处逻辑,[checkCanSkip] 方法不应该修改中间结果,违背单一职责
@@ -74,7 +74,7 @@ func (n *uploadNode) Process(ctx context.Context) error {
// 记录中间结果
n.outputs[outputKeyForNodeSkipped] = strconv.FormatBool(false)
n.outputs[outputKeyForCertificateValidity] = strconv.FormatBool(true)
n.outputs[outputKeyForCertificateDaysLeft] = strconv.FormatInt(int64(time.Until(certificate.ExpireAt).Hours()/24), 10)
n.outputs[outputKeyForCertificateDaysLeft] = strconv.FormatInt(int64(time.Until(certificate.ValidityNotAfter).Hours()/24), 10)
n.logger.Info("uploading completed")
return nil
@@ -95,7 +95,7 @@ func (n *uploadNode) checkCanSkip(ctx context.Context, lastOutput *domain.Workfl
lastCertificate, _ := n.certRepo.GetByWorkflowRunIdAndNodeId(ctx, lastOutput.RunId, lastOutput.NodeId)
if lastCertificate != nil {
daysLeft := int(time.Until(lastCertificate.ExpireAt).Hours() / 24)
daysLeft := int(time.Until(lastCertificate.ValidityNotAfter).Hours() / 24)
n.outputs[outputKeyForCertificateValidity] = strconv.FormatBool(daysLeft > 0)
n.outputs[outputKeyForCertificateDaysLeft] = strconv.FormatInt(int64(daysLeft), 10)
+3 -3
View File
@@ -16,7 +16,7 @@ import (
)
type workflowRepository interface {
ListEnabledAuto(ctx context.Context) ([]*domain.Workflow, error)
ListEnabledScheduled(ctx context.Context) ([]*domain.Workflow, error)
GetById(ctx context.Context, id string) (*domain.Workflow, error)
Save(ctx context.Context, workflow *domain.Workflow) (*domain.Workflow, error)
}
@@ -80,7 +80,7 @@ func (s *WorkflowService) InitSchedule(ctx context.Context) error {
// 工作流
{
workflows, err := s.workflowRepo.ListEnabledAuto(ctx)
workflows, err := s.workflowRepo.ListEnabledScheduled(ctx)
if err != nil {
return err
}
@@ -91,7 +91,7 @@ func (s *WorkflowService) InitSchedule(ctx context.Context) error {
err := app.GetScheduler().Add(fmt.Sprintf("workflow#%s", workflow.Id), workflow.TriggerCron, func() {
s.StartRun(ctx, &dtos.WorkflowStartRunReq{
WorkflowId: workflow.Id,
RunTrigger: domain.WorkflowTriggerTypeAuto,
RunTrigger: domain.WorkflowTriggerTypeScheduled,
})
})
if err != nil {
+520 -113
View File
@@ -1,6 +1,8 @@
package migrations
import (
"encoding/json"
"github.com/pocketbase/pocketbase/core"
m "github.com/pocketbase/pocketbase/migrations"
)
@@ -10,72 +12,203 @@ func init() {
tracer := NewTracer("v0.4.0")
tracer.Printf("go ...")
// update collection `workflow_logs`
// update collection `access`
{
collection, err := app.FindCollectionByNameOrId("pbc_1682296116")
collection, err := app.FindCollectionByNameOrId("4yzbv8urny5ja1e")
if err != nil {
return err
} else if collection != nil {
records, err := app.FindAllRecords(collection)
if err != nil {
return err
}
for _, record := range records {
changed := false
provider := record.GetString("provider")
config := make(map[string]any)
if err := record.UnmarshalJSONField("config", &config); err != nil {
return err
}
switch provider {
case "discordbot", "mattermost", "slackbot":
if _, ok := config["defaultChannelId"]; ok {
config["channelId"] = config["defaultChannelId"]
delete(config, "defaultChannelId")
record.Set("config", config)
changed = true
}
case "email":
if _, ok := config["defaultSenderAddress"]; ok {
config["senderAddress"] = config["defaultSenderAddress"]
delete(config, "defaultSenderAddress")
record.Set("config", config)
changed = true
}
if _, ok := config["defaultSenderName"]; ok {
config["senderName"] = config["defaultSenderName"]
delete(config, "defaultSenderName")
record.Set("config", config)
changed = true
}
if _, ok := config["defaultReceiverAddress"]; ok {
config["receiverAddress"] = config["defaultReceiverAddress"]
delete(config, "defaultReceiverAddress")
record.Set("config", config)
changed = true
}
case "telegrambot":
if _, ok := config["defaultChatId"]; ok {
config["chatId"] = config["defaultChatId"]
delete(config, "defaultChatId")
record.Set("config", config)
changed = true
}
case "webhook":
if _, ok := config["defaultDataForDeployment"]; ok {
config["dataForDeployment"] = config["defaultDataForDeployment"]
delete(config, "defaultDataForDeployment")
record.Set("config", config)
changed = true
}
if _, ok := config["defaultDataForNotification"]; ok {
config["dataForNotification"] = config["defaultDataForNotification"]
delete(config, "defaultDataForNotification")
record.Set("config", config)
changed = true
}
}
if changed {
if err := app.Save(record); err != nil {
return err
}
tracer.Printf("record #%s in collection '%s' updated", record.Id, collection.Name)
}
}
}
}
field := collection.Fields.GetByName("level")
if field != nil && field.Type() == "text" {
// add temp field `levelTmp`
if err := collection.Fields.AddMarshaledJSONAt(7, []byte(`{
// update collection `certificate`
{
collection, err := app.FindCollectionByNameOrId("4szxr9x43tpj6np")
if err != nil {
return err
} else if collection != nil {
if err := collection.Fields.AddMarshaledJSONAt(1, []byte(`{
"hidden": false,
"id": "number760395071",
"max": null,
"min": null,
"name": "levelTmp",
"onlyInt": false,
"id": "by9hetqi",
"maxSelect": 1,
"name": "source",
"presentable": false,
"required": false,
"system": false,
"type": "number"
"type": "select",
"values": [
"request",
"upload"
]
}`)); err != nil {
return err
}
if err := app.Save(collection); err != nil {
return err
}
// copy `level` to `levelTmp`
if _, err := app.DB().NewQuery("UPDATE workflow_logs SET levelTmp = -4 WHERE level = 'DEBUG'").Execute(); err != nil {
return err
}
if _, err := app.DB().NewQuery("UPDATE workflow_logs SET levelTmp = 4 WHERE level = 'WARN'").Execute(); err != nil {
return err
}
if _, err := app.DB().NewQuery("UPDATE workflow_logs SET levelTmp = 8 WHERE level = 'ERROR'").Execute(); err != nil {
return err
}
if _, err := app.DB().NewQuery("UPDATE workflow_logs SET levelTmp = 0 WHERE levelTmp IS NULL").Execute(); err != nil {
return err
}
// remove old field `level`
collection.Fields.RemoveById(field.GetId())
if err := app.Save(collection); err != nil {
println(err)
return err
}
// rename field `levelTmp` to `level`
if err := collection.Fields.AddMarshaledJSONAt(6, []byte(`{
if err := collection.Fields.AddMarshaledJSONAt(9, []byte(`{
"hidden": false,
"id": "number760395071",
"max": null,
"min": null,
"name": "level",
"onlyInt": false,
"id": "v40aqzpd",
"max": "",
"min": "",
"name": "validityNotBefore",
"presentable": false,
"required": false,
"system": false,
"type": "number"
"type": "date"
}`)); err != nil {
return err
}
if err := collection.Fields.AddMarshaledJSONAt(10, []byte(`{
"hidden": false,
"id": "zgpdby2k",
"max": "",
"min": "",
"name": "validityNotAfter",
"presentable": false,
"required": false,
"system": false,
"type": "date"
}`)); err != nil {
return err
}
if err := collection.Fields.AddMarshaledJSONAt(15, []byte(`{
"cascadeDelete": false,
"collectionId": "tovyif5ax6j62ur",
"hidden": false,
"id": "uvqfamb1",
"maxSelect": 1,
"minSelect": 0,
"name": "workflowRef",
"presentable": false,
"required": false,
"system": false,
"type": "relation"
}`)); err != nil {
return err
}
if err := collection.Fields.AddMarshaledJSONAt(16, []byte(`{
"cascadeDelete": false,
"collectionId": "qjp8lygssgwyqyz",
"hidden": false,
"id": "relation3917999135",
"maxSelect": 1,
"minSelect": 0,
"name": "workflowRunRef",
"presentable": false,
"required": false,
"system": false,
"type": "relation"
}`)); err != nil {
return err
}
if err := collection.Fields.AddMarshaledJSONAt(17, []byte(`{
"cascadeDelete": false,
"collectionId": "bqnxb95f2cooowp",
"hidden": false,
"id": "2ohlr0yd",
"maxSelect": 1,
"minSelect": 0,
"name": "workflowOutputRef",
"presentable": false,
"required": false,
"system": false,
"type": "relation"
}`)); err != nil {
return err
}
if err := json.Unmarshal([]byte(`{
"indexes": [
"CREATE INDEX `+"`"+`idx_Jx8TXzDCmw`+"`"+` ON `+"`"+`certificate`+"`"+` (`+"`"+`workflowRef`+"`"+`)",
"CREATE INDEX `+"`"+`idx_2cRXqNDyyp`+"`"+` ON `+"`"+`certificate`+"`"+` (`+"`"+`workflowRunRef`+"`"+`)",
"CREATE INDEX `+"`"+`idx_kcKpgAZapk`+"`"+` ON `+"`"+`certificate`+"`"+` (`+"`"+`workflowNodeId`+"`"+`)"
]
}`), &collection); err != nil {
return err
}
if err := app.Save(collection); err != nil {
println(err)
return err
}
if _, err := app.DB().NewQuery("UPDATE certificate SET source = 'request' WHERE source = 'workflow'").Execute(); err != nil {
return err
}
@@ -83,86 +216,360 @@ func init() {
}
}
// update collection `access`
// update collection `workflow`
{
collection, err := app.FindCollectionByNameOrId("4yzbv8urny5ja1e")
collection, err := app.FindCollectionByNameOrId("tovyif5ax6j62ur")
if err != nil {
return err
}
records, err := app.FindAllRecords(collection)
if err != nil {
return err
}
for _, record := range records {
changed := false
provider := record.GetString("provider")
config := make(map[string]any)
if err := record.UnmarshalJSONField("config", &config); err != nil {
} else if collection != nil {
if err := collection.Fields.AddMarshaledJSONAt(3, []byte(`{
"hidden": false,
"id": "vqoajwjq",
"maxSelect": 1,
"name": "trigger",
"presentable": false,
"required": false,
"system": false,
"type": "select",
"values": [
"manual",
"scheduled"
]
}`)); err != nil {
return err
}
switch provider {
case "discordbot", "mattermost", "slackbot":
if _, ok := config["defaultChannelId"]; ok {
config["channelId"] = config["defaultChannelId"]
delete(config, "defaultChannelId")
record.Set("config", config)
changed = true
}
case "email":
if _, ok := config["defaultSenderAddress"]; ok {
config["senderAddress"] = config["defaultSenderAddress"]
delete(config, "defaultSenderAddress")
record.Set("config", config)
changed = true
}
if _, ok := config["defaultSenderName"]; ok {
config["senderName"] = config["defaultSenderName"]
delete(config, "defaultSenderName")
record.Set("config", config)
changed = true
}
if _, ok := config["defaultReceiverAddress"]; ok {
config["receiverAddress"] = config["defaultReceiverAddress"]
delete(config, "defaultReceiverAddress")
record.Set("config", config)
changed = true
}
case "telegrambot":
if _, ok := config["defaultChatId"]; ok {
config["chatId"] = config["defaultChatId"]
delete(config, "defaultChatId")
record.Set("config", config)
changed = true
}
case "webhook":
if _, ok := config["defaultDataForDeployment"]; ok {
config["dataForDeployment"] = config["defaultDataForDeployment"]
delete(config, "defaultDataForDeployment")
record.Set("config", config)
changed = true
}
if _, ok := config["defaultDataForNotification"]; ok {
config["dataForNotification"] = config["defaultDataForNotification"]
delete(config, "defaultDataForNotification")
record.Set("config", config)
changed = true
}
if err := collection.Fields.AddMarshaledJSONAt(9, []byte(`{
"cascadeDelete": false,
"collectionId": "qjp8lygssgwyqyz",
"hidden": false,
"id": "a23wkj9x",
"maxSelect": 1,
"minSelect": 0,
"name": "lastRunRef",
"presentable": false,
"required": false,
"system": false,
"type": "relation"
}`)); err != nil {
return err
}
if changed {
if err := app.Save(record); err != nil {
if err := app.Save(collection); err != nil {
return err
}
if _, err := app.DB().NewQuery("UPDATE workflow SET trigger = 'scheduled' WHERE trigger = 'auto'").Execute(); err != nil {
return err
}
tracer.Printf("collection '%s' updated", collection.Name)
records, err := app.FindAllRecords(collection)
if err != nil {
return err
} else {
for _, record := range records {
changed := false
draft := make(map[string]any)
if err := record.UnmarshalJSONField("draft", &draft); err == nil {
if _, ok := draft["config"]; ok {
config := draft["config"].(map[string]any)
if _, ok := config["trigger"]; ok {
trigger := config["trigger"].(string)
if trigger == "auto" {
config["trigger"] = "scheduled"
record.Set("draft", draft)
changed = true
}
}
}
}
content := make(map[string]any)
if err := record.UnmarshalJSONField("content", &content); err == nil {
if _, ok := content["config"]; ok {
config := content["config"].(map[string]any)
if _, ok := config["trigger"]; ok {
trigger := config["trigger"].(string)
if trigger == "auto" {
config["trigger"] = "scheduled"
record.Set("content", content)
changed = true
}
}
}
}
if changed {
if err := app.Save(record); err != nil {
return err
}
tracer.Printf("record #%s in collection '%s' updated", record.Id, collection.Name)
}
}
}
}
}
// update collection `workflow_run`
{
collection, err := app.FindCollectionByNameOrId("qjp8lygssgwyqyz")
if err != nil {
return err
} else if collection != nil {
if err := collection.Fields.AddMarshaledJSONAt(1, []byte(`{
"cascadeDelete": true,
"collectionId": "tovyif5ax6j62ur",
"hidden": false,
"id": "m8xfsyyy",
"maxSelect": 1,
"minSelect": 0,
"name": "workflowRef",
"presentable": false,
"required": false,
"system": false,
"type": "relation"
}`)); err != nil {
return err
}
if err := collection.Fields.AddMarshaledJSONAt(3, []byte(`{
"hidden": false,
"id": "jlroa3fk",
"maxSelect": 1,
"name": "trigger",
"presentable": false,
"required": false,
"system": false,
"type": "select",
"values": [
"manual",
"scheduled"
]
}`)); err != nil {
return err
}
if err := json.Unmarshal([]byte(`{
"indexes": [
"CREATE INDEX `+"`"+`idx_7ZpfjTFsD2`+"`"+` ON `+"`"+`workflow_run`+"`"+` (`+"`"+`workflowRef`+"`"+`)"
]
}`), &collection); err != nil {
return err
}
if err := app.Save(collection); err != nil {
return err
}
if _, err := app.DB().NewQuery("UPDATE workflow_run SET trigger = 'scheduled' WHERE trigger = 'auto'").Execute(); err != nil {
return err
}
tracer.Printf("collection '%s' updated", collection.Name)
records, err := app.FindAllRecords(collection)
if err != nil {
return err
} else {
for _, record := range records {
changed := false
detail := make(map[string]any)
if err := record.UnmarshalJSONField("detail", &detail); err == nil {
if _, ok := detail["config"]; ok {
config := detail["config"].(map[string]any)
if _, ok := config["trigger"]; ok {
trigger := config["trigger"].(string)
if trigger == "auto" {
config["trigger"] = "scheduled"
record.Set("detail", detail)
changed = true
}
}
}
}
if changed {
if err := app.Save(record); err != nil {
return err
}
tracer.Printf("record #%s in collection '%s' updated", record.Id, collection.Name)
}
}
}
}
}
// update collection `workflow_output`
{
collection, err := app.FindCollectionByNameOrId("bqnxb95f2cooowp")
if err != nil {
return err
} else if collection != nil {
if err := json.Unmarshal([]byte(`{
"indexes": [
"CREATE INDEX `+"`"+`idx_BYoQPsz4my`+"`"+` ON `+"`"+`workflow_output`+"`"+` (`+"`"+`workflowRef`+"`"+`)",
"CREATE INDEX `+"`"+`idx_O9zxLETuxJ`+"`"+` ON `+"`"+`workflow_output`+"`"+` (`+"`"+`runRef`+"`"+`)",
"CREATE INDEX `+"`"+`idx_luac8Ul34G`+"`"+` ON `+"`"+`workflow_output`+"`"+` (`+"`"+`nodeId`+"`"+`)"
]
}`), &collection); err != nil {
return err
}
if err := collection.Fields.AddMarshaledJSONAt(1, []byte(`{
"cascadeDelete": true,
"collectionId": "tovyif5ax6j62ur",
"hidden": false,
"id": "jka88auc",
"maxSelect": 1,
"minSelect": 0,
"name": "workflowRef",
"presentable": false,
"required": false,
"system": false,
"type": "relation"
}`)); err != nil {
return err
}
if err := collection.Fields.AddMarshaledJSONAt(2, []byte(`{
"cascadeDelete": true,
"collectionId": "qjp8lygssgwyqyz",
"hidden": false,
"id": "relation821863227",
"maxSelect": 1,
"minSelect": 0,
"name": "runRef",
"presentable": false,
"required": false,
"system": false,
"type": "relation"
}`)); err != nil {
return err
}
if err := app.Save(collection); err != nil {
return err
}
tracer.Printf("collection '%s' updated", collection.Name)
}
}
// update collection `workflow_logs`
{
collection, err := app.FindCollectionByNameOrId("pbc_1682296116")
if err != nil {
return err
} else if collection != nil {
if field := collection.Fields.GetByName("level"); field != nil && field.Type() == "text" {
if _, err := app.DB().NewQuery("UPDATE workflow_logs SET level = -4 WHERE level = 'DEBUG'").Execute(); err != nil {
return err
}
if _, err := app.DB().NewQuery("UPDATE workflow_logs SET level = 0 WHERE level = 'INFO'").Execute(); err != nil {
return err
}
if _, err := app.DB().NewQuery("UPDATE workflow_logs SET level = 4 WHERE level = 'WARN'").Execute(); err != nil {
return err
}
if _, err := app.DB().NewQuery("UPDATE workflow_logs SET level = 8 WHERE level = 'ERROR'").Execute(); err != nil {
return err
}
tracer.Printf("record #%s in collection '%s' updated", record.Id, collection.Name)
if err := collection.Fields.AddMarshaledJSONAt(7, []byte(`{
"hidden": false,
"id": "number760395071",
"max": null,
"min": null,
"name": "levelTmp",
"onlyInt": false,
"presentable": false,
"required": false,
"system": false,
"type": "number"
}`)); err != nil {
return err
}
if err := app.Save(collection); err != nil {
return err
}
collection.Fields.RemoveById(field.GetId())
if err := app.Save(collection); err != nil {
return err
}
if err := collection.Fields.AddMarshaledJSONAt(6, []byte(`{
"hidden": false,
"id": "number760395071",
"max": null,
"min": null,
"name": "level",
"onlyInt": false,
"presentable": false,
"required": false,
"system": false,
"type": "number"
}`)); err != nil {
return err
}
if err := app.Save(collection); err != nil {
return err
}
}
if err := collection.Fields.AddMarshaledJSONAt(1, []byte(`{
"cascadeDelete": true,
"collectionId": "tovyif5ax6j62ur",
"hidden": false,
"id": "relation3371272342",
"maxSelect": 1,
"minSelect": 0,
"name": "workflowRef",
"presentable": false,
"required": false,
"system": false,
"type": "relation"
}`)); err != nil {
return err
}
if err := collection.Fields.AddMarshaledJSONAt(2, []byte(`{
"cascadeDelete": true,
"collectionId": "qjp8lygssgwyqyz",
"hidden": false,
"id": "relation821863227",
"maxSelect": 1,
"minSelect": 0,
"name": "runRef",
"presentable": false,
"required": false,
"system": false,
"type": "relation"
}`)); err != nil {
return err
}
if err := json.Unmarshal([]byte(`{
"indexes": [
"CREATE INDEX `+"`"+`idx_IOlpy6XuJ2`+"`"+` ON `+"`"+`workflow_logs`+"`"+` (`+"`"+`workflowRef`+"`"+`)",
"CREATE INDEX `+"`"+`idx_qVlTb2yl7v`+"`"+` ON `+"`"+`workflow_logs`+"`"+` (`+"`"+`runRef`+"`"+`)",
"CREATE INDEX `+"`"+`idx_UL4tdCXNlA`+"`"+` ON `+"`"+`workflow_logs`+"`"+` (`+"`"+`nodeId`+"`"+`)"
]
}`), &collection); err != nil {
return err
}
if err := app.Save(collection); err != nil {
return err
}
tracer.Printf("collection '%s' updated", collection.Name)
}
}
@@ -45,7 +45,7 @@ const CertificateDetail = ({ data, ...props }: CertificateDetailProps) => {
<Form.Item label={t("certificate.props.validity")}>
<Input
value={`${dayjs(data.effectAt).format("YYYY-MM-DD HH:mm:ss")} ~ ${dayjs(data.expireAt).format("YYYY-MM-DD HH:mm:ss")}`}
value={`${dayjs(data.validityNotBefore).format("YYYY-MM-DD HH:mm:ss")} ~ ${dayjs(data.validityNotAfter).format("YYYY-MM-DD HH:mm:ss")}`}
variant="filled"
placeholder=""
/>
@@ -239,7 +239,7 @@ const WorkflowRunLogs = ({ runId, runStatus }: { runId: string; runStatus: strin
style={{ color: "inherit" }}
bordered={false}
defaultActiveKey={listData.map((group) => group.id)}
expandIcon={({ isActive }) => <IconChevronRight className={mergeCls(isActive ? "" : "rotate-90", "transition-transform")} size="1.25em" />}
expandIcon={({ isActive }) => <IconChevronRight className={mergeCls(isActive ? "rotate-90" : "", "transition-transform")} size="1.25em" />}
items={listData.map((group) => {
return {
key: group.id,
+6 -2
View File
@@ -64,8 +64,8 @@ const WorkflowRuns = ({ className, style, workflowId }: WorkflowRunsProps) => {
title: t("workflow_run.props.trigger"),
ellipsis: true,
render: (_, record) => {
if (record.trigger === WORKFLOW_TRIGGERS.AUTO) {
return t("workflow_run.props.trigger.auto");
if (record.trigger === WORKFLOW_TRIGGERS.SCHEDULED) {
return t("workflow_run.props.trigger.scheduled");
} else if (record.trigger === WORKFLOW_TRIGGERS.MANUAL) {
return t("workflow_run.props.trigger.manual");
}
@@ -203,6 +203,10 @@ const WorkflowRuns = ({ className, style, workflowId }: WorkflowRunsProps) => {
return [...prev];
});
if (cb.record.id === detailDrawerProps.data?.id) {
setDetailRecord({ ...detailDrawerProps.data, ...cb.record });
}
if (cb.record.status !== WORKFLOW_RUN_STATUSES.PENDING && cb.record.status !== WORKFLOW_RUN_STATUSES.RUNNING) {
unsubscribeWorkflowRun(item.id);
}
@@ -40,14 +40,14 @@ const StartNode = ({ node, disabled }: StartNodeProps) => {
return (
<div className="flex items-center justify-between space-x-2">
<Typography.Text className="truncate">
{config.trigger === WORKFLOW_TRIGGERS.AUTO
? t("workflow.props.trigger.auto")
{config.trigger === WORKFLOW_TRIGGERS.SCHEDULED
? t("workflow.props.trigger.scheduled")
: config.trigger === WORKFLOW_TRIGGERS.MANUAL
? t("workflow.props.trigger.manual")
: "\u00A0"}
</Typography.Text>
<Typography.Text className="truncate" type="secondary">
{config.trigger === WORKFLOW_TRIGGERS.AUTO ? config.triggerCron : ""}
{config.trigger === WORKFLOW_TRIGGERS.SCHEDULED ? config.triggerCron : ""}
</Typography.Text>
</div>
);
@@ -40,7 +40,7 @@ const StartNodeConfigForm = forwardRef<StartNodeConfigFormInstance, StartNodeCon
.string()
.nullish()
.refine((v) => {
if (fieldTrigger !== WORKFLOW_TRIGGERS.AUTO) return true;
if (fieldTrigger !== WORKFLOW_TRIGGERS.SCHEDULED) return true;
return validCronExpression(v!);
}, t("workflow_node.start.form.trigger_cron.errmsg.invalid")),
});
@@ -58,7 +58,7 @@ const StartNodeConfigForm = forwardRef<StartNodeConfigFormInstance, StartNodeCon
}, [fieldTriggerCron]);
const handleTriggerChange = (value: string) => {
if (value === WORKFLOW_TRIGGERS.AUTO) {
if (value === WORKFLOW_TRIGGERS.SCHEDULED) {
formInst.setFieldValue("triggerCron", formProps.initialValues?.triggerCron || initFormModel().triggerCron);
} else {
formInst.setFieldValue("triggerCron", undefined);
@@ -89,7 +89,7 @@ const StartNodeConfigForm = forwardRef<StartNodeConfigFormInstance, StartNodeCon
<Form className={className} style={style} {...formProps} disabled={disabled} layout="vertical" scrollToFirstError onValuesChange={handleFormChange}>
<Form.Item name="trigger" label={t("workflow_node.start.form.trigger.label")} rules={[formRule]}>
<Radio.Group onChange={(e) => handleTriggerChange(e.target.value)}>
<Radio value={WORKFLOW_TRIGGERS.AUTO}>{t("workflow_node.start.form.trigger.option.auto.label")}</Radio>
<Radio value={WORKFLOW_TRIGGERS.SCHEDULED}>{t("workflow_node.start.form.trigger.option.scheduled.label")}</Radio>
<Radio value={WORKFLOW_TRIGGERS.MANUAL}>{t("workflow_node.start.form.trigger.option.manual.label")}</Radio>
</Radio.Group>
</Form.Item>
@@ -97,7 +97,7 @@ const StartNodeConfigForm = forwardRef<StartNodeConfigFormInstance, StartNodeCon
<Form.Item
name="triggerCron"
label={t("workflow_node.start.form.trigger_cron.label")}
hidden={fieldTrigger !== WORKFLOW_TRIGGERS.AUTO}
hidden={fieldTrigger !== WORKFLOW_TRIGGERS.SCHEDULED}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.start.form.trigger_cron.tooltip") }}></span>}
extra={
@@ -118,7 +118,7 @@ const StartNodeConfigForm = forwardRef<StartNodeConfigFormInstance, StartNodeCon
<Input placeholder={t("workflow_node.start.form.trigger_cron.placeholder")} />
</Form.Item>
<Show when={fieldTrigger === WORKFLOW_TRIGGERS.AUTO}>
<Show when={fieldTrigger === WORKFLOW_TRIGGERS.SCHEDULED}>
<Form.Item>
<Alert type="info" message={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.start.form.trigger_cron.guide") }}></span>} />
</Form.Item>
+5 -5
View File
@@ -8,16 +8,16 @@ export interface CertificateModel extends BaseModel {
privateKey: string;
issuerOrg: string;
keyAlgorithm: string;
effectAt: ISO8601String;
expireAt: ISO8601String;
workflowId: string;
validityNotBefore: ISO8601String;
validityNotAfter: ISO8601String;
workflowRef: string;
expand?: {
workflowId?: WorkflowModel; // TODO: ugly, maybe to use an alias?
workflowRef?: WorkflowModel;
};
}
export const CERTIFICATE_SOURCES = Object.freeze({
WORKFLOW: "workflow",
REQUEST: "request",
UPLOAD: "upload",
} as const);
+3 -3
View File
@@ -13,13 +13,13 @@ export interface WorkflowModel extends BaseModel {
content?: WorkflowNode;
draft?: WorkflowNode;
hasDraft?: boolean;
lastRunId?: string;
lastRunRef?: string;
lastRunStatus?: string;
lastRunTime?: string;
}
export const WORKFLOW_TRIGGERS = Object.freeze({
AUTO: "auto",
SCHEDULED: "scheduled",
MANUAL: "manual",
} as const);
@@ -135,7 +135,7 @@ export type WorkflowNodeConfigForStart = {
export const defaultNodeConfigForStart = (): Partial<WorkflowNodeConfigForStart> => {
return {
trigger: WORKFLOW_TRIGGERS.AUTO,
trigger: WORKFLOW_TRIGGERS.SCHEDULED,
triggerCron: "0 0 * * *",
};
};
+2 -2
View File
@@ -1,14 +1,14 @@
import { type WorkflowModel } from "./workflow";
export interface WorkflowRunModel extends BaseModel {
workflowId: string;
workflowRef: string;
status: string;
trigger: string;
startedAt: ISO8601String;
endedAt: ISO8601String;
error?: string;
expand?: {
workflowId?: WorkflowModel; // TODO: ugly, maybe to use an alias?
workflowRef?: WorkflowModel;
};
}
+1 -1
View File
@@ -26,7 +26,7 @@
"certificate.props.validity.filter.expired": "Expired",
"certificate.props.brand": "Brand",
"certificate.props.source": "Source",
"certificate.props.source.workflow": "Workflow",
"certificate.props.source.request": "Request",
"certificate.props.source.upload": "Upload",
"certificate.props.certificate": "Certificate chain",
"certificate.props.private_key": "Private key",
+1 -1
View File
@@ -36,7 +36,7 @@
"workflow.props.name": "Name",
"workflow.props.description": "Description",
"workflow.props.trigger": "Trigger",
"workflow.props.trigger.auto": "Scheduled",
"workflow.props.trigger.scheduled": "Scheduled",
"workflow.props.trigger.manual": "Manual",
"workflow.props.last_run_at": "Last run at",
"workflow.props.state": "Active",
@@ -15,7 +15,7 @@
"workflow_node.start.default_name": "Start",
"workflow_node.start.form.trigger.label": "Trigger",
"workflow_node.start.form.trigger.placeholder": "Please select trigger",
"workflow_node.start.form.trigger.option.auto.label": "Scheduled",
"workflow_node.start.form.trigger.option.scheduled.label": "Scheduled",
"workflow_node.start.form.trigger.option.manual.label": "Manual",
"workflow_node.start.form.trigger_cron.label": "Cron expression",
"workflow_node.start.form.trigger_cron.placeholder": "Please enter cron expression",
@@ -20,7 +20,7 @@
"workflow_run.props.status.failed": "Failed",
"workflow_run.props.status.canceled": "Canceled",
"workflow_run.props.trigger": "Trigger",
"workflow_run.props.trigger.auto": "Timing",
"workflow_run.props.trigger.scheduled": "Scheduled",
"workflow_run.props.trigger.manual": "Manual",
"workflow_run.props.started_at": "Started at",
"workflow_run.props.ended_at": "Ended at",
+2 -2
View File
@@ -26,8 +26,8 @@
"certificate.props.validity.filter.expired": "已过期",
"certificate.props.brand": "证书品牌",
"certificate.props.source": "来源",
"certificate.props.source.workflow": "工作流",
"certificate.props.source.upload": "用户上传",
"certificate.props.source.request": "申请",
"certificate.props.source.upload": "上传",
"certificate.props.certificate": "证书内容",
"certificate.props.private_key": "私钥内容",
"certificate.props.serial_number": "证书序列号",
+1 -1
View File
@@ -36,7 +36,7 @@
"workflow.props.name": "名称",
"workflow.props.description": "描述",
"workflow.props.trigger": "触发方式",
"workflow.props.trigger.auto": "定时",
"workflow.props.trigger.scheduled": "定时",
"workflow.props.trigger.manual": "手动",
"workflow.props.last_run_at": "最近执行时间",
"workflow.props.state": "启用",
@@ -15,7 +15,7 @@
"workflow_node.start.default_name": "开始",
"workflow_node.start.form.trigger.label": "触发方式",
"workflow_node.start.form.trigger.placeholder": "请选择触发方式",
"workflow_node.start.form.trigger.option.auto.label": "定时触发",
"workflow_node.start.form.trigger.option.scheduled.label": "定时触发",
"workflow_node.start.form.trigger.option.manual.label": "手动触发",
"workflow_node.start.form.trigger_cron.label": "Cron 表达式",
"workflow_node.start.form.trigger_cron.placeholder": "请输入 Cron 表达式",
@@ -20,7 +20,7 @@
"workflow_run.props.status.failed": "已失败",
"workflow_run.props.status.canceled": "已取消",
"workflow_run.props.trigger": "执行方式",
"workflow_run.props.trigger.auto": "定时执行",
"workflow_run.props.trigger.scheduled": "定时执行",
"workflow_run.props.trigger.manual": "手动执行",
"workflow_run.props.started_at": "开始时间",
"workflow_run.props.ended_at": "完成时间",
@@ -49,14 +49,14 @@ const CertificateList = () => {
render: (_, record) => <Typography.Text>{record.subjectAltNames}</Typography.Text>,
},
{
key: "expiry",
key: "validity",
title: t("certificate.props.validity"),
sorter: true,
sortOrder: sorter.columnKey === "expiry" ? sorter.order : undefined,
sortOrder: sorter.columnKey === "validity" ? sorter.order : undefined,
render: (_, record) => {
const total = dayjs(record.expireAt).diff(dayjs(record.created), "d") + 1;
const isExpired = dayjs().isAfter(dayjs(record.expireAt));
const leftHours = dayjs(record.expireAt).diff(dayjs(), "h");
const total = dayjs(record.validityNotAfter).diff(dayjs(record.created), "d") + 1;
const isExpired = dayjs().isAfter(dayjs(record.validityNotAfter));
const leftHours = dayjs(record.validityNotAfter).diff(dayjs(), "h");
const leftDays = Math.round(leftHours / 24);
return (
@@ -83,7 +83,7 @@ const CertificateList = () => {
)}
<Typography.Text ellipsis type="secondary">
{t("certificate.props.validity.expiration", { date: dayjs(record.expireAt).format("YYYY-MM-DD") })}
{t("certificate.props.validity.expiration", { date: dayjs(record.validityNotAfter).format("YYYY-MM-DD") })}
</Typography.Text>
</div>
);
@@ -103,7 +103,7 @@ const CertificateList = () => {
key: "source",
title: t("certificate.props.source"),
render: (_, record) => {
const workflowId = record.workflowId;
const workflowId = record.workflowRef;
return (
<div className="flex max-w-full flex-col gap-1 truncate">
<Typography.Text ellipsis>{t(`certificate.props.source.${record.source}`)}</Typography.Text>
@@ -117,7 +117,7 @@ const CertificateList = () => {
}
}}
>
{record.expand?.workflowId?.name ?? <span className="font-mono">{t(`#${workflowId}`)}</span>}
{record.expand?.workflowRef?.name ?? <span className="font-mono">{t(`#${workflowId}`)}</span>}
</Typography.Link>
</div>
);
@@ -218,7 +218,7 @@ const CertificateList = () => {
() => {
const { columnKey: sorterKey, order: sorterOrder } = sorter;
let sort: string | undefined;
sort = sorterKey === "expiry" ? "expireAt" : "";
sort = sorterKey === "validity" ? "validityNotAfter" : "";
sort = sort && (sorterOrder === "ascend" ? `${sort}` : sorterOrder === "descend" ? `-${sort}` : undefined);
return listCertificates({
+2 -2
View File
@@ -245,7 +245,7 @@ const WorkflowRunHistoryTable = ({ className, style }: { className?: string; sty
key: "name",
title: t("workflow.props.name"),
render: (_, record) => {
const workflow = record.expand?.workflowId;
const workflow = record.expand?.workflowRef;
return (
<div className="max-w-full truncate">
<Typography.Link
@@ -256,7 +256,7 @@ const WorkflowRunHistoryTable = ({ className, style }: { className?: string; sty
}
}}
>
{workflow?.name ?? <span className="font-mono">{t(`#${record.workflowId}`)}</span>}
{workflow?.name ?? <span className="font-mono">{t(`#${record.workflowRef}`)}</span>}
</Typography.Link>
</div>
);
+2 -2
View File
@@ -64,10 +64,10 @@ const WorkflowList = () => {
return "-";
} else if (trigger === WORKFLOW_TRIGGERS.MANUAL) {
return <Typography.Text>{t("workflow.props.trigger.manual")}</Typography.Text>;
} else if (trigger === WORKFLOW_TRIGGERS.AUTO) {
} else if (trigger === WORKFLOW_TRIGGERS.SCHEDULED) {
return (
<div className="flex max-w-full flex-col gap-1">
<Typography.Text>{t("workflow.props.trigger.auto")}</Typography.Text>
<Typography.Text>{t("workflow.props.trigger.scheduled")}</Typography.Text>
<Typography.Text type="secondary">{record.triggerCron || "\u00A0"}</Typography.Text>
</div>
);
+4 -4
View File
@@ -19,9 +19,9 @@ export const list = async (request: ListRequest) => {
filters.push(pb.filter("(subjectAltNames~{:keyword} || serialNumber={:keyword})", { keyword: request.keyword }));
}
if (request.state === "expireSoon") {
filters.push(pb.filter("expireAt<{:expiredAt} && expireAt>@now", { expiredAt: dayjs().add(20, "d").toDate() }));
filters.push(pb.filter("validityNotAfter<{:expiredAt} && validityNotAfter>@now", { expiredAt: dayjs().add(20, "d").toDate() }));
} else if (request.state === "expired") {
filters.push(pb.filter("expireAt<={:expiredAt}", { expiredAt: new Date() }));
filters.push(pb.filter("validityNotAfter<={:expiredAt}", { expiredAt: new Date() }));
}
const sort = request.sort || "-created";
@@ -30,7 +30,7 @@ export const list = async (request: ListRequest) => {
const perPage = request.perPage || 10;
return pb.collection(COLLECTION_NAME_CERTIFICATE).getList<CertificateModel>(page, perPage, {
expand: "workflowId",
expand: "workflowRef",
filter: filters.join(" && "),
sort: sort,
requestKey: null,
@@ -42,7 +42,7 @@ export const listByWorkflowRunId = async (workflowRunId: string) => {
const list = await pb.collection(COLLECTION_NAME_CERTIFICATE).getFullList<CertificateModel>({
batch: 65535,
filter: pb.filter("workflowRunId={:workflowRunId}", { workflowRunId: workflowRunId }),
filter: pb.filter("workflowRunRef={:workflowRunId}", { workflowRunId }),
sort: "created",
requestKey: null,
});
+1 -1
View File
@@ -7,7 +7,7 @@ export const listByWorkflowRunId = async (workflowRunId: string) => {
const list = await pb.collection(COLLECTION_NAME_WORKFLOW_LOG).getFullList<WorkflowLogModel>({
batch: 65535,
filter: pb.filter("runId={:runId}", { runId: workflowRunId }),
filter: pb.filter("runRef={:workflowRunId}", { workflowRunId }),
sort: "timestamp",
requestKey: null,
});
+2 -2
View File
@@ -16,7 +16,7 @@ export const list = async (request: ListRequest) => {
const filters: string[] = [];
if (request.workflowId) {
filters.push(pb.filter("workflowId={:workflowId}", { workflowId: request.workflowId }));
filters.push(pb.filter("workflowRef={:workflowId}", { workflowId: request.workflowId }));
}
const page = request.page || 1;
@@ -25,7 +25,7 @@ export const list = async (request: ListRequest) => {
filter: filters.join(" && "),
sort: "-created",
requestKey: null,
expand: request.expand ? "workflowId" : undefined,
expand: request.expand ? "workflowRef" : undefined,
});
};