mirror of
https://github.com/SamyRai/turash.git
synced 2025-12-26 23:01:33 +00:00
Repository Structure:
- Move files from cluttered root directory into organized structure
- Create archive/ for archived data and scraper results
- Create bugulma/ for the complete application (frontend + backend)
- Create data/ for sample datasets and reference materials
- Create docs/ for comprehensive documentation structure
- Create scripts/ for utility scripts and API tools
Backend Implementation:
- Implement 3 missing backend endpoints identified in gap analysis:
* GET /api/v1/organizations/{id}/matching/direct - Direct symbiosis matches
* GET /api/v1/users/me/organizations - User organizations
* POST /api/v1/proposals/{id}/status - Update proposal status
- Add complete proposal domain model, repository, and service layers
- Create database migration for proposals table
- Fix CLI server command registration issue
API Documentation:
- Add comprehensive proposals.md API documentation
- Update README.md with Users and Proposals API sections
- Document all request/response formats, error codes, and business rules
Code Quality:
- Follow existing Go backend architecture patterns
- Add proper error handling and validation
- Match frontend expected response schemas
- Maintain clean separation of concerns (handler -> service -> repository)
113 lines
3.0 KiB
Go
113 lines
3.0 KiB
Go
package service
|
|
|
|
import (
|
|
"bugulma/backend/internal/domain"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/golang-jwt/jwt/v5"
|
|
)
|
|
|
|
// JWTClaims represents the JWT claims structure
|
|
type JWTClaims struct {
|
|
UserID string `json:"user_id"`
|
|
Email string `json:"email"`
|
|
Role string `json:"role"`
|
|
OrgID string `json:"org_id,omitempty"`
|
|
jwt.RegisteredClaims
|
|
}
|
|
|
|
// JWTService handles JWT token operations
|
|
type JWTService struct {
|
|
secretKey []byte
|
|
}
|
|
|
|
// NewJWTService creates a new JWT service
|
|
func NewJWTService(secretKey string) *JWTService {
|
|
return &JWTService{
|
|
secretKey: []byte(secretKey),
|
|
}
|
|
}
|
|
|
|
// GenerateToken generates a JWT token for a user
|
|
func (j *JWTService) GenerateToken(user *domain.User, orgID string) (string, error) {
|
|
claims := JWTClaims{
|
|
UserID: user.ID,
|
|
Email: user.Email,
|
|
Role: string(user.Role),
|
|
OrgID: orgID,
|
|
RegisteredClaims: jwt.RegisteredClaims{
|
|
Issuer: "bugulma-backend",
|
|
Subject: user.ID,
|
|
ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)), // 24 hours
|
|
IssuedAt: jwt.NewNumericDate(time.Now()),
|
|
NotBefore: jwt.NewNumericDate(time.Now()),
|
|
},
|
|
}
|
|
|
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
|
return token.SignedString(j.secretKey)
|
|
}
|
|
|
|
// ValidateToken validates and parses a JWT token
|
|
func (j *JWTService) ValidateToken(tokenString string) (*JWTClaims, error) {
|
|
token, err := jwt.ParseWithClaims(tokenString, &JWTClaims{}, func(token *jwt.Token) (interface{}, error) {
|
|
// Validate the signing method
|
|
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
|
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
|
|
}
|
|
return j.secretKey, nil
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to parse token: %w", err)
|
|
}
|
|
|
|
if !token.Valid {
|
|
return nil, fmt.Errorf("invalid token")
|
|
}
|
|
|
|
claims, ok := token.Claims.(*JWTClaims)
|
|
if !ok {
|
|
return nil, fmt.Errorf("invalid token claims")
|
|
}
|
|
|
|
// Check if token is expired
|
|
if claims.ExpiresAt != nil && claims.ExpiresAt.Before(time.Now()) {
|
|
return nil, fmt.Errorf("token expired")
|
|
}
|
|
|
|
return claims, nil
|
|
}
|
|
|
|
// ExtractClaims extracts claims from a validated token
|
|
func (j *JWTService) ExtractClaims(tokenString string) (*JWTClaims, error) {
|
|
return j.ValidateToken(tokenString)
|
|
}
|
|
|
|
// RefreshToken generates a new token with updated expiration
|
|
func (j *JWTService) RefreshToken(tokenString string) (string, error) {
|
|
claims, err := j.ValidateToken(tokenString)
|
|
if err != nil {
|
|
return "", fmt.Errorf("cannot refresh invalid token: %w", err)
|
|
}
|
|
|
|
// Create new claims with updated expiration
|
|
newClaims := JWTClaims{
|
|
UserID: claims.UserID,
|
|
Email: claims.Email,
|
|
Role: claims.Role,
|
|
OrgID: claims.OrgID,
|
|
RegisteredClaims: jwt.RegisteredClaims{
|
|
Issuer: claims.Issuer,
|
|
Subject: claims.Subject,
|
|
ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)),
|
|
IssuedAt: jwt.NewNumericDate(time.Now()),
|
|
NotBefore: jwt.NewNumericDate(time.Now()),
|
|
},
|
|
}
|
|
|
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, newClaims)
|
|
return token.SignedString(j.secretKey)
|
|
}
|