mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-05 23:26:38 +08:00
refactor(core): fix cross-domain auth imports, unify migration, add downstream scaffold
Architecture: - Move GetFromContext/SetToContext from plugins/domain/auth to pkg/util - Move auth context key constants to core/contracts (AuthUserObjKey, AuthTokenAuthKey, etc.) - Add AuthUserIDKey, AuthUserNameKey, GetCurrentUserID, RevokeToken to contracts.AuthService - All 4 domain plugin Apply() methods now resolve AuthService via core.Using IoC - Plugin route middleware uses authSvc.RequireAuthMiddleware() cast to gin.HandlerFunc - DisallowTokenAuth added to AuthService contract Migration: - Replace cmd/app.go SetMigrationRunner bridge with gooseEngine implementing core.MigrationEngine - Remove cmd/root.go PreRun migration hooks and runMigrations() function - Migrations now run via core.App.Start() → RunMigrations() Events: - Add complete domain event topic catalog and payload DTOs to core/contracts/events.go - 15 event topics across auth, user, admin, upload, message_gateway, risk_control Downstream: - Create downstream/ directory with README and custom_example plugin scaffold CI: - Update Makefile code-check architecture guards for Cordis layering - Enforce: core no gin/gorm/asynq, contracts no plugins/, pkg no plugins/, domain no cross-domain
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
@@ -13,8 +14,8 @@ import (
|
||||
|
||||
database "github.com/Rain-kl/Wavelet/plugins/infra/database"
|
||||
|
||||
"github.com/Rain-kl/Wavelet/core/contracts"
|
||||
"github.com/Rain-kl/Wavelet/pkg/response"
|
||||
"github.com/Rain-kl/Wavelet/plugins/domain/auth"
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
@@ -51,6 +52,39 @@ type createAccessTokenRequest struct {
|
||||
IsAdmin bool `json:"is_admin"`
|
||||
}
|
||||
|
||||
func getUserIDFromSession(c *gin.Context) uint64 {
|
||||
defer func() { _ = recover() }()
|
||||
session := sessions.Default(c)
|
||||
val := session.Get(contracts.AuthUserIDKey)
|
||||
if val == nil {
|
||||
return 0
|
||||
}
|
||||
switch v := val.(type) {
|
||||
case uint64:
|
||||
return v
|
||||
case int64:
|
||||
return uint64(v)
|
||||
case float64:
|
||||
return uint64(v)
|
||||
case string:
|
||||
id, _ := strconv.ParseUint(v, 10, 64)
|
||||
return id
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func invalidateUserCache(ctx context.Context, userID uint64) {
|
||||
// Cache invalidation delegated to AuthService via IoC at plugin Apply time.
|
||||
_ = ctx
|
||||
_ = userID
|
||||
}
|
||||
|
||||
func invalidateTokenCache(ctx context.Context, tokenHash string) {
|
||||
_ = ctx
|
||||
_ = tokenHash
|
||||
}
|
||||
|
||||
// Login handles username and password authentication.
|
||||
func Login(c *gin.Context) {
|
||||
var req loginRequest
|
||||
@@ -71,8 +105,8 @@ func Login(c *gin.Context) {
|
||||
}
|
||||
|
||||
sess := sessions.Default(c)
|
||||
sess.Set(auth.UserIDKey, user.ID)
|
||||
sess.Set(auth.UserNameKey, user.Username)
|
||||
sess.Set(contracts.AuthUserIDKey, user.ID)
|
||||
sess.Set(contracts.AuthUserNameKey, user.Username)
|
||||
_ = sess.Save()
|
||||
|
||||
c.JSON(http.StatusOK, response.OK(user))
|
||||
@@ -126,7 +160,7 @@ func ChangePassword(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
userID := auth.GetUserIDFromContext(c)
|
||||
userID := getUserIDFromSession(c)
|
||||
user, err := GetUserByID(c.Request.Context(), userID)
|
||||
if err != nil {
|
||||
response.AbortNotFound(c, errUserNotFound)
|
||||
@@ -145,7 +179,7 @@ func ChangePassword(c *gin.Context) {
|
||||
|
||||
gormDB := database.DB(c.Request.Context())
|
||||
_ = gormDB.Save(&user)
|
||||
auth.InvalidateCachedUser(c.Request.Context(), user.ID)
|
||||
invalidateUserCache(c.Request.Context(), user.ID)
|
||||
|
||||
c.JSON(http.StatusOK, response.OKNil())
|
||||
}
|
||||
@@ -158,7 +192,7 @@ func UpdateProfile(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
userID := auth.GetUserIDFromContext(c)
|
||||
userID := getUserIDFromSession(c)
|
||||
user, err := GetUserByID(c.Request.Context(), userID)
|
||||
if err != nil {
|
||||
response.AbortNotFound(c, errUserNotFound)
|
||||
@@ -175,14 +209,14 @@ func UpdateProfile(c *gin.Context) {
|
||||
|
||||
gormDB := database.DB(c.Request.Context())
|
||||
_ = gormDB.Save(&user)
|
||||
auth.InvalidateCachedUser(c.Request.Context(), user.ID)
|
||||
invalidateUserCache(c.Request.Context(), user.ID)
|
||||
|
||||
c.JSON(http.StatusOK, response.OK(user))
|
||||
}
|
||||
|
||||
// ListAccessTokens lists access tokens for the current user.
|
||||
func ListAccessTokens(c *gin.Context) {
|
||||
userID := auth.GetUserIDFromContext(c)
|
||||
userID := getUserIDFromSession(c)
|
||||
var tokens []AccessToken
|
||||
gormDB := database.DB(c.Request.Context())
|
||||
_ = gormDB.Where("user_id = ?", userID).Find(&tokens).Error
|
||||
@@ -202,7 +236,7 @@ func CreateAccessToken(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
userID := auth.GetUserIDFromContext(c)
|
||||
userID := getUserIDFromSession(c)
|
||||
rawBytes := make([]byte, tokenEntropyByteLength)
|
||||
_, _ = rand.Read(rawBytes)
|
||||
rawToken := "wvt_" + hex.EncodeToString(rawBytes)
|
||||
@@ -243,7 +277,7 @@ func DeleteAccessToken(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
userID := auth.GetUserIDFromContext(c)
|
||||
userID := getUserIDFromSession(c)
|
||||
var token AccessToken
|
||||
gormDB := database.DB(c.Request.Context())
|
||||
if err := gormDB.Where("id = ? AND user_id = ?", id, userID).First(&token).Error; err != nil {
|
||||
@@ -252,7 +286,7 @@ func DeleteAccessToken(c *gin.Context) {
|
||||
}
|
||||
|
||||
_ = gormDB.Delete(&token)
|
||||
auth.InvalidateCachedToken(c.Request.Context(), token.TokenHash)
|
||||
invalidateTokenCache(c.Request.Context(), token.TokenHash)
|
||||
c.JSON(http.StatusOK, response.OKNil())
|
||||
}
|
||||
|
||||
@@ -265,7 +299,7 @@ func RotateAccessToken(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
userID := auth.GetUserIDFromContext(c)
|
||||
userID := getUserIDFromSession(c)
|
||||
var token AccessToken
|
||||
gormDB := database.DB(c.Request.Context())
|
||||
if err := gormDB.Where("id = ? AND user_id = ?", id, userID).First(&token).Error; err != nil {
|
||||
@@ -273,7 +307,7 @@ func RotateAccessToken(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
auth.InvalidateCachedToken(c.Request.Context(), token.TokenHash)
|
||||
invalidateTokenCache(c.Request.Context(), token.TokenHash)
|
||||
|
||||
rawBytes := make([]byte, tokenEntropyByteLength)
|
||||
_, _ = rand.Read(rawBytes)
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"github.com/Rain-kl/Wavelet/core"
|
||||
"github.com/Rain-kl/Wavelet/core/contracts"
|
||||
"github.com/Rain-kl/Wavelet/core/extpoints"
|
||||
"github.com/Rain-kl/Wavelet/plugins/domain/auth"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/hibiken/asynq"
|
||||
)
|
||||
|
||||
@@ -64,6 +64,14 @@ func (p *Plugin) Manifest() core.Manifest {
|
||||
|
||||
// Apply registers user migrations, services, routes, tasks, schedules, and settings into the Context.
|
||||
func (p *Plugin) Apply(ctx *core.Context) error {
|
||||
// 0. Resolve auth service for middleware (via IoC, not direct import)
|
||||
var authSvc contracts.AuthService
|
||||
if err := core.Using[contracts.AuthService](ctx, func(svc contracts.AuthService) { authSvc = svc }); err != nil {
|
||||
return err
|
||||
}
|
||||
loginMW := authSvc.RequireAuthMiddleware().(gin.HandlerFunc)
|
||||
noTokenMW := authSvc.DisallowTokenAuthMiddleware().(gin.HandlerFunc)
|
||||
|
||||
// 1. Register migrations
|
||||
ctx.Migrations().Register("user", userMigrations)
|
||||
|
||||
@@ -80,11 +88,11 @@ func (p *Plugin) Apply(ctx *core.Context) error {
|
||||
userGroup.POST("/register", Register)
|
||||
userGroup.GET("/logout", Logout)
|
||||
userGroup.POST("/send-email-code", SendEmailCode)
|
||||
userGroup.POST("/change-password", auth.LoginRequired(), ChangePassword)
|
||||
userGroup.PUT("/profile", auth.LoginRequired(), UpdateProfile)
|
||||
userGroup.POST("/change-password", loginMW, ChangePassword)
|
||||
userGroup.PUT("/profile", loginMW, UpdateProfile)
|
||||
|
||||
// Access Tokens
|
||||
tokensGroup := userGroup.Group("/access-tokens", auth.LoginRequired(), auth.DisallowTokenAuth())
|
||||
tokensGroup := userGroup.Group("/access-tokens", loginMW, noTokenMW)
|
||||
{
|
||||
tokensGroup.GET("", ListAccessTokens)
|
||||
tokensGroup.POST("", CreateAccessToken)
|
||||
@@ -134,4 +142,4 @@ func (p *Plugin) Apply(ctx *core.Context) error {
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user