优化结构
This commit is contained in:
@@ -13,6 +13,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"kra/internal/utils/uploadpolicy"
|
||||
)
|
||||
|
||||
type MediaRepo interface {
|
||||
@@ -39,23 +40,8 @@ func (uc *MediaUsecase) maxMediaFileSize() int64 {
|
||||
return uc.settings.MediaSettings().EffectiveMaxFileSize()
|
||||
}
|
||||
|
||||
var allowedMediaExtensions = map[string]bool{
|
||||
".jpg": true, ".jpeg": true, ".png": true, ".gif": true, ".webp": true, ".bmp": true, ".ico": true, ".avif": true,
|
||||
".mp3": true, ".wav": true, ".ogg": true, ".m4a": true, ".flac": true, ".aac": true,
|
||||
".mp4": true, ".webm": true, ".mov": true, ".avi": true, ".mkv": true,
|
||||
".txt": true, ".md": true, ".csv": true, ".json": true, ".log": true, ".pdf": true,
|
||||
".doc": true, ".docx": true, ".xls": true, ".xlsx": true, ".ppt": true, ".pptx": true,
|
||||
".zip": true, ".rar": true, ".7z": true, ".tar": true, ".gz": true, ".tgz": true, ".bin": true,
|
||||
}
|
||||
|
||||
func validateMediaName(name string) error {
|
||||
if name == "" || strings.TrimSpace(name) != name || strings.ContainsAny(name, `/\`) {
|
||||
return errors.New("file extension is not allowed")
|
||||
}
|
||||
if !allowedMediaExtensions[strings.ToLower(filepath.Ext(name))] {
|
||||
return errors.New("file extension is not allowed")
|
||||
}
|
||||
return nil
|
||||
return uploadpolicy.ValidateFileName(name)
|
||||
}
|
||||
func (uc *MediaUsecase) Upload(ctx context.Context, userID uint, name, suppliedMIME string, categoryID int, reader io.Reader, save bool) (*MediaFile, error) {
|
||||
if err := validateMediaName(name); err != nil {
|
||||
@@ -69,7 +55,7 @@ func (uc *MediaUsecase) Upload(ctx context.Context, userID uint, name, suppliedM
|
||||
// http.DetectContentType changes the media record for common uploads
|
||||
// (for example SVG/JSON), and diverges from the compatible DetectMIME helper.
|
||||
if strings.TrimSpace(suppliedMIME) == "" {
|
||||
if detected := mediaMIMEByExtension(ext); detected != "" {
|
||||
if detected := uploadpolicy.MIMEByExtension(ext); detected != "" {
|
||||
suppliedMIME = detected
|
||||
} else if header, _ := buffered.Peek(512); len(header) > 0 {
|
||||
suppliedMIME = http.DetectContentType(header)
|
||||
@@ -113,29 +99,6 @@ func (uc *MediaUsecase) Upload(ctx context.Context, userID uint, name, suppliedM
|
||||
return media, nil
|
||||
}
|
||||
|
||||
// mediaMIMEByExtension mirrors the small extension fallback table. It is
|
||||
// intentionally narrower than mime.TypeByExtension: all other types are sniffed.
|
||||
// types from the first 512 bytes.
|
||||
func mediaMIMEByExtension(ext string) string {
|
||||
switch ext {
|
||||
case ".json":
|
||||
return "application/json"
|
||||
case ".xml":
|
||||
return "application/xml"
|
||||
case ".pdf":
|
||||
return "application/pdf"
|
||||
case ".zip":
|
||||
return "application/zip"
|
||||
case ".mp4":
|
||||
return "video/mp4"
|
||||
case ".mp3":
|
||||
return "audio/mpeg"
|
||||
case ".csv":
|
||||
return "text/csv"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
func (uc *MediaUsecase) Delete(ctx context.Context, id uint) error {
|
||||
media, err := uc.FindMedia(ctx, id)
|
||||
if err != nil {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"kra/internal/config"
|
||||
)
|
||||
|
||||
var ErrTaskRuntimeReload = errors.New("task runtime reload failed")
|
||||
@@ -22,8 +23,7 @@ type InitializationRepo interface {
|
||||
IsInitialized(context.Context) (bool, error)
|
||||
Initialize(context.Context, *DatabaseConfig) error
|
||||
PersistConfig(context.Context) error
|
||||
PersistAdminConfig(context.Context, []byte) error
|
||||
PersistRuntimeConfig(context.Context, []byte, []byte) error
|
||||
PersistRuntimeConfig(context.Context, *config.Config) error
|
||||
ReloadConfig(context.Context) error
|
||||
ConfigurationJSON() (json.RawMessage, error)
|
||||
SaveConfigurationJSON(context.Context, json.RawMessage) error
|
||||
@@ -63,11 +63,8 @@ func (uc *SystemConfigUsecase) Initialize(ctx context.Context, config *DatabaseC
|
||||
func (uc *SystemConfigUsecase) PersistConfig(ctx context.Context) error {
|
||||
return uc.repo.PersistConfig(ctx)
|
||||
}
|
||||
func (uc *SystemConfigUsecase) PersistAdminConfig(ctx context.Context, value []byte) error {
|
||||
return uc.repo.PersistAdminConfig(ctx, value)
|
||||
}
|
||||
func (uc *SystemConfigUsecase) PersistRuntimeConfig(ctx context.Context, data, admin []byte) error {
|
||||
return uc.repo.PersistRuntimeConfig(ctx, data, admin)
|
||||
func (uc *SystemConfigUsecase) PersistRuntimeConfig(ctx context.Context, value *config.Config) error {
|
||||
return uc.repo.PersistRuntimeConfig(ctx, value)
|
||||
}
|
||||
func (uc *SystemConfigUsecase) ReloadConfig(ctx context.Context) error {
|
||||
if err := uc.repo.ReloadConfig(ctx); err != nil {
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/robfig/cron/v3"
|
||||
platformtask "kra/pkg/task"
|
||||
)
|
||||
|
||||
type TimedTask struct {
|
||||
@@ -77,13 +78,9 @@ type TaskUsecase struct {
|
||||
methods TaskMethodRegistry
|
||||
}
|
||||
|
||||
func NewTaskUsecase(repo TaskRepo) *TaskUsecase {
|
||||
return NewTaskUsecaseWithRegistry(repo, DefaultTaskMethodRegistry())
|
||||
}
|
||||
|
||||
func NewTaskUsecaseWithRegistry(repo TaskRepo, methods TaskMethodRegistry) *TaskUsecase {
|
||||
if methods == nil {
|
||||
methods = DefaultTaskMethodRegistry()
|
||||
methods = platformtask.NewRegistry()
|
||||
}
|
||||
return &TaskUsecase{TaskRepo: repo, methods: methods}
|
||||
}
|
||||
|
||||
@@ -6,23 +6,6 @@ type TaskMethodFunc = platformtask.MethodFunc
|
||||
type TaskMethod = platformtask.Method
|
||||
|
||||
type TaskMethodRegistry interface {
|
||||
Register(platformtask.Method)
|
||||
Lookup(string) (TaskMethodFunc, bool)
|
||||
List() []TaskMethod
|
||||
}
|
||||
|
||||
var defaultTaskMethods = platformtask.NewRegistry()
|
||||
|
||||
// RegisterTaskMethod remains as a compatibility helper for tests and callers
|
||||
// that have not moved to constructor injection yet.
|
||||
func RegisterTaskMethod(name, description string, fn TaskMethodFunc) {
|
||||
defaultTaskMethods.Register(platformtask.Method{Name: name, Description: description, Run: fn})
|
||||
}
|
||||
|
||||
func TaskMethodByName(name string) (TaskMethodFunc, bool) {
|
||||
return defaultTaskMethods.Lookup(name)
|
||||
}
|
||||
|
||||
func RegisteredTaskMethods() []TaskMethod { return defaultTaskMethods.List() }
|
||||
|
||||
func DefaultTaskMethodRegistry() TaskMethodRegistry { return defaultTaskMethods }
|
||||
|
||||
@@ -4,11 +4,14 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
platformtask "kra/pkg/task"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var testTaskMethods = platformtask.NewRegistry()
|
||||
|
||||
type applicationTaskRepo struct {
|
||||
mu sync.Mutex
|
||||
deleted bool
|
||||
@@ -112,14 +115,14 @@ func (r *applicationTaskRuntime) Unsubscribe(uint, chan []byte) {}
|
||||
|
||||
func validApplicationTask(id uint) *TimedTask {
|
||||
const methodName = "biz-test-application-task"
|
||||
RegisterTaskMethod(methodName, "test", func(context.Context, json.RawMessage) error { return nil })
|
||||
testTaskMethods.Register(platformtask.Method{Name: methodName, Description: "test", Run: func(context.Context, json.RawMessage) error { return nil }})
|
||||
return &TimedTask{ID: id, Name: "test", Spec: "0 0 * * *", ExecutorType: TaskExecutorMethod, MethodName: methodName, Enabled: true}
|
||||
}
|
||||
|
||||
func TestTaskApplicationRepairsRuntimeAfterScheduleFailure(t *testing.T) {
|
||||
repo := &applicationTaskRepo{createdID: 41}
|
||||
runtime := &applicationTaskRuntime{scheduleErr: errors.New("schedule failed")}
|
||||
uc := NewTaskApplicationUsecase(NewTaskUsecase(repo), runtime)
|
||||
uc := NewTaskApplicationUsecase(NewTaskUsecaseWithRegistry(repo, testTaskMethods), runtime)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
@@ -137,7 +140,7 @@ func TestTaskApplicationRepairsRuntimeAfterScheduleFailure(t *testing.T) {
|
||||
func TestTaskApplicationReturnsScheduleErrorWhenRepairFails(t *testing.T) {
|
||||
repo := &applicationTaskRepo{createdID: 42}
|
||||
runtime := &applicationTaskRuntime{scheduleErr: errors.New("schedule failed"), reloadErr: errors.New("reload failed")}
|
||||
uc := NewTaskApplicationUsecase(NewTaskUsecase(repo), runtime)
|
||||
uc := NewTaskApplicationUsecase(NewTaskUsecaseWithRegistry(repo, testTaskMethods), runtime)
|
||||
|
||||
err := uc.Create(context.Background(), validApplicationTask(0))
|
||||
if !errors.Is(err, ErrTaskSchedule) {
|
||||
@@ -158,7 +161,7 @@ func TestTaskApplicationUpdateAndToggleUseRuntimeRepair(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
repo := &applicationTaskRepo{}
|
||||
runtime := &applicationTaskRuntime{scheduleErr: errors.New("schedule failed")}
|
||||
uc := NewTaskApplicationUsecase(NewTaskUsecase(repo), runtime)
|
||||
uc := NewTaskApplicationUsecase(NewTaskUsecaseWithRegistry(repo, testTaskMethods), runtime)
|
||||
if err := test.run(uc); err != nil {
|
||||
t.Fatalf("operation error = %v", err)
|
||||
}
|
||||
@@ -172,7 +175,7 @@ func TestTaskApplicationUpdateAndToggleUseRuntimeRepair(t *testing.T) {
|
||||
func TestTaskApplicationDeleteRemovesRuntimeAfterDatabase(t *testing.T) {
|
||||
repo := &applicationTaskRepo{}
|
||||
runtime := &applicationTaskRuntime{repo: repo}
|
||||
uc := NewTaskApplicationUsecase(NewTaskUsecase(repo), runtime)
|
||||
uc := NewTaskApplicationUsecase(NewTaskUsecaseWithRegistry(repo, testTaskMethods), runtime)
|
||||
if err := uc.Delete(context.Background(), 8); err != nil {
|
||||
t.Fatalf("Delete() error = %v", err)
|
||||
}
|
||||
@@ -187,7 +190,7 @@ func TestTaskApplicationDeleteRemovesRuntimeAfterDatabase(t *testing.T) {
|
||||
func TestTaskApplicationDeleteKeepsRuntimeWhenDatabaseFails(t *testing.T) {
|
||||
repo := &applicationTaskRepo{deleteErr: errors.New("delete failed")}
|
||||
runtime := &applicationTaskRuntime{repo: repo}
|
||||
uc := NewTaskApplicationUsecase(NewTaskUsecase(repo), runtime)
|
||||
uc := NewTaskApplicationUsecase(NewTaskUsecaseWithRegistry(repo, testTaskMethods), runtime)
|
||||
if err := uc.Delete(context.Background(), 8); err == nil {
|
||||
t.Fatal("Delete() error = nil, want database error")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user