Compare commits
5 Commits
main
...
integratio
Author | SHA1 | Date | |
---|---|---|---|
59747f9525 | |||
9ade0ce99a | |||
18fe7fd62a | |||
2dd714b369 | |||
ff8956355f |
113
app/app.go
113
app/app.go
@ -4,27 +4,14 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"gitea.pena/PenaSide/common/log_mw"
|
|
||||||
"gitea.pena/PenaSide/hlog"
|
|
||||||
"gitea.pena/PenaSide/trashlog/wrappers/zaptrashlog"
|
|
||||||
"gitea.pena/SQuiz/answerer/clients"
|
"gitea.pena/SQuiz/answerer/clients"
|
||||||
dalBS "gitea.pena/SQuiz/answerer/dal"
|
"gitea.pena/SQuiz/answerer/dal"
|
||||||
"gitea.pena/SQuiz/answerer/models"
|
"gitea.pena/SQuiz/answerer/healthchecks"
|
||||||
"gitea.pena/SQuiz/answerer/savewc"
|
"gitea.pena/SQuiz/answerer/middleware"
|
||||||
"gitea.pena/SQuiz/answerer/service"
|
"gitea.pena/SQuiz/answerer/service"
|
||||||
"gitea.pena/SQuiz/common/dal"
|
|
||||||
"gitea.pena/SQuiz/common/healthchecks"
|
|
||||||
"gitea.pena/SQuiz/common/middleware"
|
|
||||||
"gitea.pena/SQuiz/common/model"
|
|
||||||
"gitea.pena/SQuiz/common/utils"
|
|
||||||
"github.com/go-redis/redis/v8"
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/minio/minio-go/v7"
|
|
||||||
"github.com/minio/minio-go/v7/pkg/credentials"
|
|
||||||
"github.com/skeris/appInit"
|
"github.com/skeris/appInit"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"go.uber.org/zap/zapcore"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type App struct {
|
type App struct {
|
||||||
@ -55,21 +42,10 @@ var _ appInit.CommonApp = (*App)(nil)
|
|||||||
type Options struct {
|
type Options struct {
|
||||||
LoggerProdMode bool `env:"IS_PROD_LOG" default:"false"`
|
LoggerProdMode bool `env:"IS_PROD_LOG" default:"false"`
|
||||||
IsProd bool `env:"IS_PROD" default:"false"`
|
IsProd bool `env:"IS_PROD" default:"false"`
|
||||||
MinioEP string `env:"MINIO_EP" default:"localhost:3002"`
|
|
||||||
MinioAK string `env:"MINIO_AK" default:"minio"`
|
|
||||||
MinioSK string `env:"MINIO_SK" default:"miniostorage"`
|
|
||||||
NumberPort string `env:"PORT" default:"1490"`
|
NumberPort string `env:"PORT" default:"1490"`
|
||||||
CrtFile string `env:"CRT" default:"server.crt"`
|
CrtFile string `env:"CRT" default:"server.crt"`
|
||||||
KeyFile string `env:"KEY" default:"server.key"`
|
KeyFile string `env:"KEY" default:"server.key"`
|
||||||
PostgresCredentials string `env:"PG_CRED" default:"host=localhost port=5432 user=squiz password=Redalert2 dbname=squiz sslmode=disable"`
|
PostgresCredentials string `env:"PG_CRED" default:"host=localhost port=5432 user=squiz password=Redalert2 dbname=squiz sslmode=disable"`
|
||||||
RedisHost string `env:"REDIS_HOST"`
|
|
||||||
RedisPassword string `env:"REDIS_PASSWORD"`
|
|
||||||
RedisDB uint64 `env:"REDIS_DB"`
|
|
||||||
RedirectURL string `env:"REDIRECT_URL" default:"https://squiz.pena.digital"`
|
|
||||||
PubKey string `env:"PUBLIC_KEY"`
|
|
||||||
PrivKey string `env:"PRIVATE_KEY"`
|
|
||||||
TrashLogHost string `env:"TRASH_LOG_HOST" default:"localhost:7113"`
|
|
||||||
ModuleLogger string `env:"MODULE_LOGGER" default:"answerer-local"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(ctx context.Context, opts interface{}, ver appInit.Version) (appInit.CommonApp, error) {
|
func New(ctx context.Context, opts interface{}, ver appInit.Version) (appInit.CommonApp, error) {
|
||||||
@ -102,77 +78,22 @@ func New(ctx context.Context, opts interface{}, ver appInit.Version) (appInit.Co
|
|||||||
zap.String("SvcVersion", ver.Release),
|
zap.String("SvcVersion", ver.Release),
|
||||||
zap.String("SvcBuildTime", ver.BuildTime),
|
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 {
|
pgdal, err := dal.New(ctx, options.PostgresCredentials)
|
||||||
return zapcore.NewTee(core, clickHouseLogger)
|
|
||||||
}))
|
|
||||||
|
|
||||||
loggerHlog := hlog.New(loggerForHlog).Module(options.ModuleLogger)
|
|
||||||
|
|
||||||
loggerHlog.With(models.AllFields{})
|
|
||||||
loggerHlog.Emit(InfoSvcStarted{})
|
|
||||||
|
|
||||||
// Initialize minio client object.
|
|
||||||
minioClient, err := minio.New(options.MinioEP, &minio.Options{
|
|
||||||
Creds: credentials.NewStaticV4(options.MinioAK, options.MinioSK, ""),
|
|
||||||
Secure: options.IsProd,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("MINIOERR", options.MinioEP, err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
pgdal, err := dal.New(ctx, options.PostgresCredentials, minioClient)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
zapLogger.Info("config", zap.Any("options", options))
|
zapLogger.Info("config", zap.Any("options", options))
|
||||||
//init redis
|
|
||||||
redisClient := redis.NewClient(&redis.Options{
|
svc, err := service.New(service.ServiceDeps{
|
||||||
Addr: options.RedisHost,
|
Dal: pgdal,
|
||||||
Password: options.RedisPassword,
|
AiClient: clients.NewAiClient("https://alvatar.com/api/engine/send_answer"),
|
||||||
DB: int(options.RedisDB),
|
|
||||||
})
|
})
|
||||||
|
|
||||||
encrypt := utils.NewEncrypt(options.PubKey, options.PrivKey)
|
|
||||||
|
|
||||||
workerSendClientCh := make(chan model.Answer, 50)
|
|
||||||
workerRespondentCh := make(chan []model.Answer, 50)
|
|
||||||
blobstore, err := dalBS.New(ctx, minioClient)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
zapLogger.Error("failed to create service", zap.Error(err))
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
//svc := service.New(blobstore, pgdal, workerRespondentCh, workerSendClientCh)
|
|
||||||
svc := service.New(service.ServiceDeps{
|
|
||||||
Store: blobstore,
|
|
||||||
Dal: pgdal,
|
|
||||||
WorkerSendClientCh: workerSendClientCh,
|
|
||||||
WorkerRespondentCh: workerRespondentCh,
|
|
||||||
Encrypt: encrypt,
|
|
||||||
RedirectURl: options.RedirectURL,
|
|
||||||
AiClient: clients.NewAiClient("https://alvatar.com/api/engine/send_answer"),
|
|
||||||
})
|
|
||||||
|
|
||||||
saveRespWcData := savewc.DepsForResp{
|
|
||||||
WorkerRespondentCh: workerRespondentCh,
|
|
||||||
Redis: redisClient,
|
|
||||||
}
|
|
||||||
|
|
||||||
saveClientWcData := savewc.DepsForClient{
|
|
||||||
WorkerSendClientCh: workerSendClientCh,
|
|
||||||
Redis: redisClient,
|
|
||||||
}
|
|
||||||
|
|
||||||
saveRespWorker := savewc.NewSaveRespWorker(saveRespWcData, errChan, loggerHlog)
|
|
||||||
|
|
||||||
saveClientWorker := savewc.NewSaveClientWorker(saveClientWcData, errChan, loggerHlog)
|
|
||||||
|
|
||||||
go saveRespWorker.Start(ctx)
|
|
||||||
go saveClientWorker.Start(ctx)
|
|
||||||
|
|
||||||
app := fiber.New(fiber.Config{BodyLimit: 70 * 1024 * 1024})
|
app := fiber.New(fiber.Config{BodyLimit: 70 * 1024 * 1024})
|
||||||
|
|
||||||
@ -186,38 +107,28 @@ func New(ctx context.Context, opts interface{}, ver appInit.Version) (appInit.Co
|
|||||||
})
|
})
|
||||||
|
|
||||||
app.Use(middleware.AnswererChain())
|
app.Use(middleware.AnswererChain())
|
||||||
app.Use(log_mw.ContextLogger(loggerHlog))
|
|
||||||
app.Get("/liveness", healthchecks.Liveness)
|
app.Get("/liveness", healthchecks.Liveness)
|
||||||
app.Get("/readiness", healthchecks.Readiness(&workerErr)) //todo parametrized readiness. should discuss ready reason
|
app.Get("/readiness", healthchecks.Readiness(&workerErr)) //todo parametrized readiness. should discuss ready reason
|
||||||
app = svc.Register(app)
|
app = svc.Register(app)
|
||||||
|
|
||||||
fmt.Println("SERVERSTART", fmt.Sprintf(":%s", options.NumberPort))
|
fmt.Println("SERVERSTART", fmt.Sprintf(":%s", options.NumberPort))
|
||||||
|
|
||||||
loggerHlog.Emit(InfoSvcReady{})
|
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
defer func() {
|
defer func() {
|
||||||
//if pgdal != nil {
|
|
||||||
// pgdal.CloseAnswerer()
|
|
||||||
//}
|
|
||||||
err := app.Shutdown()
|
err := app.Shutdown()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
loggerHlog.Emit(InfoSvcShutdown{Signal: err.Error()})
|
zapLogger.Error("failed graceful shutdown", zap.Error(err))
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if options.IsProd {
|
if options.IsProd {
|
||||||
if err := app.ListenTLS(fmt.Sprintf(":%s", options.NumberPort), options.CrtFile, options.KeyFile); err != nil {
|
if err := app.ListenTLS(fmt.Sprintf(":%s", options.NumberPort), options.CrtFile, options.KeyFile); err != nil {
|
||||||
loggerHlog.Emit(ErrorCanNotServe{
|
zapLogger.Error("failed start server", zap.Error(err))
|
||||||
Err: err,
|
|
||||||
})
|
|
||||||
errChan <- err
|
errChan <- err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if err := app.Listen(fmt.Sprintf(":%s", options.NumberPort)); err != nil {
|
if err := app.Listen(fmt.Sprintf(":%s", options.NumberPort)); err != nil {
|
||||||
loggerHlog.Emit(ErrorCanNotServe{
|
zapLogger.Error("failed start server", zap.Error(err))
|
||||||
Err: err,
|
|
||||||
})
|
|
||||||
errChan <- err
|
errChan <- err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
package app
|
|
||||||
|
|
||||||
type InfoSvcStarted struct{}
|
|
||||||
type InfoSvcReady struct{}
|
|
||||||
type InfoSvcShutdown struct {
|
|
||||||
Signal string
|
|
||||||
}
|
|
||||||
type ErrorCanNotServe struct {
|
|
||||||
Err error
|
|
||||||
}
|
|
255
dal/dal.go
255
dal/dal.go
@ -2,31 +2,254 @@ package dal
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
_ "embed"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/minio/minio-go/v7"
|
"gitea.pena/SQuiz/answerer/dal/sqlcgen"
|
||||||
"io"
|
"gitea.pena/SQuiz/answerer/model"
|
||||||
|
_ "github.com/ClickHouse/clickhouse-go"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/lib/pq"
|
||||||
|
_ "github.com/lib/pq"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
type DAL struct {
|
||||||
bucket = "3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b"
|
pool *sql.DB
|
||||||
bucketAnswers = "squizanswer"
|
queries *sqlcgen.Queries
|
||||||
)
|
|
||||||
|
|
||||||
type Storer struct {
|
|
||||||
client *minio.Client
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(ctx context.Context, minioClient *minio.Client) (*Storer, error) {
|
func New(ctx context.Context, cred string) (*DAL, error) {
|
||||||
return &Storer{
|
pool, err := sql.Open("postgres", cred)
|
||||||
client: minioClient,
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
timeoutCtx, cancel := context.WithTimeout(ctx, time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if err := pool.PingContext(timeoutCtx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
queries := sqlcgen.New(pool)
|
||||||
|
|
||||||
|
return &DAL{
|
||||||
|
pool: pool,
|
||||||
|
queries: queries,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Storer) PutAnswer(ctx context.Context, file io.Reader, quizId, name string, question uint64, size int64) error {
|
func (d *DAL) Close(ctx context.Context) error {
|
||||||
if _, err := s.client.PutObject(ctx, bucket, bucketAnswers + "/" +fmt.Sprintf("%s/%d/%s", quizId, question, name), file, size,
|
err := d.pool.Close()
|
||||||
minio.PutObjectOptions{}); err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *DAL) GetQuizByQid(ctx context.Context, qid string) (model.Quiz, error) {
|
||||||
|
_, err := uuid.Parse(qid)
|
||||||
|
if err != nil {
|
||||||
|
return model.Quiz{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("QUID", `
|
||||||
|
SELECT * FROM quiz
|
||||||
|
WHERE
|
||||||
|
deleted = false AND
|
||||||
|
archived = false AND
|
||||||
|
status = 'start' AND
|
||||||
|
qid = $1;
|
||||||
|
`)
|
||||||
|
rows, err := d.pool.QueryContext(ctx, `
|
||||||
|
SELECT * FROM quiz
|
||||||
|
WHERE
|
||||||
|
deleted = false AND
|
||||||
|
archived = false AND
|
||||||
|
(status = 'start' OR status = 'ai') AND
|
||||||
|
qid = $1;
|
||||||
|
`, qid)
|
||||||
|
if err != nil {
|
||||||
|
return model.Quiz{}, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
if !rows.Next() {
|
||||||
|
return model.Quiz{}, rows.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
var piece model.Quiz
|
||||||
|
pIds := pq.Int32Array{}
|
||||||
|
if err := rows.Scan(
|
||||||
|
&piece.Id,
|
||||||
|
&piece.Qid,
|
||||||
|
&piece.AccountId,
|
||||||
|
&piece.Deleted,
|
||||||
|
&piece.Archived,
|
||||||
|
&piece.Fingerprinting,
|
||||||
|
&piece.Repeatable,
|
||||||
|
&piece.NotePrevented,
|
||||||
|
&piece.MailNotifications,
|
||||||
|
&piece.UniqueAnswers,
|
||||||
|
&piece.Super,
|
||||||
|
&piece.GroupId,
|
||||||
|
&piece.Name,
|
||||||
|
&piece.Description,
|
||||||
|
&piece.Config,
|
||||||
|
&piece.Status,
|
||||||
|
&piece.Limit,
|
||||||
|
&piece.DueTo,
|
||||||
|
&piece.TimeOfPassing,
|
||||||
|
&piece.Pausable,
|
||||||
|
&piece.Version,
|
||||||
|
&piece.VersionComment,
|
||||||
|
&pIds,
|
||||||
|
&piece.CreatedAt,
|
||||||
|
&piece.UpdatedAt,
|
||||||
|
&piece.QuestionsCount,
|
||||||
|
&piece.PassedCount,
|
||||||
|
&piece.AverageTime,
|
||||||
|
&piece.SessionCount,
|
||||||
|
&piece.GigaChat,
|
||||||
|
); err != nil {
|
||||||
|
return model.Quiz{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
piece.ParentIds = pIds
|
||||||
|
|
||||||
|
return piece, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DAL) GetQuestionListByIDs(ctx context.Context, ids []int32) ([]model.Question, error) {
|
||||||
|
rows, err := d.queries.GetQuestionListByIDs(ctx, ids)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var questions []model.Question
|
||||||
|
for _, row := range rows {
|
||||||
|
question := model.Question{
|
||||||
|
Id: uint64(row.ID),
|
||||||
|
QuizId: uint64(row.QuizID),
|
||||||
|
Title: row.Title,
|
||||||
|
Description: row.Description.String,
|
||||||
|
Type: string(row.Questiontype.([]byte)),
|
||||||
|
Required: row.Required.Bool,
|
||||||
|
Deleted: row.Deleted.Bool,
|
||||||
|
Page: int(row.Page.Int16),
|
||||||
|
Content: row.Content.String,
|
||||||
|
Version: int(row.Version.Int16),
|
||||||
|
ParentIds: row.ParentIds,
|
||||||
|
CreatedAt: row.CreatedAt.Time,
|
||||||
|
UpdatedAt: row.UpdatedAt.Time,
|
||||||
|
Auditory: row.Auditory,
|
||||||
|
}
|
||||||
|
|
||||||
|
questions = append(questions, question)
|
||||||
|
}
|
||||||
|
|
||||||
|
return questions, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DAL) CreateQuestion(ctx context.Context, record *model.Question) (uint64, error) {
|
||||||
|
params := sqlcgen.InsertQuestionParams{
|
||||||
|
QuizID: int64(record.QuizId),
|
||||||
|
Title: record.Title,
|
||||||
|
Description: sql.NullString{String: record.Description, Valid: true},
|
||||||
|
Questiontype: record.Type,
|
||||||
|
Required: sql.NullBool{Bool: record.Required, Valid: true},
|
||||||
|
Page: sql.NullInt16{Int16: int16(record.Page), Valid: true},
|
||||||
|
Content: sql.NullString{String: record.Content, Valid: true},
|
||||||
|
ParentIds: record.ParentIds,
|
||||||
|
UpdatedAt: sql.NullTime{Time: time.Now(), Valid: true},
|
||||||
|
Session: record.Session,
|
||||||
|
Auditory: record.Auditory,
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := d.queries.InsertQuestion(ctx, params)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
record.Id = uint64(data.ID)
|
||||||
|
record.CreatedAt = data.CreatedAt.Time
|
||||||
|
record.UpdatedAt = data.UpdatedAt.Time
|
||||||
|
|
||||||
|
return record.Id, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DAL) CreateAnswers(ctx context.Context, answers []model.Answer, session, fp string, quizID uint64) ([]model.Answer, []error) {
|
||||||
|
var (
|
||||||
|
createdAnswers []model.Answer
|
||||||
|
errs []error
|
||||||
|
)
|
||||||
|
|
||||||
|
tx, err := d.pool.BeginTx(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, []error{err}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ans := range answers {
|
||||||
|
if ans.Utm == nil {
|
||||||
|
ans.Utm = make(model.UTMSavingMap)
|
||||||
|
}
|
||||||
|
utmJSON, err := json.Marshal(ans.Utm)
|
||||||
|
if err != nil {
|
||||||
|
return nil, []error{err}
|
||||||
|
}
|
||||||
|
|
||||||
|
params := sqlcgen.InsertAnswersParams{
|
||||||
|
Content: sql.NullString{String: ans.Content, Valid: true},
|
||||||
|
QuizID: int64(quizID),
|
||||||
|
QuestionID: int64(ans.QuestionId),
|
||||||
|
Fingerprint: sql.NullString{String: fp, Valid: true},
|
||||||
|
Session: sql.NullString{String: session, Valid: true},
|
||||||
|
Result: sql.NullBool{Bool: ans.Result, Valid: true},
|
||||||
|
Email: ans.Email,
|
||||||
|
Device: ans.Device,
|
||||||
|
DeviceType: ans.DeviceType,
|
||||||
|
Ip: ans.IP,
|
||||||
|
Browser: ans.Browser,
|
||||||
|
Os: ans.OS,
|
||||||
|
Start: ans.Start,
|
||||||
|
Utm: utmJSON,
|
||||||
|
Version: ans.Version,
|
||||||
|
}
|
||||||
|
|
||||||
|
row, err := d.queries.InsertAnswers(ctx, params)
|
||||||
|
createdAnswer := model.Answer{
|
||||||
|
Id: uint64(row.ID),
|
||||||
|
Content: row.Content.String,
|
||||||
|
QuizId: uint64(row.QuizID),
|
||||||
|
QuestionId: uint64(row.QuestionID),
|
||||||
|
Fingerprint: row.Fingerprint.String,
|
||||||
|
Session: row.Session.String,
|
||||||
|
Result: row.Result.Bool,
|
||||||
|
New: row.New.Bool,
|
||||||
|
Email: row.Email,
|
||||||
|
DeviceType: row.DeviceType,
|
||||||
|
Device: row.Device,
|
||||||
|
OS: row.Os,
|
||||||
|
Browser: row.Browser,
|
||||||
|
IP: row.Ip,
|
||||||
|
Start: row.Start,
|
||||||
|
Version: row.Version,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
} else {
|
||||||
|
createdAnswers = append(createdAnswers, createdAnswer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tx.Commit()
|
||||||
|
if err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
return nil, errs
|
||||||
|
}
|
||||||
|
|
||||||
|
return createdAnswers, errs
|
||||||
|
}
|
||||||
|
31
dal/sqlcgen/db.go
Normal file
31
dal/sqlcgen/db.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// sqlc v1.29.0
|
||||||
|
|
||||||
|
package sqlcgen
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DBTX interface {
|
||||||
|
ExecContext(context.Context, string, ...interface{}) (sql.Result, error)
|
||||||
|
PrepareContext(context.Context, string) (*sql.Stmt, error)
|
||||||
|
QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error)
|
||||||
|
QueryRowContext(context.Context, string, ...interface{}) *sql.Row
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(db DBTX) *Queries {
|
||||||
|
return &Queries{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Queries struct {
|
||||||
|
db DBTX
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) WithTx(tx *sql.Tx) *Queries {
|
||||||
|
return &Queries{
|
||||||
|
db: tx,
|
||||||
|
}
|
||||||
|
}
|
400
dal/sqlcgen/models.go
Normal file
400
dal/sqlcgen/models.go
Normal file
@ -0,0 +1,400 @@
|
|||||||
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// sqlc v1.29.0
|
||||||
|
|
||||||
|
package sqlcgen
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Account struct {
|
||||||
|
ID uuid.UUID `db:"id" json:"id"`
|
||||||
|
UserID string `db:"user_id" json:"user_id"`
|
||||||
|
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||||
|
Deleted bool `db:"deleted" json:"deleted"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Accountsamo struct {
|
||||||
|
ID int64 `db:"id" json:"id"`
|
||||||
|
Accountid string `db:"accountid" json:"accountid"`
|
||||||
|
Amoid int32 `db:"amoid" json:"amoid"`
|
||||||
|
Name string `db:"name" json:"name"`
|
||||||
|
Deleted bool `db:"deleted" json:"deleted"`
|
||||||
|
Createdat time.Time `db:"createdat" json:"createdat"`
|
||||||
|
Subdomain string `db:"subdomain" json:"subdomain"`
|
||||||
|
Country string `db:"country" json:"country"`
|
||||||
|
Driveurl string `db:"driveurl" json:"driveurl"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Amocontact struct {
|
||||||
|
ID int64 `db:"id" json:"id"`
|
||||||
|
Accountid int32 `db:"accountid" json:"accountid"`
|
||||||
|
Amoid int32 `db:"amoid" json:"amoid"`
|
||||||
|
Field string `db:"field" json:"field"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Amocrmstatus struct {
|
||||||
|
ID int64 `db:"id" json:"id"`
|
||||||
|
Accountid int32 `db:"accountid" json:"accountid"`
|
||||||
|
Dealid int32 `db:"dealid" json:"dealid"`
|
||||||
|
Answerid int64 `db:"answerid" json:"answerid"`
|
||||||
|
Status string `db:"status" json:"status"`
|
||||||
|
Createdat sql.NullTime `db:"createdat" json:"createdat"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Answer struct {
|
||||||
|
ID int64 `db:"id" json:"id"`
|
||||||
|
Content sql.NullString `db:"content" json:"content"`
|
||||||
|
QuizID int64 `db:"quiz_id" json:"quiz_id"`
|
||||||
|
QuestionID int64 `db:"question_id" json:"question_id"`
|
||||||
|
Fingerprint sql.NullString `db:"fingerprint" json:"fingerprint"`
|
||||||
|
Session sql.NullString `db:"session" json:"session"`
|
||||||
|
CreatedAt sql.NullTime `db:"created_at" json:"created_at"`
|
||||||
|
Result sql.NullBool `db:"result" json:"result"`
|
||||||
|
New sql.NullBool `db:"new" json:"new"`
|
||||||
|
Deleted sql.NullBool `db:"deleted" json:"deleted"`
|
||||||
|
Email string `db:"email" json:"email"`
|
||||||
|
DeviceType string `db:"device_type" json:"device_type"`
|
||||||
|
Device string `db:"device" json:"device"`
|
||||||
|
Os string `db:"os" json:"os"`
|
||||||
|
Browser string `db:"browser" json:"browser"`
|
||||||
|
Ip string `db:"ip" json:"ip"`
|
||||||
|
Start bool `db:"start" json:"start"`
|
||||||
|
Utm json.RawMessage `db:"utm" json:"utm"`
|
||||||
|
Version int32 `db:"version" json:"version"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Bitrixaccount struct {
|
||||||
|
ID int64 `db:"id" json:"id"`
|
||||||
|
Accountid string `db:"accountid" json:"accountid"`
|
||||||
|
Bitrixid string `db:"bitrixid" json:"bitrixid"`
|
||||||
|
Deleted bool `db:"deleted" json:"deleted"`
|
||||||
|
Createdat time.Time `db:"createdat" json:"createdat"`
|
||||||
|
Subdomain string `db:"subdomain" json:"subdomain"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Bitrixaccountuser struct {
|
||||||
|
ID int64 `db:"id" json:"id"`
|
||||||
|
Accountid string `db:"accountid" json:"accountid"`
|
||||||
|
Bitrixiduserid string `db:"bitrixiduserid" json:"bitrixiduserid"`
|
||||||
|
Name string `db:"name" json:"name"`
|
||||||
|
Lastname string `db:"lastname" json:"lastname"`
|
||||||
|
Secondname string `db:"secondname" json:"secondname"`
|
||||||
|
Title string `db:"title" json:"title"`
|
||||||
|
Email string `db:"email" json:"email"`
|
||||||
|
Ufdepartment []int32 `db:"ufdepartment" json:"ufdepartment"`
|
||||||
|
Workposition string `db:"workposition" json:"workposition"`
|
||||||
|
Deleted bool `db:"deleted" json:"deleted"`
|
||||||
|
Createdat time.Time `db:"createdat" json:"createdat"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Bitrixcontact struct {
|
||||||
|
ID int64 `db:"id" json:"id"`
|
||||||
|
Accountid string `db:"accountid" json:"accountid"`
|
||||||
|
Bitrixid int32 `db:"bitrixid" json:"bitrixid"`
|
||||||
|
Field string `db:"field" json:"field"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Bitrixcrmstatus struct {
|
||||||
|
ID int64 `db:"id" json:"id"`
|
||||||
|
Accountid string `db:"accountid" json:"accountid"`
|
||||||
|
Dealid int32 `db:"dealid" json:"dealid"`
|
||||||
|
Answerid int64 `db:"answerid" json:"answerid"`
|
||||||
|
Status string `db:"status" json:"status"`
|
||||||
|
Createdat time.Time `db:"createdat" json:"createdat"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Bitrixfield struct {
|
||||||
|
ID int64 `db:"id" json:"id"`
|
||||||
|
Bitrixid string `db:"bitrixid" json:"bitrixid"`
|
||||||
|
Accountid string `db:"accountid" json:"accountid"`
|
||||||
|
Entityid interface{} `db:"entityid" json:"entityid"`
|
||||||
|
Fieldname string `db:"fieldname" json:"fieldname"`
|
||||||
|
Editfromlabel string `db:"editfromlabel" json:"editfromlabel"`
|
||||||
|
Fieldtype interface{} `db:"fieldtype" json:"fieldtype"`
|
||||||
|
Deleted bool `db:"deleted" json:"deleted"`
|
||||||
|
Createdat time.Time `db:"createdat" json:"createdat"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Bitrixrule struct {
|
||||||
|
ID int64 `db:"id" json:"id"`
|
||||||
|
Accountid string `db:"accountid" json:"accountid"`
|
||||||
|
Quizid int32 `db:"quizid" json:"quizid"`
|
||||||
|
Performerid string `db:"performerid" json:"performerid"`
|
||||||
|
Pipelineid int32 `db:"pipelineid" json:"pipelineid"`
|
||||||
|
Typeid string `db:"typeid" json:"typeid"`
|
||||||
|
Stageid string `db:"stageid" json:"stageid"`
|
||||||
|
Sourceid string `db:"sourceid" json:"sourceid"`
|
||||||
|
Statusid string `db:"statusid" json:"statusid"`
|
||||||
|
Fieldsrule json.RawMessage `db:"fieldsrule" json:"fieldsrule"`
|
||||||
|
Tagstoadd json.RawMessage `db:"tagstoadd" json:"tagstoadd"`
|
||||||
|
Deleted bool `db:"deleted" json:"deleted"`
|
||||||
|
Createdat time.Time `db:"createdat" json:"createdat"`
|
||||||
|
Leadflag bool `db:"leadflag" json:"leadflag"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Bitrixtoken struct {
|
||||||
|
Accountid string `db:"accountid" json:"accountid"`
|
||||||
|
Refreshtoken string `db:"refreshtoken" json:"refreshtoken"`
|
||||||
|
Accesstoken string `db:"accesstoken" json:"accesstoken"`
|
||||||
|
Authcode string `db:"authcode" json:"authcode"`
|
||||||
|
Expiration time.Time `db:"expiration" json:"expiration"`
|
||||||
|
Createdat time.Time `db:"createdat" json:"createdat"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Field struct {
|
||||||
|
ID int64 `db:"id" json:"id"`
|
||||||
|
Amoid int32 `db:"amoid" json:"amoid"`
|
||||||
|
Code string `db:"code" json:"code"`
|
||||||
|
Accountid int32 `db:"accountid" json:"accountid"`
|
||||||
|
Name string `db:"name" json:"name"`
|
||||||
|
Entity interface{} `db:"entity" json:"entity"`
|
||||||
|
Type interface{} `db:"type" json:"type"`
|
||||||
|
Deleted bool `db:"deleted" json:"deleted"`
|
||||||
|
Createdat sql.NullTime `db:"createdat" json:"createdat"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Gigachataudience struct {
|
||||||
|
ID int64 `db:"id" json:"id"`
|
||||||
|
Quizid int64 `db:"quizid" json:"quizid"`
|
||||||
|
Age string `db:"age" json:"age"`
|
||||||
|
Deleted bool `db:"deleted" json:"deleted"`
|
||||||
|
Createdat sql.NullTime `db:"createdat" json:"createdat"`
|
||||||
|
Sex int32 `db:"sex" json:"sex"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Leadtarget struct {
|
||||||
|
ID int64 `db:"id" json:"id"`
|
||||||
|
Accountid string `db:"accountid" json:"accountid"`
|
||||||
|
Type interface{} `db:"type" json:"type"`
|
||||||
|
Quizid int32 `db:"quizid" json:"quizid"`
|
||||||
|
Target string `db:"target" json:"target"`
|
||||||
|
Invitelink string `db:"invitelink" json:"invitelink"`
|
||||||
|
Deleted bool `db:"deleted" json:"deleted"`
|
||||||
|
Createdat time.Time `db:"createdat" json:"createdat"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Pipeline struct {
|
||||||
|
ID int64 `db:"id" json:"id"`
|
||||||
|
Amoid int32 `db:"amoid" json:"amoid"`
|
||||||
|
Accountid int32 `db:"accountid" json:"accountid"`
|
||||||
|
Name string `db:"name" json:"name"`
|
||||||
|
Isarchive bool `db:"isarchive" json:"isarchive"`
|
||||||
|
Deleted bool `db:"deleted" json:"deleted"`
|
||||||
|
Createdat sql.NullTime `db:"createdat" json:"createdat"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Pipelinebitrix struct {
|
||||||
|
ID int64 `db:"id" json:"id"`
|
||||||
|
Bitrixid int32 `db:"bitrixid" json:"bitrixid"`
|
||||||
|
Accountid string `db:"accountid" json:"accountid"`
|
||||||
|
Name string `db:"name" json:"name"`
|
||||||
|
Entitytypeid int32 `db:"entitytypeid" json:"entitytypeid"`
|
||||||
|
Deleted bool `db:"deleted" json:"deleted"`
|
||||||
|
Createdat time.Time `db:"createdat" json:"createdat"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Privilege struct {
|
||||||
|
ID int32 `db:"id" json:"id"`
|
||||||
|
Privilegeid string `db:"privilegeid" json:"privilegeid"`
|
||||||
|
AccountID uuid.UUID `db:"account_id" json:"account_id"`
|
||||||
|
PrivilegeName string `db:"privilege_name" json:"privilege_name"`
|
||||||
|
Amount int32 `db:"amount" json:"amount"`
|
||||||
|
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Question struct {
|
||||||
|
ID int64 `db:"id" json:"id"`
|
||||||
|
QuizID int64 `db:"quiz_id" json:"quiz_id"`
|
||||||
|
Title string `db:"title" json:"title"`
|
||||||
|
Description sql.NullString `db:"description" json:"description"`
|
||||||
|
Questiontype interface{} `db:"questiontype" json:"questiontype"`
|
||||||
|
Required sql.NullBool `db:"required" json:"required"`
|
||||||
|
Deleted sql.NullBool `db:"deleted" json:"deleted"`
|
||||||
|
Page sql.NullInt16 `db:"page" json:"page"`
|
||||||
|
Content sql.NullString `db:"content" json:"content"`
|
||||||
|
Version sql.NullInt16 `db:"version" json:"version"`
|
||||||
|
ParentIds []int32 `db:"parent_ids" json:"parent_ids"`
|
||||||
|
CreatedAt sql.NullTime `db:"created_at" json:"created_at"`
|
||||||
|
UpdatedAt sql.NullTime `db:"updated_at" json:"updated_at"`
|
||||||
|
Session string `db:"session" json:"session"`
|
||||||
|
Auditory int64 `db:"auditory" json:"auditory"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Quiz struct {
|
||||||
|
ID int64 `db:"id" json:"id"`
|
||||||
|
Qid uuid.NullUUID `db:"qid" json:"qid"`
|
||||||
|
Accountid string `db:"accountid" json:"accountid"`
|
||||||
|
Deleted sql.NullBool `db:"deleted" json:"deleted"`
|
||||||
|
Archived sql.NullBool `db:"archived" json:"archived"`
|
||||||
|
Fingerprinting sql.NullBool `db:"fingerprinting" json:"fingerprinting"`
|
||||||
|
Repeatable sql.NullBool `db:"repeatable" json:"repeatable"`
|
||||||
|
NotePrevented sql.NullBool `db:"note_prevented" json:"note_prevented"`
|
||||||
|
MailNotifications sql.NullBool `db:"mail_notifications" json:"mail_notifications"`
|
||||||
|
UniqueAnswers sql.NullBool `db:"unique_answers" json:"unique_answers"`
|
||||||
|
Super sql.NullBool `db:"super" json:"super"`
|
||||||
|
GroupID sql.NullInt64 `db:"group_id" json:"group_id"`
|
||||||
|
Name sql.NullString `db:"name" json:"name"`
|
||||||
|
Description sql.NullString `db:"description" json:"description"`
|
||||||
|
Config sql.NullString `db:"config" json:"config"`
|
||||||
|
Status interface{} `db:"status" json:"status"`
|
||||||
|
LimitAnswers sql.NullInt32 `db:"limit_answers" json:"limit_answers"`
|
||||||
|
DueTo sql.NullInt32 `db:"due_to" json:"due_to"`
|
||||||
|
TimeOfPassing sql.NullInt32 `db:"time_of_passing" json:"time_of_passing"`
|
||||||
|
Pausable sql.NullBool `db:"pausable" json:"pausable"`
|
||||||
|
Version sql.NullInt16 `db:"version" json:"version"`
|
||||||
|
VersionComment sql.NullString `db:"version_comment" json:"version_comment"`
|
||||||
|
ParentIds []int32 `db:"parent_ids" json:"parent_ids"`
|
||||||
|
CreatedAt sql.NullTime `db:"created_at" json:"created_at"`
|
||||||
|
UpdatedAt sql.NullTime `db:"updated_at" json:"updated_at"`
|
||||||
|
QuestionsCount sql.NullInt32 `db:"questions_count" json:"questions_count"`
|
||||||
|
AnswersCount sql.NullInt32 `db:"answers_count" json:"answers_count"`
|
||||||
|
AverageTimePassing sql.NullInt32 `db:"average_time_passing" json:"average_time_passing"`
|
||||||
|
SessionsCount sql.NullInt32 `db:"sessions_count" json:"sessions_count"`
|
||||||
|
Gigachat bool `db:"gigachat" json:"gigachat"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type QuizPrivilegeUsage struct {
|
||||||
|
ID int64 `db:"id" json:"id"`
|
||||||
|
QuizID int64 `db:"quiz_id" json:"quiz_id"`
|
||||||
|
PrivilegeID string `db:"privilege_id" json:"privilege_id"`
|
||||||
|
UsedCount int32 `db:"used_count" json:"used_count"`
|
||||||
|
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||||
|
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type QuizUtm struct {
|
||||||
|
ID int64 `db:"id" json:"id"`
|
||||||
|
Quizid int64 `db:"quizid" json:"quizid"`
|
||||||
|
Utm string `db:"utm" json:"utm"`
|
||||||
|
Deleted bool `db:"deleted" json:"deleted"`
|
||||||
|
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RespondentState struct {
|
||||||
|
ID int64 `db:"id" json:"id"`
|
||||||
|
TelegramID int32 `db:"telegram_id" json:"telegram_id"`
|
||||||
|
Quizid int32 `db:"quizid" json:"quizid"`
|
||||||
|
State int64 `db:"state" json:"state"`
|
||||||
|
Lang string `db:"lang" json:"lang"`
|
||||||
|
Contact string `db:"contact" json:"contact"`
|
||||||
|
Finish bool `db:"finish" json:"finish"`
|
||||||
|
Session sql.NullString `db:"session" json:"session"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Rule struct {
|
||||||
|
ID int64 `db:"id" json:"id"`
|
||||||
|
Accountid int32 `db:"accountid" json:"accountid"`
|
||||||
|
Quizid int32 `db:"quizid" json:"quizid"`
|
||||||
|
Performerid int32 `db:"performerid" json:"performerid"`
|
||||||
|
Pipelineid int32 `db:"pipelineid" json:"pipelineid"`
|
||||||
|
Stepid int32 `db:"stepid" json:"stepid"`
|
||||||
|
Fieldsrule json.RawMessage `db:"fieldsrule" json:"fieldsrule"`
|
||||||
|
Deleted bool `db:"deleted" json:"deleted"`
|
||||||
|
Createdat sql.NullTime `db:"createdat" json:"createdat"`
|
||||||
|
Tagstoadd json.RawMessage `db:"tagstoadd" json:"tagstoadd"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Step struct {
|
||||||
|
ID int64 `db:"id" json:"id"`
|
||||||
|
Amoid int32 `db:"amoid" json:"amoid"`
|
||||||
|
Pipelineid int32 `db:"pipelineid" json:"pipelineid"`
|
||||||
|
Accountid int32 `db:"accountid" json:"accountid"`
|
||||||
|
Name string `db:"name" json:"name"`
|
||||||
|
Color string `db:"color" json:"color"`
|
||||||
|
Deleted bool `db:"deleted" json:"deleted"`
|
||||||
|
Createdat sql.NullTime `db:"createdat" json:"createdat"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Stepbitrix struct {
|
||||||
|
ID int64 `db:"id" json:"id"`
|
||||||
|
Accountid string `db:"accountid" json:"accountid"`
|
||||||
|
Bitrixid string `db:"bitrixid" json:"bitrixid"`
|
||||||
|
Entityid string `db:"entityid" json:"entityid"`
|
||||||
|
Statusid string `db:"statusid" json:"statusid"`
|
||||||
|
Name string `db:"name" json:"name"`
|
||||||
|
Nameinit string `db:"nameinit" json:"nameinit"`
|
||||||
|
Color string `db:"color" json:"color"`
|
||||||
|
Pipelineid int32 `db:"pipelineid" json:"pipelineid"`
|
||||||
|
Deleted bool `db:"deleted" json:"deleted"`
|
||||||
|
Createdat time.Time `db:"createdat" json:"createdat"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Tag struct {
|
||||||
|
ID int64 `db:"id" json:"id"`
|
||||||
|
Amoid int32 `db:"amoid" json:"amoid"`
|
||||||
|
Accountid int32 `db:"accountid" json:"accountid"`
|
||||||
|
Entity interface{} `db:"entity" json:"entity"`
|
||||||
|
Name string `db:"name" json:"name"`
|
||||||
|
Color string `db:"color" json:"color"`
|
||||||
|
Deleted bool `db:"deleted" json:"deleted"`
|
||||||
|
Createdat sql.NullTime `db:"createdat" json:"createdat"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TelegramIntegration struct {
|
||||||
|
ID int64 `db:"id" json:"id"`
|
||||||
|
Accountid string `db:"accountid" json:"accountid"`
|
||||||
|
Quizid int32 `db:"quizid" json:"quizid"`
|
||||||
|
BotToken string `db:"bot_token" json:"bot_token"`
|
||||||
|
BotName string `db:"bot_name" json:"bot_name"`
|
||||||
|
Repeatable sql.NullBool `db:"repeatable" json:"repeatable"`
|
||||||
|
Deleted bool `db:"deleted" json:"deleted"`
|
||||||
|
Status interface{} `db:"status" json:"status"`
|
||||||
|
InstanceID int32 `db:"instance_id" json:"instance_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TelegramIntegrationInstance struct {
|
||||||
|
ID int64 `db:"id" json:"id"`
|
||||||
|
Checkout int64 `db:"checkout" json:"checkout"`
|
||||||
|
Limited int32 `db:"limited" json:"limited"`
|
||||||
|
Amount int32 `db:"amount" json:"amount"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TelegramUserQuizResult struct {
|
||||||
|
ID int64 `db:"id" json:"id"`
|
||||||
|
BotID int64 `db:"bot_id" json:"bot_id"`
|
||||||
|
QuizID int64 `db:"quiz_id" json:"quiz_id"`
|
||||||
|
CurrentField sql.NullString `db:"current_field" json:"current_field"`
|
||||||
|
UserAnswer sql.NullString `db:"user_answer" json:"user_answer"`
|
||||||
|
State interface{} `db:"state" json:"state"`
|
||||||
|
Session string `db:"session" json:"session"`
|
||||||
|
QuestionID int64 `db:"question_id" json:"question_id"`
|
||||||
|
Iter int32 `db:"iter" json:"iter"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Tgaccount struct {
|
||||||
|
ID int64 `db:"id" json:"id"`
|
||||||
|
Apiid int32 `db:"apiid" json:"apiid"`
|
||||||
|
Apihash string `db:"apihash" json:"apihash"`
|
||||||
|
Phonenumber string `db:"phonenumber" json:"phonenumber"`
|
||||||
|
Password string `db:"password" json:"password"`
|
||||||
|
Status interface{} `db:"status" json:"status"`
|
||||||
|
Deleted bool `db:"deleted" json:"deleted"`
|
||||||
|
Createdat time.Time `db:"createdat" json:"createdat"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Token struct {
|
||||||
|
Accountid string `db:"accountid" json:"accountid"`
|
||||||
|
Refreshtoken string `db:"refreshtoken" json:"refreshtoken"`
|
||||||
|
Accesstoken string `db:"accesstoken" json:"accesstoken"`
|
||||||
|
Authcode string `db:"authcode" json:"authcode"`
|
||||||
|
Expiration time.Time `db:"expiration" json:"expiration"`
|
||||||
|
Createdat sql.NullTime `db:"createdat" json:"createdat"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Usersamo struct {
|
||||||
|
ID int64 `db:"id" json:"id"`
|
||||||
|
Amoid int32 `db:"amoid" json:"amoid"`
|
||||||
|
Amouserid int32 `db:"amouserid" json:"amouserid"`
|
||||||
|
Name string `db:"name" json:"name"`
|
||||||
|
Email string `db:"email" json:"email"`
|
||||||
|
Role int32 `db:"role" json:"role"`
|
||||||
|
Group int32 `db:"Group" json:"Group"`
|
||||||
|
Deleted bool `db:"deleted" json:"deleted"`
|
||||||
|
Createdat time.Time `db:"createdat" json:"createdat"`
|
||||||
|
}
|
196
dal/sqlcgen/queries.sql.go
Normal file
196
dal/sqlcgen/queries.sql.go
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// sqlc v1.29.0
|
||||||
|
// source: queries.sql
|
||||||
|
|
||||||
|
package sqlcgen
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/lib/pq"
|
||||||
|
)
|
||||||
|
|
||||||
|
const getQuestionListByIDs = `-- name: GetQuestionListByIDs :many
|
||||||
|
SELECT id, quiz_id, title, description, questiontype, required, deleted, page, content, version, parent_ids, created_at, updated_at, session, auditory FROM question WHERE id = ANY($1::int[]) AND deleted = FALSE
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetQuestionListByIDs(ctx context.Context, dollar_1 []int32) ([]Question, error) {
|
||||||
|
rows, err := q.db.QueryContext(ctx, getQuestionListByIDs, pq.Array(dollar_1))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []Question
|
||||||
|
for rows.Next() {
|
||||||
|
var i Question
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.QuizID,
|
||||||
|
&i.Title,
|
||||||
|
&i.Description,
|
||||||
|
&i.Questiontype,
|
||||||
|
&i.Required,
|
||||||
|
&i.Deleted,
|
||||||
|
&i.Page,
|
||||||
|
&i.Content,
|
||||||
|
&i.Version,
|
||||||
|
pq.Array(&i.ParentIds),
|
||||||
|
&i.CreatedAt,
|
||||||
|
&i.UpdatedAt,
|
||||||
|
&i.Session,
|
||||||
|
&i.Auditory,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Close(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const insertAnswers = `-- name: InsertAnswers :one
|
||||||
|
INSERT INTO answer(
|
||||||
|
content,
|
||||||
|
quiz_id,
|
||||||
|
question_id,
|
||||||
|
fingerprint,
|
||||||
|
session,
|
||||||
|
result,
|
||||||
|
email,
|
||||||
|
device_type,
|
||||||
|
device,
|
||||||
|
os,
|
||||||
|
browser,
|
||||||
|
ip,
|
||||||
|
start,
|
||||||
|
utm,
|
||||||
|
version
|
||||||
|
) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15)
|
||||||
|
RETURNING id, content, quiz_id, question_id, fingerprint, session, created_at, result, new, deleted, email, device_type, device, os, browser, ip, start, utm, version
|
||||||
|
`
|
||||||
|
|
||||||
|
type InsertAnswersParams struct {
|
||||||
|
Content sql.NullString `db:"content" json:"content"`
|
||||||
|
QuizID int64 `db:"quiz_id" json:"quiz_id"`
|
||||||
|
QuestionID int64 `db:"question_id" json:"question_id"`
|
||||||
|
Fingerprint sql.NullString `db:"fingerprint" json:"fingerprint"`
|
||||||
|
Session sql.NullString `db:"session" json:"session"`
|
||||||
|
Result sql.NullBool `db:"result" json:"result"`
|
||||||
|
Email string `db:"email" json:"email"`
|
||||||
|
DeviceType string `db:"device_type" json:"device_type"`
|
||||||
|
Device string `db:"device" json:"device"`
|
||||||
|
Os string `db:"os" json:"os"`
|
||||||
|
Browser string `db:"browser" json:"browser"`
|
||||||
|
Ip string `db:"ip" json:"ip"`
|
||||||
|
Start bool `db:"start" json:"start"`
|
||||||
|
Utm json.RawMessage `db:"utm" json:"utm"`
|
||||||
|
Version int32 `db:"version" json:"version"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) InsertAnswers(ctx context.Context, arg InsertAnswersParams) (Answer, error) {
|
||||||
|
row := q.db.QueryRowContext(ctx, insertAnswers,
|
||||||
|
arg.Content,
|
||||||
|
arg.QuizID,
|
||||||
|
arg.QuestionID,
|
||||||
|
arg.Fingerprint,
|
||||||
|
arg.Session,
|
||||||
|
arg.Result,
|
||||||
|
arg.Email,
|
||||||
|
arg.DeviceType,
|
||||||
|
arg.Device,
|
||||||
|
arg.Os,
|
||||||
|
arg.Browser,
|
||||||
|
arg.Ip,
|
||||||
|
arg.Start,
|
||||||
|
arg.Utm,
|
||||||
|
arg.Version,
|
||||||
|
)
|
||||||
|
var i Answer
|
||||||
|
err := row.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.Content,
|
||||||
|
&i.QuizID,
|
||||||
|
&i.QuestionID,
|
||||||
|
&i.Fingerprint,
|
||||||
|
&i.Session,
|
||||||
|
&i.CreatedAt,
|
||||||
|
&i.Result,
|
||||||
|
&i.New,
|
||||||
|
&i.Deleted,
|
||||||
|
&i.Email,
|
||||||
|
&i.DeviceType,
|
||||||
|
&i.Device,
|
||||||
|
&i.Os,
|
||||||
|
&i.Browser,
|
||||||
|
&i.Ip,
|
||||||
|
&i.Start,
|
||||||
|
&i.Utm,
|
||||||
|
&i.Version,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const insertQuestion = `-- name: InsertQuestion :one
|
||||||
|
INSERT INTO question (
|
||||||
|
quiz_id,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
questiontype,
|
||||||
|
required,
|
||||||
|
page,
|
||||||
|
content,
|
||||||
|
parent_ids,
|
||||||
|
updated_at,
|
||||||
|
session,
|
||||||
|
auditory
|
||||||
|
)
|
||||||
|
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11)
|
||||||
|
RETURNING id, created_at, updated_at
|
||||||
|
`
|
||||||
|
|
||||||
|
type InsertQuestionParams struct {
|
||||||
|
QuizID int64 `db:"quiz_id" json:"quiz_id"`
|
||||||
|
Title string `db:"title" json:"title"`
|
||||||
|
Description sql.NullString `db:"description" json:"description"`
|
||||||
|
Questiontype interface{} `db:"questiontype" json:"questiontype"`
|
||||||
|
Required sql.NullBool `db:"required" json:"required"`
|
||||||
|
Page sql.NullInt16 `db:"page" json:"page"`
|
||||||
|
Content sql.NullString `db:"content" json:"content"`
|
||||||
|
ParentIds []int32 `db:"parent_ids" json:"parent_ids"`
|
||||||
|
UpdatedAt sql.NullTime `db:"updated_at" json:"updated_at"`
|
||||||
|
Session string `db:"session" json:"session"`
|
||||||
|
Auditory int64 `db:"auditory" json:"auditory"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type InsertQuestionRow struct {
|
||||||
|
ID int64 `db:"id" json:"id"`
|
||||||
|
CreatedAt sql.NullTime `db:"created_at" json:"created_at"`
|
||||||
|
UpdatedAt sql.NullTime `db:"updated_at" json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) InsertQuestion(ctx context.Context, arg InsertQuestionParams) (InsertQuestionRow, error) {
|
||||||
|
row := q.db.QueryRowContext(ctx, insertQuestion,
|
||||||
|
arg.QuizID,
|
||||||
|
arg.Title,
|
||||||
|
arg.Description,
|
||||||
|
arg.Questiontype,
|
||||||
|
arg.Required,
|
||||||
|
arg.Page,
|
||||||
|
arg.Content,
|
||||||
|
pq.Array(arg.ParentIds),
|
||||||
|
arg.UpdatedAt,
|
||||||
|
arg.Session,
|
||||||
|
arg.Auditory,
|
||||||
|
)
|
||||||
|
var i InsertQuestionRow
|
||||||
|
err := row.Scan(&i.ID, &i.CreatedAt, &i.UpdatedAt)
|
||||||
|
return i, err
|
||||||
|
}
|
31
go.mod
31
go.mod
@ -5,50 +5,27 @@ go 1.23.2
|
|||||||
toolchain go1.23.4
|
toolchain go1.23.4
|
||||||
|
|
||||||
require (
|
require (
|
||||||
gitea.pena/PenaSide/common v0.0.0-20250103085335-91ea31fee517
|
github.com/ClickHouse/clickhouse-go v1.5.4
|
||||||
gitea.pena/PenaSide/hlog v0.0.0-20241125221102-a54c29c002a9
|
|
||||||
gitea.pena/PenaSide/trashlog v0.0.0-20250224122049-ddb4d72e9d07
|
|
||||||
gitea.pena/SQuiz/common v0.0.0-20250610213406-ac5c9d667dc7
|
|
||||||
github.com/go-redis/redis/v8 v8.11.5
|
|
||||||
github.com/gofiber/fiber/v2 v2.52.5
|
github.com/gofiber/fiber/v2 v2.52.5
|
||||||
github.com/minio/minio-go/v7 v7.0.81
|
github.com/google/uuid v1.6.0
|
||||||
|
github.com/lib/pq v1.10.9
|
||||||
github.com/rs/xid v1.6.0
|
github.com/rs/xid v1.6.0
|
||||||
github.com/skeris/appInit v1.0.2
|
github.com/skeris/appInit v1.0.2
|
||||||
go.uber.org/zap v1.27.0
|
go.uber.org/zap v1.27.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
gitea.pena/PenaSide/linters-golang v0.0.0-20241207122018-933207374735 // indirect
|
|
||||||
github.com/ClickHouse/clickhouse-go v1.5.4 // indirect
|
|
||||||
github.com/andybalholm/brotli v1.1.0 // indirect
|
github.com/andybalholm/brotli v1.1.0 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
|
||||||
github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58 // indirect
|
github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58 // indirect
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
|
||||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
|
||||||
github.com/go-ini/ini v1.67.0 // indirect
|
|
||||||
github.com/goccy/go-json v0.10.3 // indirect
|
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
|
|
||||||
github.com/golang/protobuf v1.5.4 // indirect
|
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
|
||||||
github.com/klauspost/compress v1.17.11 // indirect
|
github.com/klauspost/compress v1.17.11 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
|
|
||||||
github.com/lib/pq v1.10.9 // indirect
|
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||||
github.com/minio/md5-simd v1.1.2 // indirect
|
|
||||||
github.com/rivo/uniseg v0.2.0 // indirect
|
github.com/rivo/uniseg v0.2.0 // indirect
|
||||||
github.com/tealeg/xlsx v1.0.5 // indirect
|
github.com/stretchr/testify v1.9.0 // indirect
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
github.com/valyala/fasthttp v1.51.0 // indirect
|
github.com/valyala/fasthttp v1.51.0 // indirect
|
||||||
github.com/valyala/tcplisten v1.0.0 // indirect
|
github.com/valyala/tcplisten v1.0.0 // indirect
|
||||||
go.etcd.io/bbolt v1.3.10 // indirect
|
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
golang.org/x/crypto v0.28.0 // indirect
|
|
||||||
golang.org/x/net v0.30.0 // indirect
|
|
||||||
golang.org/x/sys v0.28.0 // indirect
|
golang.org/x/sys v0.28.0 // indirect
|
||||||
golang.org/x/text v0.19.0 // indirect
|
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3 // indirect
|
|
||||||
google.golang.org/grpc v1.64.0 // indirect
|
|
||||||
google.golang.org/protobuf v1.34.2 // indirect
|
|
||||||
)
|
)
|
||||||
|
86
go.sum
86
go.sum
@ -1,17 +1,3 @@
|
|||||||
gitea.pena/PenaSide/common v0.0.0-20250103085335-91ea31fee517 h1:EgBe8VcdPwmxbSzYLndncP+NmR73uYuXxkTeDlEttEE=
|
|
||||||
gitea.pena/PenaSide/common v0.0.0-20250103085335-91ea31fee517/go.mod h1:91EuBCgcqgJ6mG36n2pds8sPwwfaJytLWOzY3h2YFKU=
|
|
||||||
gitea.pena/PenaSide/hlog v0.0.0-20241125221102-a54c29c002a9 h1:tBkXWNIt8icmkMMnq8MA421RWkUy4OZh5P7C3q8uCu4=
|
|
||||||
gitea.pena/PenaSide/hlog v0.0.0-20241125221102-a54c29c002a9/go.mod h1:sanhSL8aEsfcq21P+eItYiAnKAre+B67nGJmDfk2cf0=
|
|
||||||
gitea.pena/PenaSide/linters-golang v0.0.0-20241207122018-933207374735 h1:jDVeUhGBTXBibmW5dmtJg2m2+z5z2Rf6J4G0LpjVoJ0=
|
|
||||||
gitea.pena/PenaSide/linters-golang v0.0.0-20241207122018-933207374735/go.mod h1:gdd+vOT6up9STkEbxa2qESLIMZFjCmRbkcheFQCVgZU=
|
|
||||||
gitea.pena/PenaSide/trashlog v0.0.0-20250224122049-ddb4d72e9d07 h1:bUIUgzXQt16aBqSccI//BaODpRCTIaqlddSepM98QSc=
|
|
||||||
gitea.pena/PenaSide/trashlog v0.0.0-20250224122049-ddb4d72e9d07/go.mod h1:GRfWJerTUlgy82CiYAxE4tVYSVV54zEJJQy17Fx46E4=
|
|
||||||
gitea.pena/SQuiz/common v0.0.0-20250514124515-870e52266ca5 h1:C+iCsGMSUJonOTNNk8wWYOfzZ0Jjw+2IQ5FaEGwRVT0=
|
|
||||||
gitea.pena/SQuiz/common v0.0.0-20250514124515-870e52266ca5/go.mod h1:zCrUwDh0APpztKk6NUqTZv+zhjVbWpGBJiJ5z9dAH0U=
|
|
||||||
gitea.pena/SQuiz/common v0.0.0-20250610192732-9f01faa53ea9 h1:y97VEguCy1Qf/hl/XT9IfX3EgwLgAkn2+1sBDuxjo7Q=
|
|
||||||
gitea.pena/SQuiz/common v0.0.0-20250610192732-9f01faa53ea9/go.mod h1:zCrUwDh0APpztKk6NUqTZv+zhjVbWpGBJiJ5z9dAH0U=
|
|
||||||
gitea.pena/SQuiz/common v0.0.0-20250610213406-ac5c9d667dc7 h1:LdL8G958qTeDNNsLbqhnlnFbbiSMoqqULnNqxIR4c/w=
|
|
||||||
gitea.pena/SQuiz/common v0.0.0-20250610213406-ac5c9d667dc7/go.mod h1:zCrUwDh0APpztKk6NUqTZv+zhjVbWpGBJiJ5z9dAH0U=
|
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/ClickHouse/clickhouse-go v1.5.4 h1:cKjXeYLNWVJIx2J1K6H2CqyRmfwVJVY1OV1coaaFcI0=
|
github.com/ClickHouse/clickhouse-go v1.5.4 h1:cKjXeYLNWVJIx2J1K6H2CqyRmfwVJVY1OV1coaaFcI0=
|
||||||
github.com/ClickHouse/clickhouse-go v1.5.4/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI=
|
github.com/ClickHouse/clickhouse-go v1.5.4/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI=
|
||||||
@ -19,38 +5,14 @@ github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1
|
|||||||
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
||||||
github.com/bkaradzic/go-lz4 v1.0.0 h1:RXc4wYsyz985CkXXeX04y4VnZFGG8Rd43pRaHsOXAKk=
|
github.com/bkaradzic/go-lz4 v1.0.0 h1:RXc4wYsyz985CkXXeX04y4VnZFGG8Rd43pRaHsOXAKk=
|
||||||
github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4=
|
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/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
|
||||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
|
||||||
github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58 h1:F1EaeKL/ta07PY/k9Os/UFtwERei2/XzGemhpGnBKNg=
|
github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58 h1:F1EaeKL/ta07PY/k9Os/UFtwERei2/XzGemhpGnBKNg=
|
||||||
github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80=
|
github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
|
||||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
|
||||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
|
||||||
github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
|
|
||||||
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
|
||||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
|
||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
|
||||||
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
|
|
||||||
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
|
||||||
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
|
|
||||||
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
|
|
||||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||||
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
|
|
||||||
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
|
||||||
github.com/gofiber/fiber/v2 v2.52.5 h1:tWoP1MJQjGEe4GB5TUGOi7P2E0ZMMRx5ZTG4rT+yGMo=
|
github.com/gofiber/fiber/v2 v2.52.5 h1:tWoP1MJQjGEe4GB5TUGOi7P2E0ZMMRx5ZTG4rT+yGMo=
|
||||||
github.com/gofiber/fiber/v2 v2.52.5/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
|
github.com/gofiber/fiber/v2 v2.52.5/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
|
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
|
||||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
|
||||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
|
||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
@ -58,15 +20,9 @@ github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhB
|
|||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
||||||
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
|
||||||
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
|
|
||||||
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
|
|
||||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
|
||||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
@ -78,23 +34,9 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D
|
|||||||
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||||
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
|
|
||||||
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
|
|
||||||
github.com/minio/minio-go/v7 v7.0.81 h1:SzhMN0TQ6T/xSBu6Nvw3M5M8voM+Ht8RH3hE8S7zxaA=
|
|
||||||
github.com/minio/minio-go/v7 v7.0.81/go.mod h1:84gmIilaX4zcvAWWzJ5Z1WI5axN+hAbM5w25xf8xvC0=
|
|
||||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
|
||||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
|
||||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
|
||||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
|
||||||
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
|
|
||||||
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
|
|
||||||
github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I=
|
github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I=
|
||||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||||
github.com/pioz/faker v1.7.3 h1:Tez8Emuq0UN+/d6mo3a9m/9ZZ/zdfJk0c5RtRatrceM=
|
|
||||||
github.com/pioz/faker v1.7.3/go.mod h1:xSpay5w/oz1a6+ww0M3vfpe40pSIykeUPeWEc3TvVlc=
|
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||||
@ -109,18 +51,12 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
|
|||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
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/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/tealeg/xlsx v1.0.5 h1:+f8oFmvY8Gw1iUXzPk+kz+4GpbDZPK1FhPiQRd+ypgE=
|
|
||||||
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/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA=
|
github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA=
|
||||||
github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g=
|
github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g=
|
||||||
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
|
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
|
||||||
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
|
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
|
||||||
go.etcd.io/bbolt v1.3.10 h1:+BqfJTcCzTItrop8mq/lbzL8wSGtj94UO/3U31shqG0=
|
|
||||||
go.etcd.io/bbolt v1.3.10/go.mod h1:bK3UQLPJZly7IlNmV7uVHJDxfe5aK9Ll93e/74Y9oEQ=
|
|
||||||
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||||
@ -133,18 +69,12 @@ go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
|||||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
|
|
||||||
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
|
|
||||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
|
|
||||||
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
|
||||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
@ -152,31 +82,15 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
|
||||||
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
|
||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3 h1:9Xyg6I9IWQZhRVfCWjKK+l6kI0jHcPesVlMnT//aHNo=
|
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0=
|
|
||||||
google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
|
|
||||||
google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
|
|
||||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
|
||||||
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 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-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
|
||||||
gopkg.in/tucnak/telebot.v2 v2.5.0 h1:i+NynLo443Vp+Zn3Gv9JBjh3Z/PaiKAQwcnhNI7y6Po=
|
|
||||||
gopkg.in/tucnak/telebot.v2 v2.5.0/go.mod h1:BgaIIx50PSRS9pG59JH+geT82cfvoJU/IaI5TJdN3v8=
|
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||||
|
18
healthchecks/healthchecks.go
Normal file
18
healthchecks/healthchecks.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package healthchecks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Liveness(c *fiber.Ctx) error {
|
||||||
|
return c.SendStatus(fiber.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Readiness(err *error) fiber.Handler {
|
||||||
|
return func(c *fiber.Ctx) error {
|
||||||
|
if *err != nil {
|
||||||
|
return c.SendString((*err).Error())
|
||||||
|
}
|
||||||
|
return c.SendStatus(fiber.StatusOK)
|
||||||
|
}
|
||||||
|
}
|
28
middleware/middleware.go
Normal file
28
middleware/middleware.go
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"github.com/rs/xid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ContextKey string
|
||||||
|
|
||||||
|
const (
|
||||||
|
SessionKey = "X-SessionKey"
|
||||||
|
)
|
||||||
|
|
||||||
|
func AnswererChain() fiber.Handler {
|
||||||
|
return func(c *fiber.Ctx) error {
|
||||||
|
session := c.Get(SessionKey)
|
||||||
|
|
||||||
|
if session == "" {
|
||||||
|
session := xid.New().String()
|
||||||
|
c.Set(SessionKey, session)
|
||||||
|
c.Locals(ContextKey(SessionKey), session)
|
||||||
|
} else {
|
||||||
|
c.Locals(ContextKey(SessionKey), session)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Next()
|
||||||
|
}
|
||||||
|
}
|
133
model/model.go
Normal file
133
model/model.go
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
const TypeText = "text"
|
||||||
|
|
||||||
|
type Quiz struct {
|
||||||
|
Id uint64 `json:"id"`
|
||||||
|
Qid string `json:"qid"` // uuid for secure data get and post
|
||||||
|
AccountId string `json:"accountid"` // account that created the quiz
|
||||||
|
|
||||||
|
Deleted bool `json:"deleted"` // fake delete field
|
||||||
|
Archived bool `json:"archived"` // field for archiving quiz
|
||||||
|
|
||||||
|
Fingerprinting bool `json:"fingerprinting"` // field that need for storing device id
|
||||||
|
Repeatable bool `json:"repeatable"` // make it true for allow more than one quiz checkouting
|
||||||
|
NotePrevented bool `json:"note_prevented"` // note answers even if the quiz was aborted
|
||||||
|
MailNotifications bool `json:"mail_notifications"` // set true if you want get an email with every quiz passing
|
||||||
|
UniqueAnswers bool `json:"unique_answers"` // set true if we you mention only last quiz passing
|
||||||
|
|
||||||
|
Name string `json:"name"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Config string `json:"config"` // serialize json with config for page rules
|
||||||
|
Status string `json:"status"` // status of quiz as enum. see Status const higher
|
||||||
|
Limit uint64 `json:"limit"` // max count of quiz passing
|
||||||
|
DueTo uint64 `json:"due_to"` // time when quiz is end
|
||||||
|
|
||||||
|
TimeOfPassing uint64 `json:"time_of_passing"` // amount of seconds for give all appropriate answers for quiz
|
||||||
|
Pausable bool `json:"pausable"` // true allows to pause the quiz taking
|
||||||
|
|
||||||
|
Version int `json:"version"`
|
||||||
|
VersionComment string `json:"version_comment"`
|
||||||
|
ParentIds []int32 `json:"parent_ids"`
|
||||||
|
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
|
||||||
|
QuestionsCount uint64 `json:"questions_count"`
|
||||||
|
SessionCount uint64 `json:"session_count"`
|
||||||
|
PassedCount uint64 `json:"passed_count"`
|
||||||
|
AverageTime uint64 `json:"average_time"`
|
||||||
|
|
||||||
|
Super bool `json:"super"`
|
||||||
|
GroupId uint64 `json:"group_id"`
|
||||||
|
|
||||||
|
GigaChat bool `json:"giga_chat"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Question struct {
|
||||||
|
Id uint64 `json:"id"`
|
||||||
|
QuizId uint64 `json:"quiz_id"` // relation to quiz table
|
||||||
|
|
||||||
|
Title string `json:"title"` // title of question
|
||||||
|
Description string `json:"description"` // html\text representation of question and question description for answerer
|
||||||
|
Type string `json:"type"` // type field. enum with constants from consts higher
|
||||||
|
|
||||||
|
Required bool `json:"required"` // answerer must answer this question
|
||||||
|
Deleted bool `json:"deleted"` // fake deleting field
|
||||||
|
|
||||||
|
Page int `json:"page"` // set page number for question
|
||||||
|
//serialized json. caption for button type, array of key-value pairs for checkbox and select types,
|
||||||
|
// placeholder and input title for text and file types
|
||||||
|
Content string `json:"content"`
|
||||||
|
|
||||||
|
Version int `json:"version"`
|
||||||
|
//todo check the best choice: question duplication and no statistics about the most unstable question or
|
||||||
|
//low performance of high complexity join-on-array query
|
||||||
|
ParentIds []int32 `json:"parent_ids"`
|
||||||
|
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
|
||||||
|
Session string `json:"session"`
|
||||||
|
Auditory int64 `json:"auditory"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Answer struct {
|
||||||
|
Id uint64
|
||||||
|
|
||||||
|
Content string `json:"content"` //serialized json. empty for buttons
|
||||||
|
|
||||||
|
QuestionId uint64 `json:"question_id"` // relation for quiz
|
||||||
|
QuizId uint64 // relation for quiz
|
||||||
|
|
||||||
|
Fingerprint string // device Id
|
||||||
|
Session string // xid of session
|
||||||
|
|
||||||
|
Result bool
|
||||||
|
CreatedAt time.Time
|
||||||
|
New bool `json:"new"`
|
||||||
|
Deleted bool
|
||||||
|
Email string
|
||||||
|
|
||||||
|
DeviceType string
|
||||||
|
Device string
|
||||||
|
Browser string
|
||||||
|
IP string
|
||||||
|
OS string
|
||||||
|
Start bool
|
||||||
|
Utm UTMSavingMap
|
||||||
|
Version int32
|
||||||
|
}
|
||||||
|
|
||||||
|
type UTMSavingMap map[string]string
|
||||||
|
|
||||||
|
type ResultContent struct {
|
||||||
|
Text string `json:"text"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
Phone string `json:"phone"`
|
||||||
|
Address string `json:"address"`
|
||||||
|
Telegram string `json:"telegram"`
|
||||||
|
Wechat string `json:"wechat"`
|
||||||
|
Viber string `json:"viber"`
|
||||||
|
Vk string `json:"vk"`
|
||||||
|
Skype string `json:"skype"`
|
||||||
|
Whatsup string `json:"whatsup"`
|
||||||
|
Messenger string `json:"messenger"`
|
||||||
|
Custom map[string]string `json:"customs"`
|
||||||
|
Start bool `json:"start"`
|
||||||
|
//IMGContent ImageContent `json:"imagecontent"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type QuizConfig struct {
|
||||||
|
Mailing ResultInfo `json:"resultInfo"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ResultInfo struct {
|
||||||
|
When string `json:"when"` // before|after|email
|
||||||
|
Theme string `json:"theme"` // тема письма
|
||||||
|
Reply string `json:"reply"` // email для ответов, указывается в создании письма
|
||||||
|
ReplName string `json:"repl_name"` // имя отправителя
|
||||||
|
}
|
@ -1,70 +0,0 @@
|
|||||||
package models
|
|
||||||
|
|
||||||
type AllFields struct {
|
|
||||||
CtxUserIP string
|
|
||||||
CtxUserPort string
|
|
||||||
KeyDomain string
|
|
||||||
KeyPath string
|
|
||||||
KeyOS string
|
|
||||||
KeyDevice string
|
|
||||||
KeyDeviceType string
|
|
||||||
KeyBrowser string
|
|
||||||
CtxQuiz string
|
|
||||||
CtxReferrer string
|
|
||||||
CtxIDInt int64
|
|
||||||
CtxSession string
|
|
||||||
CtxQuizID int64
|
|
||||||
CtxQuestionID int64
|
|
||||||
}
|
|
||||||
|
|
||||||
type InfoQuizOpen struct { // при получении настроек квиза
|
|
||||||
KeyOS string
|
|
||||||
KeyDevice string
|
|
||||||
KeyDeviceType string
|
|
||||||
KeyBrowser string // то самое, что получается из заголовков и складывается в модель ответа. на самом деле, ему место тут
|
|
||||||
CtxQuiz string // айдишник квиза, который qid
|
|
||||||
CtxQuizID int64 // айдишник квиза
|
|
||||||
CtxReferrer string // тоже из заголовков
|
|
||||||
CtxIDInt int64 // айдишник ответа
|
|
||||||
CtxSession string // сессия
|
|
||||||
}
|
|
||||||
|
|
||||||
type InfoAnswer struct { // при любом ответе на вопрос
|
|
||||||
KeyOS string
|
|
||||||
KeyDevice string
|
|
||||||
KeyDeviceType string
|
|
||||||
KeyBrowser string // то самое, что получается из заголовков и складывается в модель ответа. на самом деле, ему место тут
|
|
||||||
CtxQuiz string // айдишник квиза, который qid
|
|
||||||
CtxQuizID int64 // айдишник квиза
|
|
||||||
CtxReferrer string // тоже из заголовков
|
|
||||||
CtxQuestionID int64 // айдишник вопроса, на который отвечено
|
|
||||||
CtxIDInt int64 // айдишник ответа
|
|
||||||
CtxSession string // сессия
|
|
||||||
}
|
|
||||||
|
|
||||||
type InfoResult struct { // если ответ на вопрос с типом result
|
|
||||||
KeyOS string
|
|
||||||
KeyDevice string
|
|
||||||
KeyDeviceType string
|
|
||||||
KeyBrowser string // то самое, что получается из заголовков и складывается в модель ответа. на самом деле, ему место тут
|
|
||||||
CtxQuiz string // айдишник квиза, который qid
|
|
||||||
CtxQuizID int64 // айдишник квиза
|
|
||||||
CtxReferrer string // тоже из заголовков
|
|
||||||
CtxQuestionID int64 // айдишник вопроса, на который отвечено
|
|
||||||
CtxIDInt int64 // айдишник ответа
|
|
||||||
CtxSession string // сессия
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo понять для чего это событие вроде как контакты приходят в ответахс с result = true там парситься контент с контактной информацией
|
|
||||||
type InfoContactForm struct { // если ответ на вопрос с типом result, без result == true (возможно перепутал с предыдущим. в этом ответе приходят контактные данные респондента)
|
|
||||||
KeyOS string
|
|
||||||
KeyDevice string
|
|
||||||
KeyDeviceType string
|
|
||||||
KeyBrowser string // то самое, что получается из заголовков и складывается в модель ответа. на самом деле, ему место тут
|
|
||||||
CtxQuiz string // айдишник квиза, который qid
|
|
||||||
CtxQuizID int64 // айдишник квиза
|
|
||||||
CtxReferrer string // тоже из заголовков
|
|
||||||
CtxQuestionID int64 // айдишник вопроса, на который отвечено
|
|
||||||
CtxIDInt int64 // айдишник ответа
|
|
||||||
CtxSession string // сессия
|
|
||||||
}
|
|
3
quiz_config.json
Normal file
3
quiz_config.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
|
||||||
|
}
|
@ -1,72 +0,0 @@
|
|||||||
package savewc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"gitea.pena/PenaSide/hlog"
|
|
||||||
"gitea.pena/SQuiz/common/model"
|
|
||||||
"github.com/go-redis/redis/v8"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type DepsForClient struct {
|
|
||||||
WorkerSendClientCh chan model.Answer
|
|
||||||
Redis *redis.Client
|
|
||||||
}
|
|
||||||
|
|
||||||
type SaveForClient struct {
|
|
||||||
deps DepsForClient
|
|
||||||
errChan chan<- error
|
|
||||||
logger hlog.Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSaveClientWorker(deps DepsForClient, errChan chan<- error, logger hlog.Logger) *SaveForClient {
|
|
||||||
return &SaveForClient{
|
|
||||||
deps: deps,
|
|
||||||
errChan: errChan,
|
|
||||||
logger: logger,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *SaveForClient) Start(ctx context.Context) {
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case answer, ok := <-w.deps.WorkerSendClientCh:
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fmt.Println("SAVECLINT")
|
|
||||||
err := w.saveAnswer(ctx, answer)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Error save answer")
|
|
||||||
w.errChan <- err
|
|
||||||
}
|
|
||||||
|
|
||||||
case <-ctx.Done():
|
|
||||||
fmt.Println("Save for client worker terminated")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *SaveForClient) saveAnswer(ctx context.Context, answer model.Answer) error {
|
|
||||||
answerJSON, err := json.Marshal(answer)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Error marshal answer to redis", err)
|
|
||||||
w.errChan <- err
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
key := fmt.Sprintf("answer:%d", time.Now().UnixNano())
|
|
||||||
|
|
||||||
err = w.deps.Redis.Set(ctx, key, answerJSON, 0).Err()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Error saving answer to redis", err)
|
|
||||||
w.errChan <- err
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,71 +0,0 @@
|
|||||||
package savewc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"gitea.pena/PenaSide/hlog"
|
|
||||||
"gitea.pena/SQuiz/common/model"
|
|
||||||
"github.com/go-redis/redis/v8"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type DepsForResp struct {
|
|
||||||
WorkerRespondentCh chan []model.Answer
|
|
||||||
Redis *redis.Client
|
|
||||||
}
|
|
||||||
|
|
||||||
type SaveForRespondent struct {
|
|
||||||
deps DepsForResp
|
|
||||||
errChan chan<- error
|
|
||||||
logger hlog.Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSaveRespWorker(deps DepsForResp, errChan chan<- error, logger hlog.Logger) *SaveForRespondent {
|
|
||||||
return &SaveForRespondent{
|
|
||||||
deps: deps,
|
|
||||||
errChan: errChan,
|
|
||||||
logger: logger,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *SaveForRespondent) Start(ctx context.Context) {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case answer, ok := <-w.deps.WorkerRespondentCh:
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fmt.Println("SAVERESP")
|
|
||||||
err := w.saveAnswers(ctx, answer)
|
|
||||||
if err != nil {
|
|
||||||
w.logger.Module("Error save answers")
|
|
||||||
w.errChan <- err
|
|
||||||
}
|
|
||||||
|
|
||||||
case <-ctx.Done():
|
|
||||||
w.logger.Module("Save for respondent worker terminated")
|
|
||||||
return
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *SaveForRespondent) saveAnswers(ctx context.Context, answers []model.Answer) error {
|
|
||||||
for _, answer := range answers {
|
|
||||||
answerJSON, err := json.Marshal(answer)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Error marshal answer", err)
|
|
||||||
w.errChan <- err
|
|
||||||
}
|
|
||||||
|
|
||||||
key := fmt.Sprintf("toRespondent:%d", time.Now().UnixNano())
|
|
||||||
|
|
||||||
err = w.deps.Redis.Set(ctx, key, answerJSON, 0).Err()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Error setting to redis", err)
|
|
||||||
w.errChan <- err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
21
service/config.go
Normal file
21
service/config.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func loadQuizDataConfig() (GetQuizDataResp, error) {
|
||||||
|
configPath := "./quiz_config.json"
|
||||||
|
data, err := os.ReadFile(configPath)
|
||||||
|
if err != nil {
|
||||||
|
return GetQuizDataResp{}, fmt.Errorf("failed to read config file %s: %w", configPath, err)
|
||||||
|
}
|
||||||
|
var config GetQuizDataResp
|
||||||
|
if err := json.Unmarshal(data, &config); err != nil {
|
||||||
|
return GetQuizDataResp{}, fmt.Errorf("failed to parse config file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return config, nil
|
||||||
|
}
|
@ -1,23 +1,17 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"unicode/utf8"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"gitea.pena/PenaSide/common/log_mw"
|
|
||||||
"gitea.pena/SQuiz/answerer/clients"
|
"gitea.pena/SQuiz/answerer/clients"
|
||||||
"gitea.pena/SQuiz/answerer/dal"
|
quizdal "gitea.pena/SQuiz/answerer/dal"
|
||||||
"gitea.pena/SQuiz/answerer/models"
|
"gitea.pena/SQuiz/answerer/middleware"
|
||||||
quizdal "gitea.pena/SQuiz/common/dal"
|
"gitea.pena/SQuiz/answerer/model"
|
||||||
"gitea.pena/SQuiz/common/middleware"
|
|
||||||
"gitea.pena/SQuiz/common/model"
|
|
||||||
"gitea.pena/SQuiz/common/utils"
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"net/url"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
"github.com/rs/xid"
|
"github.com/rs/xid"
|
||||||
)
|
)
|
||||||
@ -28,58 +22,35 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Service struct {
|
type Service struct {
|
||||||
store *dal.Storer
|
dal *quizdal.DAL
|
||||||
dal *quizdal.DAL
|
aiClient *clients.AIClient
|
||||||
batch []model.Answer
|
quizConfigData GetQuizDataResp
|
||||||
m sync.Mutex
|
|
||||||
workerRespondentCh chan<- []model.Answer
|
|
||||||
workerSendClientCh chan<- model.Answer
|
|
||||||
encrypt *utils.Encrypt
|
|
||||||
redirectURl string
|
|
||||||
aiClient *clients.AIClient
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ServiceDeps struct {
|
type ServiceDeps struct {
|
||||||
Store *dal.Storer
|
Dal *quizdal.DAL
|
||||||
Dal *quizdal.DAL
|
AiClient *clients.AIClient
|
||||||
WorkerRespondentCh chan<- []model.Answer
|
|
||||||
WorkerSendClientCh chan<- model.Answer
|
|
||||||
Encrypt *utils.Encrypt
|
|
||||||
RedirectURl string
|
|
||||||
AiClient *clients.AIClient
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(deps ServiceDeps) *Service {
|
func New(deps ServiceDeps) (*Service, error) {
|
||||||
return &Service{
|
quizData, err := loadQuizDataConfig()
|
||||||
store: deps.Store,
|
if err != nil {
|
||||||
dal: deps.Dal,
|
return nil, err
|
||||||
m: sync.Mutex{},
|
|
||||||
batch: []model.Answer{},
|
|
||||||
workerRespondentCh: deps.WorkerRespondentCh,
|
|
||||||
workerSendClientCh: deps.WorkerSendClientCh,
|
|
||||||
encrypt: deps.Encrypt,
|
|
||||||
redirectURl: deps.RedirectURl,
|
|
||||||
aiClient: deps.AiClient,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return &Service{
|
||||||
|
dal: deps.Dal,
|
||||||
|
aiClient: deps.AiClient,
|
||||||
|
quizConfigData: quizData,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) Register(app *fiber.App) *fiber.App {
|
func (s *Service) Register(app *fiber.App) *fiber.App {
|
||||||
app.Post("/answer", s.PutAnswersOnePiece)
|
app.Post("/answer", s.PutAnswersOnePiece)
|
||||||
app.Post("/settings", s.GetQuizData)
|
app.Post("/settings", s.GetQuizData)
|
||||||
app.Get("/logo", s.MiniPart)
|
|
||||||
return app
|
return app
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetQuizDataReq request data for get data for user
|
|
||||||
type GetQuizDataReq struct {
|
|
||||||
QuizId string `json:"quiz_id"` // relation to quiz
|
|
||||||
Limit uint64 `json:"limit"`
|
|
||||||
Page uint64 `json:"page"`
|
|
||||||
NeedConfig bool `json:"need_config"` // true if you need not only question page
|
|
||||||
Auditory int64 `json:"auditory"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetQuizDataResp response with prepared data for user
|
|
||||||
type GetQuizDataResp struct {
|
type GetQuizDataResp struct {
|
||||||
Settings *ShavedQuiz `json:"settings,omitempty"`
|
Settings *ShavedQuiz `json:"settings,omitempty"`
|
||||||
Items []ShavedQuestion `json:"items"`
|
Items []ShavedQuestion `json:"items"`
|
||||||
@ -87,7 +58,6 @@ type GetQuizDataResp struct {
|
|||||||
ShowBadge bool `json:"show_badge"`
|
ShowBadge bool `json:"show_badge"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ShavedQuiz shortened struct for delivery data to customer
|
|
||||||
type ShavedQuiz struct {
|
type ShavedQuiz struct {
|
||||||
Fingerprinting bool `json:"fp"`
|
Fingerprinting bool `json:"fp"`
|
||||||
Repeatable bool `json:"rep"`
|
Repeatable bool `json:"rep"`
|
||||||
@ -100,7 +70,6 @@ type ShavedQuiz struct {
|
|||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ShavedQuestion shortened struct for delivery data to customer
|
|
||||||
type ShavedQuestion struct {
|
type ShavedQuestion struct {
|
||||||
Id uint64 `json:"id"`
|
Id uint64 `json:"id"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
@ -113,209 +82,7 @@ type ShavedQuestion struct {
|
|||||||
|
|
||||||
// GetQuizData handler for obtaining data for quiz front rendering
|
// GetQuizData handler for obtaining data for quiz front rendering
|
||||||
func (s *Service) GetQuizData(c *fiber.Ctx) error {
|
func (s *Service) GetQuizData(c *fiber.Ctx) error {
|
||||||
cs, ok := c.Context().Value(middleware.ContextKey(middleware.SessionKey)).(string)
|
return c.Status(fiber.StatusOK).JSON(s.quizConfigData)
|
||||||
if !ok {
|
|
||||||
return c.Status(fiber.StatusUnauthorized).SendString("no session in cookie")
|
|
||||||
}
|
|
||||||
|
|
||||||
hlogger := log_mw.ExtractLogger(c)
|
|
||||||
var req GetQuizDataReq
|
|
||||||
if err := c.BodyParser(&req); err != nil {
|
|
||||||
return c.Status(fiber.StatusBadRequest).SendString("Invalid request data")
|
|
||||||
}
|
|
||||||
|
|
||||||
if req.QuizId == "" {
|
|
||||||
return c.Status(fiber.StatusBadRequest).SendString("Invalid request data")
|
|
||||||
}
|
|
||||||
|
|
||||||
if req.Limit == 0 && !req.NeedConfig {
|
|
||||||
return c.Status(fiber.StatusLengthRequired).SendString("no data requested")
|
|
||||||
}
|
|
||||||
|
|
||||||
quiz, err := s.dal.QuizRepo.GetQuizByQid(c.Context(), req.QuizId)
|
|
||||||
if err != nil {
|
|
||||||
return c.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if req.Limit == 0 && req.NeedConfig {
|
|
||||||
quizDto := dao2dtoQuiz(quiz)
|
|
||||||
return c.Status(fiber.StatusOK).JSON(GetQuizDataResp{
|
|
||||||
Settings: &quizDto,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if quiz.UniqueAnswers {
|
|
||||||
//todo implement after creating store answers
|
|
||||||
}
|
|
||||||
if quiz.Status != model.StatusStart && quiz.Status != model.StatusAI {
|
|
||||||
return c.Status(fiber.StatusLocked).SendString("quiz is inactive")
|
|
||||||
}
|
|
||||||
|
|
||||||
if quiz.Limit > 0 {
|
|
||||||
// todo implement after creating store answer
|
|
||||||
}
|
|
||||||
|
|
||||||
if quiz.DueTo < uint64(time.Now().Unix()) && quiz.DueTo > 0 {
|
|
||||||
return c.Status(fiber.StatusGone).SendString("quiz timeouted")
|
|
||||||
}
|
|
||||||
|
|
||||||
account, err := s.dal.AccountRepo.GetAccountByID(c.Context(), quiz.AccountId)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return c.Status(fiber.StatusInternalServerError).SendString("can`t get account by quiz.AccountId")
|
|
||||||
}
|
|
||||||
|
|
||||||
showBadge := true
|
|
||||||
|
|
||||||
if priv, ok := account.Privileges["squizHideBadge"]; ok {
|
|
||||||
expiration := priv.CreatedAt.Add(time.Duration(priv.Amount) * 24 * time.Hour)
|
|
||||||
|
|
||||||
if time.Now().Before(expiration) {
|
|
||||||
showBadge = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var questions []model.Question
|
|
||||||
var cnt uint64
|
|
||||||
|
|
||||||
if quiz.Status == model.StatusAI {
|
|
||||||
if req.Page >= 1 {
|
|
||||||
req.Page+=1
|
|
||||||
}
|
|
||||||
questions, cnt, err = s.dal.QuestionRepo.GetQuestionsAI(c.Context(), int64(quiz.Id), cs, int32(req.Limit), int32(req.Page*req.Limit), req.Auditory)
|
|
||||||
if err != nil {
|
|
||||||
return c.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
questions, cnt, err = s.dal.QuestionRepo.GetQuestionList(
|
|
||||||
c.Context(),
|
|
||||||
req.Limit,
|
|
||||||
req.Page*req.Limit,
|
|
||||||
0, 0, quiz.Id, false, false, "", "", req.Auditory,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return c.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result := GetQuizDataResp{
|
|
||||||
Count: cnt,
|
|
||||||
Items: []ShavedQuestion{},
|
|
||||||
ShowBadge: showBadge,
|
|
||||||
}
|
|
||||||
|
|
||||||
if req.NeedConfig {
|
|
||||||
quizDto := dao2dtoQuiz(quiz)
|
|
||||||
result.Settings = &quizDto
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, q := range questions {
|
|
||||||
result.Items = append(result.Items, dao2dtoQuestion(q))
|
|
||||||
}
|
|
||||||
|
|
||||||
utmData := model.UTMSavingMap{
|
|
||||||
"utm_content": c.Query("utm_content"),
|
|
||||||
"utm_medium": c.Query("utm_medium"),
|
|
||||||
"utm_campaign": c.Query("utm_campaign"),
|
|
||||||
"utm_source": c.Query("utm_source"),
|
|
||||||
"utm_term": c.Query("utm_term"),
|
|
||||||
"utm_referrer": c.Query("utm_referrer"),
|
|
||||||
"roistat": c.Query("roistat"),
|
|
||||||
"referrer": c.Query("referrer"),
|
|
||||||
"openstat_service": c.Query("openstat_service"),
|
|
||||||
"openstat_campaign": c.Query("openstat_campaign"),
|
|
||||||
"openstat_ad": c.Query("openstat_ad"),
|
|
||||||
"openstat_source": c.Query("openstat_source"),
|
|
||||||
"from": c.Query("from"),
|
|
||||||
"gclientid": c.Query("gclientid"),
|
|
||||||
"_ym_uid": c.Query("_ym_uid"),
|
|
||||||
"_ym_counter": c.Query("_ym_counter"),
|
|
||||||
"gclid": c.Query("gclid"),
|
|
||||||
"yclid": c.Query("yclid"),
|
|
||||||
"fbclid": c.Query("fbclid"),
|
|
||||||
}
|
|
||||||
|
|
||||||
deviceType := c.Get("DeviceType")
|
|
||||||
os := c.Get("OS")
|
|
||||||
browser := c.Get("Browser")
|
|
||||||
ip := c.IP()
|
|
||||||
device := c.Get("Device")
|
|
||||||
referrer := c.Get("Referer")
|
|
||||||
fp := ""
|
|
||||||
if cfp := c.Cookies(fingerprintCookie); cfp != "" {
|
|
||||||
fp = cfp
|
|
||||||
}
|
|
||||||
|
|
||||||
if req.NeedConfig {
|
|
||||||
if len(questions) == 0 {
|
|
||||||
if req.Auditory != 0 {
|
|
||||||
return c.Status(fiber.StatusAccepted).SendString("questions are not ready yet")
|
|
||||||
}
|
|
||||||
return c.Status(fiber.StatusNotFound).SendString("question not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
answers, errs := s.dal.AnswerRepo.CreateAnswers(c.Context(), []model.Answer{{
|
|
||||||
Content: "start",
|
|
||||||
QuestionId: questions[0].Id,
|
|
||||||
QuizId: quiz.Id,
|
|
||||||
Start: true,
|
|
||||||
DeviceType: deviceType,
|
|
||||||
Device: device,
|
|
||||||
Browser: browser,
|
|
||||||
IP: ip,
|
|
||||||
OS: os,
|
|
||||||
Utm: utmData,
|
|
||||||
}}, cs, fp, quiz.Id)
|
|
||||||
if len(errs) != 0 {
|
|
||||||
return c.Status(fiber.StatusInternalServerError).SendString(errs[0].Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
hlogger.Emit(models.InfoQuizOpen{
|
|
||||||
KeyOS: os,
|
|
||||||
KeyDevice: device,
|
|
||||||
KeyDeviceType: deviceType,
|
|
||||||
KeyBrowser: browser,
|
|
||||||
CtxQuiz: req.QuizId,
|
|
||||||
CtxQuizID: int64(quiz.Id),
|
|
||||||
CtxReferrer: referrer,
|
|
||||||
CtxIDInt: int64(answers[0].Id),
|
|
||||||
CtxSession: cs,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
fmt.Println("SETTIIIIII", cnt <= req.Limit, result)
|
|
||||||
|
|
||||||
if cnt <= req.Limit {
|
|
||||||
return c.Status(fiber.StatusOK).JSON(result)
|
|
||||||
} else {
|
|
||||||
return c.Status(fiber.StatusPartialContent).JSON(result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func dao2dtoQuestion(question model.Question) ShavedQuestion {
|
|
||||||
return ShavedQuestion{
|
|
||||||
Id: question.Id,
|
|
||||||
Title: question.Title,
|
|
||||||
Description: question.Description,
|
|
||||||
Type: question.Type,
|
|
||||||
Required: false,
|
|
||||||
Page: question.Page,
|
|
||||||
Content: question.Content,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func dao2dtoQuiz(quiz model.Quiz) ShavedQuiz {
|
|
||||||
|
|
||||||
return ShavedQuiz{
|
|
||||||
Fingerprinting: quiz.Fingerprinting,
|
|
||||||
Repeatable: quiz.Repeatable,
|
|
||||||
Name: quiz.Name,
|
|
||||||
Config: quiz.Config,
|
|
||||||
Limit: quiz.Limit,
|
|
||||||
DueTo: quiz.DueTo,
|
|
||||||
TimeOfPassing: quiz.TimeOfPassing,
|
|
||||||
Pausable: quiz.Pausable,
|
|
||||||
Status: quiz.Status,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MB Size constants
|
// MB Size constants
|
||||||
@ -329,17 +96,12 @@ type PutAnswersResponse struct {
|
|||||||
Stored []uint64 `json:"stored"`
|
Stored []uint64 `json:"stored"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo не отдавать ответы с типом start но сохранять брать из контента ответа
|
|
||||||
// поле бул также в GetQuizdata не отдавать его, но по запросам идет GetQuizdata получаем данные квиза
|
|
||||||
// аотом отправляем ответы в PutAnswers и сохраяняем их подумать как делать
|
|
||||||
func (s *Service) PutAnswersOnePiece(c *fiber.Ctx) error {
|
func (s *Service) PutAnswersOnePiece(c *fiber.Ctx) error {
|
||||||
cs, ok := c.Context().Value(middleware.ContextKey(middleware.SessionKey)).(string)
|
cs, ok := c.Context().Value(middleware.ContextKey(middleware.SessionKey)).(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
return c.Status(fiber.StatusUnauthorized).SendString("no session in cookie")
|
return c.Status(fiber.StatusUnauthorized).SendString("no session in cookie")
|
||||||
}
|
}
|
||||||
|
|
||||||
hlogger := log_mw.ExtractLogger(c)
|
|
||||||
|
|
||||||
form, err := c.MultipartForm()
|
form, err := c.MultipartForm()
|
||||||
if err != nil || form == nil || form.File == nil {
|
if err != nil || form == nil || form.File == nil {
|
||||||
return c.Status(fiber.StatusBadRequest).SendString("expecting multipart form file")
|
return c.Status(fiber.StatusBadRequest).SendString("expecting multipart form file")
|
||||||
@ -361,8 +123,8 @@ func (s *Service) PutAnswersOnePiece(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
answersRaw, answers, trueRes []model.Answer
|
answersRaw, answers []model.Answer
|
||||||
errs []error
|
errs []error
|
||||||
)
|
)
|
||||||
|
|
||||||
deviceType := c.Get("DeviceType")
|
deviceType := c.Get("DeviceType")
|
||||||
@ -370,7 +132,6 @@ func (s *Service) PutAnswersOnePiece(c *fiber.Ctx) error {
|
|||||||
browser := c.Get("Browser")
|
browser := c.Get("Browser")
|
||||||
ip := c.IP()
|
ip := c.IP()
|
||||||
device := c.Get("Device")
|
device := c.Get("Device")
|
||||||
referrer := c.Get("Referer")
|
|
||||||
|
|
||||||
if err := json.Unmarshal([]byte(answersStr[0]), &answersRaw); err != nil {
|
if err := json.Unmarshal([]byte(answersStr[0]), &answersRaw); err != nil {
|
||||||
return c.Status(fiber.StatusBadRequest).SendString("not valid answers string")
|
return c.Status(fiber.StatusBadRequest).SendString("not valid answers string")
|
||||||
@ -386,7 +147,7 @@ func (s *Service) PutAnswersOnePiece(c *fiber.Ctx) error {
|
|||||||
fp = cfp
|
fp = cfp
|
||||||
}
|
}
|
||||||
|
|
||||||
quiz, err := s.dal.QuizRepo.GetQuizByQid(c.Context(), quizID[0])
|
quiz, err := s.dal.GetQuizByQid(c.Context(), quizID[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.Status(fiber.StatusInternalServerError).SendString("can not get quiz")
|
return c.Status(fiber.StatusInternalServerError).SendString("can not get quiz")
|
||||||
}
|
}
|
||||||
@ -394,42 +155,39 @@ func (s *Service) PutAnswersOnePiece(c *fiber.Ctx) error {
|
|||||||
fileIDMap := make(map[uint64]string)
|
fileIDMap := make(map[uint64]string)
|
||||||
|
|
||||||
for _, ans := range answersRaw {
|
for _, ans := range answersRaw {
|
||||||
|
final := false
|
||||||
if quiz.Status == model.StatusAI {
|
if finalStr, exists := form.Value["final"]; exists && len(finalStr) > 0 {
|
||||||
final := false
|
parsedFinal, err := strconv.ParseBool(finalStr[0])
|
||||||
if finalStr, exists := form.Value["final"]; exists && len(finalStr) > 0 {
|
|
||||||
parsedFinal, err := strconv.ParseBool(finalStr[0])
|
|
||||||
if err != nil {
|
|
||||||
return c.Status(fiber.StatusBadRequest).SendString("invalid final value")
|
|
||||||
}
|
|
||||||
final = parsedFinal
|
|
||||||
}
|
|
||||||
|
|
||||||
question, err := s.dal.QuestionRepo.GetQuestionListByIDs(c.Context(), []int32{int32(ans.QuestionId)})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.Status(fiber.StatusInternalServerError).SendString("can not get questions")
|
return c.Status(fiber.StatusBadRequest).SendString("invalid final value")
|
||||||
}
|
}
|
||||||
|
final = parsedFinal
|
||||||
|
}
|
||||||
|
|
||||||
if len(question) == 0 {
|
question, err := s.dal.GetQuestionListByIDs(c.Context(), []int32{int32(ans.QuestionId)})
|
||||||
return c.Status(fiber.StatusNotFound).SendString("no questions found")
|
if err != nil {
|
||||||
}
|
return c.Status(fiber.StatusInternalServerError).SendString("can not get questions")
|
||||||
|
}
|
||||||
|
|
||||||
questionText, err := s.aiClient.SendAnswerer(final, question[0].Type, ans.Content, cs)
|
if len(question) == 0 {
|
||||||
if err != nil {
|
return c.Status(fiber.StatusNotFound).SendString("no questions found")
|
||||||
return c.Status(fiber.StatusInternalServerError).SendString(fmt.Sprintf("failed send answer to ai, err: %s", err.Error()))
|
}
|
||||||
}
|
|
||||||
|
|
||||||
_, err = s.dal.QuestionRepo.CreateQuestion(c.Context(), &model.Question{
|
questionText, err := s.aiClient.SendAnswerer(final, question[0].Type, ans.Content, cs)
|
||||||
QuizId: quiz.Id,
|
if err != nil {
|
||||||
Title: truncateUTF8(questionText,512),
|
return c.Status(fiber.StatusInternalServerError).SendString(fmt.Sprintf("failed send answer to ai, err: %s", err.Error()))
|
||||||
Type: model.TypeText,
|
}
|
||||||
Session: cs,
|
|
||||||
Content: `{"id":"gcZ-9SET-sM6stZSzQMmu","hint":{"text":" ","video":" "},"rule":{"children":[],"main":[],"parentId":" ","default":" "},"back":" ","originalBack":" ","autofill":false,"placeholder":" ","innerNameCheck":false,"innerName":" ","required":false,"answerType":"single","onlyNumbers":false}`,
|
_, err = s.dal.CreateQuestion(c.Context(), &model.Question{
|
||||||
Description: questionText,
|
QuizId: quiz.Id,
|
||||||
})
|
Title: truncateUTF8(questionText, 512),
|
||||||
if err != nil {
|
Type: model.TypeText,
|
||||||
return c.Status(fiber.StatusInternalServerError).SendString(fmt.Sprintf("failed create question type ai, err: %s", err.Error()))
|
Session: cs,
|
||||||
}
|
Content: `{"id":"gcZ-9SET-sM6stZSzQMmu","hint":{"text":" ","video":" "},"rule":{"children":[],"main":[],"parentId":" ","default":" "},"back":" ","originalBack":" ","autofill":false,"placeholder":" ","innerNameCheck":false,"innerName":" ","required":false,"answerType":"single","onlyNumbers":false}`,
|
||||||
|
Description: questionText,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusInternalServerError).SendString(fmt.Sprintf("failed create question type ai, err: %s", err.Error()))
|
||||||
}
|
}
|
||||||
|
|
||||||
ans.DeviceType = deviceType
|
ans.DeviceType = deviceType
|
||||||
@ -449,16 +207,8 @@ func (s *Service) PutAnswersOnePiece(c *fiber.Ctx) error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
r, err := fileparts[0].Open()
|
|
||||||
if err != nil {
|
|
||||||
errs = append(errs, fmt.Errorf("can not open part for file: %s", filekey))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
fname := fmt.Sprintf("%s.%s", xid.New().String(), filetail)
|
fname := fmt.Sprintf("%s.%s", xid.New().String(), filetail)
|
||||||
if err := s.store.PutAnswer(c.Context(), r, quizID[0], fname, ans.QuestionId, fileparts[0].Size); err != nil {
|
|
||||||
return c.Status(fiber.StatusInternalServerError).SendString("can not upload file answers")
|
|
||||||
}
|
|
||||||
ans.Content = fname
|
ans.Content = fname
|
||||||
|
|
||||||
fileIDMap[ans.QuestionId] = fname
|
fileIDMap[ans.QuestionId] = fname
|
||||||
@ -475,22 +225,6 @@ func (s *Service) PutAnswersOnePiece(c *fiber.Ctx) error {
|
|||||||
return c.Status(fiber.StatusInternalServerError).SendString("error unmarshalling answer content: " + err.Error())
|
return c.Status(fiber.StatusInternalServerError).SendString("error unmarshalling answer content: " + err.Error())
|
||||||
}
|
}
|
||||||
ans.Email = content.Email
|
ans.Email = content.Email
|
||||||
|
|
||||||
hlogger.Emit(models.InfoContactForm{
|
|
||||||
KeyOS: os,
|
|
||||||
KeyDevice: device,
|
|
||||||
KeyDeviceType: deviceType,
|
|
||||||
KeyBrowser: browser,
|
|
||||||
CtxQuiz: quizID[0],
|
|
||||||
CtxQuizID: int64(quiz.Id),
|
|
||||||
CtxReferrer: referrer,
|
|
||||||
CtxQuestionID: int64(ans.QuestionId),
|
|
||||||
CtxIDInt: int64(ans.Id),
|
|
||||||
CtxSession: cs,
|
|
||||||
})
|
|
||||||
|
|
||||||
s.workerSendClientCh <- ans
|
|
||||||
trueRes = append(trueRes, ans)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -500,11 +234,7 @@ func (s *Service) PutAnswersOnePiece(c *fiber.Ctx) error {
|
|||||||
return c.Status(fiber.StatusInternalServerError).SendString("can not unmarshal quiz config")
|
return c.Status(fiber.StatusInternalServerError).SendString("can not unmarshal quiz config")
|
||||||
}
|
}
|
||||||
|
|
||||||
if quizConfig.Mailing.When == "email" && len(trueRes) > 0 {
|
stored, ers := s.dal.CreateAnswers(c.Context(), answers, cs, fp, quiz.Id)
|
||||||
s.workerRespondentCh <- trueRes
|
|
||||||
}
|
|
||||||
|
|
||||||
stored, ers := s.dal.AnswerRepo.CreateAnswers(c.Context(), answers, cs, fp, quiz.Id)
|
|
||||||
if len(ers) != 0 {
|
if len(ers) != 0 {
|
||||||
for _, err := range ers {
|
for _, err := range ers {
|
||||||
if strings.Contains(err.Error(), "duplicate key value") {
|
if strings.Contains(err.Error(), "duplicate key value") {
|
||||||
@ -517,33 +247,6 @@ func (s *Service) PutAnswersOnePiece(c *fiber.Ctx) error {
|
|||||||
var questionIDs []uint64
|
var questionIDs []uint64
|
||||||
for _, ans := range stored {
|
for _, ans := range stored {
|
||||||
questionIDs = append(questionIDs, ans.QuestionId)
|
questionIDs = append(questionIDs, ans.QuestionId)
|
||||||
if ans.Result {
|
|
||||||
hlogger.Emit(models.InfoResult{
|
|
||||||
KeyOS: os,
|
|
||||||
KeyDevice: device,
|
|
||||||
KeyDeviceType: deviceType,
|
|
||||||
KeyBrowser: browser,
|
|
||||||
CtxQuiz: quizID[0],
|
|
||||||
CtxQuizID: int64(quiz.Id),
|
|
||||||
CtxReferrer: referrer,
|
|
||||||
CtxQuestionID: int64(ans.QuestionId),
|
|
||||||
CtxIDInt: int64(ans.Id),
|
|
||||||
CtxSession: cs,
|
|
||||||
})
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
hlogger.Emit(models.InfoAnswer{
|
|
||||||
KeyOS: os,
|
|
||||||
KeyDevice: device,
|
|
||||||
KeyDeviceType: deviceType,
|
|
||||||
KeyBrowser: browser,
|
|
||||||
CtxQuiz: quizID[0],
|
|
||||||
CtxQuizID: int64(quiz.Id),
|
|
||||||
CtxReferrer: referrer,
|
|
||||||
CtxQuestionID: int64(ans.QuestionId),
|
|
||||||
CtxIDInt: int64(ans.Id),
|
|
||||||
CtxSession: cs,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
response := PutAnswersResponse{
|
response := PutAnswersResponse{
|
||||||
@ -554,39 +257,11 @@ func (s *Service) PutAnswersOnePiece(c *fiber.Ctx) error {
|
|||||||
return c.Status(fiber.StatusOK).JSON(response)
|
return c.Status(fiber.StatusOK).JSON(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) MiniPart(ctx *fiber.Ctx) error {
|
|
||||||
qid := ctx.Query("q")
|
|
||||||
if qid == "" {
|
|
||||||
return ctx.Status(fiber.StatusBadRequest).SendString("qid is nil")
|
|
||||||
}
|
|
||||||
ctx.Cookie(&fiber.Cookie{
|
|
||||||
Name: "quizFrom",
|
|
||||||
Value: qid,
|
|
||||||
})
|
|
||||||
|
|
||||||
userID, err := s.dal.AccountRepo.GetQidOwner(ctx.Context(), qid)
|
|
||||||
if err != nil {
|
|
||||||
return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
shifr, err := s.encrypt.EncryptStr(userID)
|
|
||||||
if err != nil {
|
|
||||||
return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.Cookie(&fiber.Cookie{
|
|
||||||
Name: "quizUser",
|
|
||||||
Value: url.QueryEscape(string(shifr)),
|
|
||||||
})
|
|
||||||
|
|
||||||
return ctx.Redirect(s.redirectURl, fiber.StatusFound)
|
|
||||||
}
|
|
||||||
|
|
||||||
func truncateUTF8(s string, maxLen int) string {
|
func truncateUTF8(s string, maxLen int) string {
|
||||||
if utf8.RuneCountInString(s) <= maxLen {
|
if utf8.RuneCountInString(s) <= maxLen {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
// Конвертируем строку в руны для корректной обработки Unicode
|
// Конвертируем строку в руны для корректной обработки Unicode
|
||||||
runes := []rune(s)
|
runes := []rune(s)
|
||||||
return string(runes[:maxLen])
|
return string(runes[:maxLen])
|
||||||
|
Loading…
Reference in New Issue
Block a user