formatting to our pj layout
This commit is contained in:
parent
8f56724bac
commit
da9c292adf
259
app/app.go
259
app/app.go
@ -1,259 +0,0 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/skeris/appInit"
|
||||
"github.com/themakers/hlog"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"penahub.gitlab.yandexcloud.net/backend/penahub_common/log_mw"
|
||||
"penahub.gitlab.yandexcloud.net/backend/penahub_common/privilege"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/dal"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/healthchecks"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/middleware"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/model"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/brokers"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/clients/auth"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/clients/telegram"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/initialize"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/models"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/server"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/service"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/tools"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/workers"
|
||||
"penahub.gitlab.yandexcloud.net/external/trashlog/wrappers/zaptrashlog"
|
||||
"time"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
var (
|
||||
errInvalidOptions = errors.New("invalid options")
|
||||
)
|
||||
|
||||
var zapOptions = []zap.Option{
|
||||
zap.AddCaller(),
|
||||
zap.AddCallerSkip(2),
|
||||
zap.AddStacktrace(zap.ErrorLevel),
|
||||
}
|
||||
|
||||
var _ appInit.CommonApp = (*App)(nil)
|
||||
|
||||
type Options struct {
|
||||
LoggerProdMode bool `env:"IS_PROD_LOG" default:"false"`
|
||||
IsProd bool `env:"IS_PROD" default:"false"`
|
||||
NumberPort string `env:"PORT" default:"1488"`
|
||||
CrtFile string `env:"CRT" default:"server.crt"`
|
||||
KeyFile string `env:"KEY" default:"server.key"`
|
||||
PostgresCredentials string `env:"PG_CRED" default:"host=localhost port=35432 user=squiz password=Redalert2 dbname=squiz sslmode=disable"`
|
||||
HubAdminUrl string `env:"HUB_ADMIN_URL" default:"http://localhost:8001/"`
|
||||
ServiceName string `env:"SERVICE_NAME" default:"squiz"`
|
||||
AuthServiceURL string `env:"AUTH_URL" default:"http://localhost:8000/"`
|
||||
GrpcHost string `env:"GRPC_HOST" default:"localhost"`
|
||||
GrpcPort string `env:"GRPC_PORT" default:"9000"`
|
||||
KafkaBrokers string `env:"KAFKA_BROKERS" default:"localhost:9092"`
|
||||
KafkaTopic string `env:"KAFKA_TOPIC" default:"test-topic"`
|
||||
KafkaGroup string `env:"KAFKA_GROUP" default:"mailnotifier"`
|
||||
TrashLogHost string `env:"TRASH_LOG_HOST" default:"localhost:7113"`
|
||||
ModuleLogger string `env:"MODULE_LOGGER" default:"core-local"`
|
||||
ClickHouseCred string `env:"CLICK_HOUSE_CRED" default:"tcp://10.8.0.15:9000/default?sslmode=disable"`
|
||||
RedisHost string `env:"REDIS_HOST" default:"localhost:6379"`
|
||||
RedisPassword string `env:"REDIS_PASSWORD" default:"admin"`
|
||||
RedisDB uint64 `env:"REDIS_DB" default:"2"`
|
||||
S3Prefix string `env:"S3_PREFIX"`
|
||||
}
|
||||
|
||||
func New(ctx context.Context, opts interface{}, ver appInit.Version) (appInit.CommonApp, error) {
|
||||
var (
|
||||
err, workerErr error
|
||||
zapLogger *zap.Logger
|
||||
errChan = make(chan error)
|
||||
options Options
|
||||
ok bool
|
||||
)
|
||||
|
||||
if options, ok = opts.(Options); !ok {
|
||||
return App{}, errInvalidOptions
|
||||
}
|
||||
|
||||
if options.LoggerProdMode {
|
||||
zapLogger, err = zap.NewProduction(zapOptions...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
zapLogger, err = zap.NewDevelopment(zapOptions...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
zapLogger = zapLogger.With(
|
||||
zap.String("SvcCommit", ver.Commit),
|
||||
zap.String("SvcVersion", ver.Release),
|
||||
zap.String("SvcBuildTime", ver.BuildTime),
|
||||
)
|
||||
|
||||
clickHouseLogger, err := zaptrashlog.NewCore(ctx, zap.InfoLevel, options.TrashLogHost, ver.Release, ver.Commit, time.Now().Unix())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
loggerForHlog := zapLogger.WithOptions(zap.WrapCore(func(core zapcore.Core) zapcore.Core {
|
||||
return zapcore.NewTee(core, clickHouseLogger)
|
||||
}))
|
||||
|
||||
loggerHlog := hlog.New(loggerForHlog).Module(options.ModuleLogger)
|
||||
loggerHlog.With(models.AllFields{})
|
||||
loggerHlog.Emit(InfoSvcStarted{})
|
||||
|
||||
authClient := auth.NewAuthClient(options.AuthServiceURL)
|
||||
|
||||
pgdal, err := dal.New(ctx, options.PostgresCredentials, nil)
|
||||
if err != nil {
|
||||
fmt.Println("NEW", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
chDal, err := dal.NewClickHouseDAL(ctx, options.ClickHouseCred)
|
||||
if err != nil {
|
||||
fmt.Println("failed init clickhouse", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
kafkaClient, err := initialize.KafkaInit(ctx, initialize.KafkaDeps{
|
||||
KafkaGroup: options.KafkaGroup,
|
||||
KafkaBrokers: options.KafkaBrokers,
|
||||
KafkaTopic: options.KafkaTopic,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
producer := brokers.NewProducer(brokers.ProducerDeps{
|
||||
KafkaClient: kafkaClient,
|
||||
Logger: zapLogger,
|
||||
})
|
||||
|
||||
redisClient := redis.NewClient(&redis.Options{
|
||||
Addr: options.RedisHost,
|
||||
Password: options.RedisPassword,
|
||||
DB: int(options.RedisDB),
|
||||
})
|
||||
err = redisClient.Ping(ctx).Err()
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("error ping to redis db %v", err))
|
||||
}
|
||||
|
||||
clientData := privilege.Client{
|
||||
URL: options.HubAdminUrl,
|
||||
ServiceName: options.ServiceName,
|
||||
Privileges: model.Privileges,
|
||||
}
|
||||
fiberClient := &fiber.Client{}
|
||||
privilegeController := privilege.NewPrivilege(clientData, fiberClient)
|
||||
go tools.PublishPrivilege(privilegeController, 10, 5*time.Minute)
|
||||
|
||||
tgClient, err := telegram.NewTelegramClient(ctx, pgdal)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed init tg clietns: %v", err))
|
||||
}
|
||||
|
||||
tgWC := workers.NewTgListenerWC(workers.Deps{
|
||||
BotID: int64(6712573453), // todo убрать
|
||||
Redis: redisClient,
|
||||
Dal: pgdal,
|
||||
TgClient: tgClient,
|
||||
})
|
||||
|
||||
go tgWC.Start(ctx)
|
||||
|
||||
// todo подумать над реализацией всего а то пока мне кажется что немного каша получается такой предикт что через некоторое время
|
||||
// сложно будет разобраться что есть где
|
||||
grpcControllers := initialize.InitRpcControllers(pgdal)
|
||||
grpc, err := server.NewGRPC(zapLogger)
|
||||
if err != nil {
|
||||
fmt.Println("error:", err)
|
||||
panic("err init grpc server")
|
||||
}
|
||||
grpc.Register(grpcControllers)
|
||||
go grpc.Run(server.DepsGrpcRun{
|
||||
Host: options.GrpcHost,
|
||||
Port: options.GrpcPort,
|
||||
})
|
||||
|
||||
app := fiber.New()
|
||||
app.Use(middleware.JWTAuth())
|
||||
app.Use(log_mw.ContextLogger(loggerHlog))
|
||||
app.Get("/liveness", healthchecks.Liveness)
|
||||
app.Get("/readiness", healthchecks.Readiness(&workerErr)) //todo parametrized readiness. should discuss ready reason
|
||||
|
||||
svc := service.New(service.Deps{
|
||||
Dal: pgdal,
|
||||
AuthClient: authClient,
|
||||
Producer: producer,
|
||||
ServiceName: options.ServiceName,
|
||||
ChDAL: chDal,
|
||||
TelegramClient: tgClient,
|
||||
RedisClient: redisClient,
|
||||
S3Prefix: options.S3Prefix,
|
||||
})
|
||||
|
||||
svc.Register(app)
|
||||
|
||||
loggerHlog.Emit(InfoSvcReady{})
|
||||
|
||||
go func() {
|
||||
defer func() {
|
||||
if pgdal != nil {
|
||||
pgdal.Close()
|
||||
}
|
||||
if chDal != nil {
|
||||
if derr := chDal.Close(ctx); derr != nil {
|
||||
fmt.Printf("error closing clickhouse: %v", derr)
|
||||
}
|
||||
}
|
||||
err := grpc.Stop(ctx)
|
||||
err = app.Shutdown()
|
||||
loggerHlog.Emit(InfoSvcShutdown{Signal: err.Error()})
|
||||
}()
|
||||
|
||||
if options.IsProd {
|
||||
if err := app.ListenTLS(fmt.Sprintf(":%s", options.NumberPort), options.CrtFile, options.KeyFile); err != nil {
|
||||
loggerHlog.Emit(ErrorCanNotServe{
|
||||
Err: err,
|
||||
})
|
||||
errChan <- err
|
||||
}
|
||||
} else {
|
||||
if err := app.Listen(fmt.Sprintf(":%s", options.NumberPort)); err != nil {
|
||||
loggerHlog.Emit(ErrorCanNotServe{
|
||||
Err: err,
|
||||
})
|
||||
errChan <- err
|
||||
}
|
||||
}
|
||||
|
||||
errChan <- nil
|
||||
}()
|
||||
// todo implement helper func for service app type. such as server preparing, logger preparing, healthchecks and etc.
|
||||
return &App{
|
||||
logger: zapLogger,
|
||||
err: errChan,
|
||||
}, err
|
||||
}
|
41
cmd/main.go
Normal file
41
cmd/main.go
Normal file
@ -0,0 +1,41 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"go.uber.org/zap"
|
||||
"os"
|
||||
"os/signal"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/app"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/initialize"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var (
|
||||
commit string = os.Getenv("COMMIT")
|
||||
buildTime string = os.Getenv("BUILD_TIME")
|
||||
version string = os.Getenv("VERSION")
|
||||
)
|
||||
|
||||
func main() {
|
||||
logger, err := zap.NewProduction()
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to initialize logger: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
config, err := initialize.LoadConfig()
|
||||
if err != nil {
|
||||
logger.Fatal("Failed to load config", zap.Error(err))
|
||||
}
|
||||
|
||||
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
||||
defer stop()
|
||||
|
||||
if err = app.Run(ctx, *config, logger, app.Build{
|
||||
Commit: commit,
|
||||
Version: version,
|
||||
}); err != nil {
|
||||
logger.Fatal("App exited with error", zap.Error(err))
|
||||
}
|
||||
}
|
3
go.mod
3
go.mod
@ -20,7 +20,7 @@ require (
|
||||
google.golang.org/grpc v1.64.0
|
||||
google.golang.org/protobuf v1.34.2
|
||||
penahub.gitlab.yandexcloud.net/backend/penahub_common v0.0.0-20240607202348-efe5f2bf3e8c
|
||||
penahub.gitlab.yandexcloud.net/backend/quiz/common.git v0.0.0-20240711133242-0b8534fae5b2
|
||||
penahub.gitlab.yandexcloud.net/backend/quiz/common.git v0.0.0-20241025130405-8ab347d96f1f
|
||||
penahub.gitlab.yandexcloud.net/backend/quiz/worker.git v0.0.0-20240421230341-0e086fcbb990
|
||||
penahub.gitlab.yandexcloud.net/backend/tdlib v0.0.0-20240701075856-1731684c936f
|
||||
penahub.gitlab.yandexcloud.net/external/trashlog v0.1.6-0.20240827173635-78ce9878c387
|
||||
@ -29,6 +29,7 @@ require (
|
||||
require (
|
||||
github.com/ClickHouse/clickhouse-go v1.5.4 // indirect
|
||||
github.com/andybalholm/brotli v1.1.0 // indirect
|
||||
github.com/caarlos0/env/v8 v8.0.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
|
6
go.sum
6
go.sum
@ -7,6 +7,8 @@ github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer5
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/bkaradzic/go-lz4 v1.0.0 h1:RXc4wYsyz985CkXXeX04y4VnZFGG8Rd43pRaHsOXAKk=
|
||||
github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4=
|
||||
github.com/caarlos0/env/v8 v8.0.0 h1:POhxHhSpuxrLMIdvTGARuZqR4Jjm8AYmoi/JKlcScs0=
|
||||
github.com/caarlos0/env/v8 v8.0.0/go.mod h1:7K4wMY9bH0esiXSSHlfHLX5xKGQMnkH5Fk4TDSSSzfo=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
@ -140,6 +142,7 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tealeg/xlsx v1.0.5/go.mod h1:btRS8dz54TDnvKNosuAqxrM1QgN1udgk9O34bDCnORM=
|
||||
github.com/themakers/bdd v0.0.0-20210316111417-6b1dfe326f33 h1:N9f/Q+2Ssa+yDcbfaoLTYvXmdeyUUxsJKdPUVsjSmiA=
|
||||
github.com/themakers/bdd v0.0.0-20210316111417-6b1dfe326f33/go.mod h1:rpcH99JknBh8seZmlOlUg51gasZH6QH34oXNsIwYT6E=
|
||||
github.com/themakers/hlog v0.0.0-20191205140925-235e0e4baddf h1:TJJm6KcBssmbWzplF5lzixXl1RBAi/ViPs1GaSOkhwo=
|
||||
@ -260,6 +263,7 @@ google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6h
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
@ -284,6 +288,8 @@ penahub.gitlab.yandexcloud.net/backend/penahub_common v0.0.0-20240607202348-efe5
|
||||
penahub.gitlab.yandexcloud.net/backend/penahub_common v0.0.0-20240607202348-efe5f2bf3e8c/go.mod h1:+bPxq2wfW5S1gd+83vZYmHm33AE7nEBfznWS8AM1TKE=
|
||||
penahub.gitlab.yandexcloud.net/backend/quiz/common.git v0.0.0-20240711133242-0b8534fae5b2 h1:0t6pQHJvA3jMeBB3FPUmA+hw8rWlksTah8KJEtf2KD8=
|
||||
penahub.gitlab.yandexcloud.net/backend/quiz/common.git v0.0.0-20240711133242-0b8534fae5b2/go.mod h1:uOuosXduBzd2WbLH6TDZO7ME7ZextulA662oZ6OsoB0=
|
||||
penahub.gitlab.yandexcloud.net/backend/quiz/common.git v0.0.0-20241025130405-8ab347d96f1f h1:HMXxbIkNmFeQTFI/MlsDBIRUTu8MTbn3i2PVcpQbXEI=
|
||||
penahub.gitlab.yandexcloud.net/backend/quiz/common.git v0.0.0-20241025130405-8ab347d96f1f/go.mod h1:uOuosXduBzd2WbLH6TDZO7ME7ZextulA662oZ6OsoB0=
|
||||
penahub.gitlab.yandexcloud.net/backend/quiz/worker.git v0.0.0-20240421230341-0e086fcbb990 h1:jiO8GWO+3sCnDAV8/NAV8tQIUwae/I6/xiDilW7zf0o=
|
||||
penahub.gitlab.yandexcloud.net/backend/quiz/worker.git v0.0.0-20240421230341-0e086fcbb990/go.mod h1:zswBuTwmEsFHBVRu1nkG3/Fwylk5Vcm8OUm9iWxccSE=
|
||||
penahub.gitlab.yandexcloud.net/backend/tdlib v0.0.0-20240701075856-1731684c936f h1:Qli89wgu0T7nG4VECXZOZ40fjE/hVVfxF3hTaSYS008=
|
||||
|
@ -1,16 +0,0 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/dal"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/rpc_service"
|
||||
)
|
||||
|
||||
type RpcRegister struct {
|
||||
MailNotify *rpc_service.MailNotify
|
||||
}
|
||||
|
||||
func InitRpcControllers(dal *dal.DAL) *RpcRegister {
|
||||
return &RpcRegister{
|
||||
MailNotify: rpc_service.NewMailNotify(dal),
|
||||
}
|
||||
}
|
187
internal/app/app.go
Normal file
187
internal/app/app.go
Normal file
@ -0,0 +1,187 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/themakers/hlog"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"penahub.gitlab.yandexcloud.net/backend/penahub_common/privilege"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/model"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/brokers"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/initialize"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/models"
|
||||
server "penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/server/grpc"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/server/http"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/tools"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/workers"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/pkg/closer"
|
||||
"penahub.gitlab.yandexcloud.net/external/trashlog/wrappers/zaptrashlog"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Build struct {
|
||||
Commit string
|
||||
Version string
|
||||
}
|
||||
|
||||
var zapOptions = []zap.Option{
|
||||
zap.AddCaller(),
|
||||
zap.AddCallerSkip(2),
|
||||
zap.AddStacktrace(zap.ErrorLevel),
|
||||
}
|
||||
|
||||
func Run(ctx context.Context, cfg initialize.Config, logger *zap.Logger, build Build) error {
|
||||
var (
|
||||
err error
|
||||
zapLogger *zap.Logger
|
||||
)
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logger.Error("Recovered from a panic", zap.Any("error", r))
|
||||
}
|
||||
}()
|
||||
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
if cfg.LoggerProdMode {
|
||||
zapLogger, err = zap.NewProduction(zapOptions...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
zapLogger, err = zap.NewDevelopment(zapOptions...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
zapLogger = zapLogger.With(
|
||||
zap.String("SvcCommit", build.Commit),
|
||||
zap.String("SvcVersion", build.Version),
|
||||
zap.String("SvcBuildTime", time.Now().String()),
|
||||
)
|
||||
|
||||
clickHouseLogger, err := zaptrashlog.NewCore(ctx, zap.InfoLevel, cfg.TrashLogHost, build.Version, build.Commit, time.Now().Unix())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
loggerForHlog := zapLogger.WithOptions(zap.WrapCore(func(core zapcore.Core) zapcore.Core {
|
||||
return zapcore.NewTee(core, clickHouseLogger)
|
||||
}))
|
||||
|
||||
loggerHlog := hlog.New(loggerForHlog).Module(cfg.ModuleLogger)
|
||||
loggerHlog.With(models.AllFields{})
|
||||
loggerHlog.Emit(InfoSvcStarted{})
|
||||
|
||||
shutdownGroup := closer.NewCloserGroup()
|
||||
|
||||
dalS, err := initialize.NewDALs(ctx, cfg)
|
||||
if err != nil {
|
||||
logger.Error("Error initializing dals", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
kafkaClient, err := initialize.KafkaInit(ctx, initialize.KafkaDeps{
|
||||
KafkaGroup: cfg.KafkaGroup,
|
||||
KafkaBrokers: cfg.KafkaBrokers,
|
||||
KafkaTopic: cfg.KafkaTopic,
|
||||
})
|
||||
if err != nil {
|
||||
logger.Error("Error initializing kafka", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
producer := brokers.NewProducer(brokers.ProducerDeps{
|
||||
KafkaClient: kafkaClient,
|
||||
Logger: zapLogger,
|
||||
})
|
||||
|
||||
redisClient, err := initialize.Redis(ctx, cfg)
|
||||
if err != nil {
|
||||
logger.Error("Error initializing redis", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
go tools.PublishPrivilege(privilege.NewPrivilege(privilege.Client{
|
||||
URL: cfg.HubAdminUrl,
|
||||
ServiceName: cfg.ServiceName,
|
||||
Privileges: model.Privileges,
|
||||
}, &fiber.Client{}), 10, 5*time.Minute)
|
||||
|
||||
clients, err := initialize.NewClients(ctx, cfg, dalS.PgDAL)
|
||||
if err != nil {
|
||||
logger.Error("Error initializing clients", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
tgWC := workers.NewTgListenerWC(workers.Deps{
|
||||
BotID: int64(6712573453), // todo убрать
|
||||
Redis: redisClient,
|
||||
Dal: dalS.PgDAL,
|
||||
TgClient: clients.TgClient,
|
||||
})
|
||||
|
||||
go tgWC.Start(ctx)
|
||||
|
||||
controllers := initialize.NewControllers(initialize.ControllerDeps{
|
||||
Clients: clients,
|
||||
DALs: dalS,
|
||||
Config: cfg,
|
||||
Producer: producer,
|
||||
RedisClient: redisClient,
|
||||
})
|
||||
|
||||
grpc, err := server.NewGRPC(zapLogger)
|
||||
if err != nil {
|
||||
logger.Error("Error initializing grpc", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
grpc.Register(controllers.GRpcControllers)
|
||||
|
||||
srv := http.NewServer(http.ServerConfig{
|
||||
Logger: logger,
|
||||
Controllers: []http.Controller{controllers.HttpControllers.Account, controllers.HttpControllers.Telegram, controllers.HttpControllers.Result,
|
||||
controllers.HttpControllers.Question, controllers.HttpControllers.Quiz, controllers.HttpControllers.Statistic},
|
||||
Hlogger: loggerHlog,
|
||||
})
|
||||
|
||||
go func() {
|
||||
if err := srv.Start(cfg.HttpHost + ":" + cfg.NumberPort); err != nil {
|
||||
logger.Error("HTTP server startup error", zap.Error(err))
|
||||
cancel()
|
||||
}
|
||||
}()
|
||||
|
||||
go grpc.Run(server.DepsGrpcRun{
|
||||
Host: cfg.GrpcHost,
|
||||
Port: cfg.GrpcPort,
|
||||
})
|
||||
|
||||
srv.ListRoutes()
|
||||
|
||||
shutdownGroup.Add(closer.CloserFunc(srv.Shutdown))
|
||||
shutdownGroup.Add(closer.CloserFunc(grpc.Stop))
|
||||
shutdownGroup.Add(closer.CloserFunc(dalS.PgDAL.Close))
|
||||
shutdownGroup.Add(closer.CloserFunc(dalS.ChDAL.Close))
|
||||
|
||||
<-ctx.Done()
|
||||
|
||||
timeoutCtx, timeoutCancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer timeoutCancel()
|
||||
if err := shutdownGroup.Call(timeoutCtx); err != nil {
|
||||
if errors.Is(err, context.DeadlineExceeded) {
|
||||
logger.Error("Shutdown timed out", zap.Error(err))
|
||||
} else {
|
||||
logger.Error("Failed to shutdown services gracefully", zap.Error(err))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Info("Application has stopped")
|
||||
return nil
|
||||
}
|
@ -1,21 +1,50 @@
|
||||
package service
|
||||
package account
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"penahub.gitlab.yandexcloud.net/backend/penahub_common/log_mw"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/dal"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/middleware"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/model"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/pj_errors"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/brokers"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/models"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/brokers"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/clients/auth"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/models"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Deps struct {
|
||||
Dal *dal.DAL
|
||||
AuthClient *auth.AuthClient
|
||||
Producer *brokers.Producer
|
||||
ServiceName string
|
||||
RedisClient *redis.Client
|
||||
}
|
||||
|
||||
type Account struct {
|
||||
dal *dal.DAL
|
||||
authClient *auth.AuthClient
|
||||
producer *brokers.Producer
|
||||
serviceName string
|
||||
redisClient *redis.Client
|
||||
}
|
||||
|
||||
func NewAccountController(deps Deps) *Account {
|
||||
return &Account{
|
||||
dal: deps.Dal,
|
||||
authClient: deps.AuthClient,
|
||||
producer: deps.Producer,
|
||||
serviceName: deps.ServiceName,
|
||||
redisClient: deps.RedisClient,
|
||||
}
|
||||
}
|
||||
|
||||
type CreateAccountReq struct {
|
||||
UserID string `json:"userId"`
|
||||
}
|
||||
@ -51,13 +80,13 @@ type GetAccountsResp struct {
|
||||
}
|
||||
|
||||
// getCurrentAccount обработчик для получения текущего аккаунта
|
||||
func (s *Service) getCurrentAccount(ctx *fiber.Ctx) error {
|
||||
func (r *Account) GetCurrentAccount(ctx *fiber.Ctx) error {
|
||||
accountID, ok := middleware.GetAccountId(ctx)
|
||||
if !ok {
|
||||
return ctx.Status(fiber.StatusUnauthorized).SendString("account id is required")
|
||||
}
|
||||
|
||||
account, err := s.dal.AccountRepo.GetAccountByID(ctx.Context(), accountID)
|
||||
account, err := r.dal.AccountRepo.GetAccountByID(ctx.Context(), accountID)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
||||
}
|
||||
@ -71,14 +100,14 @@ func (s *Service) getCurrentAccount(ctx *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
// createAccount обработчик для создания нового аккаунта
|
||||
func (s *Service) createAccount(ctx *fiber.Ctx) error {
|
||||
func (r *Account) CreateAccount(ctx *fiber.Ctx) error {
|
||||
accountID, ok := middleware.GetAccountId(ctx)
|
||||
if !ok {
|
||||
return ctx.Status(fiber.StatusUnauthorized).SendString("account id is required")
|
||||
}
|
||||
hlogger := log_mw.ExtractLogger(ctx)
|
||||
|
||||
existingAccount, err := s.dal.AccountRepo.GetAccountByID(ctx.Context(), accountID)
|
||||
existingAccount, err := r.dal.AccountRepo.GetAccountByID(ctx.Context(), accountID)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
||||
}
|
||||
@ -86,7 +115,7 @@ func (s *Service) createAccount(ctx *fiber.Ctx) error {
|
||||
return ctx.Status(fiber.StatusConflict).SendString("user with this ID already exists")
|
||||
}
|
||||
|
||||
email, err := s.authClient.GetUserEmail(accountID)
|
||||
email, err := r.authClient.GetUserEmail(accountID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -105,11 +134,11 @@ func (s *Service) createAccount(ctx *fiber.Ctx) error {
|
||||
},
|
||||
}
|
||||
|
||||
createdAcc, err := s.dal.AccountRepo.CreateAccount(ctx.Context(), &newAccount)
|
||||
createdAcc, err := r.dal.AccountRepo.CreateAccount(ctx.Context(), &newAccount)
|
||||
if err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
||||
}
|
||||
_, err = s.dal.AccountRepo.PostLeadTarget(ctx.Context(), model.LeadTarget{
|
||||
_, err = r.dal.AccountRepo.PostLeadTarget(ctx.Context(), model.LeadTarget{
|
||||
AccountID: accountID,
|
||||
Target: email,
|
||||
Type: model.LeadTargetEmail,
|
||||
@ -124,10 +153,10 @@ func (s *Service) createAccount(ctx *fiber.Ctx) error {
|
||||
CtxAccountID: createdAcc.ID,
|
||||
})
|
||||
|
||||
err = s.producer.ToMailNotify(ctx.Context(), brokers.Message{
|
||||
err = r.producer.ToMailNotify(ctx.Context(), brokers.Message{
|
||||
AccountID: accountID,
|
||||
Email: email,
|
||||
ServiceKey: s.serviceName,
|
||||
ServiceKey: r.serviceName,
|
||||
SendAt: time.Now(),
|
||||
})
|
||||
if err != nil {
|
||||
@ -140,18 +169,18 @@ func (s *Service) createAccount(ctx *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
// deleteAccount обработчик для удаления текущего аккаунта
|
||||
func (s *Service) deleteAccount(ctx *fiber.Ctx) error {
|
||||
func (r *Account) DeleteAccount(ctx *fiber.Ctx) error {
|
||||
accountID, ok := middleware.GetAccountId(ctx)
|
||||
if !ok {
|
||||
return ctx.Status(fiber.StatusUnauthorized).SendString("account id is required")
|
||||
}
|
||||
|
||||
account, err := s.dal.AccountRepo.GetAccountByID(ctx.Context(), accountID)
|
||||
account, err := r.dal.AccountRepo.GetAccountByID(ctx.Context(), accountID)
|
||||
if err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
||||
}
|
||||
|
||||
if err := s.dal.AccountRepo.DeleteAccount(ctx.Context(), account.ID); err != nil {
|
||||
if err := r.dal.AccountRepo.DeleteAccount(ctx.Context(), account.ID); err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
||||
}
|
||||
|
||||
@ -161,13 +190,13 @@ func (s *Service) deleteAccount(ctx *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
// getPrivilegeByUserID обработчик для получения привилегий аккаунта по ID пользователя
|
||||
func (s *Service) getPrivilegeByUserID(ctx *fiber.Ctx) error {
|
||||
func (r *Account) GetPrivilegeByUserID(ctx *fiber.Ctx) error {
|
||||
var req GetPrivilegeByUserIDReq
|
||||
if err := ctx.BodyParser(&req); err != nil {
|
||||
return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data")
|
||||
}
|
||||
|
||||
privilege, err := s.dal.AccountRepo.GetPrivilegesByAccountID(ctx.Context(), req.UserID)
|
||||
privilege, err := r.dal.AccountRepo.GetPrivilegesByAccountID(ctx.Context(), req.UserID)
|
||||
if err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
||||
}
|
||||
@ -176,13 +205,13 @@ func (s *Service) getPrivilegeByUserID(ctx *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
// deleteAccountByUserID обработчик для удаления аккаунта по ID пользователя
|
||||
func (s *Service) deleteAccountByUserID(ctx *fiber.Ctx) error {
|
||||
func (r *Account) DeleteAccountByUserID(ctx *fiber.Ctx) error {
|
||||
var req DeleteAccountByUserIDReq
|
||||
if err := ctx.BodyParser(&req); err != nil {
|
||||
return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data")
|
||||
}
|
||||
|
||||
existingAccount, err := s.dal.AccountRepo.GetAccountByID(ctx.Context(), req.UserID)
|
||||
existingAccount, err := r.dal.AccountRepo.GetAccountByID(ctx.Context(), req.UserID)
|
||||
if err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
||||
}
|
||||
@ -191,7 +220,7 @@ func (s *Service) deleteAccountByUserID(ctx *fiber.Ctx) error {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString("user with this ID not found")
|
||||
}
|
||||
|
||||
if err := s.dal.AccountRepo.DeleteAccount(ctx.Context(), existingAccount.ID); err != nil {
|
||||
if err := r.dal.AccountRepo.DeleteAccount(ctx.Context(), existingAccount.ID); err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
||||
}
|
||||
|
||||
@ -201,7 +230,7 @@ func (s *Service) deleteAccountByUserID(ctx *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
// getAccounts обработчик для получения списка аккаунтов с пагинацией
|
||||
func (s *Service) getAccounts(ctx *fiber.Ctx) error {
|
||||
func (r *Account) GetAccounts(ctx *fiber.Ctx) error {
|
||||
var req GetAccountsReq
|
||||
if err := ctx.BodyParser(&req); err != nil {
|
||||
return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data")
|
||||
@ -212,7 +241,7 @@ func (s *Service) getAccounts(ctx *fiber.Ctx) error {
|
||||
return ctx.Status(fiber.StatusUnauthorized).SendString("account id is required")
|
||||
}
|
||||
|
||||
accounts, totalCount, err := s.dal.AccountRepo.GetAccounts(ctx.Context(), req.Limit, req.Page)
|
||||
accounts, totalCount, err := r.dal.AccountRepo.GetAccounts(ctx.Context(), req.Limit, req.Page)
|
||||
if err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
||||
}
|
||||
@ -225,7 +254,7 @@ func (s *Service) getAccounts(ctx *fiber.Ctx) error {
|
||||
return ctx.Status(fiber.StatusOK).JSON(response)
|
||||
}
|
||||
|
||||
func (s *Service) ManualDone(ctx *fiber.Ctx) error {
|
||||
func (r *Account) ManualDone(ctx *fiber.Ctx) error {
|
||||
var req struct {
|
||||
Id string `json:"id"`
|
||||
}
|
||||
@ -237,7 +266,7 @@ func (s *Service) ManualDone(ctx *fiber.Ctx) error {
|
||||
return ctx.Status(fiber.StatusBadRequest).SendString("User id is required")
|
||||
}
|
||||
|
||||
err := s.dal.AccountRepo.ManualDone(ctx.Context(), req.Id)
|
||||
err := r.dal.AccountRepo.ManualDone(ctx.Context(), req.Id)
|
||||
if err != nil {
|
||||
if errors.Is(err, pj_errors.ErrNotFound) {
|
||||
return ctx.Status(fiber.StatusNotFound).SendString("user don't have this privilege")
|
||||
@ -248,7 +277,7 @@ func (s *Service) ManualDone(ctx *fiber.Ctx) error {
|
||||
return ctx.SendStatus(fiber.StatusOK)
|
||||
}
|
||||
|
||||
func (s *Service) PostLeadTarget(ctx *fiber.Ctx) error {
|
||||
func (r *Account) PostLeadTarget(ctx *fiber.Ctx) error {
|
||||
var req struct {
|
||||
Type string `json:"type"`
|
||||
QuizID int32 `json:"quizID"`
|
||||
@ -276,7 +305,7 @@ func (s *Service) PostLeadTarget(ctx *fiber.Ctx) error {
|
||||
|
||||
switch req.Type {
|
||||
case "mail":
|
||||
_, err := s.dal.AccountRepo.PostLeadTarget(ctx.Context(), model.LeadTarget{
|
||||
_, err := r.dal.AccountRepo.PostLeadTarget(ctx.Context(), model.LeadTarget{
|
||||
AccountID: accountID,
|
||||
Target: req.Target,
|
||||
Type: model.LeadTargetType(req.Type),
|
||||
@ -287,7 +316,7 @@ func (s *Service) PostLeadTarget(ctx *fiber.Ctx) error {
|
||||
}
|
||||
return ctx.SendStatus(fiber.StatusOK)
|
||||
case "telegram":
|
||||
targets, err := s.dal.AccountRepo.GetLeadTarget(ctx.Context(), accountID, req.QuizID)
|
||||
targets, err := r.dal.AccountRepo.GetLeadTarget(ctx.Context(), accountID, req.QuizID)
|
||||
if err != nil && !errors.Is(err, pj_errors.ErrNotFound) {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
||||
}
|
||||
@ -311,7 +340,7 @@ func (s *Service) PostLeadTarget(ctx *fiber.Ctx) error {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
||||
}
|
||||
|
||||
if err := s.redisClient.Set(ctx.Context(), taskKey, taskData, 0).Err(); err != nil {
|
||||
if err := r.redisClient.Set(ctx.Context(), taskKey, taskData, 0).Err(); err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
||||
}
|
||||
case "whatsapp":
|
||||
@ -321,21 +350,21 @@ func (s *Service) PostLeadTarget(ctx *fiber.Ctx) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) DeleteLeadTarget(ctx *fiber.Ctx) error {
|
||||
func (r *Account) DeleteLeadTarget(ctx *fiber.Ctx) error {
|
||||
leadIDStr := ctx.Params("id")
|
||||
leadID, err := strconv.ParseInt(leadIDStr, 10, 64)
|
||||
if err != nil {
|
||||
return ctx.Status(fiber.StatusBadRequest).SendString("Invalid lead ID format")
|
||||
}
|
||||
|
||||
err = s.dal.AccountRepo.DeleteLeadTarget(ctx.Context(), leadID)
|
||||
err = r.dal.AccountRepo.DeleteLeadTarget(ctx.Context(), leadID)
|
||||
if err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
||||
}
|
||||
return ctx.SendStatus(fiber.StatusOK)
|
||||
}
|
||||
|
||||
func (s *Service) GetLeadTarget(ctx *fiber.Ctx) error {
|
||||
func (r *Account) GetLeadTarget(ctx *fiber.Ctx) error {
|
||||
accountID, ok := middleware.GetAccountId(ctx)
|
||||
if !ok {
|
||||
return ctx.Status(fiber.StatusUnauthorized).SendString("account id is required")
|
||||
@ -347,7 +376,7 @@ func (s *Service) GetLeadTarget(ctx *fiber.Ctx) error {
|
||||
return ctx.Status(fiber.StatusBadRequest).SendString("Invalid quiz ID format")
|
||||
}
|
||||
|
||||
result, err := s.dal.AccountRepo.GetLeadTarget(ctx.Context(), accountID, int32(quizID))
|
||||
result, err := r.dal.AccountRepo.GetLeadTarget(ctx.Context(), accountID, int32(quizID))
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, pj_errors.ErrNotFound):
|
||||
@ -360,7 +389,7 @@ func (s *Service) GetLeadTarget(ctx *fiber.Ctx) error {
|
||||
return ctx.Status(fiber.StatusOK).JSON(result)
|
||||
}
|
||||
|
||||
func (s *Service) UpdateLeadTarget(ctx *fiber.Ctx) error {
|
||||
func (r *Account) UpdateLeadTarget(ctx *fiber.Ctx) error {
|
||||
var req struct {
|
||||
ID int64 `json:"id"`
|
||||
Target string `json:"target"`
|
||||
@ -374,7 +403,7 @@ func (s *Service) UpdateLeadTarget(ctx *fiber.Ctx) error {
|
||||
return ctx.Status(fiber.StatusBadRequest).SendString("ID and Target don't be nil")
|
||||
}
|
||||
|
||||
result, err := s.dal.AccountRepo.UpdateLeadTarget(ctx.Context(), model.LeadTarget{
|
||||
result, err := r.dal.AccountRepo.UpdateLeadTarget(ctx.Context(), model.LeadTarget{
|
||||
ID: req.ID,
|
||||
Target: req.Target,
|
||||
})
|
21
internal/controllers/http_controllers/account/route.go
Normal file
21
internal/controllers/http_controllers/account/route.go
Normal file
@ -0,0 +1,21 @@
|
||||
package account
|
||||
|
||||
import "github.com/gofiber/fiber/v2"
|
||||
|
||||
func (r *Account) Register(router fiber.Router) {
|
||||
router.Get("/account/get", r.GetCurrentAccount)
|
||||
router.Post("/account/create", r.CreateAccount)
|
||||
router.Delete("/account/delete", r.DeleteAccount)
|
||||
router.Get("/accounts", r.GetAccounts)
|
||||
router.Get("/privilege/:userId", r.GetPrivilegeByUserID)
|
||||
router.Delete("/account/:userId", r.DeleteAccountByUserID)
|
||||
router.Post("/account/manualdone", r.ManualDone)
|
||||
router.Post("/account/leadtarget", r.PostLeadTarget)
|
||||
router.Delete("/account/leadtarget/:id", r.DeleteLeadTarget)
|
||||
router.Get("/account/leadtarget/:quizID", r.GetLeadTarget)
|
||||
router.Put("/account/leadtarget", r.UpdateLeadTarget)
|
||||
}
|
||||
|
||||
func (r *Account) Name() string {
|
||||
return ""
|
||||
}
|
@ -1,15 +1,28 @@
|
||||
package service
|
||||
package question
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/lib/pq"
|
||||
"penahub.gitlab.yandexcloud.net/backend/penahub_common/log_mw"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/dal"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/middleware"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/model"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/models"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/models"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type Deps struct {
|
||||
DAL *dal.DAL
|
||||
}
|
||||
|
||||
type Question struct {
|
||||
dal *dal.DAL
|
||||
}
|
||||
|
||||
func NewQuestionController(deps Deps) *Question {
|
||||
return &Question{dal: deps.DAL}
|
||||
}
|
||||
|
||||
// QuestionCreateReq request structure for creating Question
|
||||
type QuestionCreateReq struct {
|
||||
QuizId uint64 `json:"quiz_id"` // relation to quiz
|
||||
@ -23,7 +36,7 @@ type QuestionCreateReq struct {
|
||||
}
|
||||
|
||||
// CreateQuestion service handler for creating question for quiz
|
||||
func (s *Service) CreateQuestion(ctx *fiber.Ctx) error {
|
||||
func (r *Question) CreateQuestion(ctx *fiber.Ctx) error {
|
||||
accountID, ok := middleware.GetAccountId(ctx)
|
||||
if !ok {
|
||||
return ctx.Status(fiber.StatusUnauthorized).SendString("account id is required")
|
||||
@ -64,7 +77,7 @@ func (s *Service) CreateQuestion(ctx *fiber.Ctx) error {
|
||||
Content: req.Content,
|
||||
}
|
||||
|
||||
questionID, err := s.dal.QuestionRepo.CreateQuestion(ctx.Context(), &result)
|
||||
questionID, err := r.dal.QuestionRepo.CreateQuestion(ctx.Context(), &result)
|
||||
if err != nil {
|
||||
if e, ok := err.(*pq.Error); ok {
|
||||
if e.Constraint == "quiz_relation" {
|
||||
@ -104,7 +117,7 @@ type GetQuestionListResp struct {
|
||||
}
|
||||
|
||||
// GetQuestionList handler for paginated list question
|
||||
func (s *Service) GetQuestionList(ctx *fiber.Ctx) error {
|
||||
func (r *Question) GetQuestionList(ctx *fiber.Ctx) error {
|
||||
var req GetQuestionListReq
|
||||
if err := ctx.BodyParser(&req); err != nil {
|
||||
return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data")
|
||||
@ -127,7 +140,7 @@ func (s *Service) GetQuestionList(ctx *fiber.Ctx) error {
|
||||
"'test','none','file', 'button','select','checkbox'")
|
||||
}
|
||||
|
||||
res, cnt, err := s.dal.QuestionRepo.GetQuestionList(ctx.Context(),
|
||||
res, cnt, err := r.dal.QuestionRepo.GetQuestionList(ctx.Context(),
|
||||
req.Limit,
|
||||
req.Page*req.Limit,
|
||||
uint64(req.From),
|
||||
@ -165,7 +178,7 @@ type UpdateResp struct {
|
||||
}
|
||||
|
||||
// UpdateQuestion handler for update question
|
||||
func (s *Service) UpdateQuestion(ctx *fiber.Ctx) error {
|
||||
func (r *Question) UpdateQuestion(ctx *fiber.Ctx) error {
|
||||
var req UpdateQuestionReq
|
||||
if err := ctx.BodyParser(&req); err != nil {
|
||||
return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data")
|
||||
@ -195,7 +208,7 @@ func (s *Service) UpdateQuestion(ctx *fiber.Ctx) error {
|
||||
return ctx.Status(fiber.StatusNotAcceptable).SendString("type must be only test,button,file,checkbox,select, none or empty string")
|
||||
}
|
||||
|
||||
question, err := s.dal.QuestionRepo.MoveToHistoryQuestion(ctx.Context(), req.Id)
|
||||
question, err := r.dal.QuestionRepo.MoveToHistoryQuestion(ctx.Context(), req.Id)
|
||||
if err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
||||
}
|
||||
@ -227,7 +240,7 @@ func (s *Service) UpdateQuestion(ctx *fiber.Ctx) error {
|
||||
question.Content = req.Content
|
||||
}
|
||||
|
||||
if err := s.dal.QuestionRepo.UpdateQuestion(ctx.Context(), question); err != nil {
|
||||
if err := r.dal.QuestionRepo.UpdateQuestion(ctx.Context(), question); err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
||||
}
|
||||
|
||||
@ -243,7 +256,7 @@ type CopyQuestionReq struct {
|
||||
}
|
||||
|
||||
// CopyQuestion handler for copy question
|
||||
func (s *Service) CopyQuestion(ctx *fiber.Ctx) error {
|
||||
func (r *Question) CopyQuestion(ctx *fiber.Ctx) error {
|
||||
var req CopyQuestionReq
|
||||
if err := ctx.BodyParser(&req); err != nil {
|
||||
return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data")
|
||||
@ -253,7 +266,7 @@ func (s *Service) CopyQuestion(ctx *fiber.Ctx) error {
|
||||
return ctx.Status(fiber.StatusFailedDependency).SendString("no id provided")
|
||||
}
|
||||
|
||||
question, err := s.dal.QuestionRepo.CopyQuestion(ctx.Context(), req.Id, req.QuizId)
|
||||
question, err := r.dal.QuestionRepo.CopyQuestion(ctx.Context(), req.Id, req.QuizId)
|
||||
if err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
||||
}
|
||||
@ -271,8 +284,8 @@ type GetQuestionHistoryReq struct {
|
||||
}
|
||||
|
||||
// GetQuestionHistory handler for history of quiz
|
||||
func (s *Service) GetQuestionHistory(ctx *fiber.Ctx) error {
|
||||
var req GetQuizHistoryReq
|
||||
func (r *Question) GetQuestionHistory(ctx *fiber.Ctx) error {
|
||||
var req GetQuestionHistoryReq
|
||||
if err := ctx.BodyParser(&req); err != nil {
|
||||
return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data")
|
||||
}
|
||||
@ -281,7 +294,7 @@ func (s *Service) GetQuestionHistory(ctx *fiber.Ctx) error {
|
||||
return ctx.Status(fiber.StatusFailedDependency).SendString("no id provided")
|
||||
}
|
||||
|
||||
history, err := s.dal.QuestionRepo.QuestionHistory(ctx.Context(), req.Id, req.Limit, req.Page*req.Limit)
|
||||
history, err := r.dal.QuestionRepo.QuestionHistory(ctx.Context(), req.Id, req.Limit, req.Page*req.Limit)
|
||||
if err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
||||
}
|
||||
@ -289,15 +302,22 @@ func (s *Service) GetQuestionHistory(ctx *fiber.Ctx) error {
|
||||
return ctx.Status(fiber.StatusOK).JSON(history)
|
||||
}
|
||||
|
||||
type DeactivateResp struct {
|
||||
Deactivated uint64 `json:"deactivated"`
|
||||
}
|
||||
|
||||
// DeleteQuestion handler for fake delete question
|
||||
func (s *Service) DeleteQuestion(ctx *fiber.Ctx) error {
|
||||
func (r *Question) DeleteQuestion(ctx *fiber.Ctx) error {
|
||||
accountID, ok := middleware.GetAccountId(ctx)
|
||||
if !ok {
|
||||
return ctx.Status(fiber.StatusUnauthorized).SendString("account id is required")
|
||||
}
|
||||
hlogger := log_mw.ExtractLogger(ctx)
|
||||
|
||||
var req DeactivateReq
|
||||
var req struct {
|
||||
Id uint64 `json:"id"`
|
||||
}
|
||||
|
||||
if err := ctx.BodyParser(&req); err != nil {
|
||||
return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data")
|
||||
}
|
||||
@ -306,7 +326,7 @@ func (s *Service) DeleteQuestion(ctx *fiber.Ctx) error {
|
||||
return ctx.Status(fiber.StatusFailedDependency).SendString("id for deleting question is required")
|
||||
}
|
||||
|
||||
deleted, err := s.dal.QuestionRepo.DeleteQuestion(ctx.Context(), req.Id)
|
||||
deleted, err := r.dal.QuestionRepo.DeleteQuestion(ctx.Context(), req.Id)
|
||||
if err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
||||
}
|
16
internal/controllers/http_controllers/question/route.go
Normal file
16
internal/controllers/http_controllers/question/route.go
Normal file
@ -0,0 +1,16 @@
|
||||
package question
|
||||
|
||||
import "github.com/gofiber/fiber/v2"
|
||||
|
||||
func (r *Question) Register(router fiber.Router) {
|
||||
router.Post("/create", r.CreateQuestion)
|
||||
router.Post("/getList", r.GetQuestionList)
|
||||
router.Patch("/edit", r.UpdateQuestion)
|
||||
router.Post("/copy", r.CopyQuestion)
|
||||
router.Post("/history", r.GetQuestionHistory)
|
||||
router.Delete("/delete", r.DeleteQuestion)
|
||||
}
|
||||
|
||||
func (r *Question) Name() string {
|
||||
return "question"
|
||||
}
|
@ -1,17 +1,30 @@
|
||||
package service
|
||||
package quiz
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"penahub.gitlab.yandexcloud.net/backend/penahub_common/log_mw"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/dal"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/middleware"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/model"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/repository/quiz"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/models"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/models"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Deps struct {
|
||||
DAL *dal.DAL
|
||||
}
|
||||
|
||||
type Quiz struct {
|
||||
dal *dal.DAL
|
||||
}
|
||||
|
||||
func NewQuizController(deps Deps) *Quiz {
|
||||
return &Quiz{dal: deps.DAL}
|
||||
}
|
||||
|
||||
type CreateQuizReq struct {
|
||||
Fingerprinting bool `json:"fingerprinting"` // true if you need to store device id
|
||||
Repeatable bool `json:"repeatable"` // make it true for allow more than one quiz checkouting
|
||||
@ -36,7 +49,7 @@ type CreateQuizReq struct {
|
||||
}
|
||||
|
||||
// CreateQuiz handler for quiz creating request
|
||||
func (s *Service) CreateQuiz(ctx *fiber.Ctx) error {
|
||||
func (r *Quiz) CreateQuiz(ctx *fiber.Ctx) error {
|
||||
var req CreateQuizReq
|
||||
if err := ctx.BodyParser(&req); err != nil {
|
||||
return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data")
|
||||
@ -95,7 +108,7 @@ func (s *Service) CreateQuiz(ctx *fiber.Ctx) error {
|
||||
GroupId: req.GroupId,
|
||||
}
|
||||
|
||||
quizID, err := s.dal.QuizRepo.CreateQuiz(ctx.Context(), &record)
|
||||
quizID, err := r.dal.QuizRepo.CreateQuiz(ctx.Context(), &record)
|
||||
if err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
||||
}
|
||||
@ -129,7 +142,7 @@ type GetQuizListResp struct {
|
||||
}
|
||||
|
||||
// GetQuizList handler for paginated list quiz
|
||||
func (s *Service) GetQuizList(ctx *fiber.Ctx) error {
|
||||
func (r *Quiz) GetQuizList(ctx *fiber.Ctx) error {
|
||||
var req GetQuizListReq
|
||||
if err := ctx.BodyParser(&req); err != nil {
|
||||
return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data")
|
||||
@ -151,7 +164,7 @@ func (s *Service) GetQuizList(ctx *fiber.Ctx) error {
|
||||
"'stop','start','draft', 'template','timeout','offlimit'")
|
||||
}
|
||||
|
||||
res, cnt, err := s.dal.QuizRepo.GetQuizList(ctx.Context(),
|
||||
res, cnt, err := r.dal.QuizRepo.GetQuizList(ctx.Context(),
|
||||
quiz.GetQuizListDeps{
|
||||
Limit: req.Limit,
|
||||
Offset: req.Limit * req.Page,
|
||||
@ -195,7 +208,11 @@ type UpdateQuizReq struct {
|
||||
GroupId uint64 `json:"group_id"`
|
||||
}
|
||||
|
||||
func (s *Service) UpdateQuiz(ctx *fiber.Ctx) error {
|
||||
type UpdateResp struct {
|
||||
Updated uint64 `json:"updated"`
|
||||
}
|
||||
|
||||
func (r *Quiz) UpdateQuiz(ctx *fiber.Ctx) error {
|
||||
var req UpdateQuizReq
|
||||
if err := ctx.BodyParser(&req); err != nil {
|
||||
return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data")
|
||||
@ -236,7 +253,7 @@ func (s *Service) UpdateQuiz(ctx *fiber.Ctx) error {
|
||||
return ctx.Status(fiber.StatusConflict).SendString("you can pause quiz only if it has deadline for passing")
|
||||
}
|
||||
|
||||
quiz, err := s.dal.QuizRepo.MoveToHistoryQuiz(ctx.Context(), req.Id, accountId)
|
||||
quiz, err := r.dal.QuizRepo.MoveToHistoryQuiz(ctx.Context(), req.Id, accountId)
|
||||
if err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
||||
}
|
||||
@ -308,7 +325,7 @@ func (s *Service) UpdateQuiz(ctx *fiber.Ctx) error {
|
||||
|
||||
quiz.ParentIds = append(quiz.ParentIds, int32(quiz.Id))
|
||||
|
||||
if err := s.dal.QuizRepo.UpdateQuiz(ctx.Context(), accountId, quiz); err != nil {
|
||||
if err := r.dal.QuizRepo.UpdateQuiz(ctx.Context(), accountId, quiz); err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
||||
}
|
||||
|
||||
@ -336,7 +353,7 @@ type CopyQuizReq struct {
|
||||
}
|
||||
|
||||
// CopyQuiz request handler for copy quiz
|
||||
func (s *Service) CopyQuiz(ctx *fiber.Ctx) error {
|
||||
func (r *Quiz) CopyQuiz(ctx *fiber.Ctx) error {
|
||||
var req CopyQuizReq
|
||||
if err := ctx.BodyParser(&req); err != nil {
|
||||
return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data")
|
||||
@ -351,7 +368,7 @@ func (s *Service) CopyQuiz(ctx *fiber.Ctx) error {
|
||||
return ctx.Status(fiber.StatusFailedDependency).SendString("no id provided")
|
||||
}
|
||||
|
||||
quiz, err := s.dal.QuizRepo.CopyQuiz(ctx.Context(), accountId, req.Id)
|
||||
quiz, err := r.dal.QuizRepo.CopyQuiz(ctx.Context(), accountId, req.Id)
|
||||
if err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
||||
}
|
||||
@ -369,7 +386,7 @@ type GetQuizHistoryReq struct {
|
||||
}
|
||||
|
||||
// GetQuizHistory handler for history of quiz
|
||||
func (s *Service) GetQuizHistory(ctx *fiber.Ctx) error {
|
||||
func (r *Quiz) GetQuizHistory(ctx *fiber.Ctx) error {
|
||||
var req GetQuizHistoryReq
|
||||
if err := ctx.BodyParser(&req); err != nil {
|
||||
return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data")
|
||||
@ -383,7 +400,7 @@ func (s *Service) GetQuizHistory(ctx *fiber.Ctx) error {
|
||||
return ctx.Status(fiber.StatusFailedDependency).SendString("no id provided")
|
||||
}
|
||||
|
||||
history, err := s.dal.QuizRepo.QuizHistory(ctx.Context(), quiz.QuizHistoryDeps{
|
||||
history, err := r.dal.QuizRepo.QuizHistory(ctx.Context(), quiz.QuizHistoryDeps{
|
||||
Id: req.Id,
|
||||
Limit: req.Limit,
|
||||
Offset: req.Page * req.Limit,
|
||||
@ -406,7 +423,7 @@ type DeactivateResp struct {
|
||||
}
|
||||
|
||||
// DeleteQuiz handler for fake delete quiz
|
||||
func (s *Service) DeleteQuiz(ctx *fiber.Ctx) error {
|
||||
func (r *Quiz) DeleteQuiz(ctx *fiber.Ctx) error {
|
||||
var req DeactivateReq
|
||||
if err := ctx.BodyParser(&req); err != nil {
|
||||
return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data")
|
||||
@ -422,7 +439,7 @@ func (s *Service) DeleteQuiz(ctx *fiber.Ctx) error {
|
||||
return ctx.Status(fiber.StatusFailedDependency).SendString("id for deleting is required")
|
||||
}
|
||||
|
||||
deleted, err := s.dal.QuizRepo.DeleteQuiz(ctx.Context(), accountId, req.Id)
|
||||
deleted, err := r.dal.QuizRepo.DeleteQuiz(ctx.Context(), accountId, req.Id)
|
||||
if err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
||||
}
|
||||
@ -438,7 +455,7 @@ func (s *Service) DeleteQuiz(ctx *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
// ArchiveQuiz handler for archiving quiz
|
||||
func (s *Service) ArchiveQuiz(ctx *fiber.Ctx) error {
|
||||
func (r *Quiz) ArchiveQuiz(ctx *fiber.Ctx) error {
|
||||
var req DeactivateReq
|
||||
if err := ctx.BodyParser(&req); err != nil {
|
||||
return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data")
|
||||
@ -453,7 +470,7 @@ func (s *Service) ArchiveQuiz(ctx *fiber.Ctx) error {
|
||||
return ctx.Status(fiber.StatusFailedDependency).SendString("id for archive quiz is required")
|
||||
}
|
||||
|
||||
archived, err := s.dal.QuizRepo.DeleteQuiz(ctx.Context(), accountId, req.Id)
|
||||
archived, err := r.dal.QuizRepo.DeleteQuiz(ctx.Context(), accountId, req.Id)
|
||||
if err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
||||
}
|
||||
@ -467,7 +484,7 @@ type QuizMoveReq struct {
|
||||
Qid, AccountID string
|
||||
}
|
||||
|
||||
func (s *Service) QuizMove(ctx *fiber.Ctx) error {
|
||||
func (r *Quiz) QuizMove(ctx *fiber.Ctx) error {
|
||||
var req QuizMoveReq
|
||||
if err := ctx.BodyParser(&req); err != nil {
|
||||
return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data")
|
||||
@ -477,7 +494,7 @@ func (s *Service) QuizMove(ctx *fiber.Ctx) error {
|
||||
return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request qid and accountID is required")
|
||||
}
|
||||
|
||||
resp, err := s.dal.QuizRepo.QuizMove(ctx.Context(), req.Qid, req.AccountID)
|
||||
resp, err := r.dal.QuizRepo.QuizMove(ctx.Context(), req.Qid, req.AccountID)
|
||||
if err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
||||
}
|
||||
@ -485,7 +502,7 @@ func (s *Service) QuizMove(ctx *fiber.Ctx) error {
|
||||
return ctx.Status(fiber.StatusOK).JSON(resp)
|
||||
}
|
||||
|
||||
func (s *Service) TemplateCopy(ctx *fiber.Ctx) error {
|
||||
func (r *Quiz) TemplateCopy(ctx *fiber.Ctx) error {
|
||||
accountID, ok := middleware.GetAccountId(ctx)
|
||||
if !ok {
|
||||
return ctx.Status(fiber.StatusUnauthorized).SendString("account id is required")
|
||||
@ -504,7 +521,7 @@ func (s *Service) TemplateCopy(ctx *fiber.Ctx) error {
|
||||
return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request qid is required")
|
||||
}
|
||||
|
||||
qizID, err := s.dal.QuizRepo.TemplateCopy(ctx.Context(), accountID, req.Qid)
|
||||
qizID, err := r.dal.QuizRepo.TemplateCopy(ctx.Context(), accountID, req.Qid)
|
||||
if err != nil {
|
||||
fmt.Println("TEMPLERR", err)
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
19
internal/controllers/http_controllers/quiz/route.go
Normal file
19
internal/controllers/http_controllers/quiz/route.go
Normal file
@ -0,0 +1,19 @@
|
||||
package quiz
|
||||
|
||||
import "github.com/gofiber/fiber/v2"
|
||||
|
||||
func (r *Quiz) Register(router fiber.Router) {
|
||||
router.Post("/create", r.CreateQuiz)
|
||||
router.Post("/getList", r.GetQuizList)
|
||||
router.Patch("/edit", r.UpdateQuiz)
|
||||
router.Post("/copy", r.CopyQuiz)
|
||||
router.Post("/history", r.GetQuizHistory)
|
||||
router.Delete("/delete", r.DeleteQuiz)
|
||||
router.Patch("/archive", r.ArchiveQuiz)
|
||||
router.Post("/move", r.QuizMove)
|
||||
router.Post("/template", r.TemplateCopy)
|
||||
}
|
||||
|
||||
func (r *Quiz) Name() string {
|
||||
return "quiz"
|
||||
}
|
@ -1,16 +1,34 @@
|
||||
package service
|
||||
package result
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/dal"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/middleware"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/model"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/repository/result"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/tools"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/tools"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Deps struct {
|
||||
DAL *dal.DAL
|
||||
S3Prefix string
|
||||
}
|
||||
|
||||
type Result struct {
|
||||
dal *dal.DAL
|
||||
s3Prefix string
|
||||
}
|
||||
|
||||
func NewResultController(deps Deps) *Result {
|
||||
return &Result{
|
||||
dal: deps.DAL,
|
||||
s3Prefix: deps.S3Prefix,
|
||||
}
|
||||
}
|
||||
|
||||
type ReqExport struct {
|
||||
To, From time.Time
|
||||
New bool
|
||||
@ -23,7 +41,7 @@ type ReqExportResponse struct {
|
||||
Results []model.AnswerExport `json:"results"`
|
||||
}
|
||||
|
||||
func (s *Service) GetResultsByQuizID(ctx *fiber.Ctx) error {
|
||||
func (r *Result) GetResultsByQuizID(ctx *fiber.Ctx) error {
|
||||
payment := true // параметр для определения существования текущих привилегий юзера
|
||||
|
||||
accountID, ok := middleware.GetAccountId(ctx)
|
||||
@ -42,7 +60,7 @@ func (s *Service) GetResultsByQuizID(ctx *fiber.Ctx) error {
|
||||
return ctx.Status(fiber.StatusBadRequest).SendString("Invalid quiz ID format")
|
||||
}
|
||||
|
||||
account, err := s.dal.AccountRepo.GetAccountByID(ctx.Context(), accountID)
|
||||
account, err := r.dal.AccountRepo.GetAccountByID(ctx.Context(), accountID)
|
||||
if err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
||||
}
|
||||
@ -52,7 +70,7 @@ func (s *Service) GetResultsByQuizID(ctx *fiber.Ctx) error {
|
||||
}
|
||||
}
|
||||
|
||||
results, totalCount, err := s.dal.ResultRepo.GetQuizResults(ctx.Context(), quizID, result.GetQuizResDeps{
|
||||
results, totalCount, err := r.dal.ResultRepo.GetQuizResults(ctx.Context(), quizID, result.GetQuizResDeps{
|
||||
To: req.To,
|
||||
From: req.From,
|
||||
New: req.New,
|
||||
@ -71,7 +89,7 @@ func (s *Service) GetResultsByQuizID(ctx *fiber.Ctx) error {
|
||||
return ctx.Status(fiber.StatusOK).JSON(resp)
|
||||
}
|
||||
|
||||
func (s *Service) DelResultByID(ctx *fiber.Ctx) error {
|
||||
func (r *Result) DelResultByID(ctx *fiber.Ctx) error {
|
||||
accountID, ok := middleware.GetAccountId(ctx)
|
||||
if !ok {
|
||||
return ctx.Status(fiber.StatusUnauthorized).SendString("could not get account ID from token")
|
||||
@ -83,7 +101,7 @@ func (s *Service) DelResultByID(ctx *fiber.Ctx) error {
|
||||
return ctx.Status(fiber.StatusBadRequest).SendString("Invalid result ID format")
|
||||
}
|
||||
|
||||
isOwner, err := s.dal.ResultRepo.CheckResultOwner(ctx.Context(), resultID, accountID)
|
||||
isOwner, err := r.dal.ResultRepo.CheckResultOwner(ctx.Context(), resultID, accountID)
|
||||
if err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
||||
}
|
||||
@ -92,7 +110,7 @@ func (s *Service) DelResultByID(ctx *fiber.Ctx) error {
|
||||
return ctx.Status(fiber.StatusUnauthorized).SendString("not the owner of the result")
|
||||
}
|
||||
|
||||
if err := s.dal.ResultRepo.SoftDeleteResultByID(ctx.Context(), resultID); err != nil {
|
||||
if err := r.dal.ResultRepo.SoftDeleteResultByID(ctx.Context(), resultID); err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
||||
}
|
||||
|
||||
@ -104,7 +122,7 @@ type ReqSeen struct {
|
||||
Answers []int64
|
||||
}
|
||||
|
||||
func (s *Service) SetStatus(ctx *fiber.Ctx) error {
|
||||
func (r *Result) SetStatus(ctx *fiber.Ctx) error {
|
||||
var req ReqSeen
|
||||
if err := ctx.BodyParser(&req); err != nil {
|
||||
return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data")
|
||||
@ -115,7 +133,7 @@ func (s *Service) SetStatus(ctx *fiber.Ctx) error {
|
||||
return ctx.Status(fiber.StatusUnauthorized).SendString("could not get account ID from token")
|
||||
}
|
||||
|
||||
answers, err := s.dal.ResultRepo.CheckResultsOwner(ctx.Context(), req.Answers, accountID)
|
||||
answers, err := r.dal.ResultRepo.CheckResultsOwner(ctx.Context(), req.Answers, accountID)
|
||||
if err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
||||
}
|
||||
@ -124,14 +142,14 @@ func (s *Service) SetStatus(ctx *fiber.Ctx) error {
|
||||
return ctx.Status(fiber.StatusNotAcceptable).SendString("could not update some answers because you don't have rights")
|
||||
}
|
||||
|
||||
if err := s.dal.ResultRepo.UpdateAnswersStatus(ctx.Context(), accountID, answers); err != nil {
|
||||
if err := r.dal.ResultRepo.UpdateAnswersStatus(ctx.Context(), accountID, answers); err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
||||
}
|
||||
|
||||
return ctx.Status(fiber.StatusOK).JSON(nil)
|
||||
}
|
||||
|
||||
func (s *Service) ExportResultsToCSV(ctx *fiber.Ctx) error {
|
||||
func (r *Result) ExportResultsToCSV(ctx *fiber.Ctx) error {
|
||||
accountID, ok := middleware.GetAccountId(ctx)
|
||||
if !ok {
|
||||
return ctx.Status(fiber.StatusUnauthorized).SendString("account id is required")
|
||||
@ -148,7 +166,7 @@ func (s *Service) ExportResultsToCSV(ctx *fiber.Ctx) error {
|
||||
return ctx.Status(fiber.StatusBadRequest).SendString("invalid request body")
|
||||
}
|
||||
|
||||
account, err := s.dal.AccountRepo.GetAccountByID(ctx.Context(), accountID)
|
||||
account, err := r.dal.AccountRepo.GetAccountByID(ctx.Context(), accountID)
|
||||
if err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
||||
}
|
||||
@ -159,17 +177,17 @@ func (s *Service) ExportResultsToCSV(ctx *fiber.Ctx) error {
|
||||
}
|
||||
}
|
||||
|
||||
quiz, err := s.dal.QuizRepo.GetQuizById(ctx.Context(), accountID, quizID)
|
||||
quiz, err := r.dal.QuizRepo.GetQuizById(ctx.Context(), accountID, quizID)
|
||||
if err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString("failed to get quiz")
|
||||
}
|
||||
|
||||
questions, err := s.dal.ResultRepo.GetQuestions(ctx.Context(), quizID)
|
||||
questions, err := r.dal.ResultRepo.GetQuestions(ctx.Context(), quizID)
|
||||
if err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString("failed to get questions")
|
||||
}
|
||||
|
||||
answers, err := s.dal.ResultRepo.GetQuizResultsCSV(ctx.Context(), quizID, result.GetQuizResDeps{
|
||||
answers, err := r.dal.ResultRepo.GetQuizResultsCSV(ctx.Context(), quizID, result.GetQuizResDeps{
|
||||
To: req.To,
|
||||
From: req.From,
|
||||
New: req.New,
|
||||
@ -182,7 +200,7 @@ func (s *Service) ExportResultsToCSV(ctx *fiber.Ctx) error {
|
||||
|
||||
buffer := new(bytes.Buffer)
|
||||
|
||||
if err := tools.WriteDataToExcel(buffer, questions, answers, s.s3Prefix + quiz.Qid + "/"); err != nil {
|
||||
if err := tools.WriteDataToExcel(buffer, questions, answers, r.s3Prefix+quiz.Qid+"/"); err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString("failed to write data to Excel")
|
||||
}
|
||||
|
||||
@ -192,7 +210,7 @@ func (s *Service) ExportResultsToCSV(ctx *fiber.Ctx) error {
|
||||
return ctx.Send(buffer.Bytes())
|
||||
}
|
||||
|
||||
func (s *Service) GetResultAnswers(ctx *fiber.Ctx) error {
|
||||
func (r *Result) GetResultAnswers(ctx *fiber.Ctx) error {
|
||||
accountID, ok := middleware.GetAccountId(ctx)
|
||||
if !ok {
|
||||
return ctx.Status(fiber.StatusUnauthorized).SendString("account id is required")
|
||||
@ -203,7 +221,7 @@ func (s *Service) GetResultAnswers(ctx *fiber.Ctx) error {
|
||||
return ctx.Status(fiber.StatusBadRequest).SendString("invalid quiz ID")
|
||||
}
|
||||
|
||||
account, err := s.dal.AccountRepo.GetAccountByID(ctx.Context(), accountID)
|
||||
account, err := r.dal.AccountRepo.GetAccountByID(ctx.Context(), accountID)
|
||||
if err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
||||
}
|
||||
@ -214,11 +232,11 @@ func (s *Service) GetResultAnswers(ctx *fiber.Ctx) error {
|
||||
}
|
||||
}
|
||||
|
||||
answers, err := s.dal.ResultRepo.GetResultAnswers(ctx.Context(), resultID)
|
||||
answers, err := r.dal.ResultRepo.GetResultAnswers(ctx.Context(), resultID)
|
||||
if err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString("failed to get result answers")
|
||||
}
|
||||
sortedAnswers, err := s.dal.QuestionRepo.ForSortingResults(ctx.Context(), answers)
|
||||
sortedAnswers, err := r.dal.QuestionRepo.ForSortingResults(ctx.Context(), answers)
|
||||
if err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString("failed sort result answers")
|
||||
}
|
15
internal/controllers/http_controllers/result/route.go
Normal file
15
internal/controllers/http_controllers/result/route.go
Normal file
@ -0,0 +1,15 @@
|
||||
package result
|
||||
|
||||
import "github.com/gofiber/fiber/v2"
|
||||
|
||||
func (r *Result) Register(router fiber.Router) {
|
||||
router.Post("/results/getResults/:quizId", r.GetResultsByQuizID)
|
||||
router.Delete("/results/delete/:resultId", r.DelResultByID)
|
||||
router.Patch("/result/seen", r.SetStatus)
|
||||
router.Post("/results/:quizID/export", r.ExportResultsToCSV)
|
||||
router.Get("/result/:resultID", r.GetResultAnswers)
|
||||
}
|
||||
|
||||
func (r *Result) Name() string {
|
||||
return ""
|
||||
}
|
15
internal/controllers/http_controllers/statistic/route.go
Normal file
15
internal/controllers/http_controllers/statistic/route.go
Normal file
@ -0,0 +1,15 @@
|
||||
package statistic
|
||||
|
||||
import "github.com/gofiber/fiber/v2"
|
||||
|
||||
func (r *Statistic) Register(router fiber.Router) {
|
||||
router.Post("/statistic/:quizID/devices", r.GetDeviceStatistics)
|
||||
router.Post("/statistic/:quizID/general", r.GetGeneralStatistics)
|
||||
router.Post("/statistic/:quizID/questions", r.GetQuestionsStatistics)
|
||||
router.Post("/statistic", r.AllServiceStatistics)
|
||||
router.Get("/statistics/:quizID/pipelines", r.GetPipelinesStatistics)
|
||||
}
|
||||
|
||||
func (r *Statistic) Name() string {
|
||||
return ""
|
||||
}
|
@ -1,17 +1,35 @@
|
||||
package service
|
||||
package statistic
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/dal"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/repository/statistics"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type Deps struct {
|
||||
DAL *dal.DAL
|
||||
ChDAL *dal.ClickHouseDAL
|
||||
}
|
||||
|
||||
type Statistic struct {
|
||||
dal *dal.DAL
|
||||
chDAL *dal.ClickHouseDAL
|
||||
}
|
||||
|
||||
func NewStatisticController(deps Deps) *Statistic {
|
||||
return &Statistic{
|
||||
dal: deps.DAL,
|
||||
chDAL: deps.ChDAL,
|
||||
}
|
||||
}
|
||||
|
||||
type DeviceStatReq struct {
|
||||
From uint64 // временные границы выбора статистики
|
||||
To uint64
|
||||
}
|
||||
|
||||
func (s *Service) GetDeviceStatistics(ctx *fiber.Ctx) error {
|
||||
func (r *Statistic) GetDeviceStatistics(ctx *fiber.Ctx) error {
|
||||
quizIDStr := ctx.Params("quizID")
|
||||
|
||||
quizID, err := strconv.ParseInt(quizIDStr, 10, 64)
|
||||
@ -24,7 +42,7 @@ func (s *Service) GetDeviceStatistics(ctx *fiber.Ctx) error {
|
||||
return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data")
|
||||
}
|
||||
|
||||
deviceStats, err := s.dal.StatisticsRepo.GetDeviceStatistics(ctx.Context(), statistics.DeviceStatReq{
|
||||
deviceStats, err := r.dal.StatisticsRepo.GetDeviceStatistics(ctx.Context(), statistics.DeviceStatReq{
|
||||
QuizId: quizID,
|
||||
From: req.From,
|
||||
To: req.To,
|
||||
@ -40,7 +58,7 @@ type GeneralStatsResp struct {
|
||||
Open, Result, AvTime, Conversion map[uint64]uint64
|
||||
}
|
||||
|
||||
func (s *Service) GetGeneralStatistics(ctx *fiber.Ctx) error {
|
||||
func (r *Statistic) GetGeneralStatistics(ctx *fiber.Ctx) error {
|
||||
quizIDStr := ctx.Params("quizID")
|
||||
quizID, err := strconv.ParseInt(quizIDStr, 10, 64)
|
||||
if err != nil {
|
||||
@ -52,7 +70,7 @@ func (s *Service) GetGeneralStatistics(ctx *fiber.Ctx) error {
|
||||
return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data")
|
||||
}
|
||||
|
||||
generalStats, err := s.dal.StatisticsRepo.GetGeneralStatistics(ctx.Context(), statistics.DeviceStatReq{
|
||||
generalStats, err := r.dal.StatisticsRepo.GetGeneralStatistics(ctx.Context(), statistics.DeviceStatReq{
|
||||
QuizId: quizID,
|
||||
From: req.From,
|
||||
To: req.To,
|
||||
@ -64,7 +82,7 @@ func (s *Service) GetGeneralStatistics(ctx *fiber.Ctx) error {
|
||||
return ctx.Status(fiber.StatusOK).JSON(generalStats)
|
||||
}
|
||||
|
||||
func (s *Service) GetQuestionsStatistics(ctx *fiber.Ctx) error {
|
||||
func (r *Statistic) GetQuestionsStatistics(ctx *fiber.Ctx) error {
|
||||
quizIDStr := ctx.Params("quizID")
|
||||
quizID, err := strconv.ParseInt(quizIDStr, 0, 64)
|
||||
if err != nil {
|
||||
@ -76,7 +94,7 @@ func (s *Service) GetQuestionsStatistics(ctx *fiber.Ctx) error {
|
||||
return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data")
|
||||
}
|
||||
|
||||
questionsStats, err := s.dal.StatisticsRepo.GetQuestionsStatistics(ctx.Context(), statistics.DeviceStatReq{
|
||||
questionsStats, err := r.dal.StatisticsRepo.GetQuestionsStatistics(ctx.Context(), statistics.DeviceStatReq{
|
||||
QuizId: quizID,
|
||||
From: req.From,
|
||||
To: req.To,
|
||||
@ -92,13 +110,13 @@ type StatisticReq struct {
|
||||
From, To uint64 // временные границы выбора статистики
|
||||
}
|
||||
|
||||
func (s *Service) AllServiceStatistics(ctx *fiber.Ctx) error {
|
||||
func (r *Statistic) AllServiceStatistics(ctx *fiber.Ctx) error {
|
||||
var req StatisticReq
|
||||
if err := ctx.BodyParser(&req); err != nil {
|
||||
return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data")
|
||||
}
|
||||
|
||||
allSvcStats, err := s.dal.StatisticsRepo.AllServiceStatistics(ctx.Context(), req.From, req.To)
|
||||
allSvcStats, err := r.dal.StatisticsRepo.AllServiceStatistics(ctx.Context(), req.From, req.To)
|
||||
if err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
||||
}
|
||||
@ -106,7 +124,7 @@ func (s *Service) AllServiceStatistics(ctx *fiber.Ctx) error {
|
||||
return ctx.Status(fiber.StatusOK).JSON(allSvcStats)
|
||||
}
|
||||
|
||||
func (s *Service) GetPipelinesStatistics(ctx *fiber.Ctx) error {
|
||||
func (r *Statistic) GetPipelinesStatistics(ctx *fiber.Ctx) error {
|
||||
var req StatisticReq
|
||||
if err := ctx.BodyParser(&req); err != nil {
|
||||
return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data")
|
||||
@ -118,7 +136,7 @@ func (s *Service) GetPipelinesStatistics(ctx *fiber.Ctx) error {
|
||||
return ctx.Status(fiber.StatusBadRequest).SendString("Invalid quiz ID format")
|
||||
}
|
||||
|
||||
result, err := s.chDAL.StatisticClickRepo.GetPipelinesStatistics(ctx.Context(), quizID, req.From, req.To)
|
||||
result, err := r.chDAL.StatisticClickRepo.GetPipelinesStatistics(ctx.Context(), quizID, req.From, req.To)
|
||||
if err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
||||
}
|
14
internal/controllers/http_controllers/telegram/route.go
Normal file
14
internal/controllers/http_controllers/telegram/route.go
Normal file
@ -0,0 +1,14 @@
|
||||
package telegram
|
||||
|
||||
import "github.com/gofiber/fiber/v2"
|
||||
|
||||
func (r *Telegram) Register(router fiber.Router) {
|
||||
router.Get("/pool", r.GetPoolTgAccounts)
|
||||
router.Post("/create", r.AddingTgAccount)
|
||||
router.Delete("/:id", r.DeleteTgAccountByID)
|
||||
router.Post("/setCode", r.SettingTgCode)
|
||||
}
|
||||
|
||||
func (r *Telegram) Name() string {
|
||||
return "telegram"
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package service
|
||||
package telegram
|
||||
|
||||
import (
|
||||
"errors"
|
||||
@ -6,20 +6,38 @@ import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/rs/xid"
|
||||
"path/filepath"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/dal"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/model"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/pj_errors"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/clients/telegram"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/clients/telegram"
|
||||
"penahub.gitlab.yandexcloud.net/backend/tdlib/client"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type Deps struct {
|
||||
DAL *dal.DAL
|
||||
TelegramClient *telegram.TelegramClient
|
||||
}
|
||||
|
||||
type Telegram struct {
|
||||
dal *dal.DAL
|
||||
telegramClient *telegram.TelegramClient
|
||||
}
|
||||
|
||||
func NewTelegramController(deps Deps) *Telegram {
|
||||
return &Telegram{
|
||||
dal: deps.DAL,
|
||||
telegramClient: deps.TelegramClient,
|
||||
}
|
||||
}
|
||||
|
||||
type Message struct {
|
||||
Type string `json:"type"`
|
||||
Data string `json:"data"`
|
||||
}
|
||||
|
||||
func (s *Service) GetPoolTgAccounts(ctx *fiber.Ctx) error {
|
||||
allAccounts, err := s.dal.TgRepo.GetAllTgAccounts(ctx.Context())
|
||||
func (r *Telegram) GetPoolTgAccounts(ctx *fiber.Ctx) error {
|
||||
allAccounts, err := r.dal.TgRepo.GetAllTgAccounts(ctx.Context())
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, pj_errors.ErrNotFound):
|
||||
@ -31,7 +49,7 @@ func (s *Service) GetPoolTgAccounts(ctx *fiber.Ctx) error {
|
||||
return ctx.Status(fiber.StatusOK).JSON(allAccounts)
|
||||
}
|
||||
|
||||
func (s *Service) AddingTgAccount(ctx *fiber.Ctx) error {
|
||||
func (r *Telegram) AddingTgAccount(ctx *fiber.Ctx) error {
|
||||
var req telegram.AuthTgUserReq
|
||||
if err := ctx.BodyParser(&req); err != nil {
|
||||
return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data")
|
||||
@ -39,7 +57,7 @@ func (s *Service) AddingTgAccount(ctx *fiber.Ctx) error {
|
||||
if req.ApiID == 0 || req.ApiHash == "" || req.Password == "" || req.PhoneNumber == "" {
|
||||
return ctx.Status(fiber.StatusBadRequest).SendString("empty required fields")
|
||||
}
|
||||
allAccounts, err := s.dal.TgRepo.GetAllTgAccounts(ctx.Context())
|
||||
allAccounts, err := r.dal.TgRepo.GetAllTgAccounts(ctx.Context())
|
||||
if err != nil && !errors.Is(err, pj_errors.ErrNotFound) {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
||||
}
|
||||
@ -84,7 +102,7 @@ func (s *Service) AddingTgAccount(ctx *fiber.Ctx) error {
|
||||
fmt.Println("new client failed", err)
|
||||
return
|
||||
}
|
||||
s.telegramClient.SaveTgAccount(req.ApiID, req.ApiHash, tdlibClient)
|
||||
r.telegramClient.SaveTgAccount(req.ApiID, req.ApiHash, tdlibClient)
|
||||
fmt.Println("i am down")
|
||||
}()
|
||||
if goErr != nil {
|
||||
@ -102,7 +120,7 @@ func (s *Service) AddingTgAccount(ctx *fiber.Ctx) error {
|
||||
authorizer.PhoneNumber <- req.PhoneNumber
|
||||
case client.TypeAuthorizationStateWaitCode:
|
||||
signature := xid.New()
|
||||
s.telegramClient.AddedToMap(telegram.WaitingClient{
|
||||
r.telegramClient.AddedToMap(telegram.WaitingClient{
|
||||
PreviousReq: req,
|
||||
Authorizer: authorizer,
|
||||
}, signature.String())
|
||||
@ -114,7 +132,7 @@ func (s *Service) AddingTgAccount(ctx *fiber.Ctx) error {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) SettingTgCode(ctx *fiber.Ctx) error {
|
||||
func (r *Telegram) SettingTgCode(ctx *fiber.Ctx) error {
|
||||
var req struct {
|
||||
Code string `json:"code"`
|
||||
Signature string `json:"signature"`
|
||||
@ -127,7 +145,7 @@ func (s *Service) SettingTgCode(ctx *fiber.Ctx) error {
|
||||
return ctx.Status(fiber.StatusBadRequest).SendString("empty required fields")
|
||||
}
|
||||
|
||||
data, ok := s.telegramClient.GetFromMap(req.Signature)
|
||||
data, ok := r.telegramClient.GetFromMap(req.Signature)
|
||||
if !ok {
|
||||
return ctx.Status(fiber.StatusBadRequest).SendString("Invalid id, don't have data")
|
||||
}
|
||||
@ -140,7 +158,7 @@ func (s *Service) SettingTgCode(ctx *fiber.Ctx) error {
|
||||
fmt.Println("currnet state:", state)
|
||||
switch state.AuthorizationStateType() {
|
||||
case client.TypeAuthorizationStateReady:
|
||||
id, err := s.dal.TgRepo.CreateTgAccount(ctx.Context(), model.TgAccount{
|
||||
id, err := r.dal.TgRepo.CreateTgAccount(ctx.Context(), model.TgAccount{
|
||||
ApiID: data.PreviousReq.ApiID,
|
||||
ApiHash: data.PreviousReq.ApiHash,
|
||||
PhoneNumber: data.PreviousReq.PhoneNumber,
|
||||
@ -159,12 +177,12 @@ func (s *Service) SettingTgCode(ctx *fiber.Ctx) error {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) DeleteTgAccountByID(ctx *fiber.Ctx) error {
|
||||
func (r *Telegram) DeleteTgAccountByID(ctx *fiber.Ctx) error {
|
||||
id, err := strconv.ParseInt(ctx.Params("id"), 10, 64)
|
||||
if err != nil {
|
||||
return ctx.Status(fiber.StatusBadRequest).SendString("invalid id format")
|
||||
}
|
||||
err = s.dal.TgRepo.SoftDeleteTgAccount(ctx.Context(), id)
|
||||
err = r.dal.TgRepo.SoftDeleteTgAccount(ctx.Context(), id)
|
||||
if err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
||||
}
|
@ -1,13 +1,13 @@
|
||||
package rpc_service
|
||||
package rpc_controllers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/dal"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/proto/notifyer"
|
||||
notifyer2 "penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/proto/notifyer"
|
||||
)
|
||||
|
||||
type MailNotify struct {
|
||||
notifyer.UnimplementedQuizServiceServer
|
||||
notifyer2.UnimplementedQuizServiceServer
|
||||
dal *dal.DAL
|
||||
}
|
||||
|
||||
@ -17,23 +17,23 @@ func NewMailNotify(dal *dal.DAL) *MailNotify {
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MailNotify) GetQuizzes(ctx context.Context, in *notifyer.GetQuizzesRequest) (*notifyer.GetQuizzesResponse, error) {
|
||||
func (m *MailNotify) GetQuizzes(ctx context.Context, in *notifyer2.GetQuizzesRequest) (*notifyer2.GetQuizzesResponse, error) {
|
||||
ids, err := m.dal.QuizRepo.GetAllQuizzesID(ctx, in.AccountId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp := ¬ifyer.GetQuizzesResponse{
|
||||
resp := ¬ifyer2.GetQuizzesResponse{
|
||||
QuizIds: ids,
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (m *MailNotify) GetStartedQuizzes(ctx context.Context, in *notifyer.GetStartedQuizzesRequest) (*notifyer.GetStartedQuizzesResponse, error) {
|
||||
func (m *MailNotify) GetStartedQuizzes(ctx context.Context, in *notifyer2.GetStartedQuizzesRequest) (*notifyer2.GetStartedQuizzesResponse, error) {
|
||||
ids, err := m.dal.QuizRepo.GetStartedQuizzesID(ctx, in.AccountId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp := ¬ifyer.GetStartedQuizzesResponse{
|
||||
resp := ¬ifyer2.GetStartedQuizzesResponse{
|
||||
QuizIds: ids,
|
||||
}
|
||||
return resp, nil
|
25
internal/initialize/clients.go
Normal file
25
internal/initialize/clients.go
Normal file
@ -0,0 +1,25 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"context"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/dal"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/clients/auth"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/clients/telegram"
|
||||
)
|
||||
|
||||
type Clients struct {
|
||||
AuthClient *auth.AuthClient
|
||||
TgClient *telegram.TelegramClient
|
||||
}
|
||||
|
||||
func NewClients(ctx context.Context, cfg Config, pgDAL *dal.DAL) (*Clients, error) {
|
||||
tgClient, err := telegram.NewTelegramClient(ctx, pgDAL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Clients{
|
||||
TgClient: tgClient,
|
||||
AuthClient: auth.NewAuthClient(cfg.AuthServiceURL),
|
||||
}, nil
|
||||
}
|
43
internal/initialize/config.go
Normal file
43
internal/initialize/config.go
Normal file
@ -0,0 +1,43 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"github.com/caarlos0/env/v8"
|
||||
"github.com/joho/godotenv"
|
||||
"log"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
LoggerProdMode bool `env:"IS_PROD_LOG" envDefault:"false"`
|
||||
IsProd bool `env:"IS_PROD" envDefault:"false"`
|
||||
NumberPort string `env:"PORT" envDefault:"1488"`
|
||||
HttpHost string `env:"HTTP_HOST" envDefault:"0.0.0.0"`
|
||||
CrtFile string `env:"CRT" envDefault:"server.crt"`
|
||||
KeyFile string `env:"KEY" envDefault:"server.key"`
|
||||
PostgresCredentials string `env:"PG_CRED" envDefault:"host=localhost port=35432 user=squiz password=Redalert2 dbname=squiz sslmode=disable"`
|
||||
HubAdminUrl string `env:"HUB_ADMIN_URL" envDefault:"http://localhost:8001/"`
|
||||
ServiceName string `env:"SERVICE_NAME" envDefault:"squiz"`
|
||||
AuthServiceURL string `env:"AUTH_URL" envDefault:"http://localhost:8000/"`
|
||||
GrpcHost string `env:"GRPC_HOST" envDefault:"localhost"`
|
||||
GrpcPort string `env:"GRPC_PORT" envDefault:"9000"`
|
||||
KafkaBrokers string `env:"KAFKA_BROKERS" envDefault:"localhost:9092"`
|
||||
KafkaTopic string `env:"KAFKA_TOPIC" envDefault:"test-topic"`
|
||||
KafkaGroup string `env:"KAFKA_GROUP" envDefault:"mailnotifier"`
|
||||
TrashLogHost string `env:"TRASH_LOG_HOST" envDefault:"localhost:7113"`
|
||||
ModuleLogger string `env:"MODULE_LOGGER" envDefault:"core-local"`
|
||||
ClickHouseCred string `env:"CLICK_HOUSE_CRED" envDefault:"tcp://10.8.0.15:9000/default?sslmode=disable"`
|
||||
RedisHost string `env:"REDIS_HOST" envDefault:"localhost:6379"`
|
||||
RedisPassword string `env:"REDIS_PASSWORD" envDefault:"admin"`
|
||||
RedisDB uint64 `env:"REDIS_DB" envDefault:"2"`
|
||||
S3Prefix string `env:"S3_PREFIX"`
|
||||
}
|
||||
|
||||
func LoadConfig() (*Config, error) {
|
||||
if err := godotenv.Load(); err != nil {
|
||||
log.Print("No .env file found")
|
||||
}
|
||||
var config Config
|
||||
if err := env.Parse(&config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &config, nil
|
||||
}
|
73
internal/initialize/controllers.go
Normal file
73
internal/initialize/controllers.go
Normal file
@ -0,0 +1,73 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"github.com/go-redis/redis/v8"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/brokers"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/controllers/http_controllers/account"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/controllers/http_controllers/question"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/controllers/http_controllers/quiz"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/controllers/http_controllers/result"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/controllers/http_controllers/statistic"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/controllers/http_controllers/telegram"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/controllers/rpc_controllers"
|
||||
)
|
||||
|
||||
type ControllerDeps struct {
|
||||
Clients *Clients
|
||||
DALs *DALs
|
||||
Config Config
|
||||
Producer *brokers.Producer
|
||||
RedisClient *redis.Client
|
||||
}
|
||||
|
||||
type Controller struct {
|
||||
GRpcControllers GRpcControllers
|
||||
HttpControllers HttpControllers
|
||||
}
|
||||
|
||||
type GRpcControllers struct {
|
||||
MailNotify *rpc_controllers.MailNotify
|
||||
}
|
||||
type HttpControllers struct {
|
||||
Account *account.Account
|
||||
Question *question.Question
|
||||
Quiz *quiz.Quiz
|
||||
Result *result.Result
|
||||
Statistic *statistic.Statistic
|
||||
Telegram *telegram.Telegram
|
||||
}
|
||||
|
||||
func NewControllers(deps ControllerDeps) *Controller {
|
||||
return &Controller{
|
||||
GRpcControllers: GRpcControllers{
|
||||
MailNotify: rpc_controllers.NewMailNotify(deps.DALs.PgDAL),
|
||||
},
|
||||
HttpControllers: HttpControllers{
|
||||
Account: account.NewAccountController(account.Deps{
|
||||
Dal: deps.DALs.PgDAL,
|
||||
AuthClient: deps.Clients.AuthClient,
|
||||
Producer: deps.Producer,
|
||||
ServiceName: deps.Config.ServiceName,
|
||||
RedisClient: deps.RedisClient,
|
||||
}),
|
||||
Question: question.NewQuestionController(question.Deps{
|
||||
DAL: deps.DALs.PgDAL,
|
||||
}),
|
||||
Quiz: quiz.NewQuizController(quiz.Deps{
|
||||
DAL: deps.DALs.PgDAL,
|
||||
}),
|
||||
Result: result.NewResultController(result.Deps{
|
||||
DAL: deps.DALs.PgDAL,
|
||||
S3Prefix: deps.Config.S3Prefix,
|
||||
}),
|
||||
Statistic: statistic.NewStatisticController(statistic.Deps{
|
||||
DAL: deps.DALs.PgDAL,
|
||||
ChDAL: deps.DALs.ChDAL,
|
||||
}),
|
||||
Telegram: telegram.NewTelegramController(telegram.Deps{
|
||||
DAL: deps.DALs.PgDAL,
|
||||
TelegramClient: deps.Clients.TgClient,
|
||||
}),
|
||||
},
|
||||
}
|
||||
}
|
28
internal/initialize/dals.go
Normal file
28
internal/initialize/dals.go
Normal file
@ -0,0 +1,28 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"context"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/dal"
|
||||
)
|
||||
|
||||
type DALs struct {
|
||||
PgDAL *dal.DAL
|
||||
ChDAL *dal.ClickHouseDAL
|
||||
}
|
||||
|
||||
func NewDALs(ctx context.Context, cfg Config) (*DALs, error) {
|
||||
pgDal, err := dal.New(ctx, cfg.PostgresCredentials, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
chDal, err := dal.NewClickHouseDAL(ctx, cfg.ClickHouseCred)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &DALs{
|
||||
PgDAL: pgDal,
|
||||
ChDAL: chDal,
|
||||
}, nil
|
||||
}
|
21
internal/initialize/redis.go
Normal file
21
internal/initialize/redis.go
Normal file
@ -0,0 +1,21 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/go-redis/redis/v8"
|
||||
)
|
||||
|
||||
func Redis(ctx context.Context, cfg Config) (*redis.Client, error) {
|
||||
rdb := redis.NewClient(&redis.Options{
|
||||
Addr: cfg.RedisHost,
|
||||
Password: cfg.RedisPassword,
|
||||
DB: int(cfg.RedisDB),
|
||||
})
|
||||
|
||||
status := rdb.Ping(ctx)
|
||||
if err := status.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return rdb, nil
|
||||
}
|
@ -9,8 +9,8 @@ import (
|
||||
"go.uber.org/zap"
|
||||
"google.golang.org/grpc"
|
||||
"net"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/initialize"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/proto/notifyer"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/initialize"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/proto/notifyer"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -58,7 +58,7 @@ func (g *GRPC) Stop(_ context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *GRPC) Register(reg *initialize.RpcRegister) *GRPC {
|
||||
func (g *GRPC) Register(reg initialize.GRpcControllers) *GRPC {
|
||||
notifyer.RegisterQuizServiceServer(g.grpc, reg.MailNotify)
|
||||
// another
|
||||
return g
|
73
internal/server/http/http_server.go
Normal file
73
internal/server/http/http_server.go
Normal file
@ -0,0 +1,73 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/themakers/hlog"
|
||||
"go.uber.org/zap"
|
||||
"penahub.gitlab.yandexcloud.net/backend/penahub_common/log_mw"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/middleware"
|
||||
)
|
||||
|
||||
type ServerConfig struct {
|
||||
Logger *zap.Logger
|
||||
Controllers []Controller
|
||||
Hlogger hlog.Logger
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
Logger *zap.Logger
|
||||
Controllers []Controller
|
||||
app *fiber.App
|
||||
}
|
||||
|
||||
func NewServer(config ServerConfig) *Server {
|
||||
app := fiber.New()
|
||||
app.Use(middleware.JWTAuth())
|
||||
app.Use(log_mw.ContextLogger(config.Hlogger))
|
||||
//app.Get("/liveness", healthchecks.Liveness)
|
||||
//app.Get("/readiness", healthchecks.Readiness(&workerErr)) //todo parametrized readiness. should discuss ready reason
|
||||
s := &Server{
|
||||
Logger: config.Logger,
|
||||
Controllers: config.Controllers,
|
||||
app: app,
|
||||
}
|
||||
|
||||
s.registerRoutes()
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Server) Start(addr string) error {
|
||||
if err := s.app.Listen(addr); err != nil {
|
||||
s.Logger.Error("Failed to start server", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) Shutdown(ctx context.Context) error {
|
||||
return s.app.Shutdown()
|
||||
}
|
||||
|
||||
func (s *Server) registerRoutes() {
|
||||
for _, c := range s.Controllers {
|
||||
router := s.app.Group(c.Name())
|
||||
c.Register(router)
|
||||
}
|
||||
}
|
||||
|
||||
type Controller interface {
|
||||
Register(router fiber.Router)
|
||||
Name() string
|
||||
}
|
||||
|
||||
func (s *Server) ListRoutes() {
|
||||
fmt.Println("Registered routes:")
|
||||
for _, stack := range s.app.Stack() {
|
||||
for _, route := range stack {
|
||||
fmt.Printf("%s %s\n", route.Method, route.Path)
|
||||
}
|
||||
}
|
||||
}
|
@ -187,66 +187,6 @@ func handleImage(file *excelize.File, sheet, cell, content string, count, row in
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
}
|
||||
count := 2
|
||||
for _, q := range questions {
|
||||
if !q.Deleted && q.Type != model.TypeResult {
|
||||
index := binarySearch(response, q.Id)
|
||||
if index != -1 {
|
||||
cell := ToAlphaString(count) + strconv.Itoa(row)
|
||||
tipe := FileSearch(response[index].Content)
|
||||
noAccept := make(map[string]struct{})
|
||||
todoMap := make(map[string]string)
|
||||
if tipe != "Text" && q.Type == model.TypeImages || q.Type == model.TypeVarImages {
|
||||
urle := ExtractImageURL(response[index].Content)
|
||||
urlData := strings.Split(urle, " ")
|
||||
if len(urlData) == 1 {
|
||||
u, err := url.Parse(urle)
|
||||
if err == nil && u.Scheme != "" && u.Host != "" {
|
||||
picture, err := downloadImage(urle)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
err = file.SetColWidth(sheet, ToAlphaString(count), ToAlphaString(count), 50)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
err = file.SetRowHeight(sheet, row, 150)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
if err := file.AddPictureFromBytes(sheet, cell, picture); err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
noAccept[response[index].Content] = struct{}{}
|
||||
} else {
|
||||
todoMap[response[index].Content] = cell
|
||||
}
|
||||
} else {
|
||||
todoMap[response[index].Content] = cell
|
||||
}
|
||||
} else if tipe != "Text" && q.Type == model.TypeFile {
|
||||
urle := ExtractImageURL(response[index].Content)
|
||||
display, tooltip := urle, urle
|
||||
if err := file.SetCellValue(sheet, cell, response[index].Content); err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
if err := file.SetCellHyperLink(sheet, cell, urle, "External", excelize.HyperlinkOpts{
|
||||
Display: &display,
|
||||
Tooltip: &tooltip,
|
||||
}); err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
noAccept[response[index].Content] = struct{}{}
|
||||
} else {
|
||||
todoMap[response[index].Content] = cell
|
||||
}
|
||||
for cnt, cel := range todoMap {
|
||||
if _, ok := noAccept[cnt]; !ok {
|
||||
if err := file.SetCellValue(sheet, cel, cnt); err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mediaRow := row
|
||||
for i, imgContent := range multiImgArr {
|
||||
@ -356,7 +296,6 @@ func handleImage(file *excelize.File, sheet, cell, content string, count, row in
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handleFile(file *excelize.File, sheet, cell, content, s3Prefix string, noAccept map[string]struct{}) {
|
||||
urle := content
|
||||
if urle != "" && !strings.HasPrefix(urle, "https") {
|
@ -7,7 +7,7 @@ import (
|
||||
"github.com/go-redis/redis/v8"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/dal"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/model"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/clients/telegram"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/clients/telegram"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
11
main.go
11
main.go
@ -1,11 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/skeris/appInit"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/app"
|
||||
_ "penahub.gitlab.yandexcloud.net/devops/linters/golang.git/pkg/dummy"
|
||||
)
|
||||
|
||||
func main() {
|
||||
appInit.Initialize(app.New, app.Options{})
|
||||
}
|
37
pkg/closer/closer.go
Normal file
37
pkg/closer/closer.go
Normal file
@ -0,0 +1,37 @@
|
||||
package closer
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
type Closer interface {
|
||||
Close(ctx context.Context) error
|
||||
}
|
||||
|
||||
type CloserFunc func(ctx context.Context) error
|
||||
|
||||
func (cf CloserFunc) Close(ctx context.Context) error {
|
||||
return cf(ctx)
|
||||
}
|
||||
|
||||
type CloserGroup struct {
|
||||
closers []Closer
|
||||
}
|
||||
|
||||
func NewCloserGroup() *CloserGroup {
|
||||
return &CloserGroup{}
|
||||
}
|
||||
|
||||
func (cg *CloserGroup) Add(c Closer) {
|
||||
cg.closers = append(cg.closers, c)
|
||||
}
|
||||
|
||||
func (cg *CloserGroup) Call(ctx context.Context) error {
|
||||
var closeErr error
|
||||
for i := len(cg.closers) - 1; i >= 0; i-- {
|
||||
if err := cg.closers[i].Close(ctx); err != nil && closeErr == nil {
|
||||
closeErr = err
|
||||
}
|
||||
}
|
||||
return closeErr
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
-- Drop indexes
|
||||
DROP INDEX IF EXISTS subquizes;
|
||||
DROP INDEX IF EXISTS birthtime;
|
||||
DROP INDEX IF EXISTS groups;
|
||||
DROP INDEX IF EXISTS timeouted;
|
||||
DROP INDEX IF EXISTS active ON quiz;
|
||||
DROP INDEX IF EXISTS questiontype;
|
||||
DROP INDEX IF EXISTS required;
|
||||
DROP INDEX IF EXISTS relation;
|
||||
DROP INDEX IF EXISTS active ON question;
|
||||
|
||||
-- Drop tables
|
||||
DROP TABLE IF EXISTS privileges;
|
||||
DROP TABLE IF EXISTS answer;
|
||||
DROP TABLE IF EXISTS question;
|
||||
DROP TABLE IF EXISTS quiz;
|
||||
DROP TABLE IF EXISTS account;
|
||||
|
||||
-- Drop types
|
||||
DO $$
|
||||
BEGIN
|
||||
DROP TYPE IF EXISTS question_type;
|
||||
DROP TYPE IF EXISTS quiz_status;
|
||||
END$$;
|
@ -1,120 +0,0 @@
|
||||
-- Create types
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'question_type') THEN
|
||||
CREATE TYPE question_type AS ENUM (
|
||||
'variant',
|
||||
'images',
|
||||
'varimg',
|
||||
'emoji',
|
||||
'text',
|
||||
'select',
|
||||
'date',
|
||||
'number',
|
||||
'file',
|
||||
'page',
|
||||
'rating'
|
||||
);
|
||||
END IF;
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'quiz_status') THEN
|
||||
CREATE TYPE quiz_status AS ENUM (
|
||||
'draft',
|
||||
'template',
|
||||
'stop',
|
||||
'start',
|
||||
'timeout',
|
||||
'offlimit'
|
||||
);
|
||||
END IF;
|
||||
|
||||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||
END$$;
|
||||
|
||||
-- Create tables
|
||||
CREATE TABLE IF NOT EXISTS account (
|
||||
id UUID PRIMARY KEY,
|
||||
user_id VARCHAR(24),
|
||||
email VARCHAR(50),
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted BOOLEAN DEFAULT false
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS quiz (
|
||||
id bigserial UNIQUE NOT NULL PRIMARY KEY,
|
||||
qid uuid DEFAULT uuid_generate_v4(),
|
||||
accountid varchar(30) NOT NULL,
|
||||
deleted boolean DEFAULT false,
|
||||
archived boolean DEFAULT false,
|
||||
fingerprinting boolean DEFAULT false,
|
||||
repeatable boolean DEFAULT false,
|
||||
note_prevented boolean DEFAULT false,
|
||||
mail_notifications boolean DEFAULT false,
|
||||
unique_answers boolean DEFAULT false,
|
||||
super boolean DEFAULT false,
|
||||
group_id bigint DEFAULT 0,
|
||||
name varchar(280),
|
||||
description text,
|
||||
config text,
|
||||
status quiz_status DEFAULT 'draft',
|
||||
limit_answers integer DEFAULT 0,
|
||||
due_to integer DEFAULT 0,
|
||||
time_of_passing integer DEFAULT 0,
|
||||
pausable boolean DEFAULT false,
|
||||
version smallint DEFAULT 0,
|
||||
version_comment text DEFAULT '',
|
||||
parent_ids integer[],
|
||||
created_at timestamp DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at timestamp DEFAULT CURRENT_TIMESTAMP,
|
||||
questions_count integer DEFAULT 0,
|
||||
answers_count integer DEFAULT 0,
|
||||
average_time_passing integer DEFAULT 0
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS question (
|
||||
id bigserial UNIQUE NOT NULL PRIMARY KEY,
|
||||
quiz_id bigint NOT NULL,
|
||||
title varchar(512) NOT NULL,
|
||||
description text,
|
||||
questiontype question_type DEFAULT 'text',
|
||||
required boolean DEFAULT false,
|
||||
deleted boolean DEFAULT false,
|
||||
page smallint DEFAULT 0,
|
||||
content text,
|
||||
version smallint DEFAULT 0,
|
||||
parent_ids integer[],
|
||||
created_at timestamp DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at timestamp DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT quiz_relation FOREIGN KEY(quiz_id) REFERENCES quiz(id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS answer (
|
||||
id bigserial UNIQUE NOT NULL PRIMARY KEY,
|
||||
content text,
|
||||
quiz_id bigint NOT NULL REFERENCES quiz(id),
|
||||
question_id bigint NOT NULL REFERENCES question(id),
|
||||
fingerprint varchar(1024),
|
||||
session varchar(20),
|
||||
created_at timestamp DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS privileges (
|
||||
id SERIAL PRIMARY KEY,
|
||||
privilegeID VARCHAR(50),
|
||||
account_id UUID,
|
||||
privilege_name VARCHAR(255),
|
||||
amount INT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (account_id) REFERENCES account (id)
|
||||
);
|
||||
|
||||
-- Create indexes
|
||||
CREATE INDEX IF NOT EXISTS active ON question(deleted) WHERE deleted=false;
|
||||
CREATE INDEX IF NOT EXISTS relation ON question(quiz_id DESC);
|
||||
CREATE INDEX IF NOT EXISTS required ON question(required DESC);
|
||||
CREATE INDEX IF NOT EXISTS questiontype ON question(questiontype);
|
||||
CREATE INDEX IF NOT EXISTS active ON quiz(deleted, archived, status) WHERE deleted = false AND archived = false AND status = 'start';
|
||||
CREATE INDEX IF NOT EXISTS timeouted ON quiz(due_to DESC) WHERE deleted = false AND due_to <> 0 AND status <> 'timeout';
|
||||
CREATE INDEX IF NOT EXISTS groups ON quiz(super) WHERE super = true;
|
||||
CREATE INDEX IF NOT EXISTS birthtime ON quiz(created_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS subquizes ON quiz(group_id DESC) WHERE group_id <> 0;
|
@ -1 +0,0 @@
|
||||
ALTER TABLE answer DROP COLUMN IF EXISTS result;
|
@ -1 +0,0 @@
|
||||
ALTER TABLE answer ADD COLUMN result BOOLEAN DEFAULT FALSE;
|
@ -1 +0,0 @@
|
||||
ALTER TABLE quiz DROP COLUMN IF EXISTS sessions_count;
|
@ -1 +0,0 @@
|
||||
ALTER TABLE quiz ADD COLUMN sessions_count integer;
|
@ -1,2 +0,0 @@
|
||||
ALTER TABLE quiz DROP COLUMN IF EXISTS new;
|
||||
ALTER TABLE quiz DROP COLUMN IF EXISTS deleted;
|
@ -1,2 +0,0 @@
|
||||
ALTER TABLE answer ADD COLUMN new BOOLEAN DEFAULT TRUE;
|
||||
ALTER TABLE answer ADD COLUMN deleted BOOLEAN DEFAULT FALSE;
|
@ -1,2 +0,0 @@
|
||||
ALTER TABLE answer DROP COLUMN IF EXISTS email;
|
||||
DROP INDEX IF EXISTS answer_email_unique_idx;
|
@ -1,2 +0,0 @@
|
||||
ALTER TABLE answer ADD COLUMN email VARCHAR(50) NOT NULL DEFAULT '';
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS answer_email_unique_idx ON answer (quiz_id, email) WHERE email <> '';
|
@ -1,6 +0,0 @@
|
||||
ALTER TABLE answer
|
||||
DROP COLUMN device_type,
|
||||
DROP COLUMN device,
|
||||
DROP COLUMN os,
|
||||
DROP COLUMN browser,
|
||||
DROP COLUMN ip;
|
@ -1,6 +0,0 @@
|
||||
ALTER TABLE answer
|
||||
ADD COLUMN device_type VARCHAR(50) NOT NULL DEFAULT '',
|
||||
ADD COLUMN device VARCHAR(100) NOT NULL DEFAULT '',
|
||||
ADD COLUMN os VARCHAR(100) NOT NULL DEFAULT '',
|
||||
ADD COLUMN browser VARCHAR(100) NOT NULL DEFAULT '',
|
||||
ADD COLUMN ip VARCHAR(50) NOT NULL DEFAULT '';
|
@ -1,2 +0,0 @@
|
||||
ALTER TABLE answer
|
||||
DROP COLUMN start;
|
@ -1,2 +0,0 @@
|
||||
ALTER TABLE answer
|
||||
ADD COLUMN start BOOLEAN NOT NULL DEFAULT FALSE;
|
@ -1,4 +0,0 @@
|
||||
ALTER TABLE answer
|
||||
ALTER COLUMN device TYPE VARCHAR(100),
|
||||
ALTER COLUMN os TYPE VARCHAR(100),
|
||||
ALTER COLUMN browser TYPE VARCHAR(100);
|
@ -1,4 +0,0 @@
|
||||
ALTER TABLE answer
|
||||
ALTER COLUMN device TYPE VARCHAR(1024),
|
||||
ALTER COLUMN os TYPE VARCHAR(1024),
|
||||
ALTER COLUMN browser TYPE VARCHAR(1024);
|
@ -1,2 +0,0 @@
|
||||
ALTER TABLE quiz
|
||||
ALTER COLUMN name TYPE VARCHAR(280);
|
@ -1,2 +0,0 @@
|
||||
ALTER TABLE quiz
|
||||
ALTER COLUMN name TYPE VARCHAR(1024);
|
@ -1,101 +0,0 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"github.com/go-redis/redis/v8"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/dal"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/brokers"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/clients/auth"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/clients/telegram"
|
||||
)
|
||||
|
||||
// Service is an entity for http requests handling
|
||||
type Service struct {
|
||||
dal *dal.DAL
|
||||
authClient *auth.AuthClient
|
||||
producer *brokers.Producer
|
||||
serviceName string
|
||||
chDAL *dal.ClickHouseDAL
|
||||
telegramClient *telegram.TelegramClient
|
||||
redisClient *redis.Client
|
||||
s3Prefix string
|
||||
}
|
||||
|
||||
type Deps struct {
|
||||
Dal *dal.DAL
|
||||
AuthClient *auth.AuthClient
|
||||
Producer *brokers.Producer
|
||||
ServiceName string
|
||||
ChDAL *dal.ClickHouseDAL
|
||||
TelegramClient *telegram.TelegramClient
|
||||
RedisClient *redis.Client
|
||||
S3Prefix string
|
||||
}
|
||||
|
||||
func New(deps Deps) *Service {
|
||||
return &Service{
|
||||
dal: deps.Dal,
|
||||
authClient: deps.AuthClient,
|
||||
producer: deps.Producer,
|
||||
serviceName: deps.ServiceName,
|
||||
chDAL: deps.ChDAL,
|
||||
telegramClient: deps.TelegramClient,
|
||||
redisClient: deps.RedisClient,
|
||||
s3Prefix: deps.S3Prefix,
|
||||
}
|
||||
}
|
||||
|
||||
// Register is a function for add handlers of service to external multiplexer
|
||||
func (s *Service) Register(app *fiber.App) {
|
||||
// quiz manipulating handlers
|
||||
app.Post("/quiz/create", s.CreateQuiz)
|
||||
app.Post("/quiz/getList", s.GetQuizList)
|
||||
app.Patch("/quiz/edit", s.UpdateQuiz)
|
||||
app.Post("/quiz/copy", s.CopyQuiz)
|
||||
app.Post("/quiz/history", s.GetQuizHistory)
|
||||
app.Delete("/quiz/delete", s.DeleteQuiz)
|
||||
app.Patch("/quiz/archive", s.ArchiveQuiz)
|
||||
app.Post("/quiz/move", s.QuizMove)
|
||||
app.Post("/quiz/template", s.TemplateCopy)
|
||||
|
||||
// question manipulating handlers
|
||||
app.Post("/question/create", s.CreateQuestion)
|
||||
app.Post("/question/getList", s.GetQuestionList)
|
||||
app.Patch("/question/edit", s.UpdateQuestion)
|
||||
app.Post("/question/copy", s.CopyQuestion)
|
||||
app.Post("/question/history", s.GetQuestionHistory)
|
||||
app.Delete("/question/delete", s.DeleteQuestion)
|
||||
|
||||
// account handlers
|
||||
app.Get("/account/get", s.getCurrentAccount)
|
||||
app.Post("/account/create", s.createAccount)
|
||||
app.Delete("/account/delete", s.deleteAccount)
|
||||
app.Get("/accounts", s.getAccounts)
|
||||
app.Get("/privilege/:userId", s.getPrivilegeByUserID)
|
||||
app.Delete("/account/:userId", s.deleteAccountByUserID)
|
||||
app.Post("/account/manualdone", s.ManualDone)
|
||||
app.Post("/account/leadtarget", s.PostLeadTarget)
|
||||
app.Delete("/account/leadtarget/:id", s.DeleteLeadTarget)
|
||||
app.Get("/account/leadtarget/:quizID", s.GetLeadTarget)
|
||||
app.Put("/account/leadtarget", s.UpdateLeadTarget)
|
||||
|
||||
// result handlers
|
||||
app.Post("/results/getResults/:quizId", s.GetResultsByQuizID)
|
||||
app.Delete("/results/delete/:resultId", s.DelResultByID)
|
||||
app.Patch("/result/seen", s.SetStatus)
|
||||
app.Post("/results/:quizID/export", s.ExportResultsToCSV)
|
||||
app.Get("/result/:resultID", s.GetResultAnswers)
|
||||
|
||||
// statistics handlers
|
||||
app.Post("/statistic/:quizID/devices", s.GetDeviceStatistics)
|
||||
app.Post("/statistic/:quizID/general", s.GetGeneralStatistics)
|
||||
app.Post("/statistic/:quizID/questions", s.GetQuestionsStatistics)
|
||||
app.Post("/statistic", s.AllServiceStatistics)
|
||||
app.Get("/statistics/:quizID/pipelines", s.GetPipelinesStatistics)
|
||||
|
||||
//telegram handlers
|
||||
app.Get("/telegram/pool", s.GetPoolTgAccounts)
|
||||
app.Post("/telegram/create", s.AddingTgAccount)
|
||||
app.Delete("/telegram/:id", s.DeleteTgAccountByID)
|
||||
app.Post("/telegram/setCode", s.SettingTgCode)
|
||||
}
|
@ -5,8 +5,8 @@ import (
|
||||
"github.com/pioz/faker"
|
||||
"go.uber.org/zap"
|
||||
"log"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/brokers"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/initialize"
|
||||
"penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/brokers"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user