accounts/rag-server/internal/auth/middleware.go
Haitao Pan 0a55d3c4a1 refactor: move Sidebar to hero section with two-column layout
### Layout Changes:
1. **Homepage Layout Restructure** (`src/modules/homepage/page.tsx`)
   - Changed hero section to two-column grid: `lg:grid-cols-[minmax(0,1fr)_360px]`
   - Moved Sidebar component to right side of hero section (360px width)
   - Hero content now spans left column with full-width appearance
   - Sidebar is sticky on large screens: `lg:sticky lg:top-0`
   - Maintains consistent width between upper and lower sections

2. **Template System Updates**
   - Removed Sidebar from `HomePageTemplateSlots` type (`src/modules/templates/types.ts`)
   - Updated `defaultHomeLayoutConfig` to remove Sidebar from content slots
   - Updated default template to remove Sidebar registration
   - Updated `app/page.tsx` to pass only ProductMatrix and CommunityFeed slots

3. **Sidebar Component Conversion** (`src/components/home/Sidebar.tsx`)
   - Converted from Server Component to Client Component
   - Added 'use client' directive to enable useLanguage hook
   - Replaced dynamic CMS content with static bilingual content
   - Hardcoded sections: 社区热议, 推荐资源, 热门标签
   - Maintains same visual structure and styling

4. **Homepage Module Cleanup** (`src/modules/homepage/page.tsx`)
   - Removed unused `getHomepagePosts` import (was causing fs error in client)
   - Kept useLanguage hook for internationalization support
   - Sidebar now works as client component alongside homepage

### Technical Details:
- Grid layout: Two columns on large screens (hero + sidebar)
- Sidebar width: Fixed 360px, sticky positioning
- Hero content: Flexible width with max-w-6xl constraint
- Visual consistency: Upper and lower sections maintain same max-width
- Component architecture: All client components, no server-client mixing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-11 13:06:49 +08:00

153 lines
3.4 KiB
Go

package auth
import (
"context"
"net/http"
"strings"
"github.com/gin-gonic/gin"
)
// Context keys for storing user information
type contextKey string
const (
userIDKey contextKey = "user_id"
emailKey contextKey = "email"
rolesKey contextKey = "roles"
serviceKey contextKey = "service"
bearerPrefix = "Bearer "
)
// AuthMiddleware is a middleware that validates JWT access tokens
func (s *TokenService) AuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
authHeader := c.GetHeader("Authorization")
if authHeader == "" {
c.JSON(http.StatusUnauthorized, gin.H{
"error": "missing authorization header",
})
c.Abort()
return
}
if !strings.HasPrefix(authHeader, bearerPrefix) {
c.JSON(http.StatusUnauthorized, gin.H{
"error": "invalid authorization header format",
})
c.Abort()
return
}
token := strings.TrimPrefix(authHeader, bearerPrefix)
claims, err := s.ValidateAccessToken(token)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{
"error": "invalid or expired token",
"detail": err.Error(),
})
c.Abort()
return
}
// Verify service claim
if claims.Service != "rag-server" {
c.JSON(http.StatusForbidden, gin.H{
"error": "invalid token for this service",
})
c.Abort()
return
}
// Store claims in context
ctx := context.WithValue(c.Request.Context(), userIDKey, claims.UserID)
ctx = context.WithValue(ctx, emailKey, claims.Email)
ctx = context.WithValue(ctx, rolesKey, claims.Roles)
ctx = context.WithValue(ctx, serviceKey, claims.Service)
c.Request = c.Request.WithContext(ctx)
c.Next()
}
}
// RequireRole is a middleware that requires a specific role
func RequireRole(role string) gin.HandlerFunc {
return func(c *gin.Context) {
roles := c.Request.Context().Value(rolesKey)
if roles == nil {
c.JSON(http.StatusForbidden, gin.H{
"error": "no roles found in token",
})
c.Abort()
return
}
roleSlice, ok := roles.([]string)
if !ok {
c.JSON(http.StatusForbidden, gin.H{
"error": "invalid roles format in token",
})
c.Abort()
return
}
for _, r := range roleSlice {
if r == role {
c.Next()
return
}
}
c.JSON(http.StatusForbidden, gin.H{
"error": "insufficient permissions",
"required_role": role,
})
c.Abort()
}
}
// GetUserID extracts user ID from context
func GetUserID(c *gin.Context) string {
userID := c.Request.Context().Value(userIDKey)
if userID == nil {
return ""
}
return userID.(string)
}
// GetEmail extracts email from context
func GetEmail(c *gin.Context) string {
email := c.Request.Context().Value(emailKey)
if email == nil {
return ""
}
return email.(string)
}
// GetRoles extracts roles from context
func GetRoles(c *gin.Context) []string {
roles := c.Request.Context().Value(rolesKey)
if roles == nil {
return nil
}
return roles.([]string)
}
// VerifyTokenMiddleware creates a middleware that verifies JWT tokens
func VerifyTokenMiddleware(config *MiddlewareConfig) gin.HandlerFunc {
// For now, just return the basic auth middleware
// The config parameters (SkipPaths, CacheTTL) can be used for optimization
return (&TokenService{}).AuthMiddleware()
}
// HealthCheckHandler returns a health check handler
func HealthCheckHandler(client *AuthClient) gin.HandlerFunc {
return func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"status": "ok",
"auth": "enabled",
})
}
}