2024-09-30 12:49:03 +00:00
|
|
|
|
package other
|
|
|
|
|
|
|
|
|
|
import (
|
2024-09-30 19:31:57 +00:00
|
|
|
|
"crypto/sha256"
|
|
|
|
|
"encoding/hex"
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"fmt"
|
2024-09-30 16:17:17 +00:00
|
|
|
|
"github.com/go-redis/redis/v8"
|
2024-09-30 12:49:03 +00:00
|
|
|
|
"github.com/gofiber/fiber/v2"
|
2024-09-30 19:31:57 +00:00
|
|
|
|
"github.com/rs/xid"
|
|
|
|
|
"gopkg.in/telebot.v3"
|
|
|
|
|
tb "gopkg.in/tucnak/telebot.v2"
|
|
|
|
|
"heruvym/internal/repository/minio"
|
2024-09-30 12:49:03 +00:00
|
|
|
|
"heruvym/internal/repository/mongo"
|
2024-10-01 11:34:09 +00:00
|
|
|
|
"heruvym/internal/utils/helpers"
|
2024-09-30 12:49:03 +00:00
|
|
|
|
"heruvym/internal/utils/jwt_adapter"
|
2024-09-30 19:31:57 +00:00
|
|
|
|
"heruvym/internal/utils/middleware"
|
|
|
|
|
"io"
|
|
|
|
|
"strings"
|
|
|
|
|
"sync"
|
|
|
|
|
"time"
|
2024-09-30 12:49:03 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type Deps struct {
|
2024-09-30 16:17:17 +00:00
|
|
|
|
Dal *mongo.DAL
|
|
|
|
|
RedisClient *redis.Client
|
2024-09-30 19:31:57 +00:00
|
|
|
|
BS *minio.BlobStore
|
|
|
|
|
Notifier *telebot.Bot
|
|
|
|
|
TgChatID int64
|
2024-09-30 12:49:03 +00:00
|
|
|
|
}
|
|
|
|
|
type OtherController struct {
|
2024-09-30 16:17:17 +00:00
|
|
|
|
dal *mongo.DAL
|
|
|
|
|
redisClient *redis.Client
|
2024-09-30 19:31:57 +00:00
|
|
|
|
bs *minio.BlobStore
|
|
|
|
|
notifier *telebot.Bot
|
|
|
|
|
tgChatID int64
|
2024-09-30 12:49:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewOtherController(deps Deps) *OtherController {
|
|
|
|
|
return &OtherController{
|
2024-09-30 16:17:17 +00:00
|
|
|
|
dal: deps.Dal,
|
|
|
|
|
redisClient: deps.RedisClient,
|
2024-09-30 19:31:57 +00:00
|
|
|
|
bs: deps.BS,
|
|
|
|
|
notifier: deps.Notifier,
|
|
|
|
|
tgChatID: deps.TgChatID,
|
2024-09-30 12:49:03 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type ReqScreenshot struct {
|
|
|
|
|
TicketID string `json:"ticket"`
|
|
|
|
|
Lang string `json:"lang"`
|
2024-10-02 13:36:49 +00:00
|
|
|
|
System bool `json:"system"`
|
2024-09-30 12:49:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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,
|
2024-10-02 13:36:49 +00:00
|
|
|
|
request.System, // system error flag
|
2024-09-30 12:49:03 +00:00
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ctx.SendStatus(fiber.StatusOK)
|
|
|
|
|
}
|
2024-09-30 19:31:57 +00:00
|
|
|
|
|
|
|
|
|
// 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"`
|
2024-10-02 13:36:49 +00:00
|
|
|
|
System bool `json:"system"`
|
2024-09-30 19:31:57 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type PutFileResp struct {
|
|
|
|
|
Message string `json:"message"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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"})
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-14 12:18:32 +00:00
|
|
|
|
domain, ok := ctx.Context().Value(middleware.HostKey).(string)
|
|
|
|
|
if !ok || domain == "" {
|
|
|
|
|
fmt.Println("domain is nil err")
|
|
|
|
|
return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "domain is nil"})
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-01 14:38:05 +00:00
|
|
|
|
form, err := ctx.MultipartForm()
|
2024-09-30 19:31:57 +00:00
|
|
|
|
if err != nil {
|
2024-10-01 14:38:05 +00:00
|
|
|
|
return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "can not parse multipart: " + err.Error()})
|
2024-09-30 19:31:57 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-10-01 14:38:05 +00:00
|
|
|
|
if form == nil || form.File == nil {
|
|
|
|
|
return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "no multipart or file"})
|
2024-09-30 19:31:57 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-10-01 14:38:05 +00:00
|
|
|
|
TimeKey := fmt.Sprintf("sendLockHeruvym:%s", sess.Id)
|
|
|
|
|
isNewKey, err := o.redisClient.SetNX(ctx.Context(), TimeKey, time.Now().Unix(), 30*time.Second).Result()
|
2024-09-30 19:31:57 +00:00
|
|
|
|
if err != nil {
|
2024-10-01 14:38:05 +00:00
|
|
|
|
return ctx.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
2024-09-30 19:31:57 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-10-01 14:38:05 +00:00
|
|
|
|
if !isNewKey {
|
|
|
|
|
return ctx.Status(fiber.StatusTooManyRequests).JSON(fiber.Map{"error": "file upload limit exceeded"})
|
2024-09-30 19:31:57 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
files := form.File
|
|
|
|
|
for _, fileHeaders := range files {
|
|
|
|
|
for _, fileHeader := range fileHeaders {
|
|
|
|
|
fileSize := fileHeader.Size
|
2024-10-01 11:34:09 +00:00
|
|
|
|
fileType := helpers.GetFileType(fileHeader.Filename)
|
2024-09-30 19:31:57 +00:00
|
|
|
|
|
|
|
|
|
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,
|
2024-10-02 13:36:49 +00:00
|
|
|
|
req.System, // system error flag
|
2024-09-30 19:31:57 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
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()})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 = "незарегистрированного пользователя"
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-02 13:36:49 +00:00
|
|
|
|
var message string
|
|
|
|
|
if req.System {
|
|
|
|
|
message = fmt.Sprintf("СИСТЕМНАЯ ОШИБКА! Произошла ошибка в сообщении от %s c %s. Ссылка на чат: %s", userLink, domain, supportLink)
|
|
|
|
|
} else {
|
|
|
|
|
message = fmt.Sprintf("Вам пришло сообщение от %s ссылка на пользователя с %s, ссылка на чат - %s",
|
|
|
|
|
userLink, domain, supportLink)
|
|
|
|
|
}
|
2024-09-30 19:31:57 +00:00
|
|
|
|
|
|
|
|
|
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"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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})
|
|
|
|
|
}
|