treasurer/app/app.go
2023-06-02 00:38:33 +03:00

294 lines
8.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package app
import (
"context"
"errors"
"fmt"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"bitbucket.org/skeris/bbfoundation/middleware"
"bitbucket.org/skeris/bbfoundation/role"
r "bitbucket.org/skeris/bbfoundation/router"
"bitbucket.org/skeris/treasurer/dal"
h "bitbucket.org/skeris/treasurer/handlers"
"bitbucket.org/skeris/treasurer/payway"
v "bitbucket.org/skeris/treasurer/version"
"github.com/danilsolovyov/validator"
"github.com/skeris/appInit"
"github.com/themakers/hlog"
"go.uber.org/zap"
tb "gopkg.in/tucnak/telebot.v2"
)
//#region ======== Application Structs & Functions ========
type Options struct {
IsProduction bool `env:"IS_PRODUCTION" default:"false"`
Development bool `env:"DEVELOPMENT" default:"true"`
AppName string `env:"APP_NAME" default:"treasurer"`
AppAddr string `env:"APP_ADDR" default:"localhost:3001"` //localhost:3000
AllowedOrigins string `env:"ALLOWED_ORIGINS" default:"*"`
AllowedHeaders string `env:"ALLOWED_HEADERS" default:"*"`
ExposeHeaders string `env:"EXPOSE_HEADERS" default:"*"`
MongoDbUri string `env:"DATABASE_URI" default:"mongodb+srv://user_1:cEDxC8ptLMMeoA5m@cluster0.aomle.mongodb.net/test"`
PayWayDbUri string `env:"PAYWAY_DB_URI" default:"mongodb+srv://user_2:vW3WNWViJaXYSraT@cluster0.aomle.mongodb.net/test"`
MongoDbTable string `env:"DATABASE_TABLE" default:"treasurer"`
MongoCollections string `env:"COLLECTION_NAME" default:"payment"`
}
type App struct {
logger *zap.Logger
err chan error
}
func (a App) GetLogger() *zap.Logger {
return a.logger
}
func (a App) GetErr() chan error {
return a.err
}
//#endregion
//#region ======== Hl Info Structs ========
type InfoSvcStarted struct{}
type InfoInterruptSignal struct{}
type InfoTerminateSignal struct{}
type InfoSvcShuttingDown struct{}
type InfoSvcDone struct{}
//#endregion
//#region ======== Hl ErrorTypeApp Structs ========
type ErrorTypeApp struct {
Error error
}
type ErrorCanNotServe ErrorTypeApp
type ErrorSvcShuttingDown ErrorTypeApp
type ErrorZlogSync ErrorTypeApp
type ErrorConnectToMongo ErrorTypeApp
type ErrorDisconnectFromMongo ErrorTypeApp
type ErrorConnectToToRoles ErrorTypeApp
//#endregion
func New(ctx context.Context, options interface{}) (appInit.CommonApp, error) {
opts := options.(Options)
var errCh chan error
// Create logger
zlog, err := zap.NewDevelopment()
if err != nil {
panic(err)
}
zlog = zlog.WithOptions(zap.AddCallerSkip(2))
hlogger := hlog.New(zlog)
hlogger = hlogger.With(v.GetVersion())
defer func() {
err = zlog.Sync()
if err != nil {
hlogger.Emit(ErrorZlogSync{err})
}
}()
// Connect to MongoDb "Treasurer"
connMongo, err := dal.CreateMongo(ctx, hlogger, dal.MongoDbOptions{
DalName: "MongoDB",
URI: opts.MongoDbUri,
DbTable: opts.MongoDbTable,
Collections: opts.MongoCollections,
})
if err != nil {
hlogger.Emit(ErrorConnectToMongo{err})
return App{zlog, errCh}, err
}
ctxL, cancL := context.WithCancel(ctx)
defer cancL()
go connMongo.ListenPayment(ctxL)
// Connect to MongoDb "PayWay"
connPayWay, err := dal.CreateMongo(ctx, hlogger, dal.MongoDbOptions{
DalName: "MongoPayWay",
URI: opts.PayWayDbUri,
DbTable: "payway",
Collections: "payway,paywayPayment",
})
if err != nil {
hlogger.Emit(ErrorConnectToMongo{err})
return App{zlog, errCh}, err
}
payWay, err := payway.NewPayWay(ctx, hlogger, connPayWay, connMongo)
if err != nil {
fmt.Println("PAYWAYERR", err)
//return nil, err
}
go connPayWay.ListenPayWay(ctx, func(data *dal.PayWay) error {
_, ok := payWay.Cache[data.ShortName]
if !ok {
errText := fmt.Sprintf("%v (%v) not found in cache", data.ID, data.ShortName)
return errors.New(errText)
}
payWay.Cache[data.ShortName].UpdateData(data)
return nil
})
// TODO: remove roles logic from project
// Start Roles
roles, err := role.NewRoles(ctx, hlogger, role.MongoDalOptions{
DalName: "MongoRoles",
URI: opts.MongoDbUri,
})
if err != nil {
hlogger.Emit(ErrorConnectToToRoles{err})
return App{zlog, errCh}, err
}
// Set routers
// TODO: remove
validators := h.CreateValidators(
&h.RequestCreatePayment{},
&h.RequestCreatePayout{},
)
chCommon := h.CommonOpts{
AllowedOrigins: opts.AllowedOrigins,
Mongo: connMongo,
Hl: hlogger,
}
// TODO: remove unnecessary with all usages
crucsNotifier, err := tb.NewBot(tb.Settings{
Token: "",
Verbose: false,
ParseMode: tb.ModeHTML,
Poller: &tb.LongPoller{
Timeout: time.Second,
},
})
if err != nil {
return nil, err
}
// TODO: use echo or gofiber. please remove this f*cking strange shit
// TODO: actualise api for invoice and available methods
// invoice используется для получения ссылки на оплату. по сути он выполняет 2 действия: складывает запись запроса в базу, чтобы хранились actions которые надо выполнить в ответ на события, и запросить у соответствующего платёжного сервиса правильную платёжную ссылку
// TODO: для каждого платёжного аггрегатора надо реализовать хендлер типа /listener/{paywayId}, где paywayId - айди аггрегатора
router, svcRouter, allowedRoles := r.NewCombineRouter("", r.Handlers{
"/listener/create/refill": {
Handle: func(w http.ResponseWriter, r *http.Request) {
ch := h.NewHandler(w, r, chCommon, validators["RequestCreatePayment"])
h.CreatePaymentHandler(ch, payWay, crucsNotifier)
},
},
"/listener/create/payout": {
Handle: func(w http.ResponseWriter, r *http.Request) {
ch := h.NewHandler(w, r, chCommon, validators["RequestCreatePayout"])
h.CreatePayoutHandler(ch, payWay)
},
}, // pub 0b922668de7e440f263d36bf91c506d3 sek 82afd8dad1db3785d4a94e539e142a39
"/listener/{payWay}": {
Handle: func(w http.ResponseWriter, r *http.Request) {
ch := h.NewHandler(w, r, chCommon, validator.Validator{})
h.PaymentListenerHandler(ch, payWay)
},
},
"/payoutListener/{payWay}": { //TODO: remove unnecessary
Handle: func(w http.ResponseWriter, r *http.Request) {
ch := h.NewHandler(w, r, chCommon, validator.Validator{})
h.PayoutListenerHandler(ch, payWay)
},
},
"/listener/getPayWays": {
Handle: func(w http.ResponseWriter, r *http.Request) {
ch := h.NewHandler(w, r, chCommon, validator.Validator{})
h.GetPayWays(ch, payWay)
},
},
})
// TODO: remove unnecessary
// Set Middlewares
mw := middleware.NewMiddleware(
opts.IsProduction,
hlogger,
roles,
opts.AllowedOrigins,
opts.AllowedHeaders,
allowedRoles,
opts.ExposeHeaders,
)
//TODO: remove unnecessary
router.Use(
mw.MiddlewareLogger,
mw.MiddlewareOriginAccess,
mw.MiddlewareRecovery,
)
// TODO: remove unnecessary
svcRouter.Use(
//mw.MiddlewareJwtCookie,
//mw.MiddlewareJwt,
//mw.MiddlewareGetJwt,
//mw.MiddlewareRoleAccess,
)
// Startup server
srv := &http.Server{Addr: opts.AppAddr, Handler: router}
go func() {
tmplKey := "%s.key"
tmplCrt := "%s.crt"
//if !options.LoggerDevMode {
tmplCrt = fmt.Sprintf(tmplCrt, "prod")
tmplKey = fmt.Sprintf(tmplKey, "prod")
if err := srv.ListenAndServeTLS(tmplCrt, tmplKey); err != http.ErrServerClosed && err != nil {
hlogger.Emit(ErrorCanNotServe{Error: err})
}
}()
hlogger.Emit(InfoSvcStarted{})
// Graceful Shutdown
interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM)
killSignal := <-interrupt
switch killSignal {
case os.Interrupt:
err = errors.New("interrupted")
hlogger.Emit(InfoInterruptSignal{})
case syscall.SIGTERM:
err = errors.New("terminated")
hlogger.Emit(InfoTerminateSignal{})
}
hlogger.Emit(InfoSvcShuttingDown{})
err = srv.Shutdown(ctx)
if err != nil {
hlogger.Emit(ErrorSvcShuttingDown{err})
}
hlogger.Emit(InfoSvcDone{})
err = errors.New("finished") // Костыль чтобы приложение нормально закрывалось...
return App{zlog, errCh}, err
}