diff --git a/backend/core/contracts/config.go b/backend/core/contracts/config.go new file mode 100644 index 00000000..0a17928c --- /dev/null +++ b/backend/core/contracts/config.go @@ -0,0 +1,33 @@ +// Copyright 2026 Arctel.net +// SPDX-License-Identifier: Apache-2.0 + +package contracts + +import ( + "context" + "time" +) + +// SystemConfigDTO represents a system configuration key-value entry. +type SystemConfigDTO struct { + Key string `json:"key"` + Value string `json:"value"` + Type string `json:"type"` + Visibility int `json:"visibility"` + Description string `json:"description"` + UpdatedAt time.Time `json:"updated_at"` + CreatedAt time.Time `json:"created_at"` +} + +// SystemConfigService defines the unified contract for querying and mutating system configurations. +type SystemConfigService interface { + GetByKey(ctx context.Context, key string) (SystemConfigDTO, error) + ListByKeys(ctx context.Context, keys []string) (map[string]SystemConfigDTO, error) + ListVisible(ctx context.Context) ([]SystemConfigDTO, error) + ListByType(ctx context.Context, configType string) ([]SystemConfigDTO, error) + GetIntByKey(ctx context.Context, key string) (int, error) + GetBoolByKey(ctx context.Context, key string) (bool, error) + SaveOrUpdate(ctx context.Context, key, value string) error + InvalidateCache(ctx context.Context, key string) error + InvalidateAllCaches(ctx context.Context) error +} diff --git a/backend/core/contracts/task.go b/backend/core/contracts/task.go index 60a051c1..efd05de6 100644 --- a/backend/core/contracts/task.go +++ b/backend/core/contracts/task.go @@ -90,4 +90,5 @@ type TaskService interface { AppendLog(ctx context.Context, format string, args ...any) ListExecutions(ctx context.Context, taskType, status string, page, pageSize int) ([]TaskExecutionDTO, int64, error) GetExecution(ctx context.Context, id uint64) (*TaskExecutionDTO, error) + GetExecutionByTaskID(ctx context.Context, taskID string) (*TaskExecutionDTO, error) } diff --git a/backend/core/contracts/upload.go b/backend/core/contracts/upload.go new file mode 100644 index 00000000..072deeef --- /dev/null +++ b/backend/core/contracts/upload.go @@ -0,0 +1,43 @@ +// Copyright 2026 Arctel.net +// SPDX-License-Identifier: Apache-2.0 + +package contracts + +import ( + "context" + "io" + "time" +) + +// UploadDTO represents an uploaded file record. +type UploadDTO struct { + ID uint64 `json:"id"` + UserID uint64 `json:"user_id"` + FileName string `json:"file_name"` + FilePath string `json:"file_path"` + MimeType string `json:"mime_type"` + Size int64 `json:"size"` + Hash string `json:"hash"` + Status string `json:"status"` + Type string `json:"type"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +// OpenedUploadDTO encapsulates the retrieved object stream and its metadata. +type OpenedUploadDTO struct { + Upload UploadDTO + Body io.ReadCloser + ContentType string + ContentLength int64 +} + +// UploadService defines the unified contract for managed file uploads and media entities. +type UploadService interface { + GetByID(ctx context.Context, id uint64) (*UploadDTO, error) + OpenStoredUpload(ctx context.Context, id uint64) (*OpenedUploadDTO, error) + Remove(ctx context.Context, id uint64) error + RemoveOwned(ctx context.Context, id uint64, userID uint64) error + FindByHash(ctx context.Context, hash string, size int64) (*UploadDTO, error) + RebuildStats(ctx context.Context) error +} diff --git a/backend/docs/docs.go b/backend/docs/docs.go index b037db64..958ad7d7 100644 --- a/backend/docs/docs.go +++ b/backend/docs/docs.go @@ -1,3 +1,6 @@ +// Copyright 2026 Arctel.net +// SPDX-License-Identifier: Apache-2.0 + // Package docs Code generated by swaggo/swag. DO NOT EDIT package docs diff --git a/backend/downstream/plugins/custom_example/consts/consts.go b/backend/downstream/plugins/custom_example/consts/consts.go index 7b332a57..9454d22f 100644 --- a/backend/downstream/plugins/custom_example/consts/consts.go +++ b/backend/downstream/plugins/custom_example/consts/consts.go @@ -1,2 +1,5 @@ +// Copyright 2026 Arctel.net +// SPDX-License-Identifier: Apache-2.0 + // Package consts defines constants and error codes for custom_example plugin. package consts diff --git a/backend/downstream/plugins/custom_example/controller/hello/hello.go b/backend/downstream/plugins/custom_example/controller/hello/hello.go index b86a314b..0cf687a3 100644 --- a/backend/downstream/plugins/custom_example/controller/hello/hello.go +++ b/backend/downstream/plugins/custom_example/controller/hello/hello.go @@ -1,2 +1,5 @@ +// Copyright 2026 Arctel.net +// SPDX-License-Identifier: Apache-2.0 + // Package hello provides HTTP API handlers for the custom_example plugin. package hello diff --git a/backend/plugins/domain/admin/plugin.go b/backend/plugins/domain/admin/plugin.go index fcbf9607..d5f6b3cf 100644 --- a/backend/plugins/domain/admin/plugin.go +++ b/backend/plugins/domain/admin/plugin.go @@ -99,6 +99,7 @@ func (p *Plugin) Apply(ctx *core.Context) error { core.Bind[contracts.RiskControlService](ctx, service.SetRiskControlService) service.SetEventEmitter(ctx.Events().Emit) core.Provide[contracts.PublicConfigProvider](ctx, service.PublicConfigAdapter{}) + core.Provide[contracts.SystemConfigService](ctx, service.SystemConfigServiceImpl{}) ctx.OnDispose(func() error { service.ResetServices() diff --git a/backend/plugins/domain/admin/service/config.go b/backend/plugins/domain/admin/service/config.go index a0ac0233..b069b511 100644 --- a/backend/plugins/domain/admin/service/config.go +++ b/backend/plugins/domain/admin/service/config.go @@ -26,6 +26,85 @@ func (PublicConfigAdapter) PublicConfig(ctx context.Context) (map[string]string, return PublicSystemConfigs(ctx) } +// SystemConfigServiceImpl implements contracts.SystemConfigService. +type SystemConfigServiceImpl struct{} + +func (SystemConfigServiceImpl) GetByKey(ctx context.Context, key string) (contracts.SystemConfigDTO, error) { + cfg, err := repository.GetSystemConfigByKey(ctx, key) + if err != nil { + return contracts.SystemConfigDTO{}, err + } + return toSystemConfigDTO(cfg), nil +} + +func (SystemConfigServiceImpl) ListByKeys(ctx context.Context, keys []string) (map[string]contracts.SystemConfigDTO, error) { + cfgs, err := repository.ListSystemConfigsByKeys(ctx, keys) + if err != nil { + return nil, err + } + res := make(map[string]contracts.SystemConfigDTO, len(cfgs)) + for k, v := range cfgs { + res[k] = toSystemConfigDTO(v) + } + return res, nil +} + +func (SystemConfigServiceImpl) ListVisible(ctx context.Context) ([]contracts.SystemConfigDTO, error) { + cfgs, err := repository.ListVisibleSystemConfigs(ctx) + if err != nil { + return nil, err + } + res := make([]contracts.SystemConfigDTO, len(cfgs)) + for i, v := range cfgs { + res[i] = toSystemConfigDTO(v) + } + return res, nil +} + +func (SystemConfigServiceImpl) ListByType(ctx context.Context, configType string) ([]contracts.SystemConfigDTO, error) { + cfgs, err := repository.ListAdminSystemConfigs(ctx, configType) + if err != nil { + return nil, err + } + res := make([]contracts.SystemConfigDTO, len(cfgs)) + for i, v := range cfgs { + res[i] = toSystemConfigDTO(v) + } + return res, nil +} + +func (SystemConfigServiceImpl) GetIntByKey(ctx context.Context, key string) (int, error) { + return repository.GetIntByKey(ctx, key) +} + +func (SystemConfigServiceImpl) GetBoolByKey(ctx context.Context, key string) (bool, error) { + return repository.GetBoolByKey(ctx, key) +} + +func (SystemConfigServiceImpl) SaveOrUpdate(ctx context.Context, key, value string) error { + return repository.SaveOrUpdateSystemConfig(ctx, key, value) +} + +func (SystemConfigServiceImpl) InvalidateCache(ctx context.Context, key string) error { + return repository.InvalidateSystemConfigCache(ctx, key) +} + +func (SystemConfigServiceImpl) InvalidateAllCaches(ctx context.Context) error { + return repository.InvalidateAllSystemConfigCaches(ctx) +} + +func toSystemConfigDTO(c model.SystemConfig) contracts.SystemConfigDTO { + return contracts.SystemConfigDTO{ + Key: c.Key, + Value: c.Value, + Type: c.Type, + Visibility: c.Visibility, + Description: c.Description, + UpdatedAt: c.UpdatedAt, + CreatedAt: c.CreatedAt, + } +} + // PublicSystemConfigs returns the key/value map exposed to unauthenticated clients. func PublicSystemConfigs(ctx context.Context) (map[string]string, error) { configs, err := repository.ListVisibleSystemConfigs(ctx) diff --git a/backend/plugins/domain/upload/plugin.go b/backend/plugins/domain/upload/plugin.go index b24a660f..39d5e91c 100644 --- a/backend/plugins/domain/upload/plugin.go +++ b/backend/plugins/domain/upload/plugin.go @@ -62,6 +62,7 @@ func (p *Plugin) Apply(ctx *core.Context) error { core.Bind[contracts.StorageService](ctx, shared.SetStorageService) core.Bind[contracts.TaskService](ctx, shared.SetTaskService) core.Bind[contracts.AuthService](ctx, shared.SetAuthService) + core.Provide[contracts.UploadService](ctx, &uploadServiceImpl{}) ctx.OnDispose(func() error { shared.ResetServices() diff --git a/backend/plugins/domain/upload/service.go b/backend/plugins/domain/upload/service.go new file mode 100644 index 00000000..9c78bae8 --- /dev/null +++ b/backend/plugins/domain/upload/service.go @@ -0,0 +1,84 @@ +// Copyright 2026 Arctel.net +// SPDX-License-Identifier: Apache-2.0 + +package upload + +import ( + "Wavelet/core/contracts" + "Wavelet/plugins/domain/upload/models" + "Wavelet/plugins/domain/upload/repository" + "Wavelet/plugins/domain/upload/shared" + "context" + "errors" +) + +type uploadServiceImpl struct{} + +func (s *uploadServiceImpl) GetByID(ctx context.Context, id uint64) (*contracts.UploadDTO, error) { + u, err := repository.GetActiveUploadByID(ctx, id) + if err != nil { + return nil, err + } + dto := toUploadDTO(&u) + return &dto, nil +} + +func (s *uploadServiceImpl) OpenStoredUpload(ctx context.Context, id uint64) (*contracts.OpenedUploadDTO, error) { + u, err := repository.GetActiveUploadByID(ctx, id) + if err != nil { + return nil, err + } + storageSvc := shared.GetStorage(ctx) + if storageSvc == nil { + return nil, errors.New("storage service not available") + } + obj, err := storageSvc.Get(ctx, u.FilePath) + if err != nil { + return nil, err + } + return &contracts.OpenedUploadDTO{ + Upload: toUploadDTO(&u), + Body: obj.Body, + ContentType: obj.ContentType, + ContentLength: obj.ContentLength, + }, nil +} + +func (s *uploadServiceImpl) Remove(ctx context.Context, id uint64) error { + _, err := Remove(ctx, id) + return err +} + +func (s *uploadServiceImpl) RemoveOwned(ctx context.Context, id uint64, userID uint64) error { + _, err := RemoveOwned(ctx, userID, id) + return err +} + +func (s *uploadServiceImpl) FindByHash(ctx context.Context, hash string, size int64) (*contracts.UploadDTO, error) { + u, err := FindByHash(ctx, hash, size) + if err != nil { + return nil, err + } + dto := toUploadDTO(&u) + return &dto, nil +} + +func (s *uploadServiceImpl) RebuildStats(ctx context.Context) error { + return RebuildUploadStats(ctx) +} + +func toUploadDTO(u *models.Upload) contracts.UploadDTO { + return contracts.UploadDTO{ + ID: u.ID, + UserID: u.UserID, + FileName: u.FileName, + FilePath: u.FilePath, + MimeType: u.MimeType, + Size: u.FileSize, + Hash: u.Hash, + Status: string(u.Status), + Type: u.Type, + CreatedAt: u.CreatedAt, + UpdatedAt: u.UpdatedAt, + } +} diff --git a/backend/plugins/drivers/driver_asynq_worker/plugin.go b/backend/plugins/drivers/driver_asynq_worker/plugin.go index fee807f8..4258c8e0 100644 --- a/backend/plugins/drivers/driver_asynq_worker/plugin.go +++ b/backend/plugins/drivers/driver_asynq_worker/plugin.go @@ -555,6 +555,15 @@ func (s *taskServiceImpl) GetExecution(ctx context.Context, id uint64) (*contrac return &dto, nil } +func (s *taskServiceImpl) GetExecutionByTaskID(ctx context.Context, taskID string) (*contracts.TaskExecutionDTO, error) { + exec, err := GetTaskExecutionByTaskID(ctx, taskID) + if err != nil { + return nil, err + } + dto := toTaskExecutionDTO(exec) + return &dto, nil +} + func toTaskExecutionDTO(exec *TaskExecution) contracts.TaskExecutionDTO { return contracts.TaskExecutionDTO{ ID: exec.ID, diff --git a/backend/plugins/drivers/driver_inproc_worker/task_service.go b/backend/plugins/drivers/driver_inproc_worker/task_service.go index a268b444..a5eadc50 100644 --- a/backend/plugins/drivers/driver_inproc_worker/task_service.go +++ b/backend/plugins/drivers/driver_inproc_worker/task_service.go @@ -125,6 +125,19 @@ func (s *inprocTaskService) GetExecution(ctx context.Context, id uint64) (*contr return &dto, nil } +func (s *inprocTaskService) GetExecutionByTaskID(ctx context.Context, taskID string) (*contracts.TaskExecutionDTO, error) { + db := getDB(ctx) + if db == nil { + return nil, errors.New("driver_inproc_worker: db not initialized") + } + var exec taskExecution + if err := db.Where("task_id = ?", taskID).First(&exec).Error; err != nil { + return nil, err + } + dto := toExecutionDTO(&exec) + return &dto, nil +} + func toExecutionDTO(exec *taskExecution) contracts.TaskExecutionDTO { return contracts.TaskExecutionDTO{ ID: exec.ID,