package main import ( "context" "errors" "fmt" "log/slog" "net/http" "os" "os/signal" "time" "git.loyso.art/frx/kurious/internal/common/client/sravni" "git.loyso.art/frx/kurious/internal/common/config" "git.loyso.art/frx/kurious/internal/common/xcontext" "git.loyso.art/frx/kurious/internal/kurious/adapters" xhttp "git.loyso.art/frx/kurious/internal/kurious/ports/http" "git.loyso.art/frx/kurious/internal/kurious/service" "golang.org/x/sync/errgroup" ) func main() { ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt) defer cancel() err := app(ctx) if err != nil { println(err.Error()) os.Exit(1) } } func app(ctx context.Context) error { var cfgpath string if len(os.Args) > 1 { cfgpath = os.Args[1] } else { cfgpath = "config.json" } cfg, err := readFromFile(cfgpath, defaultConfig) if err != nil { return fmt.Errorf("reading config from file: %w", err) } log := config.NewSLogger(cfg.Log) shutdownOtel, err := setupOtelSDK(ctx, cfg.Tracing) if err != nil { return fmt.Errorf("setting up otel sdk: %w", err) } defer func() { err := shutdownOtel(ctx) if err != nil { xcontext.LogWithError(ctx, log, err, "shutting down sdk") } }() sravniClient, err := sravni.NewClient(ctx, log, false) if err != nil { return fmt.Errorf("unable to make new sravni client: %w", err) } mainPageState, err := sravniClient.GetMainPageState() if err != nil { return fmt.Errorf("getting main page state: %w", err) } dictionaries := mainPageState.Props.InitialReduxState.Dictionaries.Data dictFieldsAsMap := func(fields ...sravni.Field) map[string]string { out := make(map[string]string, len(fields)) for _, field := range fields { out[field.Value] = field.Name } return out } courseThematcisMapped := dictFieldsAsMap(dictionaries.CourseThematics.Fields...) learningTypeMapped := dictFieldsAsMap(dictionaries.LearningType.Fields...) mapper := adapters.NewMemoryMapper(courseThematcisMapped, learningTypeMapped) var dbengine service.RepositoryEngine switch cfg.DBEngine { case "ydb": dbengine = service.RepositoryEngineYDB case "sqlite": dbengine = service.RepositoryEngineSqlite default: dbengine = service.RepositoryEngineUnknown } app, err := service.NewApplication(ctx, service.ApplicationConfig{ LogConfig: cfg.Log, YDB: cfg.YDB, Sqlite: cfg.Sqlite, Engine: dbengine, }, mapper) if err != nil { return fmt.Errorf("making new application: %w", err) } httpAPI := xhttp.NewServer(app, log.With(slog.String("component", "http"))) httpServer := setupHTTP(cfg.HTTP, httpAPI, log) eg, egctx := errgroup.WithContext(ctx) eg.Go(func() error { xcontext.LogInfo( ctx, log, "serving http", slog.String("addr", httpServer.Addr), ) if err := httpServer.ListenAndServe(); err != nil { if !errors.Is(err, http.ErrServerClosed) { return fmt.Errorf("listening http: %w", err) } } return nil }) eg.Go(func() error { <-egctx.Done() xcontext.LogInfo(ctx, log, "trying to shutdown http") sdctx, sdcancel := context.WithTimeout(context.Background(), time.Second*10) defer sdcancel() err := httpServer.Shutdown(sdctx) if err != nil { return fmt.Errorf("shutting down the server: %w", err) } xcontext.LogInfo(ctx, log, "server closed successfuly") err = shutdownOtel(sdctx) if err != nil { return fmt.Errorf("shutting down sdk: %w", err) } return nil }) return eg.Wait() }