mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-02 23:06:36 +08:00
refactor(architecture): eliminate internal package and complete cordis single-owner model and repository migration
- Physically purged all legacy internal/ packages, centralized pkg/model/ and pkg/repository/ - Migrated domain models and database repositories into self-contained owner plugins (user, auth, message_gateway, admin, upload, risk_control) - Decoupled cross-plugin interactions via pure core/contracts and typed EventBus - Ensured 100% test coverage pass, zero data races (-race clean), and 0 lint issues in make code-check
This commit is contained in:
+7
-7
@@ -11,13 +11,11 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/Rain-kl/Wavelet/internal/infra/objectstore"
|
||||
"github.com/Rain-kl/Wavelet/internal/infra/persistence"
|
||||
"github.com/Rain-kl/Wavelet/internal/model"
|
||||
"github.com/Rain-kl/Wavelet/internal/repository"
|
||||
db "github.com/Rain-kl/Wavelet/pkg/persistence"
|
||||
"github.com/Rain-kl/Wavelet/pkg/util"
|
||||
"github.com/Rain-kl/Wavelet/plugins/domain/upload/shared"
|
||||
uploadstorage "github.com/Rain-kl/Wavelet/plugins/domain/upload/storage"
|
||||
"github.com/Rain-kl/Wavelet/plugins/infra/storage/objectstore"
|
||||
)
|
||||
|
||||
const fileAccessInvalidationChannel = "upload:file_access_invalidation"
|
||||
@@ -53,12 +51,13 @@ func ensureAccessCacheListener() {
|
||||
}
|
||||
|
||||
func startAccessCacheInvalidationListener() {
|
||||
if db.Redis == nil {
|
||||
rdb := db.Redis
|
||||
if rdb == nil {
|
||||
return
|
||||
}
|
||||
|
||||
util.Go(func() {
|
||||
pubsub := db.Redis.Subscribe(
|
||||
pubsub := rdb.Subscribe(
|
||||
context.Background(),
|
||||
objectstore.ConfigInvalidationChannel,
|
||||
fileAccessInvalidationChannel,
|
||||
@@ -114,7 +113,8 @@ func fetchFileAccessWhitelist(ctx context.Context) map[string]struct{} {
|
||||
}
|
||||
|
||||
func parseFileAccessWhitelist(ctx context.Context) []string {
|
||||
sc, err := repository.GetSystemConfigByKey(ctx, model.ConfigKeyFileAccessWhitelist)
|
||||
var sc struct{ Value string }
|
||||
err := db.DB(ctx).Table("w_system_configs").Where("key = ?", "file_access_whitelist").First(&sc).Error
|
||||
if err != nil || sc.Value == "" {
|
||||
return []string{shared.DefaultPublicUploadType}
|
||||
}
|
||||
|
||||
+3
-15
@@ -8,10 +8,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/Rain-kl/Wavelet/internal/infra/persistence"
|
||||
"github.com/Rain-kl/Wavelet/internal/model"
|
||||
"github.com/Rain-kl/Wavelet/internal/repository"
|
||||
"github.com/Rain-kl/Wavelet/internal/testhelper"
|
||||
"github.com/Rain-kl/Wavelet/pkg/testhelper"
|
||||
"github.com/Rain-kl/Wavelet/plugins/domain/upload/shared"
|
||||
uploadstorage "github.com/Rain-kl/Wavelet/plugins/domain/upload/storage"
|
||||
)
|
||||
@@ -60,18 +57,9 @@ func TestResetAccessCachesRefreshesWhitelist(t *testing.T) {
|
||||
t.Fatal("expected seeded avatar whitelist before reset")
|
||||
}
|
||||
|
||||
var sc model.SystemConfig
|
||||
if err := dbConn.Where("key = ?", model.ConfigKeyFileAccessWhitelist).First(&sc).Error; err != nil {
|
||||
t.Fatalf("load whitelist config: %v", err)
|
||||
if err := dbConn.Table("w_system_configs").Where("key = ?", "file_access_whitelist").Update("value", `["attachment"]`).Error; err != nil {
|
||||
t.Fatalf("update whitelist config: %v", err)
|
||||
}
|
||||
sc.Value = `["attachment"]`
|
||||
if err := dbConn.Save(&sc).Error; err != nil {
|
||||
t.Fatalf("save whitelist config: %v", err)
|
||||
}
|
||||
if err := db.HSetJSON(ctx, repository.SystemConfigRedisHashKey, model.ConfigKeyFileAccessWhitelist, &sc); err != nil {
|
||||
t.Fatalf("refresh whitelist redis cache: %v", err)
|
||||
}
|
||||
repository.ResetSystemConfigRAMCacheForTest()
|
||||
|
||||
ResetAccessCaches()
|
||||
if !IsFilePublic(ctx, "attachment") {
|
||||
|
||||
+23
-23
@@ -9,10 +9,10 @@ import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/Rain-kl/Wavelet/internal/infra/persistence"
|
||||
"github.com/Rain-kl/Wavelet/internal/model"
|
||||
"github.com/Rain-kl/Wavelet/pkg/cache/ram"
|
||||
db "github.com/Rain-kl/Wavelet/pkg/persistence"
|
||||
"github.com/Rain-kl/Wavelet/pkg/util"
|
||||
"github.com/Rain-kl/Wavelet/plugins/domain/upload/models"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -26,7 +26,7 @@ type uploadMetaInvalidationMessage struct {
|
||||
}
|
||||
|
||||
var (
|
||||
uploadMetaRAM = ram.MustNew[uint64, model.Upload](ram.Options{MaximumSize: uploadMetaRAMMaximumSize})
|
||||
uploadMetaRAM = ram.MustNew[uint64, models.Upload](ram.Options{MaximumSize: uploadMetaRAMMaximumSize})
|
||||
uploadMetaListenerOnce sync.Once
|
||||
uploadMetaListenerCtx context.Context
|
||||
uploadMetaListenerCancel context.CancelFunc
|
||||
@@ -37,8 +37,8 @@ func uploadMetaRedisKey(id uint64) string {
|
||||
return fmt.Sprintf("upload:meta:%d", id)
|
||||
}
|
||||
|
||||
func cloneUpload(upload model.Upload) model.Upload {
|
||||
return upload
|
||||
func cloneUpload(u models.Upload) models.Upload {
|
||||
return u
|
||||
}
|
||||
|
||||
func ensureUploadMetaCacheListener() {
|
||||
@@ -88,45 +88,45 @@ func publishUploadMetaRAMInvalidation(ctx context.Context, id uint64) {
|
||||
}
|
||||
|
||||
// GetUploadByID loads upload metadata from RAM, Redis, or the database.
|
||||
func GetUploadByID(ctx context.Context, id uint64) (model.Upload, error) {
|
||||
func GetUploadByID(ctx context.Context, id uint64) (models.Upload, error) {
|
||||
ensureUploadMetaCacheListener()
|
||||
|
||||
if upload, ok := uploadMetaRAM.GetIfPresent(id); ok {
|
||||
return cloneUpload(upload), nil
|
||||
if u, ok := uploadMetaRAM.GetIfPresent(id); ok {
|
||||
return cloneUpload(u), nil
|
||||
}
|
||||
|
||||
key := uploadMetaRedisKey(id)
|
||||
if db.Redis != nil {
|
||||
var upload model.Upload
|
||||
if err := db.GetJSON(ctx, key, &upload); err == nil {
|
||||
uploadMetaRAM.Set(id, cloneUpload(upload))
|
||||
return upload, nil
|
||||
var u models.Upload
|
||||
if err := db.GetJSON(ctx, key, &u); err == nil {
|
||||
uploadMetaRAM.Set(id, cloneUpload(u))
|
||||
return u, nil
|
||||
}
|
||||
}
|
||||
|
||||
var upload model.Upload
|
||||
var u models.Upload
|
||||
if err := db.DB(ctx).
|
||||
Where("id = ? AND status IN (?, ?)", id, model.UploadStatusPending, model.UploadStatusUsed).
|
||||
First(&upload).Error; err != nil {
|
||||
return model.Upload{}, err
|
||||
Where("id = ? AND status IN (?, ?)", id, models.UploadStatusPending, models.UploadStatusUsed).
|
||||
First(&u).Error; err != nil {
|
||||
return models.Upload{}, err
|
||||
}
|
||||
|
||||
SetUploadMetaCache(ctx, &upload)
|
||||
return upload, nil
|
||||
SetUploadMetaCache(ctx, &u)
|
||||
return u, nil
|
||||
}
|
||||
|
||||
// SetUploadMetaCache populates RAM and Redis upload metadata caches.
|
||||
func SetUploadMetaCache(ctx context.Context, upload *model.Upload) {
|
||||
func SetUploadMetaCache(ctx context.Context, u *models.Upload) {
|
||||
ensureUploadMetaCacheListener()
|
||||
|
||||
if upload == nil {
|
||||
if u == nil {
|
||||
return
|
||||
}
|
||||
|
||||
cloned := cloneUpload(*upload)
|
||||
uploadMetaRAM.Set(upload.ID, cloned)
|
||||
cloned := cloneUpload(*u)
|
||||
uploadMetaRAM.Set(u.ID, cloned)
|
||||
if db.Redis != nil {
|
||||
_ = db.SetJSON(ctx, uploadMetaRedisKey(upload.ID), cloned, uploadMetaRedisCacheTTL)
|
||||
_ = db.SetJSON(ctx, uploadMetaRedisKey(u.ID), cloned, uploadMetaRedisCacheTTL)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+22
-22
@@ -9,9 +9,9 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/Rain-kl/Wavelet/internal/infra/persistence"
|
||||
"github.com/Rain-kl/Wavelet/internal/model"
|
||||
"github.com/Rain-kl/Wavelet/internal/testhelper"
|
||||
"github.com/Rain-kl/Wavelet/pkg/persistence"
|
||||
"github.com/Rain-kl/Wavelet/pkg/testhelper"
|
||||
"github.com/Rain-kl/Wavelet/plugins/domain/upload/models"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
@@ -22,7 +22,7 @@ func init() {
|
||||
})
|
||||
}
|
||||
|
||||
func seedUpload(t *testing.T, dbConn *gorm.DB, upload model.Upload) {
|
||||
func seedUpload(t *testing.T, dbConn *gorm.DB, upload models.Upload) {
|
||||
t.Helper()
|
||||
if err := dbConn.Create(&upload).Error; err != nil {
|
||||
t.Fatalf("create upload: %v", err)
|
||||
@@ -35,7 +35,7 @@ func TestGetUploadByIDLoadsFromDBAndPopulatesCache(t *testing.T) {
|
||||
ResetUploadMetaCacheForTest()
|
||||
|
||||
ctx := context.Background()
|
||||
upload := model.Upload{
|
||||
upload := models.Upload{
|
||||
ID: 91001,
|
||||
UserID: 1,
|
||||
FileName: "cached.png",
|
||||
@@ -44,7 +44,7 @@ func TestGetUploadByIDLoadsFromDBAndPopulatesCache(t *testing.T) {
|
||||
MimeType: "image/png",
|
||||
Extension: "png",
|
||||
Type: "avatar",
|
||||
Status: model.UploadStatusUsed,
|
||||
Status: models.UploadStatusUsed,
|
||||
AccessMode: 1,
|
||||
}
|
||||
seedUpload(t, dbConn, upload)
|
||||
@@ -57,7 +57,7 @@ func TestGetUploadByIDLoadsFromDBAndPopulatesCache(t *testing.T) {
|
||||
t.Fatalf("unexpected upload: %+v", got)
|
||||
}
|
||||
|
||||
var redisUpload model.Upload
|
||||
var redisUpload models.Upload
|
||||
if err := db.GetJSON(ctx, uploadMetaRedisKey(upload.ID), &redisUpload); err != nil {
|
||||
t.Fatalf("redis cache miss after DB load: %v", err)
|
||||
}
|
||||
@@ -65,7 +65,7 @@ func TestGetUploadByIDLoadsFromDBAndPopulatesCache(t *testing.T) {
|
||||
t.Fatalf("redis upload id mismatch: got=%d want=%d", redisUpload.ID, upload.ID)
|
||||
}
|
||||
|
||||
if err := dbConn.Delete(&model.Upload{}, upload.ID).Error; err != nil {
|
||||
if err := dbConn.Delete(&models.Upload{}, upload.ID).Error; err != nil {
|
||||
t.Fatalf("delete upload from db: %v", err)
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ func TestGetUploadByIDReadsFromRedisWhenRAMEmpty(t *testing.T) {
|
||||
ResetUploadMetaCacheForTest()
|
||||
|
||||
ctx := context.Background()
|
||||
upload := model.Upload{
|
||||
upload := models.Upload{
|
||||
ID: 91002,
|
||||
UserID: 1,
|
||||
FileName: "redis.png",
|
||||
@@ -93,14 +93,14 @@ func TestGetUploadByIDReadsFromRedisWhenRAMEmpty(t *testing.T) {
|
||||
MimeType: "image/png",
|
||||
Extension: "png",
|
||||
Type: "avatar",
|
||||
Status: model.UploadStatusPending,
|
||||
Status: models.UploadStatusPending,
|
||||
AccessMode: 0,
|
||||
}
|
||||
seedUpload(t, dbConn, upload)
|
||||
SetUploadMetaCache(ctx, &upload)
|
||||
ResetUploadMetaCacheForTest()
|
||||
|
||||
if err := dbConn.Delete(&model.Upload{}, upload.ID).Error; err != nil {
|
||||
if err := dbConn.Delete(&models.Upload{}, upload.ID).Error; err != nil {
|
||||
t.Fatalf("delete upload from db: %v", err)
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ func TestInvalidateUploadMetaCacheClearsRAMAndRedis(t *testing.T) {
|
||||
ResetUploadMetaCacheForTest()
|
||||
|
||||
ctx := context.Background()
|
||||
upload := model.Upload{
|
||||
upload := models.Upload{
|
||||
ID: 91003,
|
||||
UserID: 1,
|
||||
FileName: "invalidate.png",
|
||||
@@ -128,7 +128,7 @@ func TestInvalidateUploadMetaCacheClearsRAMAndRedis(t *testing.T) {
|
||||
MimeType: "image/png",
|
||||
Extension: "png",
|
||||
Type: "avatar",
|
||||
Status: model.UploadStatusUsed,
|
||||
Status: models.UploadStatusUsed,
|
||||
AccessMode: 1,
|
||||
}
|
||||
seedUpload(t, dbConn, upload)
|
||||
@@ -136,7 +136,7 @@ func TestInvalidateUploadMetaCacheClearsRAMAndRedis(t *testing.T) {
|
||||
|
||||
InvalidateUploadMetaCache(ctx, upload.ID)
|
||||
|
||||
var redisUpload model.Upload
|
||||
var redisUpload models.Upload
|
||||
if err := db.GetJSON(ctx, uploadMetaRedisKey(upload.ID), &redisUpload); err == nil {
|
||||
t.Fatal("expected redis cache to be invalidated")
|
||||
}
|
||||
@@ -159,7 +159,7 @@ func TestUploadMetaInvalidationPubSubClearsPeerRAM(t *testing.T) {
|
||||
ResetUploadMetaCacheForTest()
|
||||
|
||||
ctx := context.Background()
|
||||
upload := model.Upload{
|
||||
upload := models.Upload{
|
||||
ID: 91006,
|
||||
UserID: 1,
|
||||
FileName: "pubsub.png",
|
||||
@@ -168,7 +168,7 @@ func TestUploadMetaInvalidationPubSubClearsPeerRAM(t *testing.T) {
|
||||
MimeType: "image/png",
|
||||
Extension: "png",
|
||||
Type: "avatar",
|
||||
Status: model.UploadStatusUsed,
|
||||
Status: models.UploadStatusUsed,
|
||||
AccessMode: 1,
|
||||
}
|
||||
seedUpload(t, dbConn, upload)
|
||||
@@ -177,7 +177,7 @@ func TestUploadMetaInvalidationPubSubClearsPeerRAM(t *testing.T) {
|
||||
t.Fatalf("GetUploadByID: %v", err)
|
||||
}
|
||||
time.Sleep(50 * time.Millisecond) // allow pub/sub listener to subscribe
|
||||
if err := dbConn.Delete(&model.Upload{}, upload.ID).Error; err != nil {
|
||||
if err := dbConn.Delete(&models.Upload{}, upload.ID).Error; err != nil {
|
||||
t.Fatalf("delete upload from db: %v", err)
|
||||
}
|
||||
if _, err := GetUploadByID(ctx, upload.ID); err != nil {
|
||||
@@ -219,7 +219,7 @@ func TestGetUploadByIDSkipsDeletedUploads(t *testing.T) {
|
||||
ResetUploadMetaCacheForTest()
|
||||
|
||||
ctx := context.Background()
|
||||
upload := model.Upload{
|
||||
upload := models.Upload{
|
||||
ID: 91004,
|
||||
UserID: 1,
|
||||
FileName: "deleted.png",
|
||||
@@ -228,7 +228,7 @@ func TestGetUploadByIDSkipsDeletedUploads(t *testing.T) {
|
||||
MimeType: "image/png",
|
||||
Extension: "png",
|
||||
Type: "avatar",
|
||||
Status: model.UploadStatusDeleted,
|
||||
Status: models.UploadStatusDeleted,
|
||||
AccessMode: 1,
|
||||
}
|
||||
seedUpload(t, dbConn, upload)
|
||||
@@ -251,7 +251,7 @@ func TestGetUploadByIDWorksWithRedisDisabled(t *testing.T) {
|
||||
})
|
||||
|
||||
ctx := context.Background()
|
||||
upload := model.Upload{
|
||||
upload := models.Upload{
|
||||
ID: 91005,
|
||||
UserID: 1,
|
||||
FileName: "ram-only.png",
|
||||
@@ -260,7 +260,7 @@ func TestGetUploadByIDWorksWithRedisDisabled(t *testing.T) {
|
||||
MimeType: "image/png",
|
||||
Extension: "png",
|
||||
Type: "avatar",
|
||||
Status: model.UploadStatusUsed,
|
||||
Status: models.UploadStatusUsed,
|
||||
AccessMode: 1,
|
||||
}
|
||||
seedUpload(t, dbConn, upload)
|
||||
@@ -273,7 +273,7 @@ func TestGetUploadByIDWorksWithRedisDisabled(t *testing.T) {
|
||||
t.Fatalf("unexpected upload: %+v", got)
|
||||
}
|
||||
|
||||
if err := dbConn.Delete(&model.Upload{}, upload.ID).Error; err != nil {
|
||||
if err := dbConn.Delete(&models.Upload{}, upload.ID).Error; err != nil {
|
||||
t.Fatalf("delete upload from db: %v", err)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user