Compare commits
No commits in common. "61db4ff54d1a5c11594dd6580277b889402a35d9" and "b03f9c9704d93e596b55a474ba3656f9ba8e61b9" have entirely different histories.
61db4ff54d
...
b03f9c9704
3 changed files with 28 additions and 130 deletions
|
|
@ -25,18 +25,18 @@ import (
|
||||||
var AppConfig Config
|
var AppConfig Config
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if len(os.Args) != 2 {
|
// if len(os.Args) != 2 {
|
||||||
AppConfig.Mode = "env"
|
// AppConfig.Mode = "env"
|
||||||
} else {
|
// } else {
|
||||||
AppConfig.Mode = "argv"
|
// AppConfig.Mode = "argv"
|
||||||
}
|
// }
|
||||||
|
|
||||||
err := InitConfig()
|
// err := InitConfig()
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
log.Fatalf("Failed to init config: %v\n", err)
|
// log.Fatalf("Failed to init config: %v\n", err)
|
||||||
}
|
// }
|
||||||
|
|
||||||
pool, err := pgxpool.New(context.Background(), AppConfig.DdUrl)
|
pool, err := pgxpool.New(context.Background(), os.Getenv("DATABASE_URL"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Unable to connect to database: %v\n", err)
|
fmt.Fprintf(os.Stderr, "Unable to connect to database: %v\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|
@ -47,11 +47,16 @@ func main() {
|
||||||
r := gin.Default()
|
r := gin.Default()
|
||||||
|
|
||||||
r.Use(middleware.CSRFMiddleware())
|
r.Use(middleware.CSRFMiddleware())
|
||||||
r.Use(middleware.JWTAuthMiddleware(AppConfig.JwtPrivateKey))
|
// jwt middle will be here
|
||||||
|
|
||||||
queries := sqlc.New(pool)
|
queries := sqlc.New(pool)
|
||||||
|
|
||||||
rmqConn, err := amqp091.Dial(AppConfig.rmqURL)
|
// === RabbitMQ setup ===
|
||||||
|
rmqURL := os.Getenv("RABBITMQ_URL")
|
||||||
|
if rmqURL == "" {
|
||||||
|
rmqURL = "amqp://guest:guest@rabbitmq:5672/"
|
||||||
|
}
|
||||||
|
|
||||||
|
rmqConn, err := amqp091.Dial(rmqURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to connect to RabbitMQ: %v", err)
|
log.Fatalf("Failed to connect to RabbitMQ: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -63,7 +68,7 @@ func main() {
|
||||||
server := handlers.NewServer(queries, publisher, rpcClient)
|
server := handlers.NewServer(queries, publisher, rpcClient)
|
||||||
|
|
||||||
r.Use(cors.New(cors.Config{
|
r.Use(cors.New(cors.Config{
|
||||||
AllowOrigins: []string{AppConfig.ServiceAddress},
|
AllowOrigins: []string{"*"}, // allow all origins, change to specific domains in production
|
||||||
AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "PATCH"},
|
AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "PATCH"},
|
||||||
AllowHeaders: []string{"Origin", "Content-Type", "Accept"},
|
AllowHeaders: []string{"Origin", "Content-Type", "Accept"},
|
||||||
ExposeHeaders: []string{"Content-Length"},
|
ExposeHeaders: []string{"Content-Length"},
|
||||||
|
|
@ -73,7 +78,7 @@ func main() {
|
||||||
|
|
||||||
oapi.RegisterHandlers(r, oapi.NewStrictHandler(
|
oapi.RegisterHandlers(r, oapi.NewStrictHandler(
|
||||||
server,
|
server,
|
||||||
|
// сюда можно добавить middlewares, если нужно
|
||||||
[]oapi.StrictMiddlewareFunc{},
|
[]oapi.StrictMiddlewareFunc{},
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,109 +0,0 @@
|
||||||
package middleware
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/golang-jwt/jwt/v5"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ctxKey — приватный тип для ключа контекста
|
|
||||||
type ctxKey struct{}
|
|
||||||
|
|
||||||
// ginContextKey — уникальный ключ для хранения *gin.Context
|
|
||||||
var ginContextKey = &ctxKey{}
|
|
||||||
|
|
||||||
// GinContextToContext сохраняет *gin.Context в context.Context запроса
|
|
||||||
func GinContextToContext(c *gin.Context) {
|
|
||||||
ctx := context.WithValue(c.Request.Context(), ginContextKey, c)
|
|
||||||
c.Request = c.Request.WithContext(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GinContextFromContext извлекает *gin.Context из context.Context
|
|
||||||
func GinContextFromContext(ctx context.Context) (*gin.Context, bool) {
|
|
||||||
ginCtx, ok := ctx.Value(ginContextKey).(*gin.Context)
|
|
||||||
return ginCtx, ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func JWTAuthMiddleware(secret string) gin.HandlerFunc {
|
|
||||||
return func(c *gin.Context) {
|
|
||||||
// 1. Получаем access_token из cookie
|
|
||||||
tokenStr, err := c.Cookie("access_token")
|
|
||||||
if err != nil {
|
|
||||||
abortWithJSON(c, http.StatusUnauthorized, "missing access_token cookie")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Парсим токен с MapClaims
|
|
||||||
token, err := jwt.Parse(tokenStr, func(t *jwt.Token) (interface{}, error) {
|
|
||||||
if t.Method != jwt.SigningMethodHS256 {
|
|
||||||
return nil, errors.New("unexpected signing method: " + t.Method.Alg())
|
|
||||||
}
|
|
||||||
return []byte(secret), nil // ← конвертируем string → []byte
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
abortWithJSON(c, http.StatusUnauthorized, "invalid token: "+err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Проверяем валидность
|
|
||||||
if !token.Valid {
|
|
||||||
abortWithJSON(c, http.StatusUnauthorized, "token is invalid")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. Извлекаем user_id из claims
|
|
||||||
claims, ok := token.Claims.(jwt.MapClaims)
|
|
||||||
if !ok {
|
|
||||||
abortWithJSON(c, http.StatusUnauthorized, "invalid claims format")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
userID, ok := claims["user_id"].(string)
|
|
||||||
if !ok || userID == "" {
|
|
||||||
abortWithJSON(c, http.StatusUnauthorized, "user_id claim missing or invalid")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. Сохраняем в контексте
|
|
||||||
c.Set("user_id", userID)
|
|
||||||
|
|
||||||
// 6. Для oapi-codegen — кладём gin.Context в request context
|
|
||||||
GinContextToContext(c)
|
|
||||||
|
|
||||||
c.Next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Вспомогательные функции (без изменений)
|
|
||||||
func UserIDFromGin(c *gin.Context) (string, bool) {
|
|
||||||
id, exists := c.Get("user_id")
|
|
||||||
if !exists {
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
if s, ok := id.(string); ok {
|
|
||||||
return s, true
|
|
||||||
}
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
func UserIDFromContext(ctx context.Context) (string, error) {
|
|
||||||
ginCtx, ok := GinContextFromContext(ctx)
|
|
||||||
if !ok {
|
|
||||||
return "", errors.New("gin context not found")
|
|
||||||
}
|
|
||||||
userID, ok := UserIDFromGin(ginCtx)
|
|
||||||
if !ok {
|
|
||||||
return "", errors.New("user_id not found in context")
|
|
||||||
}
|
|
||||||
return userID, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func abortWithJSON(c *gin.Context, code int, message string) {
|
|
||||||
c.AbortWithStatusJSON(code, gin.H{
|
|
||||||
"error": "unauthorized",
|
|
||||||
"message": message,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
@ -2,9 +2,11 @@ package main
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Mode string
|
Mode string
|
||||||
ServiceAddress string `toml:"ServiceAddress" env:"SERVICE_ADDRESS"`
|
|
||||||
DdUrl string `toml:"DbUrl" env:"DATABASE_URL"`
|
|
||||||
JwtPrivateKey string `toml:"JwtPrivateKey" env:"JWT_PRIVATE_KEY"`
|
|
||||||
LogLevel string `toml:"LogLevel" env:"LOG_LEVEL"`
|
LogLevel string `toml:"LogLevel" env:"LOG_LEVEL"`
|
||||||
rmqURL string `toml:"RabbitMQUrl" env:"RABBITMQ_URL"`
|
}
|
||||||
|
|
||||||
|
type Item struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Description string `json:"description"`
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue