141 lines
3.5 KiB
Go
141 lines
3.5 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"log/slog"
|
|
"net/http"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
"time"
|
|
|
|
"dana/internal/api"
|
|
"dana/internal/bitrix"
|
|
"dana/internal/config"
|
|
"dana/internal/crm"
|
|
"dana/internal/megafon"
|
|
"dana/internal/notify"
|
|
"dana/internal/onec"
|
|
"dana/internal/prodoctorov"
|
|
"dana/internal/sberhealth"
|
|
"dana/internal/storage"
|
|
)
|
|
|
|
func main() {
|
|
if err := run(); err != nil {
|
|
slog.Error("fatal", "err", err)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
func run() error {
|
|
cfg, err := config.Load()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
level := slog.LevelInfo
|
|
if cfg.Debug {
|
|
level = slog.LevelDebug
|
|
}
|
|
slog.SetDefault(slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: level})))
|
|
|
|
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
|
defer stop()
|
|
|
|
pd, err := prodoctorov.New(cfg.ProdoctorovCookie, cfg.ProdoctorovLPU)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
sh, err := sberhealth.New(cfg.SberHealthCookie, cfg.SberHealthClinic)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
store, err := storage.New(ctx, cfg.DatabaseURL)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer store.Close()
|
|
|
|
if err := store.Migrate(ctx); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Первый администратор заводится один раз, если таблица пуста.
|
|
if err := store.EnsureAdmin(ctx, cfg.AdminEmail, cfg.AdminPassword); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := api.SetTrustedProxies(cfg.TrustedProxies); err != nil {
|
|
return err
|
|
}
|
|
handler := api.New(store, cfg.MediaDir)
|
|
if err := handler.FailInterruptedReports(ctx); err != nil {
|
|
slog.Warn("mark interrupted reports", "err", err)
|
|
}
|
|
handler.WithPBX(megafon.New(cfg.MegafonURL, cfg.MegafonKey))
|
|
if cfg.OchevidnoMegafonURL != "" && cfg.OchevidnoMegafonKey != "" {
|
|
handler.WithClinicPBX("ochevidno", megafon.New(cfg.OchevidnoMegafonURL, cfg.OchevidnoMegafonKey))
|
|
}
|
|
go handler.RunPBXSync(ctx)
|
|
handler.WithOneC(onec.New("", cfg.OnecKey, cfg.OnecAuthHeader))
|
|
handler.WithOneCWebhook(cfg.OnecWebhookKey)
|
|
handler.WithBitrix(bitrix.New(cfg.BitrixWebhookURL))
|
|
handler.WithCRM(crm.New(cfg.CrmAPIURL, cfg.CrmLeadIntakeSecret), cfg.CrmMode)
|
|
go handler.RunBookingConfirm(ctx)
|
|
handler.WithSMSC(cfg.SMSCLogin, cfg.SMSCPassword)
|
|
handler.WithSMSCCallbackToken(cfg.SMSCCallbackToken)
|
|
handler.WithProdoctorov(pd)
|
|
handler.WithSberHealth(sh)
|
|
if err := handler.WithWebsite(cfg.WebDir, cfg.SiteURL); err != nil {
|
|
return err
|
|
}
|
|
if err := handler.WithSSR(cfg.SSRRenderer, cfg.NodeBinary); err != nil {
|
|
return err
|
|
}
|
|
if cfg.SMTPHost != "" {
|
|
handler.WithNotifier(¬ify.Email{
|
|
Host: cfg.SMTPHost,
|
|
Port: cfg.SMTPPort,
|
|
User: cfg.SMTPUser,
|
|
Password: cfg.SMTPPassword,
|
|
From: cfg.NotifyFrom,
|
|
To: cfg.NotifyTo,
|
|
SiteURL: cfg.SiteURL,
|
|
})
|
|
slog.Info("lead notifications by email", "smtp", cfg.SMTPHost, "to", cfg.NotifyTo)
|
|
} else {
|
|
slog.Warn("SMTP_HOST is not set: lead notifications are disabled")
|
|
}
|
|
|
|
srv := &http.Server{
|
|
Addr: cfg.Addr,
|
|
Handler: handler.Routes(),
|
|
ReadHeaderTimeout: 10 * time.Second,
|
|
ReadTimeout: 30 * time.Second,
|
|
WriteTimeout: 30 * time.Second,
|
|
IdleTimeout: 60 * time.Second,
|
|
}
|
|
|
|
errCh := make(chan error, 1)
|
|
go func() {
|
|
slog.Info("listening", "addr", cfg.Addr)
|
|
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
|
errCh <- err
|
|
}
|
|
}()
|
|
|
|
select {
|
|
case err := <-errCh:
|
|
return err
|
|
case <-ctx.Done():
|
|
slog.Info("shutting down")
|
|
}
|
|
|
|
shutdownCtx, cancel := context.WithTimeout(context.Background(), cfg.ShutdownTimeout)
|
|
defer cancel()
|
|
return srv.Shutdown(shutdownCtx)
|
|
}
|