/
voltage
/
tcheck
Обзор
Документация
Войти
/
voltage
/
tcheck
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
backend/cmd/api/main.go
165 строк
6 KB
Владимир
front users, departments, roles
17 июн 2026, 15:31
17 июн 2026, 15:31
15308c9
Код
Авторство
О чём код?
package main import ( "context" "fmt" "log" "net/http" "os" "os/signal" "syscall" "time" "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" "github.com/go-chi/cors" "gitverse.ru/voltage/tcheck/internal/config" "gitverse.ru/voltage/tcheck/internal/db" "gitverse.ru/voltage/tcheck/internal/handler" appmiddleware "gitverse.ru/voltage/tcheck/internal/middleware" "gitverse.ru/voltage/tcheck/internal/repository" "gitverse.ru/voltage/tcheck/internal/service" "gitverse.ru/voltage/tcheck/pkg/jwt" "gitverse.ru/voltage/tcheck/pkg/logger" ) func main() { // Загрузка конфигурации cfg, err := config.New() if err != nil { log.Fatalf("ошибка загрузки конфигурации: %v", err) } // Инициализация логгера appLogger := logger.New(cfg.Logger.Level) // Подключение к БД pool, err := db.NewPostgresDB(&cfg.Database) if err != nil { log.Fatalf("ошибка подключения к БД: %v", err) } appLogger.Info("Подключение к БД успешно") // Репозитории departmentRepo := repository.NewDepartmentRepository(pool.Pool) userRepo := repository.NewUserRepository(pool.Pool) sessionRepo := repository.NewSessionRepository(pool.Pool) permissionRepo := repository.NewPermissionsRepository(pool.Pool) roleRepo := repository.NewRoleRepository(pool.Pool) // Сервисы jwtCfg := jwt.Config{ SecretKey: cfg.JWT.Secret, AccessTTL: cfg.JWT.AccessTTL, RefreshTTL: cfg.JWT.RefreshTTL, } permissionService := service.NewPermissionService(permissionRepo) departmentService := service.NewDepartmentService(departmentRepo, permissionService) userService := service.NewUserService(userRepo) authService := service.NewAuthService(userRepo, sessionRepo, jwtCfg) roleService := service.NewRoleService(roleRepo) // Проверим запись админа учетку if err := userService.CheckOrCreateAdmin(context.Background(), cfg.Admin); err != nil { appLogger.Error(fmt.Sprintf("ошибка при проверке/создании учетки админа: %v", err)) } // Роутер r := chi.NewRouter() // ========================================== // ВСЕ MIDDLEWARE (должны быть до маршрутов) // ========================================== // CORS middleware r.Use(cors.Handler(cors.Options{ AllowedOrigins: []string{"http://localhost:5173", "http://127.0.0.1:5173"}, AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"}, AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "X-Requested-With"}, ExposedHeaders: []string{"Link"}, AllowCredentials: true, MaxAge: 300, })) // Стандартные middleware chi r.Use(middleware.RequestID) r.Use(middleware.ClientIPFromRemoteAddr) r.Use(middleware.Recoverer) r.Use(middleware.Timeout(cfg.Server.ReadTimeout)) // Кастомный логгер r.Use(appmiddleware.Logger(appLogger)) // Извлечение пользователя (глобально для всех маршрутов) authMW := appmiddleware.NewAuthMiddleware(authService, userService) r.Use(authMW.ExtractUser) // ========================================== // МАРШРУТЫ // ========================================== // Health check r.Get("/health", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Write([]byte("OK")) }) // Регистрация всех хендлеров handler.NewDepartmentHandler(r, departmentService, permissionService, appLogger) handler.NewAuthHandler(r, authMW, authService, appLogger) handler.NewUserHandler(r, userService, appLogger) handler.NewRoleHandler(r, roleService, permissionService, appLogger) // HTTP сервер srv := &http.Server{ Addr: fmt.Sprintf(":%s", cfg.Server.Port), Handler: r, ReadTimeout: cfg.Server.ReadTimeout, WriteTimeout: cfg.Server.WriteTimeout, IdleTimeout: cfg.Server.IdleTimeout, } // Запуск сервера в горутине с обработкой ошибок serverErr := make(chan error, 1) go func() { appLogger.Info(fmt.Sprintf("Сервер запущен на порту %s", cfg.Server.Port)) if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { serverErr <- err } }() // Ожидание сигнала завершения или ошибки сервера quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) select { case sig := <-quit: appLogger.Info(fmt.Sprintf("Получен сигнал %v, выключаемся...", sig)) case err := <-serverErr: appLogger.Error(fmt.Sprintf("Ошибка сервера: %v", err)) } // ========================================== // ПОРЯДОК SHUTDOWN // ========================================== // 1. Сначала останавливаем HTTP сервер ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() if err := srv.Shutdown(ctx); err != nil { appLogger.Error(fmt.Sprintf("Ошибка при graceful shutdown: %v", err)) } appLogger.Info("HTTP сервер остановлен") // 2. Останавливаем фоновые сервисы (кэши) permissionService.Shutdown() appLogger.Info("Сервисы остановлены") // 3. Закрываем соединение с БД pool.Close() appLogger.Info("Подключение к БД закрыто") appLogger.Info("Сервер полностью остановлен") }