package other import ( "crypto/sha256" "encoding/hex" "encoding/json" "fmt" "github.com/go-redis/redis/v8" "github.com/gofiber/fiber/v2" "github.com/rs/xid" "gopkg.in/telebot.v3" tb "gopkg.in/tucnak/telebot.v2" "heruvym/internal/repository/minio" "heruvym/internal/repository/mongo" "heruvym/internal/utils/helpers" "heruvym/internal/utils/jwt_adapter" "heruvym/internal/utils/middleware" "io" "strings" "sync" "time" ) type Deps struct { Dal *mongo.DAL RedisClient *redis.Client BS *minio.BlobStore Notifier *telebot.Bot TgChatID int64 } type OtherController struct { dal *mongo.DAL redisClient *redis.Client bs *minio.BlobStore notifier *telebot.Bot tgChatID int64 } func NewOtherController(deps Deps) *OtherController { return &OtherController{ dal: deps.Dal, redisClient: deps.RedisClient, bs: deps.BS, notifier: deps.Notifier, tgChatID: deps.TgChatID, } } type ReqScreenshot struct { TicketID string `json:"ticket"` Lang string `json:"lang"` } func (o *OtherController) RequestScreenshot(ctx *fiber.Ctx) error { var request ReqScreenshot sess := jwt_adapter.Get(ctx.Context()) _, err := o.dal.PutSCRequest( ctx.Context(), sess.Id, sess.Id, request.TicketID, ) if err != nil { return fiber.NewError(fiber.StatusInternalServerError, err.Error()) } return ctx.SendStatus(fiber.StatusOK) } // MB Size constants const ( MB = 1 << 20 ) var fileTypeLimits = map[string]int64{ "image": 5 * MB, "video": 50 * MB, "document": 10 * MB, } type PutFileReq struct { Ticket string `json:"ticket"` } type PutFileResp struct { Message string `json:"message"` } // todo чекнуть надо как я переписал func (o *OtherController) PutFile(ctx *fiber.Ctx) error { sess := jwt_adapter.Get(ctx.Context()) if sess == nil { return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "not authorized"}) } TimeKey := fmt.Sprintf("sendLockHeruvym:%s", sess.Id) isNewKey, err := o.redisClient.SetNX(ctx.Context(), TimeKey, time.Now().Unix(), 30*time.Second).Result() if err != nil { return ctx.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } if !isNewKey { return ctx.Status(fiber.StatusTooManyRequests).JSON(fiber.Map{"error": "file upload limit exceeded"}) } form, err := ctx.MultipartForm() if err != nil { return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "can not parse multipart: " + err.Error()}) } if form == nil || form.File == nil { return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "no multipart or file"}) } files := form.File for _, fileHeaders := range files { for _, fileHeader := range fileHeaders { fileSize := fileHeader.Size fileType := helpers.GetFileType(fileHeader.Filename) if limit, ok := fileTypeLimits[fileType]; ok { if fileSize > limit { return ctx.Status(fiber.StatusRequestEntityTooLarge).JSON(fiber.Map{"error": fileType + " file size exceeds the limit"}) } } else { return ctx.Status(fiber.StatusNotAcceptable).JSON(fiber.Map{"error": "Unsupported file type"}) } } } filesCount := len(files) if filesCount == 0 { return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "no files"}) } var req PutFileReq if ticket := form.Value["ticket"]; len(ticket) > 0 { req.Ticket = ticket[0] } var ( fileIDs, filenames []string errFile error ) wg := new(sync.WaitGroup) m := new(sync.Mutex) wg.Add(filesCount) for name, file := range files { file := file name := name go func() { defer wg.Done() freader, err := file[0].Open() if err != nil { fmt.Println("can not open ", err.Error()) errFile = err return } hash := sha256.New() if _, err := io.Copy(hash, freader); err != nil { fmt.Println("error calculating file hash: ", err) errFile = err return } _, err = freader.Seek(0, 0) if err != nil { fmt.Println("error seeking back to the beginning of the file: ", err) errFile = err return } hashInBytes := hash.Sum(nil) fileHash := hex.EncodeToString(hashInBytes) splitted := strings.Split(name, ".") filename := fmt.Sprintf("%s_%s.%s", fileHash, sess.Id, splitted[len(splitted)-1]) defer func() { if err := freader.Close(); err != nil { errFile = err } }() exists, err := o.bs.FileExists(ctx.Context(), filename) if err != nil { errFile = err return } if exists { response := struct { File string `json:"file"` }{ File: filename, } resp, err := json.Marshal(response) if err != nil { ctx.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "error marshaling response: " + err.Error()}) return } ctx.Status(fiber.StatusAlreadyReported).Send(resp) m.Lock() defer m.Unlock() fileIDs = append(fileIDs, filename) filenames = append(filenames, name) return } if err := o.bs.PutFile( ctx.Context(), filename, freader, file[0].Size); err != nil { errFile = err } m.Lock() defer m.Unlock() fileIDs = append(fileIDs, filename) filenames = append(filenames, name) }() } wg.Wait() if errFile != nil || len(fileIDs) == 0 { return ctx.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "can not store files: " + errFile.Error()}) } message, err := o.dal.PutMessage( ctx.Context(), strings.Join(filenames, ", "), sess.Id, sess.Id, req.Ticket, fileIDs, ) if err != nil { for _, filename := range filenames { if err := o.bs.DeleteFile(ctx.Context(), filename); err != nil { fmt.Println("can not delete", err) } } return ctx.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "can not store message: " + err.Error()}) } domain := ctx.Context().Value(middleware.HostKey).(string) if domain == "" { fmt.Println("domain is nil err") return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "domain is nil"}) } role := jwt_adapter.GetRole(ctx.Context()) go func() { if sess.Id != "" && role != "admin" { if err == nil && o.notifier != nil { var userLink, supportLink string if sess.StandardClaims.Issuer != "" { fmt.Println("MABNAT", domain) if domain[0] == 's' { userLink = fmt.Sprintf("https://sadmin.pena/users/%s", sess.Id) supportLink = fmt.Sprintf("https://sadmin.pena/support/%s", req.Ticket) } else { userLink = fmt.Sprintf("https://admin.pena/users/%s", sess.Id) supportLink = fmt.Sprintf("https://admin.pena/support/%s", req.Ticket) } } else { if domain[0] == 's' { supportLink = fmt.Sprintf("https://sadmin.pena/support/%s", req.Ticket) } else { supportLink = fmt.Sprintf("https://admin.pena/support/%s", req.Ticket) } userLink = "незарегистрированного пользователя" } message := fmt.Sprintf("Вам пришло сообщение от %s сссылка на пользователя с %s, ccылка на чат - %s", userLink, domain, supportLink) if _, err := o.notifier.Send(tb.ChatID(o.tgChatID), message); err != nil { fmt.Println("CAN NOT NOTIFY", err) } return } } }() return ctx.Status(fiber.StatusOK).JSON(PutFileResp{Message: message.ID}) } type PutSCReq struct { Ticket string `json:"ticket"` } type PutSCResp struct { Message string `json:"message"` } // todo чекнуть надо как я переписал func (o *OtherController) PutSC(ctx *fiber.Ctx) error { form, err := ctx.MultipartForm() if err != nil { return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "can not parse multipart: " + err.Error()}) } if form == nil || form.File == nil { return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "no multipart or file"}) } files := form.File filesCount := len(files) if filesCount == 0 { return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "no files"}) } sess := jwt_adapter.Get(ctx.Context()) if sess == nil { return ctx.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "not authorized"}) } var req PutFileReq if ticket := form.Value["ticket"]; len(ticket) > 0 { req.Ticket = ticket[0] } var ( fileIDs, filenames []string errFile error ) wg := new(sync.WaitGroup) m := new(sync.Mutex) wg.Add(filesCount) for name, file := range files { file := file name := name go func() { defer wg.Done() freader, err := file[0].Open() if err != nil { fmt.Println("can not open ", err.Error()) return } defer func() { if err := freader.Close(); err != nil { errFile = err } }() splitted := strings.Split(name, ".") filename := fmt.Sprintf("%s.%s", xid.New().String(), splitted[len(splitted)-1]) if err := o.bs.PutFile(ctx.Context(), filename, freader, file[0].Size); err != nil { errFile = err return } m.Lock() defer m.Unlock() fileIDs = append(fileIDs, filename) filenames = append(filenames, name) }() } wg.Wait() if errFile != nil { return ctx.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "can not store files: " + errFile.Error()}) } message, err := o.dal.PutSCResponse( ctx.Context(), strings.Join(filenames, ", "), sess.Id, sess.Id, req.Ticket, fileIDs, ) if err != nil { for _, filename := range filenames { if err := o.bs.DeleteFile(ctx.Context(), filename); err != nil { fmt.Println("can not delete file:", err) } } return ctx.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "can not store message: " + err.Error()}) } return ctx.Status(fiber.StatusOK).JSON(PutFileResp{Message: message.ID}) }