This commit is contained in:
Timur Kh.
2025-12-15 22:28:01 +03:00
parent 4a17c75d6f
commit f7a7b19275
25 changed files with 1383 additions and 3 deletions
+229
View File
@@ -0,0 +1,229 @@
package server
import (
"context"
"fmt"
"log"
"net"
"net/http"
"strings"
"time"
"datarush/internal/auth/config"
grpcHandlers "datarush/internal/auth/handler/grpc"
httpHandlers "datarush/internal/auth/handler/http"
authPostgresRepo "datarush/internal/auth/repository/postgres"
"datarush/internal/auth/service"
pb "datarush/pkg/api/auth"
"github.com/jmoiron/sqlx"
_ "github.com/lib/pq"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
)
const (
httpReadTimeout = 10 * time.Second
httpWriteTimeout = 10 * time.Second
httpIdleTimeout = 60 * time.Second
)
type Server struct {
grpcServer *grpc.Server
config *config.Config
db *sqlx.DB
httpServer *http.Server
}
func New(cfg *config.Config) *Server {
return &Server{
config: cfg,
}
}
func (s *Server) Start() error {
// Connect to database
db, err := sqlx.Connect("postgres", s.config.BuildPostgresConnStr())
if err != nil {
return fmt.Errorf("failed to connect to postgres: %w", err)
}
s.db = db
// Create tables
if err := s.createTables(); err != nil {
return fmt.Errorf("failed to create tables: %w", err)
}
// Register gRPC services
if err := s.registerGRPCServices(); err != nil {
return fmt.Errorf("failed to register gRPC services: %w", err)
}
// Start gRPC server in a goroutine
go func() {
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", s.config.GRPCPort))
if err != nil {
log.Fatalf("failed to listen on grpc port: %v", err)
}
log.Printf("starting gRPC server on port %d", s.config.GRPCPort)
if err := s.grpcServer.Serve(lis); err != nil {
log.Fatalf("failed to serve gRPC: %v", err)
}
}()
// Start HTTP server
if err := s.startHTTPServer(); err != nil {
return fmt.Errorf("failed to start HTTP server: %w", err)
}
return nil
}
func (s *Server) registerGRPCServices() error {
s.grpcServer = grpc.NewServer()
// Create repositories
userRepo := authPostgresRepo.NewUserRepository(s.db)
// Create services
authService := service.NewAuthService(userRepo, s.config.JWTSecret)
// Create and register handlers
authHandler := grpcHandlers.NewAuthHandler(authService)
pb.RegisterAuthServiceServer(s.grpcServer, authHandler)
// Enable reflection if configured
if s.config.GRPCEnableReflection {
reflection.Register(s.grpcServer)
}
return nil
}
func (s *Server) startHTTPServer() error {
// Create repositories
userRepo := authPostgresRepo.NewUserRepository(s.db)
// Create services
authService := service.NewAuthService(userRepo, s.config.JWTSecret)
// Create HTTP handlers
authHandler := httpHandlers.NewAuthHandler(authService)
userHandler := httpHandlers.NewUserHandler(authService)
// Create router
mux := http.NewServeMux()
// Register routes with middleware wrapper
mux.HandleFunc("/api/v1/sign-up", func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
authHandler.SignUp(w, r)
})
mux.HandleFunc("/api/v1/sign-in", func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
authHandler.SignIn(w, r)
})
mux.HandleFunc("/api/v1/me", func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
userHandler.GetMe(w, r)
})
mux.HandleFunc("/api/v1/users/", func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
// Extract user_id from path /api/v1/users/:user_id
userID := strings.TrimPrefix(r.URL.Path, "/api/v1/users/")
r.Header.Set("X-User-ID", userID)
userHandler.GetUser(w, r)
})
mux.HandleFunc("/api/v1/me/stat", func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
userHandler.GetMyStat(w, r)
})
mux.HandleFunc("/api/v1/leaderboard", func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
userHandler.GetLeaderboard(w, r)
})
s.httpServer = &http.Server{
Addr: fmt.Sprintf(":%d", s.config.HTTPPort),
Handler: mux,
ReadTimeout: httpReadTimeout,
WriteTimeout: httpWriteTimeout,
IdleTimeout: httpIdleTimeout,
}
go func() {
log.Printf("starting HTTP server on port %d", s.config.HTTPPort)
if err := s.httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("failed to start HTTP server: %v", err)
}
}()
return nil
}
func (s *Server) Stop() {
log.Println("shutting down auth server...")
if s.httpServer != nil {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := s.httpServer.Shutdown(ctx); err != nil {
log.Printf("failed to shutdown HTTP server: %v", err)
}
}
if s.grpcServer != nil {
s.grpcServer.GracefulStop()
}
if s.db != nil {
if err := s.db.Close(); err != nil {
log.Printf("failed to close database: %v", err)
}
}
log.Println("auth server stopped")
}
func (s *Server) createTables() error {
schema := `
CREATE TABLE IF NOT EXISTS users (
id UUID PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
username VARCHAR(255) NOT NULL,
password VARCHAR(255) NOT NULL,
created_at TIMESTAMP NOT NULL,
updated_at TIMESTAMP NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
`
_, err := s.db.Exec(schema)
return err
}