package http_router import ( "fmt" "log/slog" "net/http" "net/http/pprof" "strings" "git-molva.ru/Molva/molva-backend/services/api_gateway/internal/logger" "git-molva.ru/Molva/molva-backend/services/api_gateway/internal/cache" "git-molva.ru/Molva/molva-backend/services/api_gateway/internal/database" "git-molva.ru/Molva/molva-backend/services/api_gateway/internal/feed" "git-molva.ru/Molva/molva-backend/services/api_gateway/internal/file_manager/s3_storage" "git-molva.ru/Molva/molva-backend/services/api_gateway/internal/integration" objectStorage "git-molva.ru/Molva/molva-backend/services/api_gateway/internal/object_storage" "git-molva.ru/Molva/molva-backend/services/api_gateway/internal/services/agent" authinfra "git-molva.ru/Molva/molva-backend/services/api_gateway/internal/services/auth_infrastructure" "git-molva.ru/Molva/molva-backend/services/api_gateway/internal/services/distributor" pgdb "git-molva.ru/Molva/molva-backend/services/api_gateway/internal/database/postgres" formgenerator "git-molva.ru/Molva/molva-backend/services/api_gateway/internal/form_generator" "git-molva.ru/Molva/molva-backend/services/api_gateway/internal/config" "github.com/gorilla/mux" ) type Router struct { Mux *mux.Router env string logger *slog.Logger middleware *Middleware shortenerCfg *config.SecretConfig abstractAPI string cache cache.Client objectStorage objectStorage.Client feedHandler *feed.Handler integrationClient integration.Client databaseClient database.Client agentService agent.AgentService distributorService distributor.DistributorService } func New(logger *slog.Logger, cfg *config.Config) (*Router, error) { middleware, err := NewMiddleware(&ConfigMiddleware{ logger: logger, keycloakCfg: &cfg.Keycloak, }) if err != nil { return nil, err } cacheClient, err := cache.New(cache.ValKeyCacheConfig{ Addrs: strings.Split(cfg.ValkeyCache.Addrs, ","), Password: cfg.ValkeyCache.Password, ReadOnly: cfg.ValkeyCache.ReadOnly, DialTimeout: cfg.ValkeyCache.DialTimeout, PoolSize: cfg.ValkeyCache.PoolSize, DefaultTTL: cfg.ValkeyCache.DefaultTTL, RootCaFilePath: cfg.ValkeyCache.RootCaFilePath, }, cache.ValkeyCacheType) if err != nil { return nil, err } databaseClient, err := database.New(database.PostgresClientType, pgdb.PostgresConfig{ Host: cfg.Database.Host, Port: cfg.Database.Port, Username: cfg.Database.Username, Password: cfg.Database.Password, Database: cfg.Database.DBName, Schema: cfg.Database.Schema, SSLMode: cfg.Database.SSLMode, SSLRootCert: cfg.Database.RootCaFilePath, }) if err != nil { return nil, err } agentService, err := agent.New(agent.CrmAgentServiceType, agent.CrmAgentServiceConfig{ DbClient: databaseClient, }) if err != nil { return nil, err } distributorService, err := distributor.New(distributor.CrmDistributorServiceType, distributor.CrmDistributorServiceConfig{ DbClient: databaseClient, }) if err != nil { return nil, err } objectStorageClient, err := objectStorage.New(objectStorage.S3StorageConfig{ Bucket: cfg.S3Storage.Bucket, DefaultLinkTTL: cfg.ValkeyCache.DefaultTTL, }, objectStorage.ClientTypeS3) if err != nil { return nil, err } integrationClient, err := integration.New(integration.Config{ Cache: cacheClient, Db: databaseClient, Logger: logger, Secrets: map[string]integration.CompanySecrets{ integration.VkusvillCompanyName: integration.VkusvillSecretsConfig{ ApiToken: cfg.Integration.Vkusvill.ApiToken, }, }, }) if err != nil { return nil, err } // TODO: вырезать ненужные аргументы в рамках https://tracker.yandex.ru/MOLVARAZRABOTKA-363 service, err := feed.NewService(fmt.Sprintf( "postgres://%s:%s@%s:%d/%s?sslmode=%s", cfg.Database.Username, cfg.Database.Password, cfg.Database.Host, cfg.Database.Port, cfg.Database.DBName, cfg.Database.SSLMode, ), cfg.Database.Schema, logger, databaseClient) if err != nil { return nil, err } return &Router{ Mux: mux.NewRouter(), env: cfg.Env, logger: logger, middleware: middleware, shortenerCfg: &cfg.Secret, abstractAPI: cfg.EmailVerificationService.APIKey, // may be nil dereference if no email verification service is configured cache: cacheClient, objectStorage: objectStorageClient, feedHandler: feed.NewFeedHandler(logger, service), databaseClient: databaseClient, agentService: agentService, distributorService: distributorService, integrationClient: integrationClient, }, nil } func SetupRouter(r *Router, buildCfg config.BuildInfo) { authInfraService, err := authinfra.New(authinfra.CacheAuthInfrastructureServiceType, authinfra.CacheAuthInfraServiceConfig{ CacheClient: r.cache, }) if err != nil { panic(err) } s3FileManager := s3_storage.NewS3Storage( r.objectStorage, r.cache, ) h := newHandler(&Config{ env: r.env, logger: r.logger, authManager: r.middleware.authManager, authInfraService: authInfraService, fileManager: s3FileManager, dbClient: r.databaseClient, agentService: r.agentService, distributorService: r.distributorService, secretConfig: r.shortenerCfg, emailVerificationServiceAPIKey: r.abstractAPI, cacheClient: r.cache, objectStorageClient: r.objectStorage, feed: r.feedHandler, integrationClient: r.integrationClient, buildConfig: buildCfg, }) r.Mux.Use(r.middleware.loggingMiddleware) setupAuthHandlers(r, h) setupIntegrationHandlers(r, h) setupClientHandlers(r, h) setupValidationHandlers(r, h) if r.env != logger.EnvProd { setupSystemInfoHandlers(r, h) } subRouter := r.Mux.PathPrefix("/api/v1").Subrouter() subRouter.Use(r.middleware.authMiddleware) setupAgentHandlers(subRouter, h) setupDistributorHandlers(subRouter, h) setupEmployeesHandlers(subRouter, h) setupLogoHandlers(subRouter, h) // --------------- DOCUMENTS FOR FORM ------------ r.Mux.HandleFunc("/api/v1/docs/{file}", h.getFileHandler).Methods(http.MethodGet) // --------------- FEED ------------ subRouter.HandleFunc("/feed/{uid}/events", h.GetUserEventsHandler).Methods(http.MethodGet) } func setupAuthHandlers(r *Router, h *handler) { r.Mux.HandleFunc("/api/v1/login", h.loginHandler).Methods(http.MethodPost) r.Mux.HandleFunc("/api/v1/register", h.registerHandler).Methods(http.MethodPost) r.Mux.HandleFunc("/api/v1/logout", h.logoutHandler).Methods(http.MethodPost) r.Mux.HandleFunc("/api/v1/refresh-token", h.refreshTokenHandler).Methods(http.MethodPost) r.Mux.HandleFunc("/api/v1/verify_email", h.verifyEmailHandler).Methods(http.MethodPatch) r.Mux.HandleFunc("/api/v1/verify_email", h.getEmailVerificationStatusHandler).Methods(http.MethodGet) r.Mux.HandleFunc("/api/v1/confirm_email", h.confirmEmailPageHandler).Methods(http.MethodGet) r.Mux.HandleFunc("/api/v1/forgot_password", h.forgotPasswordHandler).Methods(http.MethodPost) r.Mux.HandleFunc("/api/v1/validate_otp", h.validateOTPHandler).Methods(http.MethodGet) r.Mux.HandleFunc("/api/v1/reset_password", h.resetPasswordHandler).Methods(http.MethodPut) } func setupAgentHandlers(subRouter *mux.Router, h *handler) { // // =============== COMPANY HANDLERS =============== subRouter.HandleFunc("/agents/{agent_id}/companies", h.getCompanyListAgentHandler).Methods(http.MethodGet) subRouter.HandleFunc("/agents/{agent_id}/company/{company_id}", h.getCompanyByIdAgentHandler).Methods(http.MethodGet) // TODO: review // subRouter.HandleFunc("/agents/{agent_id}/company/{company_id}", h.addNewAgentCompanyMemberHandler).Methods(http.MethodPost) subRouter.HandleFunc("/agents/{agent_id}/company", h.createCompanyAgentHandler).Methods(http.MethodPost) subRouter.HandleFunc("/agents/{agent_id}/company/{company_id}", h.updateCompanyAgentHandler).Methods(http.MethodPatch) // // =============== VACANCY HANDLERS =============== subRouter.HandleFunc("/agents/{agent_id}/vacancies", h.getVacancyListAgentHandler).Methods(http.MethodGet) subRouter.HandleFunc("/agents/{agent_id}/vacancies/{vacancy_id}", h.getPersonalLinkHandler).Methods(http.MethodGet) // // =============== SUBMISSION HANDLERS =============== subRouter.HandleFunc("/agents/{agent_id}/submissions", h.getSubmissionListAgentHandler).Methods(http.MethodGet) subRouter.HandleFunc("/agents/{agent_id}/submissions/{submission_id}", h.deleteSubmissionAgentHandler).Methods(http.MethodDelete) subRouter.HandleFunc("/agents/{agent_id}/submissions/{submission_id}/cv", h.getSubmissionCVHandler).Methods(http.MethodGet) // // =============== PROFILE HANDLERS =============== subRouter.HandleFunc("/agents/{agent_id}/profile", h.getProfileAgentHandler).Methods(http.MethodGet) subRouter.HandleFunc("/agents/{agent_id}/profile", h.updateProfileAgentHandler).Methods(http.MethodPatch) // // =============== BALANCE HANDLERS =============== subRouter.HandleFunc("/agents/{agent_id}/balance", h.getBalanceAgentHandler).Methods(http.MethodGet) subRouter.HandleFunc("/agents/{agent_id}/transactions", h.getTransactionListAgentHandler).Methods(http.MethodGet) subRouter.HandleFunc("/agents/{agent_id}/transactions", h.createTransactionAgentHandler).Methods(http.MethodPost) subRouter.HandleFunc("/agents/{agent_id}/bank_accounts", h.getBankAccountListAgentHandler).Methods(http.MethodGet) subRouter.HandleFunc("/agents/{agent_id}/bank_accounts", h.createBankAccountAgentHandler).Methods(http.MethodPost) subRouter.HandleFunc("/agents/{agent_id}/bank_accounts/{bank_account_id}", h.updateBankAccountAgentHandler).Methods(http.MethodPatch) subRouter.HandleFunc("/agents/{agent_id}/bank_accounts/{bank_account_id}", h.deleteBankAccountAgentHandler).Methods(http.MethodDelete) } func setupDistributorHandlers(subRouter *mux.Router, h *handler) { // // =============== COMPANY HANDLERS =============== subRouter.HandleFunc("/distributor/{distributor_id}/companies", h.getCompanyListDistributorHandler).Methods(http.MethodGet) subRouter.HandleFunc("/distributor/{distributor_id}/company/{company_id}", h.getCompanyByIdDistributorHandler).Methods(http.MethodGet) subRouter.HandleFunc("/distributor/{distributor_id}/company/{company_id}", h.addCompanyMemberDistributorHandler).Methods(http.MethodPost) subRouter.HandleFunc("/distributor/{distributor_id}/company/{company_id}", h.updateCompanyDistributorHandler).Methods(http.MethodPatch) subRouter.HandleFunc("/distributor/{distributor_id}/company", h.createCompanyDistributorHandler).Methods(http.MethodPost) // // =============== VACANCY HANDLERS =============== subRouter.HandleFunc("/distributor/{distributor_id}/vacancies", h.getVacancyListDistributorHandler).Methods(http.MethodGet) subRouter.HandleFunc("/distributor/{distributor_id}/vacancies", h.createVacancyDistributorHandler).Methods(http.MethodPost) subRouter.HandleFunc("/distributor/{distributor_id}/vacancies/{vacancy_id}", h.updateVacancyDistributorHandler).Methods(http.MethodPatch) subRouter.HandleFunc("/distributor/{distributor_id}/vacancies/{vacancy_id}", h.deleteVacancyDistributorHandler).Methods(http.MethodDelete) subRouter.HandleFunc("/distributor/{distributor_id}/vacancies/{vacancy_id}/moderation", h.sendVacancyToModerationHandler).Methods(http.MethodPost) // // =============== SUBMISSION HANDLERS =============== subRouter.HandleFunc("/distributor/{distributor_id}/vacancies/{vacancy_id}/submissions", h.getSubmissionListDistributorHandler).Methods(http.MethodGet) subRouter.HandleFunc("/distributor/{distributor_id}/vacancies/{vacancy_id}/submissions/{submission_id}/status", h.updateSubmissionStatusDistributorHandler).Methods(http.MethodPost) subRouter.HandleFunc("/distributor/{distributor_id}/submissions/{submission_id}/cv", h.getSubmissionCVHandler).Methods(http.MethodGet) // // =============== PROFILE HANDLERS =============== subRouter.HandleFunc("/distributor/{distributor_id}/profile", h.getProfileDisributorHandler).Methods(http.MethodGet) subRouter.HandleFunc("/distributor/{distributor_id}/profile", h.updateProfileDistributorHandler).Methods(http.MethodPatch) // // =============== BALANCE HANDLERS =============== subRouter.HandleFunc("/distributor/{distributor_id}/balance", h.getBalanceDistributorHandler).Methods(http.MethodGet) subRouter.HandleFunc("/distributor/{distributor_id}/company/{company_id}/balance", h.getCompanyBalanceDistributorHandler).Methods(http.MethodGet) subRouter.HandleFunc("/distributor/{distributor_id}/transactions", h.getTransactionListDistributorHandler).Methods(http.MethodGet) subRouter.HandleFunc("/distributor/{distributor_id}/company/{company_id}/transactions", h.getCompanyTransactionListDistributorHandler).Methods(http.MethodGet) subRouter.HandleFunc("/distributor/{distributor_id}/transactions", h.createTransactionDistributorHandler).Methods(http.MethodPost) subRouter.HandleFunc("/distributor/{distributor_id}/bank_accounts", h.getBankAccountListDistributorHandler).Methods(http.MethodGet) subRouter.HandleFunc("/distributor/{distributor_id}/company/{company_id}/bank_accounts", h.getCompanyBankAccountListDistributorHandler).Methods(http.MethodGet) subRouter.HandleFunc("/distributor/{distributor_id}/company/{company_id}/bank_accounts", h.createBankAccountDistributorHandler).Methods(http.MethodPost) subRouter.HandleFunc("/distributor/{distributor_id}/bank_accounts/{bank_account_id}", h.updateBankAccountDistributorHandler).Methods(http.MethodPut) subRouter.HandleFunc("/distributor/{distributor_id}/bank_accounts/{bank_account_id}", h.deleteBankAccountDistributorHandler).Methods(http.MethodDelete) } func setupEmployeesHandlers(subRouter *mux.Router, h *handler) { // // =============== EMPLOYEES HANDLERS =============== subRouter.HandleFunc("/companies/{company_id}/employees", h.getEmployeesHandler).Methods(http.MethodGet) } func setupClientHandlers(r *Router, h *handler) { // // =============== CLIENT HANDLERS =============== r.Mux.HandleFunc("/api/v1/anketa", h.getAnketaHandler).Methods(http.MethodGet) r.Mux.HandleFunc("/api/v1/anketa", h.postAnketaHandler).Methods(http.MethodPost) // --------------- FORM METADATA --------------- //TODO FROM ALEXANDER: make err validation formSys, _ := formgenerator.GetFileSystem() r.Mux.PathPrefix("/api/v1/anketa/static/").Handler(http.StripPrefix("/api/v1/anketa/static/", http.FileServer(http.FS(formSys)))) } func setupValidationHandlers(r *Router, h *handler) { // // =============== VALIDATION HANDLERS =============== r.Mux.HandleFunc("/api/v1/{uid}/validation", h.getUserValidationStatusHandler).Methods(http.MethodGet) } func setupLogoHandlers(subRouter *mux.Router, h *handler) { // // =============== LOGO HANDLERS =============== subRouter.HandleFunc("/distributor/{distributor_id}/company/{company_id}/logo", h.getCompanyLogoHandler).Methods(http.MethodGet) subRouter.HandleFunc("/distributor/{distributor_id}/company/{company_id}/logo", h.createCompanyLogoHandler).Methods(http.MethodPost) subRouter.HandleFunc("/distributor/{distributor_id}/company/{company_id}/logo", h.updateCompanyLogoHandler).Methods(http.MethodPut) subRouter.HandleFunc("/distributor/{distributor_id}/company/{company_id}/logo", h.deleteCompanyLogoHandler).Methods(http.MethodDelete) } func setupIntegrationHandlers(r *Router, h *handler) { r.Mux.HandleFunc("/api/v1/integration/vkusvill/callback", h.vkusvillIntegrationCallbackHandler).Methods(http.MethodPost) } func setupSystemInfoHandlers(r *Router, h *handler) { r.Mux.HandleFunc("/api/v1/healthcheck", h.getBuildInfoHandler).Methods(http.MethodGet) r.Mux.HandleFunc("/debug/pprof/", pprof.Index).Methods(http.MethodGet) r.Mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline).Methods(http.MethodGet) r.Mux.HandleFunc("/debug/pprof/profile", pprof.Profile).Methods(http.MethodGet) r.Mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol).Methods(http.MethodGet) r.Mux.HandleFunc("/debug/pprof/trace", pprof.Trace).Methods(http.MethodGet) r.Mux.Handle("/debug/pprof/goroutine", pprof.Handler("goroutine")).Methods(http.MethodGet) r.Mux.Handle("/debug/pprof/heap", pprof.Handler("heap")).Methods(http.MethodGet) r.Mux.Handle("/debug/pprof/threadcreate", pprof.Handler("threadcreate")).Methods(http.MethodGet) r.Mux.Handle("/debug/pprof/block", pprof.Handler("block")).Methods(http.MethodGet) }