调度中心嵌入cms
This commit is contained in:
1
.env
1
.env
@@ -8,4 +8,5 @@ MONGO_AUTHSOURCE=admin
|
|||||||
MONGO_DATABASE=openim_v3
|
MONGO_DATABASE=openim_v3
|
||||||
SCHEDULER_MONGO_DATABASE=scheduler_center
|
SCHEDULER_MONGO_DATABASE=scheduler_center
|
||||||
REDIS_ADDR=127.0.0.1:6379
|
REDIS_ADDR=127.0.0.1:6379
|
||||||
|
REDIS_USERNAME=
|
||||||
REDIS_PASSWORD=
|
REDIS_PASSWORD=
|
||||||
|
|||||||
@@ -8,4 +8,5 @@ MONGO_AUTHSOURCE=admin
|
|||||||
MONGO_DATABASE=openim_v3
|
MONGO_DATABASE=openim_v3
|
||||||
SCHEDULER_MONGO_DATABASE=scheduler_center
|
SCHEDULER_MONGO_DATABASE=scheduler_center
|
||||||
REDIS_ADDR=127.0.0.1:6379
|
REDIS_ADDR=127.0.0.1:6379
|
||||||
|
REDIS_USERNAME=
|
||||||
REDIS_PASSWORD=
|
REDIS_PASSWORD=
|
||||||
|
|||||||
@@ -7,3 +7,6 @@ MONGO_PASSWORD=rI57PJsJhnz_qlRkfnTa0RPT
|
|||||||
MONGO_AUTHSOURCE=admin
|
MONGO_AUTHSOURCE=admin
|
||||||
MONGO_DATABASE=openim_v3
|
MONGO_DATABASE=openim_v3
|
||||||
SCHEDULER_MONGO_DATABASE=scheduler_center_local
|
SCHEDULER_MONGO_DATABASE=scheduler_center_local
|
||||||
|
REDIS_ADDR=127.0.0.1:6379
|
||||||
|
REDIS_USERNAME=
|
||||||
|
REDIS_PASSWORD=
|
||||||
|
|||||||
@@ -7,3 +7,6 @@ MONGO_PASSWORD=rI57PJsJhnz_qlRkfnTa0RPT
|
|||||||
MONGO_AUTHSOURCE=admin
|
MONGO_AUTHSOURCE=admin
|
||||||
MONGO_DATABASE=openim_v3
|
MONGO_DATABASE=openim_v3
|
||||||
SCHEDULER_MONGO_DATABASE=scheduler_center
|
SCHEDULER_MONGO_DATABASE=scheduler_center
|
||||||
|
REDIS_ADDR=127.0.0.1:6379
|
||||||
|
REDIS_USERNAME=
|
||||||
|
REDIS_PASSWORD=
|
||||||
|
|||||||
@@ -56,6 +56,9 @@ MONGO_PASSWORD=
|
|||||||
MONGO_AUTHSOURCE=admin
|
MONGO_AUTHSOURCE=admin
|
||||||
MONGO_DATABASE=openim_v3
|
MONGO_DATABASE=openim_v3
|
||||||
SCHEDULER_MONGO_DATABASE=scheduler_center
|
SCHEDULER_MONGO_DATABASE=scheduler_center
|
||||||
|
REDIS_ADDR=127.0.0.1:6379
|
||||||
|
REDIS_USERNAME=
|
||||||
|
REDIS_PASSWORD=
|
||||||
```
|
```
|
||||||
|
|
||||||
## 启动方式
|
## 启动方式
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import (
|
|||||||
storemongo "scheduler-backend/internal/store/mongo"
|
storemongo "scheduler-backend/internal/store/mongo"
|
||||||
"scheduler-backend/pkg/config"
|
"scheduler-backend/pkg/config"
|
||||||
"scheduler-backend/pkg/log"
|
"scheduler-backend/pkg/log"
|
||||||
|
redisclient "scheduler-backend/pkg/redis"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -31,6 +32,11 @@ func main() {
|
|||||||
logger.Error("scheduler api mongo connect failed", "error", err)
|
logger.Error("scheduler api mongo connect failed", "error", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
redisConn, err := redisclient.Connect(rootCtx, cfg)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("scheduler api redis connect failed", "error", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
jobConfigStore := storemongo.NewJobConfigStore(databases.MetaDB)
|
jobConfigStore := storemongo.NewJobConfigStore(databases.MetaDB)
|
||||||
if err := jobdef.SyncJobConfigs(rootCtx, "job-config-list", jobConfigStore, logger); err != nil {
|
if err := jobdef.SyncJobConfigs(rootCtx, "job-config-list", jobConfigStore, logger); err != nil {
|
||||||
logger.Error("sync job configs failed", "error", err)
|
logger.Error("sync job configs failed", "error", err)
|
||||||
@@ -42,16 +48,18 @@ func main() {
|
|||||||
Logger: logger,
|
Logger: logger,
|
||||||
MetaDB: databases.MetaDB,
|
MetaDB: databases.MetaDB,
|
||||||
BusinessDB: databases.BusinessDB,
|
BusinessDB: databases.BusinessDB,
|
||||||
|
Redis: redisConn,
|
||||||
})
|
})
|
||||||
|
|
||||||
router := api.NewRouter(api.RouterDeps{
|
router := api.NewRouter(api.RouterDeps{
|
||||||
Registry: registry,
|
Registry: registry,
|
||||||
JobConfigStore: jobConfigStore,
|
JobConfigStore: jobConfigStore,
|
||||||
ExecutionStore: storemongo.NewJobExecutionStore(databases.MetaDB),
|
ExecutionStore: storemongo.NewJobExecutionStore(databases.MetaDB),
|
||||||
ProfileStore: storemongo.NewAdminProfileStore(databases.MetaDB),
|
ProfileStore: storemongo.NewAdminProfileStore(databases.MetaDB),
|
||||||
AdminUserStore: storemongo.NewAdminUserStore(databases.MetaDB),
|
AdminUserStore: storemongo.NewAdminUserStore(databases.MetaDB),
|
||||||
ConfigStore: storemongo.NewSystemConfigStore(databases.MetaDB),
|
ConfigStore: storemongo.NewSystemConfigStore(databases.MetaDB),
|
||||||
Executor: execSvc,
|
GlobalConfigStore: storemongo.NewGlobalConfigStore(databases.MetaDB),
|
||||||
|
Executor: execSvc,
|
||||||
})
|
})
|
||||||
logger.Info("scheduler api starting", "addr", addr)
|
logger.Info("scheduler api starting", "addr", addr)
|
||||||
|
|
||||||
@@ -62,6 +70,7 @@ func main() {
|
|||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
_ = redisConn.Close()
|
||||||
_ = databases.Client.Disconnect(ctx)
|
_ = databases.Client.Disconnect(ctx)
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}()
|
}()
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import (
|
|||||||
storemongo "scheduler-backend/internal/store/mongo"
|
storemongo "scheduler-backend/internal/store/mongo"
|
||||||
"scheduler-backend/pkg/config"
|
"scheduler-backend/pkg/config"
|
||||||
"scheduler-backend/pkg/log"
|
"scheduler-backend/pkg/log"
|
||||||
|
redisclient "scheduler-backend/pkg/redis"
|
||||||
|
|
||||||
"github.com/go-co-op/gocron/v2"
|
"github.com/go-co-op/gocron/v2"
|
||||||
)
|
)
|
||||||
@@ -33,6 +34,11 @@ func main() {
|
|||||||
logger.Error("scheduler worker mongo connect failed", "error", err)
|
logger.Error("scheduler worker mongo connect failed", "error", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
redisConn, err := redisclient.Connect(rootCtx, cfg)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("scheduler worker redis connect failed", "error", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
jobConfigStore := storemongo.NewJobConfigStore(databases.MetaDB)
|
jobConfigStore := storemongo.NewJobConfigStore(databases.MetaDB)
|
||||||
if err := jobdef.SyncJobConfigs(rootCtx, "job-config-list", jobConfigStore, logger); err != nil {
|
if err := jobdef.SyncJobConfigs(rootCtx, "job-config-list", jobConfigStore, logger); err != nil {
|
||||||
@@ -45,6 +51,7 @@ func main() {
|
|||||||
Logger: logger,
|
Logger: logger,
|
||||||
MetaDB: databases.MetaDB,
|
MetaDB: databases.MetaDB,
|
||||||
BusinessDB: databases.BusinessDB,
|
BusinessDB: databases.BusinessDB,
|
||||||
|
Redis: redisConn,
|
||||||
}
|
}
|
||||||
execSvc := executor.NewService(registry, runtime)
|
execSvc := executor.NewService(registry, runtime)
|
||||||
g, err := gocron.NewScheduler()
|
g, err := gocron.NewScheduler()
|
||||||
@@ -99,6 +106,9 @@ func main() {
|
|||||||
if err := healthServer.Shutdown(shutdownCtx); err != nil {
|
if err := healthServer.Shutdown(shutdownCtx); err != nil {
|
||||||
logger.Error("scheduler worker health server shutdown failed", "error", err)
|
logger.Error("scheduler worker health server shutdown failed", "error", err)
|
||||||
}
|
}
|
||||||
|
if err := redisConn.Close(); err != nil {
|
||||||
|
logger.Error("scheduler worker redis disconnect failed", "error", err)
|
||||||
|
}
|
||||||
if err := databases.Client.Disconnect(context.Background()); err != nil {
|
if err := databases.Client.Disconnect(context.Background()); err != nil {
|
||||||
logger.Error("scheduler worker mongo disconnect failed", "error", err)
|
logger.Error("scheduler worker mongo disconnect failed", "error", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ type executionItem struct {
|
|||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
JobName string `json:"jobName"`
|
JobName string `json:"jobName"`
|
||||||
TriggerType string `json:"triggerType"`
|
TriggerType string `json:"triggerType"`
|
||||||
|
ScheduleType string `json:"scheduleType,omitempty"`
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
StartedAt string `json:"startedAt,omitempty"`
|
StartedAt string `json:"startedAt,omitempty"`
|
||||||
FinishedAt string `json:"finishedAt,omitempty"`
|
FinishedAt string `json:"finishedAt,omitempty"`
|
||||||
@@ -61,6 +62,7 @@ type executionDetailItem struct {
|
|||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
JobConfigID string `json:"jobConfigId"`
|
JobConfigID string `json:"jobConfigId"`
|
||||||
TriggerType string `json:"triggerType"`
|
TriggerType string `json:"triggerType"`
|
||||||
|
ScheduleType string `json:"scheduleType,omitempty"`
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
ParamsSnapshot string `json:"paramsSnapshot"`
|
ParamsSnapshot string `json:"paramsSnapshot"`
|
||||||
StartedAt string `json:"startedAt,omitempty"`
|
StartedAt string `json:"startedAt,omitempty"`
|
||||||
@@ -133,6 +135,12 @@ type dashboardOverviewResponse struct {
|
|||||||
RecentExecutions []executionItem `json:"recentExecutions"`
|
RecentExecutions []executionItem `json:"recentExecutions"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type globalConfigItem struct {
|
||||||
|
Config string `json:"config"`
|
||||||
|
UpdatedBy string `json:"updatedBy"`
|
||||||
|
UpdatedAt string `json:"updatedAt,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type JobConfigLister interface {
|
type JobConfigLister interface {
|
||||||
List(ctx context.Context) ([]model.JobConfig, error)
|
List(ctx context.Context) ([]model.JobConfig, error)
|
||||||
GetByID(ctx context.Context, id primitive.ObjectID) (*model.JobConfig, error)
|
GetByID(ctx context.Context, id primitive.ObjectID) (*model.JobConfig, error)
|
||||||
@@ -173,14 +181,20 @@ type SystemConfigStore interface {
|
|||||||
ToggleEnabled(ctx context.Context, id primitive.ObjectID, enabled bool) error
|
ToggleEnabled(ctx context.Context, id primitive.ObjectID, enabled bool) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GlobalConfigStore interface {
|
||||||
|
Get(ctx context.Context, key string) (*model.GlobalConfig, error)
|
||||||
|
Upsert(ctx context.Context, item *model.GlobalConfig) error
|
||||||
|
}
|
||||||
|
|
||||||
type RouterDeps struct {
|
type RouterDeps struct {
|
||||||
Registry *jobdef.Registry
|
Registry *jobdef.Registry
|
||||||
JobConfigStore JobConfigLister
|
JobConfigStore JobConfigLister
|
||||||
ExecutionStore JobExecutionLister
|
ExecutionStore JobExecutionLister
|
||||||
ProfileStore AdminProfileStore
|
ProfileStore AdminProfileStore
|
||||||
AdminUserStore AdminUserStore
|
AdminUserStore AdminUserStore
|
||||||
ConfigStore SystemConfigStore
|
ConfigStore SystemConfigStore
|
||||||
Executor *executor.Service
|
GlobalConfigStore GlobalConfigStore
|
||||||
|
Executor *executor.Service
|
||||||
}
|
}
|
||||||
|
|
||||||
type upsertJobRequest struct {
|
type upsertJobRequest struct {
|
||||||
@@ -243,6 +257,12 @@ type toggleSystemConfigRequest struct {
|
|||||||
Enabled bool `json:"enabled"`
|
Enabled bool `json:"enabled"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type updateGlobalConfigRequest struct {
|
||||||
|
Config json.RawMessage `json:"config" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
const schedulerGlobalConfigKey = "scheduler"
|
||||||
|
|
||||||
func NewRouter(deps RouterDeps) *gin.Engine {
|
func NewRouter(deps RouterDeps) *gin.Engine {
|
||||||
router := gin.Default()
|
router := gin.Default()
|
||||||
router.Use(func(c *gin.Context) {
|
router.Use(func(c *gin.Context) {
|
||||||
@@ -579,6 +599,35 @@ func NewRouter(deps RouterDeps) *gin.Engine {
|
|||||||
"errorMessage": execRecord.ErrorMessage,
|
"errorMessage": execRecord.ErrorMessage,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
router.GET("/admin/scheduler/global-config", func(c *gin.Context) {
|
||||||
|
item, err := deps.GlobalConfigStore.Get(c.Request.Context(), schedulerGlobalConfigKey)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, toGlobalConfigItem(*item))
|
||||||
|
})
|
||||||
|
router.PUT("/admin/scheduler/global-config", func(c *gin.Context) {
|
||||||
|
var req updateGlobalConfigRequest
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !json.Valid(req.Config) {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": errInvalidJSON("config").Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
item := &model.GlobalConfig{
|
||||||
|
Key: schedulerGlobalConfigKey,
|
||||||
|
Config: string(req.Config),
|
||||||
|
UpdatedBy: "admin",
|
||||||
|
}
|
||||||
|
if err := deps.GlobalConfigStore.Upsert(c.Request.Context(), item); err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, toGlobalConfigItem(*item))
|
||||||
|
})
|
||||||
router.GET("/admin/scheduler/executions", func(c *gin.Context) {
|
router.GET("/admin/scheduler/executions", func(c *gin.Context) {
|
||||||
jobs, err := deps.JobConfigStore.List(c.Request.Context())
|
jobs, err := deps.JobConfigStore.List(c.Request.Context())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -592,16 +641,20 @@ func NewRouter(deps RouterDeps) *gin.Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
jobNameMap := make(map[string]string, len(jobs))
|
jobNameMap := make(map[string]string, len(jobs))
|
||||||
|
jobScheduleTypeMap := make(map[string]string, len(jobs))
|
||||||
for _, item := range jobs {
|
for _, item := range jobs {
|
||||||
jobNameMap[item.ID.Hex()] = item.Name
|
jobNameMap[item.ID.Hex()] = item.Name
|
||||||
|
jobScheduleTypeMap[item.ID.Hex()] = item.ScheduleType
|
||||||
}
|
}
|
||||||
|
|
||||||
responseItems := make([]executionItem, 0, len(items))
|
responseItems := make([]executionItem, 0, len(items))
|
||||||
for _, item := range items {
|
for _, item := range items {
|
||||||
|
jobID := item.JobConfigID.Hex()
|
||||||
responseItems = append(responseItems, executionItem{
|
responseItems = append(responseItems, executionItem{
|
||||||
ID: objectIDToString(item.ID),
|
ID: objectIDToString(item.ID),
|
||||||
JobName: jobDisplayName(jobNameMap, item.JobConfigID),
|
JobName: jobDisplayName(jobNameMap, item.JobConfigID),
|
||||||
TriggerType: item.TriggerType,
|
TriggerType: item.TriggerType,
|
||||||
|
ScheduleType: jobScheduleTypeMap[jobID],
|
||||||
Status: item.Status,
|
Status: item.Status,
|
||||||
StartedAt: formatTime(item.StartedAt),
|
StartedAt: formatTime(item.StartedAt),
|
||||||
FinishedAt: formatTime(item.FinishedAt),
|
FinishedAt: formatTime(item.FinishedAt),
|
||||||
@@ -631,7 +684,17 @@ func NewRouter(deps RouterDeps) *gin.Engine {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, toExecutionDetailItem(*item))
|
detail := toExecutionDetailItem(*item)
|
||||||
|
job, err := deps.JobConfigStore.GetByID(c.Request.Context(), item.JobConfigID)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if job != nil {
|
||||||
|
detail.ScheduleType = job.ScheduleType
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, detail)
|
||||||
})
|
})
|
||||||
router.GET("/admin/scheduler/executions/:id/logfile", func(c *gin.Context) {
|
router.GET("/admin/scheduler/executions/:id/logfile", func(c *gin.Context) {
|
||||||
id, err := primitive.ObjectIDFromHex(c.Param("id"))
|
id, err := primitive.ObjectIDFromHex(c.Param("id"))
|
||||||
@@ -900,6 +963,14 @@ func toExecutionDetailItem(item model.JobExecution) executionDetailItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func toGlobalConfigItem(item model.GlobalConfig) globalConfigItem {
|
||||||
|
return globalConfigItem{
|
||||||
|
Config: item.Config,
|
||||||
|
UpdatedBy: item.UpdatedBy,
|
||||||
|
UpdatedAt: formatTime(&item.UpdatedAt),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func toProfileItem(item model.AdminProfile) profileItem {
|
func toProfileItem(item model.AdminProfile) profileItem {
|
||||||
return profileItem{
|
return profileItem{
|
||||||
ID: objectIDToString(item.ID),
|
ID: objectIDToString(item.ID),
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/redis/go-redis/v9"
|
||||||
"go.mongodb.org/mongo-driver/mongo"
|
"go.mongodb.org/mongo-driver/mongo"
|
||||||
|
|
||||||
"scheduler-backend/pkg/config"
|
"scheduler-backend/pkg/config"
|
||||||
@@ -93,6 +94,7 @@ type Runtime struct {
|
|||||||
Logger log.Logger
|
Logger log.Logger
|
||||||
MetaDB *mongo.Database
|
MetaDB *mongo.Database
|
||||||
BusinessDB *mongo.Database
|
BusinessDB *mongo.Database
|
||||||
|
Redis *redis.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
type Handler interface {
|
type Handler interface {
|
||||||
|
|||||||
@@ -36,7 +36,9 @@ func (URLRewriteHandler) Run(ctx context.Context, runtime Runtime, req ExecuteRe
|
|||||||
|
|
||||||
redisCfg := urlrewrite.RedisConfig{
|
redisCfg := urlrewrite.RedisConfig{
|
||||||
Addr: runtime.Config.RedisAddr,
|
Addr: runtime.Config.RedisAddr,
|
||||||
|
Username: runtime.Config.RedisUsername,
|
||||||
Password: runtime.Config.RedisPassword,
|
Password: runtime.Config.RedisPassword,
|
||||||
|
Client: runtime.Redis,
|
||||||
}
|
}
|
||||||
batchID, err := urlrewrite.Run(ctx, runtime.BusinessDB, params, redisCfg, logf)
|
batchID, err := urlrewrite.Run(ctx, runtime.BusinessDB, params, redisCfg, logf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
15
internal/store/model/global_config.go
Normal file
15
internal/store/model/global_config.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GlobalConfig struct {
|
||||||
|
ID primitive.ObjectID `bson:"_id,omitempty" json:"id"`
|
||||||
|
Key string `bson:"key" json:"key"`
|
||||||
|
Config string `bson:"config" json:"config"`
|
||||||
|
UpdatedBy string `bson:"updatedBy" json:"updatedBy"`
|
||||||
|
UpdatedAt time.Time `bson:"updatedAt" json:"updatedAt"`
|
||||||
|
}
|
||||||
86
internal/store/mongo/global_config_store.go
Normal file
86
internal/store/mongo/global_config_store.go
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
package mongo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
|
mongodriver "go.mongodb.org/mongo-driver/mongo"
|
||||||
|
"go.mongodb.org/mongo-driver/mongo/options"
|
||||||
|
|
||||||
|
"scheduler-backend/internal/store/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
const schedulerGlobalConfigKey = "scheduler"
|
||||||
|
const defaultSchedulerGlobalConfigJSON = `{
|
||||||
|
"maxConcurrentJobs": 5,
|
||||||
|
"defaultTimeoutSeconds": 300
|
||||||
|
}`
|
||||||
|
|
||||||
|
type GlobalConfigStore struct {
|
||||||
|
collection *mongodriver.Collection
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGlobalConfigStore(db *mongodriver.Database) *GlobalConfigStore {
|
||||||
|
return &GlobalConfigStore{
|
||||||
|
collection: db.Collection("global_config"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *GlobalConfigStore) Get(ctx context.Context, key string) (*model.GlobalConfig, error) {
|
||||||
|
findCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
var item model.GlobalConfig
|
||||||
|
err := s.collection.FindOne(findCtx, bson.M{"key": key}).Decode(&item)
|
||||||
|
if err == nil {
|
||||||
|
return &item, nil
|
||||||
|
}
|
||||||
|
if !errors.Is(err, mongodriver.ErrNoDocuments) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
item = model.GlobalConfig{
|
||||||
|
ID: primitive.NewObjectID(),
|
||||||
|
Key: key,
|
||||||
|
Config: defaultConfigForKey(key),
|
||||||
|
UpdatedBy: "system",
|
||||||
|
UpdatedAt: time.Now(),
|
||||||
|
}
|
||||||
|
if _, err := s.collection.InsertOne(findCtx, item); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &item, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *GlobalConfigStore) Upsert(ctx context.Context, item *model.GlobalConfig) error {
|
||||||
|
updateCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
item.UpdatedAt = time.Now()
|
||||||
|
if item.UpdatedBy == "" {
|
||||||
|
item.UpdatedBy = "system"
|
||||||
|
}
|
||||||
|
if item.Key == "" {
|
||||||
|
item.Key = schedulerGlobalConfigKey
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := s.collection.UpdateOne(updateCtx, bson.M{"key": item.Key}, bson.M{
|
||||||
|
"$set": bson.M{
|
||||||
|
"key": item.Key,
|
||||||
|
"config": item.Config,
|
||||||
|
"updatedBy": item.UpdatedBy,
|
||||||
|
"updatedAt": item.UpdatedAt,
|
||||||
|
},
|
||||||
|
}, options.Update().SetUpsert(true))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultConfigForKey(key string) string {
|
||||||
|
if key == schedulerGlobalConfigKey {
|
||||||
|
return defaultSchedulerGlobalConfigJSON
|
||||||
|
}
|
||||||
|
return "{}"
|
||||||
|
}
|
||||||
@@ -6,9 +6,10 @@ import (
|
|||||||
"github.com/redis/go-redis/v9"
|
"github.com/redis/go-redis/v9"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newRedisClient(ctx context.Context, addr, password string) (*redis.Client, error) {
|
func newRedisClient(ctx context.Context, addr, username, password string) (*redis.Client, error) {
|
||||||
rdb := redis.NewClient(&redis.Options{
|
rdb := redis.NewClient(&redis.Options{
|
||||||
Addr: addr,
|
Addr: addr,
|
||||||
|
Username: username,
|
||||||
Password: password,
|
Password: password,
|
||||||
})
|
})
|
||||||
if err := rdb.Ping(ctx).Err(); err != nil {
|
if err := rdb.Ping(ctx).Err(); err != nil {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/redis/go-redis/v9"
|
||||||
"go.mongodb.org/mongo-driver/bson"
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
"go.mongodb.org/mongo-driver/mongo"
|
"go.mongodb.org/mongo-driver/mongo"
|
||||||
@@ -35,7 +36,9 @@ type Params struct {
|
|||||||
|
|
||||||
type RedisConfig struct {
|
type RedisConfig struct {
|
||||||
Addr string
|
Addr string
|
||||||
|
Username string
|
||||||
Password string
|
Password string
|
||||||
|
Client *redis.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
type Summary struct {
|
type Summary struct {
|
||||||
@@ -216,11 +219,19 @@ func invalidateBatchCache(ctx context.Context, db *mongo.Database, redisCfg Redi
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
rdb, err := newRedisClient(ctx, redisCfg.Addr, redisCfg.Password)
|
rdb := redisCfg.Client
|
||||||
if err != nil {
|
shouldClose := false
|
||||||
return fmt.Errorf("connect redis: %w", err)
|
if rdb == nil {
|
||||||
|
var err error
|
||||||
|
rdb, err = newRedisClient(ctx, redisCfg.Addr, redisCfg.Username, redisCfg.Password)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("connect redis: %w", err)
|
||||||
|
}
|
||||||
|
shouldClose = true
|
||||||
|
}
|
||||||
|
if shouldClose {
|
||||||
|
defer rdb.Close()
|
||||||
}
|
}
|
||||||
defer rdb.Close()
|
|
||||||
|
|
||||||
if err := rdb.Del(ctx, keys...).Err(); err != nil {
|
if err := rdb.Del(ctx, keys...).Err(); err != nil {
|
||||||
return fmt.Errorf("redis del: %w", err)
|
return fmt.Errorf("redis del: %w", err)
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ type Config struct {
|
|||||||
BusinessMongoDatabase string
|
BusinessMongoDatabase string
|
||||||
SchedulerMongoDatabase string
|
SchedulerMongoDatabase string
|
||||||
RedisAddr string
|
RedisAddr string
|
||||||
|
RedisUsername string
|
||||||
RedisPassword string
|
RedisPassword string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,6 +38,7 @@ func Load() Config {
|
|||||||
BusinessMongoDatabase: getenv("MONGO_DATABASE", "openim_v3"),
|
BusinessMongoDatabase: getenv("MONGO_DATABASE", "openim_v3"),
|
||||||
SchedulerMongoDatabase: getenv("SCHEDULER_MONGO_DATABASE", "scheduler_center"),
|
SchedulerMongoDatabase: getenv("SCHEDULER_MONGO_DATABASE", "scheduler_center"),
|
||||||
RedisAddr: getenv("REDIS_ADDR", ""),
|
RedisAddr: getenv("REDIS_ADDR", ""),
|
||||||
|
RedisUsername: getenv("REDIS_USERNAME", ""),
|
||||||
RedisPassword: getenv("REDIS_PASSWORD", ""),
|
RedisPassword: getenv("REDIS_PASSWORD", ""),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
22
pkg/redis/client.go
Normal file
22
pkg/redis/client.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package redis
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
goredis "github.com/redis/go-redis/v9"
|
||||||
|
|
||||||
|
"scheduler-backend/pkg/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Connect(ctx context.Context, cfg config.Config) (*goredis.Client, error) {
|
||||||
|
client := goredis.NewClient(&goredis.Options{
|
||||||
|
Addr: cfg.RedisAddr,
|
||||||
|
Username: cfg.RedisUsername,
|
||||||
|
Password: cfg.RedisPassword,
|
||||||
|
})
|
||||||
|
if err := client.Ping(ctx).Err(); err != nil {
|
||||||
|
_ = client.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user