mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-07 08:06:37 +08:00
refactor(layout): consolidate backend codebase into backend/ package and clean root directory
- Moved cmd/, core/, plugins/, pkg/, downstream/, and main.go into backend/ directory - Batch updated all Go source files to import github.com/Rain-kl/Wavelet/backend/... - Updated Makefile, scripts/swagger.sh, architecture guards, and platform skills - Passed all quality gates (100% tests, 0 lint issues, clean build)
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package storage provides upload storage backend operations and migration state.
|
||||
package storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/Rain-kl/Wavelet/backend/plugins/domain/upload/shared"
|
||||
"github.com/Rain-kl/Wavelet/backend/plugins/drivers/driver_asynq_worker"
|
||||
"github.com/Rain-kl/Wavelet/backend/plugins/infra/storage/objectstore"
|
||||
)
|
||||
|
||||
// MigrationAccessState captures cached migration maintenance state.
|
||||
type MigrationAccessState struct {
|
||||
ReadOnly bool
|
||||
Target objectstore.Config
|
||||
HasTarget bool
|
||||
TargetErr error
|
||||
LoadErr error
|
||||
}
|
||||
|
||||
var (
|
||||
migrationAccessMu sync.RWMutex
|
||||
migrationAccessCached MigrationAccessState
|
||||
migrationAccessValid bool
|
||||
migrationAccessCheckedAt time.Time
|
||||
)
|
||||
|
||||
// ResetMigrationAccessCache clears the in-process migration access cache.
|
||||
func ResetMigrationAccessCache() {
|
||||
migrationAccessMu.Lock()
|
||||
migrationAccessValid = false
|
||||
migrationAccessMu.Unlock()
|
||||
}
|
||||
|
||||
// LoadMigrationAccessState returns cached migration maintenance state.
|
||||
func LoadMigrationAccessState(ctx context.Context) MigrationAccessState {
|
||||
migrationAccessMu.RLock()
|
||||
if migrationAccessValid && time.Since(migrationAccessCheckedAt) < time.Duration(shared.AccessCacheTTL)*time.Second {
|
||||
state := migrationAccessCached
|
||||
migrationAccessMu.RUnlock()
|
||||
return state
|
||||
}
|
||||
migrationAccessMu.RUnlock()
|
||||
|
||||
migrationAccessMu.Lock()
|
||||
defer migrationAccessMu.Unlock()
|
||||
|
||||
if migrationAccessValid && time.Since(migrationAccessCheckedAt) < time.Duration(shared.AccessCacheTTL)*time.Second {
|
||||
return migrationAccessCached
|
||||
}
|
||||
|
||||
migrationAccessCached = buildMigrationAccessState(ctx)
|
||||
migrationAccessValid = true
|
||||
migrationAccessCheckedAt = time.Now()
|
||||
return migrationAccessCached
|
||||
}
|
||||
|
||||
func buildMigrationAccessState(ctx context.Context) MigrationAccessState {
|
||||
execution, ok, err := LatestMigrationExecution(ctx)
|
||||
if err != nil {
|
||||
return MigrationAccessState{LoadErr: err, ReadOnly: true}
|
||||
}
|
||||
if !ok {
|
||||
return MigrationAccessState{}
|
||||
}
|
||||
|
||||
state := MigrationAccessState{
|
||||
ReadOnly: execution.Status != driver_asynq_worker.TaskExecutionStatusSucceeded,
|
||||
}
|
||||
if execution.Status == driver_asynq_worker.TaskExecutionStatusSucceeded {
|
||||
return state
|
||||
}
|
||||
|
||||
target, err := ParseMigrationTargetConfig(ctx, []byte(execution.Payload))
|
||||
if err != nil {
|
||||
state.TargetErr = err
|
||||
return state
|
||||
}
|
||||
|
||||
state.Target = target
|
||||
state.HasTarget = true
|
||||
return state
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/Rain-kl/Wavelet/backend/plugins/drivers/driver_asynq_worker"
|
||||
"github.com/Rain-kl/Wavelet/backend/plugins/infra/storage/objectstore"
|
||||
)
|
||||
|
||||
// StorageMigrationTask is the Asynq task name for storage migration.
|
||||
const StorageMigrationTask = "storage:migrate"
|
||||
|
||||
// LatestMigrationExecution returns the most recent storage migration task execution.
|
||||
func LatestMigrationExecution(ctx context.Context) (*driver_asynq_worker.TaskExecution, bool, error) {
|
||||
return driver_asynq_worker.GetLatestTaskExecutionByTaskType(ctx, StorageMigrationTask)
|
||||
}
|
||||
|
||||
// ParseMigrationTargetConfig parses and validates a storage migration target payload.
|
||||
func ParseMigrationTargetConfig(ctx context.Context, payload []byte) (objectstore.Config, error) {
|
||||
if strings.TrimSpace(string(payload)) == "" {
|
||||
return objectstore.Config{}, errors.New("storage migration target payload is required")
|
||||
}
|
||||
|
||||
var raw struct {
|
||||
Target json.RawMessage `json:"target"`
|
||||
}
|
||||
if err := json.Unmarshal(payload, &raw); err != nil {
|
||||
return objectstore.Config{}, fmt.Errorf("parse storage migration payload envelope: %w", err)
|
||||
}
|
||||
|
||||
if len(raw.Target) == 0 {
|
||||
return objectstore.Config{}, errors.New("storage migration target payload is required")
|
||||
}
|
||||
|
||||
var targetBytes []byte
|
||||
var targetStr string
|
||||
if err := json.Unmarshal(raw.Target, &targetStr); err == nil {
|
||||
targetBytes = []byte(targetStr)
|
||||
} else {
|
||||
targetBytes = raw.Target
|
||||
}
|
||||
|
||||
var target objectstore.Config
|
||||
if err := json.Unmarshal(targetBytes, &target); err != nil {
|
||||
return objectstore.Config{}, fmt.Errorf("parse target storage config: %w", err)
|
||||
}
|
||||
|
||||
current, err := objectstore.LoadConfig(ctx)
|
||||
if err != nil {
|
||||
return objectstore.Config{}, fmt.Errorf("load active storage config: %w", err)
|
||||
}
|
||||
target = objectstore.MergeMaskedSecrets(target, current)
|
||||
if err := objectstore.ValidateConfig(target); err != nil {
|
||||
return objectstore.Config{}, fmt.Errorf("validate target storage config: %w", err)
|
||||
}
|
||||
return target, nil
|
||||
}
|
||||
|
||||
// NormalizeMigrationPayload validates and normalizes a storage migration payload.
|
||||
func NormalizeMigrationPayload(ctx context.Context, payload []byte) ([]byte, objectstore.Config, error) {
|
||||
target, err := ParseMigrationTargetConfig(ctx, payload)
|
||||
if err != nil {
|
||||
return nil, objectstore.Config{}, err
|
||||
}
|
||||
type storageMigrationPayload struct {
|
||||
Target objectstore.Config `json:"target"`
|
||||
}
|
||||
normalized, err := json.Marshal(storageMigrationPayload{Target: target})
|
||||
if err != nil {
|
||||
return nil, objectstore.Config{}, fmt.Errorf("marshal storage migration payload: %w", err)
|
||||
}
|
||||
return normalized, target, nil
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/Rain-kl/Wavelet/backend/pkg/logger"
|
||||
"github.com/Rain-kl/Wavelet/backend/plugins/domain/upload/models"
|
||||
"github.com/Rain-kl/Wavelet/backend/plugins/infra/storage/objectstore"
|
||||
)
|
||||
|
||||
// ReadOnly checks if the storage system is in read-only maintenance mode.
|
||||
func ReadOnly(ctx context.Context) bool {
|
||||
state := LoadMigrationAccessState(ctx)
|
||||
if state.LoadErr != nil {
|
||||
logger.ErrorF(ctx, "读取存储维护状态失败: %v", state.LoadErr)
|
||||
return true
|
||||
}
|
||||
return state.ReadOnly
|
||||
}
|
||||
|
||||
// OpenStoredObject opens a stored upload object from the active storage backend.
|
||||
func OpenStoredObject(ctx context.Context, upload *models.Upload) (*objectstore.Object, error) {
|
||||
_, backend, err := objectstore.Active(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return backend.Get(ctx, upload.FilePath)
|
||||
}
|
||||
Reference in New Issue
Block a user