Merge branch 'wavelet/main' into main

This commit is contained in:
ryan
2026-09-03 10:08:50 +08:00
24 changed files with 217 additions and 91 deletions
@@ -29,6 +29,7 @@ func (PublicConfigAdapter) PublicConfig(ctx context.Context) (map[string]string,
// SystemConfigServiceImpl implements contracts.SystemConfigService.
type SystemConfigServiceImpl struct{}
// GetByKey retrieves a system config by its unique key.
func (SystemConfigServiceImpl) GetByKey(ctx context.Context, key string) (contracts.SystemConfigDTO, error) {
cfg, err := repository.GetSystemConfigByKey(ctx, key)
if err != nil {
@@ -37,6 +38,7 @@ func (SystemConfigServiceImpl) GetByKey(ctx context.Context, key string) (contra
return toSystemConfigDTO(cfg), nil
}
// ListByKeys retrieves multiple system configs by their keys.
func (SystemConfigServiceImpl) ListByKeys(ctx context.Context, keys []string) (map[string]contracts.SystemConfigDTO, error) {
cfgs, err := repository.ListSystemConfigsByKeys(ctx, keys)
if err != nil {
@@ -49,6 +51,7 @@ func (SystemConfigServiceImpl) ListByKeys(ctx context.Context, keys []string) (m
return res, nil
}
// ListVisible returns all user-visible system configs.
func (SystemConfigServiceImpl) ListVisible(ctx context.Context) ([]contracts.SystemConfigDTO, error) {
cfgs, err := repository.ListVisibleSystemConfigs(ctx)
if err != nil {
@@ -61,6 +64,7 @@ func (SystemConfigServiceImpl) ListVisible(ctx context.Context) ([]contracts.Sys
return res, nil
}
// ListByType returns all system configs belonging to a specific configuration type.
func (SystemConfigServiceImpl) ListByType(ctx context.Context, configType string) ([]contracts.SystemConfigDTO, error) {
cfgs, err := repository.ListAdminSystemConfigs(ctx, configType)
if err != nil {
@@ -73,22 +77,27 @@ func (SystemConfigServiceImpl) ListByType(ctx context.Context, configType string
return res, nil
}
// GetIntByKey retrieves an integer system config value.
func (SystemConfigServiceImpl) GetIntByKey(ctx context.Context, key string) (int, error) {
return repository.GetIntByKey(ctx, key)
}
// GetBoolByKey retrieves a boolean system config value.
func (SystemConfigServiceImpl) GetBoolByKey(ctx context.Context, key string) (bool, error) {
return repository.GetBoolByKey(ctx, key)
}
// SaveOrUpdate persists or updates a system config key-value pair.
func (SystemConfigServiceImpl) SaveOrUpdate(ctx context.Context, key, value string) error {
return repository.SaveOrUpdateSystemConfig(ctx, key, value)
}
// InvalidateCache evicts the cache entry for the specified key.
func (SystemConfigServiceImpl) InvalidateCache(ctx context.Context, key string) error {
return repository.InvalidateSystemConfigCache(ctx, key)
}
// InvalidateAllCaches purges all system configuration cache entries.
func (SystemConfigServiceImpl) InvalidateAllCaches(ctx context.Context) error {
return repository.InvalidateAllSystemConfigCaches(ctx)
}
+9 -9
View File
@@ -69,15 +69,15 @@ func (s *uploadServiceImpl) RebuildStats(ctx context.Context) error {
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,
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,
Metadata: contracts.UploadMetadataDTO{
Width: u.Metadata.Width,
Height: u.Metadata.Height,
@@ -112,30 +112,25 @@ func (s *inprocTaskService) ListExecutions(ctx context.Context, taskType, status
return res, total, nil
}
func (s *inprocTaskService) GetExecution(ctx context.Context, id uint64) (*contracts.TaskExecutionDTO, error) {
func findExecution(ctx context.Context, query any, args ...any) (*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("id = ?", id).First(&exec).Error; err != nil {
if err := db.Where(query, args...).First(&exec).Error; err != nil {
return nil, err
}
dto := toExecutionDTO(&exec)
return &dto, nil
}
func (s *inprocTaskService) GetExecution(ctx context.Context, id uint64) (*contracts.TaskExecutionDTO, error) {
return findExecution(ctx, "id = ?", id)
}
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
return findExecution(ctx, "task_id = ?", taskID)
}
func toExecutionDTO(exec *taskExecution) contracts.TaskExecutionDTO {
+25 -10
View File
@@ -19,6 +19,13 @@ import (
// DefaultFileName is the configuration file looked up when CONFIG_PATH is unset.
const DefaultFileName = "config.yaml"
// DefaultCandidates defines the prioritized list of configuration file paths looked up
// when CONFIG_PATH is unset.
var DefaultCandidates = []string{
"manifest/config/config.yaml",
DefaultFileName,
}
// EnvOnlyOrigin is reported by Describe when no configuration file was loaded.
const EnvOnlyOrigin = "<env only>"
@@ -56,7 +63,7 @@ func NewSource(opts ...Option) (*Source, error) {
s.path = os.Getenv("CONFIG_PATH")
}
if s.path == "" {
s.path = findConfigPath(DefaultFileName)
s.path = findConfigPath(DefaultCandidates...)
}
v := viper.New()
@@ -107,20 +114,28 @@ func (s *Source) Describe() string {
return s.path
}
// findConfigPath searches upward from the working directory so tests and binaries run
// from backend/ still find the repository-root configuration file.
func findConfigPath(configPath string) string {
if _, err := os.Stat(configPath); err == nil {
return configPath
// findConfigPath searches upward from the working directory through candidate paths so
// tests and binaries run from subdirectories still find configuration files in manifest/config/
// or the repository root.
func findConfigPath(candidates ...string) string {
if len(candidates) == 0 {
return DefaultFileName
}
for _, candidate := range candidates {
if _, err := os.Stat(candidate); err == nil {
return candidate
}
}
dir := "."
for range maxSearchDepth {
dir += "/.."
path := dir + "/" + configPath
if _, err := os.Stat(path); err == nil {
return path
for _, candidate := range candidates {
path := dir + "/" + candidate
if _, err := os.Stat(path); err == nil {
return path
}
}
}
return configPath
return candidates[0]
}
@@ -91,3 +91,32 @@ func TestSourcePrefersConfigPathEnvironmentVariable(t *testing.T) {
require.True(t, ok)
assert.Equal(t, ":9100", value)
}
func TestSourceFindsManifestConfigInParentDirectory(t *testing.T) {
tempRoot := t.TempDir()
manifestConfigDir := filepath.Join(tempRoot, "manifest", "config")
require.NoError(t, os.MkdirAll(manifestConfigDir, 0o755))
require.NoError(t, os.WriteFile(
filepath.Join(manifestConfigDir, "config.yaml"),
[]byte("app:\n addr: \":9200\"\n"),
0o600,
))
subDir := filepath.Join(tempRoot, "backend", "cmd")
require.NoError(t, os.MkdirAll(subDir, 0o755))
origWd, err := os.Getwd()
require.NoError(t, err)
require.NoError(t, os.Chdir(subDir))
t.Cleanup(func() {
_ = os.Chdir(origWd)
})
t.Setenv("CONFIG_PATH", "")
src, err := config.NewSource()
require.NoError(t, err)
value, ok := src.Lookup("app.addr")
require.True(t, ok)
assert.Equal(t, ":9200", value)
}