init
This commit is contained in:
18
domain/client.go
Normal file
18
domain/client.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package domain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go_oxspeak_server/models"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
User models.User
|
||||||
|
|
||||||
|
mu sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func newClient(user models.User) *Client {
|
||||||
|
return &Client{
|
||||||
|
User: user,
|
||||||
|
}
|
||||||
|
}
|
||||||
15
domain/client_manager.go
Normal file
15
domain/client_manager.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package domain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/puzpuzpuz/xsync/v4"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ClientManager struct {
|
||||||
|
clients *xsync.Map[string, Client]
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClientManager() *ClientManager {
|
||||||
|
return &ClientManager{
|
||||||
|
clients: xsync.NewMap[string, Client](),
|
||||||
|
}
|
||||||
|
}
|
||||||
3
go.mod
3
go.mod
@@ -26,6 +26,8 @@ require (
|
|||||||
github.com/go-sql-driver/mysql v1.9.3 // indirect
|
github.com/go-sql-driver/mysql v1.9.3 // indirect
|
||||||
github.com/goccy/go-json v0.10.5 // indirect
|
github.com/goccy/go-json v0.10.5 // indirect
|
||||||
github.com/goccy/go-yaml v1.18.0 // indirect
|
github.com/goccy/go-yaml v1.18.0 // indirect
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
|
||||||
|
github.com/gorilla/websocket v1.5.3 // indirect
|
||||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||||
github.com/jackc/pgx/v5 v5.7.6 // indirect
|
github.com/jackc/pgx/v5 v5.7.6 // indirect
|
||||||
@@ -40,6 +42,7 @@ require (
|
|||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||||
|
github.com/puzpuzpuz/xsync/v4 v4.2.0 // indirect
|
||||||
github.com/quic-go/qpack v0.5.1 // indirect
|
github.com/quic-go/qpack v0.5.1 // indirect
|
||||||
github.com/quic-go/quic-go v0.55.0 // indirect
|
github.com/quic-go/quic-go v0.55.0 // indirect
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
|
|||||||
10
go.sum
10
go.sum
@@ -30,9 +30,13 @@ github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
|||||||
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||||
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
|
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
|
||||||
github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||||
|
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||||
@@ -63,6 +67,12 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY
|
|||||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/puzpuzpuz/xsync v1.5.2 h1:yRAP4wqSOZG+/4pxJ08fPTwrfL0IzE/LKQ/cw509qGY=
|
||||||
|
github.com/puzpuzpuz/xsync v1.5.2/go.mod h1:K98BYhX3k1dQ2M63t1YNVDanbwUPmBCAhNmVrrxfiGg=
|
||||||
|
github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg=
|
||||||
|
github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
|
||||||
|
github.com/puzpuzpuz/xsync/v4 v4.2.0 h1:dlxm77dZj2c3rxq0/XNvvUKISAmovoXF4a4qM6Wvkr0=
|
||||||
|
github.com/puzpuzpuz/xsync/v4 v4.2.0/go.mod h1:VJDmTCJMBt8igNxnkQd86r+8KUeN1quSfNKu5bLYFQo=
|
||||||
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
||||||
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
||||||
github.com/quic-go/quic-go v0.55.0 h1:zccPQIqYCXDt5NmcEabyYvOnomjs8Tlwl7tISjJh9Mk=
|
github.com/quic-go/quic-go v0.55.0 h1:zccPQIqYCXDt5NmcEabyYvOnomjs8Tlwl7tISjJh9Mk=
|
||||||
|
|||||||
51
network/http/context/context.go
Normal file
51
network/http/context/context.go
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package context
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go_oxspeak_server/models"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Context wrapper avec des helpers
|
||||||
|
// NE contient PAS de dépendances globales (DB, Config, etc.)
|
||||||
|
type Context struct {
|
||||||
|
*gin.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewContext(c *gin.Context) *Context {
|
||||||
|
return &Context{Context: c}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helpers pour accéder aux données de REQUÊTE
|
||||||
|
|
||||||
|
func (c *Context) GetCurrentUser() (*models.User, bool) {
|
||||||
|
value, exists := c.Get("currentUser")
|
||||||
|
if !exists {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
user, ok := value.(*models.User)
|
||||||
|
return user, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) MustGetCurrentUser() *models.User {
|
||||||
|
user, ok := c.GetCurrentUser()
|
||||||
|
if !ok {
|
||||||
|
c.AbortWithStatusJSON(401, gin.H{"error": "Unauthorized"})
|
||||||
|
panic("no authenticated user")
|
||||||
|
}
|
||||||
|
return user
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) GetRequestID() string {
|
||||||
|
value, exists := c.Get("requestID")
|
||||||
|
if !exists {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
id, _ := value.(string)
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) IsAuthenticated() bool {
|
||||||
|
_, exists := c.GetCurrentUser()
|
||||||
|
return exists
|
||||||
|
}
|
||||||
13
network/http/handler/handler.go
Normal file
13
network/http/handler/handler.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import "gorm.io/gorm"
|
||||||
|
|
||||||
|
type Handler struct {
|
||||||
|
DB *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHandler(db *gorm.DB) *Handler {
|
||||||
|
return &Handler{
|
||||||
|
DB: db,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
package handlers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"go_oxspeak_server/models"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ChannelHandler struct {
|
|
||||||
*Handler
|
|
||||||
}
|
|
||||||
|
|
||||||
func AddChannelRoutes(rg *gin.RouterGroup, h *Handler) {
|
|
||||||
channel := rg.Group("/channel")
|
|
||||||
|
|
||||||
handler := &ChannelHandler{h}
|
|
||||||
channel.GET("/:id", handler.getChannels)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Handler) getChannels(c *gin.Context) {
|
|
||||||
var users []models.User
|
|
||||||
h.DB.Find(&users)
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
package handlers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"go_oxspeak_server/database"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"gorm.io/gorm"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Handler struct {
|
|
||||||
DB *gorm.DB
|
|
||||||
}
|
|
||||||
|
|
||||||
func CreateRouter() *gin.Engine {
|
|
||||||
router := gin.Default()
|
|
||||||
|
|
||||||
handler := &Handler{DB: database.DB}
|
|
||||||
|
|
||||||
api := router.Group("/api")
|
|
||||||
{
|
|
||||||
AddChannelRoutes(api, handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
router.GET("/health", handler.healthcheck)
|
|
||||||
|
|
||||||
return router
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Handler) healthcheck(c *gin.Context) {
|
|
||||||
c.JSON(200, gin.H{"status": "ok"})
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
package handlers
|
|
||||||
1
network/http/middleware/auth.go
Normal file
1
network/http/middleware/auth.go
Normal file
@@ -0,0 +1 @@
|
|||||||
|
package middleware
|
||||||
@@ -1,10 +1,6 @@
|
|||||||
package http
|
package middleware
|
||||||
|
|
||||||
import (
|
import "github.com/gin-gonic/gin"
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CORSMiddleware configure les headers CORS
|
// CORSMiddleware configure les headers CORS
|
||||||
func CORSMiddleware() gin.HandlerFunc {
|
func CORSMiddleware() gin.HandlerFunc {
|
||||||
@@ -22,11 +18,3 @@ func CORSMiddleware() gin.HandlerFunc {
|
|||||||
c.Next()
|
c.Next()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoggingMiddleware personnalisé (optionnel, Gin en a un par défaut)
|
|
||||||
func LoggingMiddleware() gin.HandlerFunc {
|
|
||||||
return func(c *gin.Context) {
|
|
||||||
log.Printf("[HTTP] %s %s", c.Request.Method, c.Request.URL.Path)
|
|
||||||
c.Next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package http
|
package http
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"go_oxspeak_server/network/http/handlers"
|
"go_oxspeak_server/network/http/web"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
@@ -12,7 +12,7 @@ type Server struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewServer(addr string) *Server {
|
func NewServer(addr string) *Server {
|
||||||
router := handlers.CreateRouter()
|
router := web.CreateRouter()
|
||||||
|
|
||||||
s := &Server{
|
s := &Server{
|
||||||
router: router,
|
router: router,
|
||||||
|
|||||||
46
network/http/web/api/category.go
Normal file
46
network/http/web/api/category.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go_oxspeak_server/models"
|
||||||
|
"go_oxspeak_server/network/http/handler"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CategoryHandler struct {
|
||||||
|
*handler.Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCategoryHandler(h *handler.Handler) *CategoryHandler {
|
||||||
|
return &CategoryHandler{h}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *CategoryHandler) RegisterRoutes(rg *gin.RouterGroup) {
|
||||||
|
category := rg.Group("/category")
|
||||||
|
category.GET("/", h.getCategories)
|
||||||
|
category.GET("/:id/", h.getCategory)
|
||||||
|
category.POST("/", h.addCategory)
|
||||||
|
category.PUT("/:id/", h.updateCategory)
|
||||||
|
category.DELETE("/:id/", h.deleteCategory)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *CategoryHandler) getCategories(c *gin.Context) {
|
||||||
|
var categories []models.Category
|
||||||
|
h.DB.Find(&categories)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *CategoryHandler) getCategory(c *gin.Context) {
|
||||||
|
var category models.Category
|
||||||
|
h.DB.Find(&category)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *CategoryHandler) addCategory(c *gin.Context) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *CategoryHandler) updateCategory(c *gin.Context) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *CategoryHandler) deleteCategory(c *gin.Context) {
|
||||||
|
}
|
||||||
51
network/http/web/api/channel.go
Normal file
51
network/http/web/api/channel.go
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go_oxspeak_server/models"
|
||||||
|
"go_oxspeak_server/network/http/handler"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ChannelHandler struct {
|
||||||
|
*handler.Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewChannelHandler(h *handler.Handler) *ChannelHandler {
|
||||||
|
return &ChannelHandler{h}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *ChannelHandler) RegisterRoutes(rg *gin.RouterGroup) {
|
||||||
|
channel := rg.Group("/channel")
|
||||||
|
channel.GET("/", h.getChannels)
|
||||||
|
channel.GET("/:id/", h.getChannel)
|
||||||
|
channel.POST("/", h.addChannel)
|
||||||
|
channel.PUT("/:id/", h.updateChannel)
|
||||||
|
channel.DELETE("/:id/", h.deleteChannel)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *ChannelHandler) getChannels(c *gin.Context) {
|
||||||
|
var users []models.User
|
||||||
|
h.DB.Find(&users)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *ChannelHandler) getChannel(c *gin.Context) {
|
||||||
|
var user models.User
|
||||||
|
h.DB.Find(&user)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *ChannelHandler) addChannel(c *gin.Context) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *ChannelHandler) updateChannel(c *gin.Context) {
|
||||||
|
var user models.User
|
||||||
|
h.DB.Find(&user)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *ChannelHandler) deleteChannel(c *gin.Context) {
|
||||||
|
var user models.User
|
||||||
|
h.DB.Find(&user)
|
||||||
|
}
|
||||||
46
network/http/web/api/message.go
Normal file
46
network/http/web/api/message.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go_oxspeak_server/models"
|
||||||
|
"go_oxspeak_server/network/http/handler"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MessageHandler struct {
|
||||||
|
*handler.Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMessageHandler(h *handler.Handler) *MessageHandler {
|
||||||
|
return &MessageHandler{h}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *MessageHandler) RegisterRoutes(rg *gin.RouterGroup) {
|
||||||
|
message := rg.Group("/message")
|
||||||
|
message.GET("/", h.getMessages)
|
||||||
|
message.GET("/:id/", h.getMessage)
|
||||||
|
message.POST("/", h.addMessage)
|
||||||
|
message.PUT("/:id/", h.updateMessage)
|
||||||
|
message.DELETE("/:id/", h.deleteMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *MessageHandler) getMessages(c *gin.Context) {
|
||||||
|
var messages []models.Message
|
||||||
|
h.DB.Find(&messages)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *MessageHandler) getMessage(c *gin.Context) {
|
||||||
|
var message models.Message
|
||||||
|
h.DB.Find(&message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *MessageHandler) addMessage(c *gin.Context) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *MessageHandler) updateMessage(c *gin.Context) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *MessageHandler) deleteMessage(c *gin.Context) {
|
||||||
|
}
|
||||||
46
network/http/web/api/server.go
Normal file
46
network/http/web/api/server.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go_oxspeak_server/models"
|
||||||
|
"go_oxspeak_server/network/http/handler"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ServerHandler struct {
|
||||||
|
*handler.Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServerHandler(h *handler.Handler) *ServerHandler {
|
||||||
|
return &ServerHandler{h}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *ServerHandler) RegisterRoutes(rg *gin.RouterGroup) {
|
||||||
|
server := rg.Group("/server")
|
||||||
|
server.GET("/", h.getServers)
|
||||||
|
server.GET("/:id/", h.getServer)
|
||||||
|
server.POST("/", h.addServer)
|
||||||
|
server.PUT("/:id/", h.updateServer)
|
||||||
|
server.DELETE("/:id/", h.deleteServer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *ServerHandler) getServers(c *gin.Context) {
|
||||||
|
var servers []models.Server
|
||||||
|
h.DB.Find(&servers)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *ServerHandler) getServer(c *gin.Context) {
|
||||||
|
var server models.Server
|
||||||
|
h.DB.Find(&server)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *ServerHandler) addServer(c *gin.Context) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *ServerHandler) updateServer(c *gin.Context) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *ServerHandler) deleteServer(c *gin.Context) {
|
||||||
|
}
|
||||||
72
network/http/web/auth.go
Normal file
72
network/http/web/auth.go
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
package web
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go_oxspeak_server/models"
|
||||||
|
"go_oxspeak_server/network/http/handler"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/golang-jwt/jwt/v5"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AuthHandler struct {
|
||||||
|
*handler.Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAuthHandler(h *handler.Handler) *AuthHandler {
|
||||||
|
return &AuthHandler{h}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *AuthHandler) RegisterRoutes(rg *gin.RouterGroup) {
|
||||||
|
channel := rg.Group("/channel")
|
||||||
|
channel.GET("/login/", h.authenticate)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
type AuthRequest struct {
|
||||||
|
PublicKey string `json:"pub_key" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AuthResponse struct {
|
||||||
|
JWT string `json:"JWT"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *AuthHandler) authenticate(c *gin.Context) {
|
||||||
|
var req AuthRequest
|
||||||
|
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var user models.User
|
||||||
|
result := h.DB.Where("public_key = ?", req.PublicKey).First(&user)
|
||||||
|
if result.Error != nil {
|
||||||
|
if result.Error == gorm.ErrRecordNotFound {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid public key"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Internal server error"})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate token
|
||||||
|
claims := jwt.MapClaims{
|
||||||
|
"user_id": user.ID,
|
||||||
|
"expiration_date": time.Now().Add(time.Hour * 72).Unix(),
|
||||||
|
"creation_date": time.Now().Unix(),
|
||||||
|
}
|
||||||
|
|
||||||
|
token := jwt.NewWithClaims(jwt.SigningMethodES256, claims)
|
||||||
|
// TODO: Remplacer par votre clé secrète (utiliser une variable d'environnement)
|
||||||
|
secretKey := []byte("votre-cle-secrete-a-changer")
|
||||||
|
|
||||||
|
jwtString, err := token.SignedString(secretKey)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur lors de la génération du JWT"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, AuthResponse{JWT: jwtString})
|
||||||
|
}
|
||||||
48
network/http/web/main.go
Normal file
48
network/http/web/main.go
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
package web
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go_oxspeak_server/database"
|
||||||
|
"go_oxspeak_server/network/http/handler"
|
||||||
|
"go_oxspeak_server/network/http/middleware"
|
||||||
|
"go_oxspeak_server/network/http/web/api"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CreateRouter() *gin.Engine {
|
||||||
|
router := gin.Default()
|
||||||
|
|
||||||
|
// Register middleware
|
||||||
|
router.Use(middleware.CORSMiddleware())
|
||||||
|
|
||||||
|
// Create base handler
|
||||||
|
baseHandler := handler.NewHandler(database.DB)
|
||||||
|
|
||||||
|
// Create specific handler
|
||||||
|
authHandler := NewAuthHandler(baseHandler)
|
||||||
|
serverHandler := api.NewServerHandler(baseHandler)
|
||||||
|
categoryHandler := api.NewCategoryHandler(baseHandler)
|
||||||
|
channelHandler := api.NewChannelHandler(baseHandler)
|
||||||
|
messageHandler := api.NewMessageHandler(baseHandler)
|
||||||
|
|
||||||
|
authGroup := router.Group("/auth")
|
||||||
|
{
|
||||||
|
authHandler.RegisterRoutes(authGroup)
|
||||||
|
}
|
||||||
|
|
||||||
|
apiGroup := router.Group("/api")
|
||||||
|
{
|
||||||
|
serverHandler.RegisterRoutes(apiGroup)
|
||||||
|
channelHandler.RegisterRoutes(apiGroup)
|
||||||
|
categoryHandler.RegisterRoutes(apiGroup)
|
||||||
|
messageHandler.RegisterRoutes(apiGroup)
|
||||||
|
}
|
||||||
|
|
||||||
|
router.GET("/health", healthcheck)
|
||||||
|
|
||||||
|
return router
|
||||||
|
}
|
||||||
|
|
||||||
|
func healthcheck(c *gin.Context) {
|
||||||
|
c.JSON(200, gin.H{"status": "ok"})
|
||||||
|
}
|
||||||
38
network/http/web/ws.go
Normal file
38
network/http/web/ws.go
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
package web
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go_oxspeak_server/network/http/handler"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
)
|
||||||
|
|
||||||
|
type WSHandler struct {
|
||||||
|
*handler.Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWSHandler(h *handler.Handler) *WSHandler {
|
||||||
|
return &WSHandler{h}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *WSHandler) RegisterRoutes(rg *gin.RouterGroup) {
|
||||||
|
ws := rg.Group("/ws")
|
||||||
|
ws.GET("/", h.handleWS)
|
||||||
|
}
|
||||||
|
|
||||||
|
var upgrader = websocket.Upgrader{}
|
||||||
|
|
||||||
|
func (h *WSHandler) handleWS(c *gin.Context) {
|
||||||
|
conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
for {
|
||||||
|
_, _, err := conn.ReadMessage()
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
network/websocket/hub.go
Normal file
1
network/websocket/hub.go
Normal file
@@ -0,0 +1 @@
|
|||||||
|
package websocket
|
||||||
97
services/jwt.go
Normal file
97
services/jwt.go
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/golang-jwt/jwt/v5"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type JWTService struct {
|
||||||
|
secretKey []byte
|
||||||
|
tokenDuration time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
type TokenClaims struct {
|
||||||
|
UserID string `json:"user_id"`
|
||||||
|
PubKey string `json:"pub_key"`
|
||||||
|
jwt.RegisteredClaims
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewJWTService(secretKey string, tokenDuration time.Duration) *JWTService {
|
||||||
|
return &JWTService{
|
||||||
|
secretKey: []byte(secretKey),
|
||||||
|
tokenDuration: tokenDuration,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateToken crée un nouveau JWT pour un utilisateur
|
||||||
|
func (s *JWTService) GenerateToken(userID uuid.UUID, pubKey string) (string, error) {
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
claims := TokenClaims{
|
||||||
|
UserID: userID.String(),
|
||||||
|
PubKey: pubKey,
|
||||||
|
RegisteredClaims: jwt.RegisteredClaims{
|
||||||
|
ExpiresAt: jwt.NewNumericDate(now.Add(s.tokenDuration)),
|
||||||
|
IssuedAt: jwt.NewNumericDate(now),
|
||||||
|
NotBefore: jwt.NewNumericDate(now),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||||
|
|
||||||
|
tokenString, err := token.SignedString(s.secretKey)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return tokenString, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateToken vérifie la validité d'un JWT et retourne les claims
|
||||||
|
func (s *JWTService) ValidateToken(tokenString string) (*TokenClaims, error) {
|
||||||
|
token, err := jwt.ParseWithClaims(tokenString, &TokenClaims{}, func(token *jwt.Token) (interface{}, error) {
|
||||||
|
// Vérifier que la méthode de signature est bien HMAC
|
||||||
|
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||||
|
return nil, errors.New("méthode de signature invalide")
|
||||||
|
}
|
||||||
|
return s.secretKey, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if claims, ok := token.Claims.(*TokenClaims); ok && token.Valid {
|
||||||
|
return claims, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errors.New("token invalide")
|
||||||
|
}
|
||||||
|
|
||||||
|
// RenewToken renouvelle un token existant (si valide et proche de l'expiration)
|
||||||
|
func (s *JWTService) RenewToken(tokenString string) (string, error) {
|
||||||
|
claims, err := s.ValidateToken(tokenString)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Créer un nouveau token avec les mêmes informations
|
||||||
|
userID, err := uuid.Parse(claims.UserID)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.GenerateToken(userID, claims.PubKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractUserID extrait l'ID utilisateur d'un token (sans validation complète)
|
||||||
|
func (s *JWTService) ExtractUserID(tokenString string) (string, error) {
|
||||||
|
claims, err := s.ValidateToken(tokenString)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return claims.UserID, nil
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user