From ff8956355f6382cca670f5e18201bec256ac6e2b Mon Sep 17 00:00:00 2001 From: pasha1coil Date: Wed, 9 Jul 2025 14:40:02 +0300 Subject: [PATCH] cleaning pj --- app/app.go | 103 +--------- dal/dal.go | 32 --- models/hlog_events.go | 70 ------- quiz_config.json | 3 + savewc/for_client.go | 72 ------- savewc/for_respondent.go | 71 ------- service/config.go | 21 ++ service/service.go | 431 ++++++--------------------------------- 8 files changed, 94 insertions(+), 709 deletions(-) delete mode 100644 dal/dal.go delete mode 100644 models/hlog_events.go create mode 100644 quiz_config.json delete mode 100644 savewc/for_client.go delete mode 100644 savewc/for_respondent.go create mode 100644 service/config.go diff --git a/app/app.go b/app/app.go index fbeed1c..eb9062d 100644 --- a/app/app.go +++ b/app/app.go @@ -4,27 +4,15 @@ import ( "context" "errors" "fmt" - "gitea.pena/PenaSide/common/log_mw" - "gitea.pena/PenaSide/hlog" - "gitea.pena/PenaSide/trashlog/wrappers/zaptrashlog" "gitea.pena/SQuiz/answerer/clients" - dalBS "gitea.pena/SQuiz/answerer/dal" - "gitea.pena/SQuiz/answerer/models" - "gitea.pena/SQuiz/answerer/savewc" "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/minio/minio-go/v7" - "github.com/minio/minio-go/v7/pkg/credentials" "github.com/skeris/appInit" "go.uber.org/zap" - "go.uber.org/zap/zapcore" - "time" ) type App struct { @@ -55,21 +43,13 @@ var _ appInit.CommonApp = (*App)(nil) type Options struct { LoggerProdMode bool `env:"IS_PROD_LOG" 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"` CrtFile string `env:"CRT" default:"server.crt"` 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"` - 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) { @@ -102,77 +82,26 @@ func New(ctx context.Context, opts interface{}, ver appInit.Version) (appInit.Co zap.String("SvcVersion", ver.Release), zap.String("SvcBuildTime", ver.BuildTime), ) - clickHouseLogger, err := zaptrashlog.NewCore(ctx, zap.InfoLevel, options.TrashLogHost, ver.Release, ver.Commit, time.Now().Unix()) - if err != nil { - panic(err) - } - loggerForHlog := zapLogger.WithOptions(zap.WrapCore(func(core zapcore.Core) zapcore.Core { - return zapcore.NewTee(core, clickHouseLogger) - })) - - loggerHlog := hlog.New(loggerForHlog).Module(options.ModuleLogger) - - loggerHlog.With(models.AllFields{}) - loggerHlog.Emit(InfoSvcStarted{}) - - // 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) + pgdal, err := dal.New(ctx, options.PostgresCredentials, nil) if err != nil { return nil, err } zapLogger.Info("config", zap.Any("options", options)) - //init redis - redisClient := redis.NewClient(&redis.Options{ - Addr: options.RedisHost, - Password: options.RedisPassword, - 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) + svc, err := service.New(service.ServiceDeps{ + Dal: pgdal, + Encrypt: encrypt, + RedirectURl: options.RedirectURL, + AiClient: clients.NewAiClient("https://alvatar.com/api/engine/send_answer"), + }) if err != nil { + zapLogger.Error("failed to create service", zap.Error(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}) @@ -186,38 +115,28 @@ func New(ctx context.Context, opts interface{}, ver appInit.Version) (appInit.Co }) app.Use(middleware.AnswererChain()) - app.Use(log_mw.ContextLogger(loggerHlog)) app.Get("/liveness", healthchecks.Liveness) app.Get("/readiness", healthchecks.Readiness(&workerErr)) //todo parametrized readiness. should discuss ready reason app = svc.Register(app) fmt.Println("SERVERSTART", fmt.Sprintf(":%s", options.NumberPort)) - loggerHlog.Emit(InfoSvcReady{}) - go func() { defer func() { - //if pgdal != nil { - // pgdal.CloseAnswerer() - //} err := app.Shutdown() if err != nil { - loggerHlog.Emit(InfoSvcShutdown{Signal: err.Error()}) + zapLogger.Error("failed graceful shutdown", zap.Error(err)) } }() if options.IsProd { if err := app.ListenTLS(fmt.Sprintf(":%s", options.NumberPort), options.CrtFile, options.KeyFile); err != nil { - loggerHlog.Emit(ErrorCanNotServe{ - Err: err, - }) + zapLogger.Error("failed start server", zap.Error(err)) errChan <- err } } else { if err := app.Listen(fmt.Sprintf(":%s", options.NumberPort)); err != nil { - loggerHlog.Emit(ErrorCanNotServe{ - Err: err, - }) + zapLogger.Error("failed start server", zap.Error(err)) errChan <- err } } diff --git a/dal/dal.go b/dal/dal.go deleted file mode 100644 index 29a79bf..0000000 --- a/dal/dal.go +++ /dev/null @@ -1,32 +0,0 @@ -package dal - -import ( - "context" - "fmt" - "github.com/minio/minio-go/v7" - "io" -) - -const ( - bucket = "3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b" - bucketAnswers = "squizanswer" -) - -type Storer struct { - client *minio.Client -} - -func New(ctx context.Context, minioClient *minio.Client) (*Storer, error) { - return &Storer{ - client: minioClient, - }, nil -} - -func (s *Storer) PutAnswer(ctx context.Context, file io.Reader, quizId, name string, question uint64, size int64) error { - if _, err := s.client.PutObject(ctx, bucket, bucketAnswers + "/" +fmt.Sprintf("%s/%d/%s", quizId, question, name), file, size, - minio.PutObjectOptions{}); err != nil { - return err - } - - return nil -} diff --git a/models/hlog_events.go b/models/hlog_events.go deleted file mode 100644 index dc0dd63..0000000 --- a/models/hlog_events.go +++ /dev/null @@ -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 // сессия -} diff --git a/quiz_config.json b/quiz_config.json new file mode 100644 index 0000000..fd1f90f --- /dev/null +++ b/quiz_config.json @@ -0,0 +1,3 @@ +{ + +} \ No newline at end of file diff --git a/savewc/for_client.go b/savewc/for_client.go deleted file mode 100644 index c886dbf..0000000 --- a/savewc/for_client.go +++ /dev/null @@ -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 -} diff --git a/savewc/for_respondent.go b/savewc/for_respondent.go deleted file mode 100644 index eb0fd64..0000000 --- a/savewc/for_respondent.go +++ /dev/null @@ -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 -} diff --git a/service/config.go b/service/config.go new file mode 100644 index 0000000..dd9c78e --- /dev/null +++ b/service/config.go @@ -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 +} diff --git a/service/service.go b/service/service.go index 82a548b..25dde0d 100644 --- a/service/service.go +++ b/service/service.go @@ -1,23 +1,19 @@ package service import ( - "unicode/utf8" "encoding/json" "fmt" - "gitea.pena/PenaSide/common/log_mw" "gitea.pena/SQuiz/answerer/clients" - "gitea.pena/SQuiz/answerer/dal" - "gitea.pena/SQuiz/answerer/models" quizdal "gitea.pena/SQuiz/common/dal" "gitea.pena/SQuiz/common/middleware" "gitea.pena/SQuiz/common/model" "gitea.pena/SQuiz/common/utils" "github.com/gofiber/fiber/v2" - "net/url" "strconv" "strings" "sync" "time" + "unicode/utf8" "github.com/rs/xid" ) @@ -28,58 +24,45 @@ const ( ) type Service struct { - store *dal.Storer - dal *quizdal.DAL - batch []model.Answer - m sync.Mutex - workerRespondentCh chan<- []model.Answer - workerSendClientCh chan<- model.Answer - encrypt *utils.Encrypt - redirectURl string - aiClient *clients.AIClient + dal *quizdal.DAL + batch []model.Answer + m sync.Mutex + encrypt *utils.Encrypt + redirectURl string + aiClient *clients.AIClient + quizConfigData GetQuizDataResp } type ServiceDeps struct { - Store *dal.Storer - Dal *quizdal.DAL - WorkerRespondentCh chan<- []model.Answer - WorkerSendClientCh chan<- model.Answer - Encrypt *utils.Encrypt - RedirectURl string - AiClient *clients.AIClient + Dal *quizdal.DAL + Encrypt *utils.Encrypt + RedirectURl string + AiClient *clients.AIClient } -func New(deps ServiceDeps) *Service { - return &Service{ - store: deps.Store, - dal: deps.Dal, - m: sync.Mutex{}, - batch: []model.Answer{}, - workerRespondentCh: deps.WorkerRespondentCh, - workerSendClientCh: deps.WorkerSendClientCh, - encrypt: deps.Encrypt, - redirectURl: deps.RedirectURl, - aiClient: deps.AiClient, +func New(deps ServiceDeps) (*Service, error) { + quizData, err := loadQuizDataConfig() + if err != nil { + return nil, err } + + return &Service{ + dal: deps.Dal, + m: sync.Mutex{}, + batch: []model.Answer{}, + encrypt: deps.Encrypt, + redirectURl: deps.RedirectURl, + aiClient: deps.AiClient, + quizConfigData: quizData, + }, nil } func (s *Service) Register(app *fiber.App) *fiber.App { app.Post("/answer", s.PutAnswersOnePiece) app.Post("/settings", s.GetQuizData) - app.Get("/logo", s.MiniPart) 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 { Settings *ShavedQuiz `json:"settings,omitempty"` Items []ShavedQuestion `json:"items"` @@ -87,7 +70,6 @@ type GetQuizDataResp struct { ShowBadge bool `json:"show_badge"` } -// ShavedQuiz shortened struct for delivery data to customer type ShavedQuiz struct { Fingerprinting bool `json:"fp"` Repeatable bool `json:"rep"` @@ -100,7 +82,6 @@ type ShavedQuiz struct { Status string `json:"status"` } -// ShavedQuestion shortened struct for delivery data to customer type ShavedQuestion struct { Id uint64 `json:"id"` Title string `json:"title"` @@ -113,209 +94,7 @@ type ShavedQuestion struct { // GetQuizData handler for obtaining data for quiz front rendering func (s *Service) GetQuizData(c *fiber.Ctx) error { - cs, ok := c.Context().Value(middleware.ContextKey(middleware.SessionKey)).(string) - 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, - } + return c.Status(fiber.StatusOK).JSON(s.quizConfigData) } // MB Size constants @@ -329,17 +108,12 @@ type PutAnswersResponse struct { Stored []uint64 `json:"stored"` } -// todo не отдавать ответы с типом start но сохранять брать из контента ответа -// поле бул также в GetQuizdata не отдавать его, но по запросам идет GetQuizdata получаем данные квиза -// аотом отправляем ответы в PutAnswers и сохраяняем их подумать как делать func (s *Service) PutAnswersOnePiece(c *fiber.Ctx) error { cs, ok := c.Context().Value(middleware.ContextKey(middleware.SessionKey)).(string) if !ok { return c.Status(fiber.StatusUnauthorized).SendString("no session in cookie") } - hlogger := log_mw.ExtractLogger(c) - form, err := c.MultipartForm() if err != nil || form == nil || form.File == nil { return c.Status(fiber.StatusBadRequest).SendString("expecting multipart form file") @@ -361,8 +135,8 @@ func (s *Service) PutAnswersOnePiece(c *fiber.Ctx) error { } var ( - answersRaw, answers, trueRes []model.Answer - errs []error + answersRaw, answers []model.Answer + errs []error ) deviceType := c.Get("DeviceType") @@ -370,7 +144,6 @@ func (s *Service) PutAnswersOnePiece(c *fiber.Ctx) error { browser := c.Get("Browser") ip := c.IP() device := c.Get("Device") - referrer := c.Get("Referer") if err := json.Unmarshal([]byte(answersStr[0]), &answersRaw); err != nil { return c.Status(fiber.StatusBadRequest).SendString("not valid answers string") @@ -394,42 +167,39 @@ func (s *Service) PutAnswersOnePiece(c *fiber.Ctx) error { fileIDMap := make(map[uint64]string) for _, ans := range answersRaw { - - if quiz.Status == model.StatusAI { - final := false - 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)}) + final := false + if finalStr, exists := form.Value["final"]; exists && len(finalStr) > 0 { + parsedFinal, err := strconv.ParseBool(finalStr[0]) 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 { - return c.Status(fiber.StatusNotFound).SendString("no questions found") - } + question, err := s.dal.QuestionRepo.GetQuestionListByIDs(c.Context(), []int32{int32(ans.QuestionId)}) + 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 err != nil { - return c.Status(fiber.StatusInternalServerError).SendString(fmt.Sprintf("failed send answer to ai, err: %s", err.Error())) - } + if len(question) == 0 { + return c.Status(fiber.StatusNotFound).SendString("no questions found") + } - _, err = s.dal.QuestionRepo.CreateQuestion(c.Context(), &model.Question{ - QuizId: quiz.Id, - Title: truncateUTF8(questionText,512), - 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}`, - Description: questionText, - }) - if err != nil { - return c.Status(fiber.StatusInternalServerError).SendString(fmt.Sprintf("failed create question type ai, err: %s", err.Error())) - } + questionText, err := s.aiClient.SendAnswerer(final, question[0].Type, ans.Content, cs) + if err != nil { + 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{ + QuizId: quiz.Id, + Title: truncateUTF8(questionText, 512), + 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}`, + 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 @@ -449,16 +219,8 @@ func (s *Service) PutAnswersOnePiece(c *fiber.Ctx) error { 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) - 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 fileIDMap[ans.QuestionId] = fname @@ -475,22 +237,6 @@ func (s *Service) PutAnswersOnePiece(c *fiber.Ctx) error { return c.Status(fiber.StatusInternalServerError).SendString("error unmarshalling answer content: " + err.Error()) } 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,10 +246,6 @@ func (s *Service) PutAnswersOnePiece(c *fiber.Ctx) error { return c.Status(fiber.StatusInternalServerError).SendString("can not unmarshal quiz config") } - if quizConfig.Mailing.When == "email" && len(trueRes) > 0 { - s.workerRespondentCh <- trueRes - } - stored, ers := s.dal.AnswerRepo.CreateAnswers(c.Context(), answers, cs, fp, quiz.Id) if len(ers) != 0 { for _, err := range ers { @@ -517,33 +259,6 @@ func (s *Service) PutAnswersOnePiece(c *fiber.Ctx) error { var questionIDs []uint64 for _, ans := range stored { 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{ @@ -554,39 +269,11 @@ func (s *Service) PutAnswersOnePiece(c *fiber.Ctx) error { 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 { if utf8.RuneCountInString(s) <= maxLen { return s } - + // Конвертируем строку в руны для корректной обработки Unicode runes := []rune(s) return string(runes[:maxLen])