add other files methods

This commit is contained in:
Pavel 2024-09-30 22:31:57 +03:00
parent 7cc65c1cd8
commit 2609f98736
2 changed files with 356 additions and 0 deletions

@ -1,25 +1,48 @@
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/jwt_adapter"
"heruvym/internal/utils/middleware"
"heruvym/utils"
"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,
}
}
@ -44,3 +67,333 @@ func (o *OtherController) RequestScreenshot(ctx *fiber.Ctx) 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 := utils.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})
}

@ -35,6 +35,9 @@ func NewControllers(deps ControllersDeps) *Controllers {
Other: other.NewOtherController(other.Deps{
Dal: deps.Reps.Mongo,
RedisClient: deps.RedisClient,
BS: deps.Reps.Minio,
Notifier: deps.Notifier,
TgChatID: deps.TgChatID,
}),
}
}