mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-04 15:06:37 +08:00
perf(cache): 三层缓存框架补强
- 新增 cache-framework skill,规范 RAM→Redis→DB 读路径、失效与 pub/sub - 上传元数据 Otter+Redis 缓存与多节点失效;Auth Source 缓存与 pub/sub - ListSystemConfigsByKeys 补 Redis 层;上传统计单事务;登录/Token 缓存预热 - cleanup 任务补 upload meta 失效钩子
This commit is contained in:
@@ -25,16 +25,16 @@ func isOIDCLoginEnabled(ctx context.Context) bool {
|
||||
func resolveAuthSource(ctx context.Context, sourceName string) (*model.AuthSource, error) {
|
||||
name := strings.TrimSpace(strings.ToLower(sourceName))
|
||||
if name == "" {
|
||||
sources, err := model.GetActiveAuthSources(ctx)
|
||||
sources, err := repository.GetActiveAuthSourcesCached(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(sources) == 0 {
|
||||
return nil, errors.New(errNoActiveAuthSource)
|
||||
}
|
||||
return &sources[0], nil
|
||||
return repository.GetAuthSourceByNameCached(ctx, sources[0].Name)
|
||||
}
|
||||
return model.GetAuthSourceByName(ctx, name)
|
||||
return repository.GetAuthSourceByNameCached(ctx, name)
|
||||
}
|
||||
|
||||
func activeLoginSources(ctx context.Context) []AuthSourceView {
|
||||
@@ -43,7 +43,7 @@ func activeLoginSources(ctx context.Context) []AuthSourceView {
|
||||
return nil
|
||||
}
|
||||
|
||||
dbSources, err := model.GetActiveAuthSources(ctx)
|
||||
dbSources, err := repository.GetActiveAuthSourcesCached(ctx)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -179,6 +179,8 @@ func handleCallbackLogin(ctx context.Context, c *gin.Context, source *model.Auth
|
||||
return
|
||||
}
|
||||
|
||||
SetCachedUser(ctx, user.ID, &user)
|
||||
|
||||
logger.InfoF(ctx, "[LoginAudit] successful OAuth login via source: %s, external ID: %s, user: %s, ID: %d, IP: %s", source.Name, userInfo.Sub, user.Username, user.ID, c.ClientIP())
|
||||
|
||||
listener.EmitAdminLoggedIn(ctx, &user, c.ClientIP())
|
||||
|
||||
@@ -95,6 +95,28 @@ func (m *mockRedisClient) Del(ctx context.Context, keys ...string) *redis.IntCmd
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (m *mockRedisClient) Scan(ctx context.Context, cursor uint64, match string, count int64) *redis.ScanCmd {
|
||||
cmd := redis.NewScanCmd(ctx, nil, cursor, match, count)
|
||||
var keys []string
|
||||
for key := range m.store {
|
||||
if redisMatchPattern(key, match) {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
}
|
||||
cmd.SetVal(keys, 0)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func redisMatchPattern(key, pattern string) bool {
|
||||
if pattern == "" || pattern == "*" {
|
||||
return true
|
||||
}
|
||||
if strings.HasSuffix(pattern, "*") {
|
||||
return strings.HasPrefix(key, strings.TrimSuffix(pattern, "*"))
|
||||
}
|
||||
return key == pattern
|
||||
}
|
||||
|
||||
func (m *mockRedisClient) HSet(ctx context.Context, key string, values ...interface{}) *redis.IntCmd {
|
||||
cmd := redis.NewIntCmd(ctx)
|
||||
if len(values) >= 2 {
|
||||
@@ -129,6 +151,12 @@ func (m *mockRedisClient) HGet(ctx context.Context, key string, field string) *r
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (m *mockRedisClient) Publish(ctx context.Context, channel string, message interface{}) *redis.IntCmd {
|
||||
cmd := redis.NewIntCmd(ctx)
|
||||
cmd.SetVal(1)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (m *mockRedisClient) Subscribe(ctx context.Context, channels ...string) *redis.PubSub {
|
||||
return redis.NewClient(&redis.Options{
|
||||
Addr: "127.0.0.1:0",
|
||||
@@ -310,6 +338,7 @@ func newMockOIDCClient(issuer, clientID string, expectedState *string, sub, user
|
||||
|
||||
func setupTestDB(t *testing.T) *gorm.DB {
|
||||
repository.ResetSystemConfigRAMCacheForTest()
|
||||
repository.ResetAuthSourceRAMCacheForTest()
|
||||
|
||||
dbConn, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
|
||||
if err != nil {
|
||||
@@ -635,6 +664,7 @@ func TestAuthorize(t *testing.T) {
|
||||
|
||||
// Case 2: Inactive Source Authorize
|
||||
dbConn.Model(&model.AuthSource{}).Where("id = ?", 101).Update("is_active", false)
|
||||
_ = repository.InvalidateAuthSourceCache(context.Background())
|
||||
w2 := performRequest(router, http.MethodGet, "/api/v1/oauth/github/authorize", nil, nil, nil)
|
||||
if w2.Code != http.StatusBadRequest {
|
||||
t.Errorf("expected 400 for inactive source, got %d", w2.Code)
|
||||
@@ -1122,6 +1152,7 @@ func TestOIDCPolicyEnforcement(t *testing.T) {
|
||||
repository.ResetSystemConfigRAMCacheForTest()
|
||||
mockRedis.Del(context.Background(), db.PrefixedKey(repository.SystemConfigRedisHashKey)+":"+model.ConfigKeyOIDCLoginEnabled)
|
||||
dbConn.Model(&model.AuthSource{}).Where("name = ?", testSourceName).Update("is_active", false)
|
||||
_ = repository.InvalidateAuthSourceCache(context.Background())
|
||||
|
||||
wSourceInactive := performRequest(router, http.MethodGet, "/api/v1/oauth/login?source="+testSourceName, nil, nil, nil)
|
||||
if wSourceInactive.Code != http.StatusBadRequest {
|
||||
@@ -1134,6 +1165,7 @@ func TestOIDCPolicyEnforcement(t *testing.T) {
|
||||
repository.ResetSystemConfigRAMCacheForTest()
|
||||
mockRedis.Del(context.Background(), db.PrefixedKey(repository.SystemConfigRedisHashKey)+":"+model.ConfigKeyOIDCLoginEnabled)
|
||||
dbConn.Model(&model.AuthSource{}).Where("name = ?", testSourceName).Update("is_active", true)
|
||||
_ = repository.InvalidateAuthSourceCache(context.Background())
|
||||
|
||||
wAuthDisabled := performRequest(router, http.MethodGet, "/api/v1/oauth/"+testSourceName+"/authorize", nil, nil, nil)
|
||||
if wAuthDisabled.Code != http.StatusBadRequest {
|
||||
@@ -1146,6 +1178,7 @@ func TestOIDCPolicyEnforcement(t *testing.T) {
|
||||
repository.ResetSystemConfigRAMCacheForTest()
|
||||
mockRedis.Del(context.Background(), db.PrefixedKey(repository.SystemConfigRedisHashKey)+":"+model.ConfigKeyOIDCLoginEnabled)
|
||||
dbConn.Model(&model.AuthSource{}).Where("name = ?", testSourceName).Update("is_active", true)
|
||||
_ = repository.InvalidateAuthSourceCache(context.Background())
|
||||
|
||||
wLogin := performRequest(router, http.MethodGet, "/api/v1/oauth/login?source="+testSourceName, nil, nil, nil)
|
||||
|
||||
@@ -1187,6 +1220,7 @@ func TestOIDCPolicyEnforcement(t *testing.T) {
|
||||
|
||||
// Since callback deletes state, we need to generate state again
|
||||
dbConn.Model(&model.AuthSource{}).Where("name = ?", testSourceName).Update("is_active", true)
|
||||
_ = repository.InvalidateAuthSourceCache(context.Background())
|
||||
wLogin2 := performRequest(router, http.MethodGet, "/api/v1/oauth/login?source="+testSourceName, nil, nil, nil)
|
||||
_ = json.Unmarshal(wLogin2.Body.Bytes(), &loginUrlResp)
|
||||
parsedURL, _ = url.Parse(loginUrlResp.Data.AuthorizeURL)
|
||||
@@ -1201,6 +1235,7 @@ func TestOIDCPolicyEnforcement(t *testing.T) {
|
||||
|
||||
// Deactivate source
|
||||
dbConn.Model(&model.AuthSource{}).Where("name = ?", testSourceName).Update("is_active", false)
|
||||
_ = repository.InvalidateAuthSourceCache(context.Background())
|
||||
reqBody2 := fmt.Sprintf(`{"state":"%s","code":"test_auth_code"}`, state)
|
||||
wCallbackSourceInactive := performRequest(router, http.MethodPost, "/api/v1/oauth/callback", []byte(reqBody2), map[string]string{
|
||||
"Content-Type": "application/json",
|
||||
|
||||
Reference in New Issue
Block a user