heruvym/service/service.go
2022-04-14 20:43:13 +03:00

941 lines
21 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package service
import (
rAL "bitbucket.org/skeris/profile/dal"
"context"
"encoding/json"
"errors"
"fmt"
"github.com/rs/xid"
"github.com/themakers/hlog"
tb "gopkg.in/tucnak/telebot.v2"
"heruvym/dal/minio"
"heruvym/dal/mongo"
"heruvym/jwt_adapter"
"heruvym/model"
"heruvym/tools"
"net/http"
"strings"
"sync"
)
type Heruvym struct {
logger hlog.Logger
dal *mongo.DAL
ral rAL.LayerMongoDb
bs *minio.BlobStore
notifier *tb.Bot
}
func New(blobs *minio.BlobStore, dataAccessLayer *mongo.DAL, ral rAL.LayerMongoDb, log hlog.Logger, notifier *tb.Bot) *Heruvym {
return &Heruvym{
logger: log.Module("Service"),
dal: dataAccessLayer,
ral: ral,
bs: blobs,
notifier:notifier,
}
}
func (h Heruvym) Register(m *http.ServeMux) *http.ServeMux {
m.HandleFunc("/create", h.CreateTicket)
m.HandleFunc("/subscribe", tools.SseWrapper(h.GetList))
m.HandleFunc("/ticket", tools.SseWrapper(h.Subscribe))
m.HandleFunc("/send", tools.HandlerWrapper(h.PutMessage))
m.HandleFunc("/getTickets", tools.HandlerWrapper(h.GetTickets))
m.HandleFunc("/getMessages", tools.HandlerWrapper(h.GetMessages))
m.HandleFunc("/pick", tools.HandlerWrapper(h.Pick))
m.HandleFunc("/delegate", tools.HandlerWrapper(h.Delegate))
m.HandleFunc("/vote", tools.HandlerWrapper(h.Vote))
m.HandleFunc("/close", tools.HandlerWrapper(h.CloseTicket))
return m
}
type CreateTicketReq struct {
Title string `json:"Title"`
Message string `json:"Message"`
}
type CreateTicketResp struct {
Ticket string `json:"Ticket"`
}
func (h *Heruvym) CreateTicket(w http.ResponseWriter, r *http.Request) {
defer func() {
if err := r.Body.Close(); err != nil {
h.logger.Emit(ErrorClose{
Err: err,
})
}
}()
var (
err error
request CreateTicketReq
)
if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
http.Error(w, "Invalid json", http.StatusBadRequest)
return
}
if request.Title == "" {
http.Error(w, "No Title", http.StatusNoContent)
return
}
if request.Message == "" {
http.Error(w, "No Message", http.StatusNoContent)
return
}
ctx := r.Context()
session := jwt_adapter.Get(ctx)
if session == nil {
http.Error(w, "No session", http.StatusMethodNotAllowed)
return
}
var (
ticketID string
tickets []model.Ticket
)
if session.User == "" {
tickets, _, err = h.dal.GetTickets4Sess(ctx, session.Session)
}
if err != nil || len(tickets) == 0 {
ticketID, err = h.dal.CreateTicket(
ctx,
session.User,
session.Session,
request.Title,
request.Message,
[]string{},
)
if err != nil {
http.Error(w, "CannotCreateTicket", http.StatusInternalServerError)
return
}
if _, err := h.dal.PutMessage(ctx,
request.Message,
session.User,
session.Session,
ticketID,
[]string{},
); err != nil {
http.Error(w, "CannotCreateMessage", http.StatusInternalServerError)
return
}
go func() {
role, _ := h.ral.GetProfileRole(context.TODO(), session.User)
if role != "user" && role != ""{
return
}
if session.User != "" {
additional, err := h.dal.GetAdditionalData(context.TODO(), session.User)
fmt.Println("CAN NOT NOTIFY", err)
if err == nil {
if _, err := h.notifier.Send(tb.ChatID(-1001344671794),
fmt.Sprintf("Поступило новое сообщение от пользователя %d с почтой %s",
additional.Uid, additional.Email)); err != nil {
fmt.Println("CAN NOT NOTIFY", err)
}
return
}
}
if _, err := h.notifier.Send(tb.ChatID(-1001344671794),
fmt.Sprintf("Поступило новое сообщение от незарегистриованного пользователя")); err != nil {
fmt.Println("CAN NOT NOTIFY", err)
}
}()
} else {
ticketID = tickets[0].ID
}
response, err := json.Marshal(CreateTicketResp{Ticket: ticketID})
if err != nil {
h.logger.Emit(ErrorMarshal{
Err: err,
})
http.Error(w, "CannotMarshalMessage", http.StatusInternalServerError)
return
}
if _, err := w.Write(response); err != nil {
h.logger.Emit(ErrorMarshal{
Err: err,
})
return
}
}
var _ tools.DataEmitter = (&Heruvym{}).GetList
func (h *Heruvym) GetList(ctx context.Context) chan interface{} {
defer func() {
if rec := recover(); rec != nil {
fmt.Println(rec)
}
}()
sess := jwt_adapter.Get(ctx)
if sess == nil {
return nil
}
output := make(chan interface{})
if sess.User == "" {
go h.unauthorizedTickets(ctx, sess.Session, output)
} else {
role, err := h.ral.GetProfileRole(ctx, sess.User)
if err != nil {
fmt.Println("HER ERR", err)
go h.hasNoRole(output)
}
fmt.Println("ALL TICKETS Sess ", sess.User, role)
if role == "admin" || role == "manager" {
go h.allTickets(ctx, output)
} else {
go h.userTickets(ctx, sess.User, output)
}
}
return output
}
func (h *Heruvym) allTickets(ctx context.Context, output chan interface{}) {
defer func() {
if v := recover(); v != nil {
fmt.Println("AllTicketsRec", v)
}
}()
//data, count, err := h.dal.YieldTickets(ctx, 20)
//
//if err != nil {
// output <- errors.New("cannot get tickets:" + err.Error())
// return
//}
//
//if data != nil {
// output <- GetTicketsResp{data, count}
//}
if err := h.dal.WatchAllTickets(ctx, func(ticket model.Ticket) error {
output <- ticket
return nil
}); err != nil {
output <- errors.New("cannot watch all tickets" + err.Error())
}
}
func (h *Heruvym) userTickets(ctx context.Context, userID string, output chan interface{}) {
defer func() {
if v := recover(); v != nil {
fmt.Println("USERTICKS", v)
}
}()
data, count, err := h.dal.YieldUserTickets(ctx,userID, 20)
if err != nil {
output <- errors.New("cannot get tickets:" + err.Error())
return
}
if data != nil {
output <- GetTicketsResp{data, count}
}
if err := h.dal.WatchTickets(ctx, userID, func(ticket model.Ticket) error {
output <- ticket
return nil
}); err != nil {
output <- errors.New("cannot watch tickets")
return
}
}
func (h *Heruvym) hasNoRole(output chan interface{}) {
output <- errors.New("no role in profile")
}
func (h *Heruvym) unauthorizedTickets(ctx context.Context, sess string, output chan interface{}) {
//defer close(output)
tickets, count, err := h.dal.GetTickets4Sess(ctx, sess)
if err != nil {
output <- errors.New("no tickets for session")
return
}
if tickets != nil {
output <- GetTicketsResp{tickets, count}
}
}
type ReqPutMessage struct {
Message string `json:"message"`
TicketID string `json:"ticket"`
Files []string `json:"files"`
Lang string `json:"lang"`
}
func (h *Heruvym) PutMessage(
ctx context.Context,
request ReqPutMessage,
) (interface{}, int) {
sess := jwt_adapter.Get(ctx)
request.Files = []string{}
message, err := h.dal.PutMessage(
ctx,
request.Message,
sess.User,
sess.Session,
request.TicketID,
[]string{},
)
if err != nil {
return errors.New("can not put message"), http.StatusInternalServerError
}
go func() {
role, _ := h.ral.GetProfileRole(context.TODO(), sess.User)
if role != "user" && role != ""{
return
}
if sess.User != "" {
additional, err := h.dal.GetAdditionalData(context.TODO(), sess.User)
fmt.Println("CAN NOT NOTIFY", err)
if err == nil {
if _, err := h.notifier.Send(tb.ChatID(-1001344671794),
fmt.Sprintf("Поступило новое сообщение от пользователя %d с почтой %s",
additional.Uid, additional.Email)); err != nil {
fmt.Println("CAN NOT NOTIFY", err)
}
return
}
}
if _, err := h.notifier.Send(tb.ChatID(-1001344671794),
fmt.Sprintf(
"Поступило новое сообщение от незарегистриованного пользователя")); err != nil {
fmt.Println("CAN NOT NOTIFY", err)
}
}()
if err := h.dal.UpdateTopMessage(ctx, request.TicketID, message); err != nil {
return errors.New("can not update ticket"), http.StatusInternalServerError
}
return nil, http.StatusOK
}
type ReqScreenshot struct {
TicketID string `json:"ticket"`
Lang string `json:"lang"`
}
func (h *Heruvym) RequestScreenshot(
ctx context.Context,
request ReqScreenshot,
) (interface{}, int) {
sess := jwt_adapter.Get(ctx)
_, err := h.dal.PutSCRequest(
ctx,
sess.User,
sess.Session,
request.TicketID,
)
if err != nil {
return errors.New("can not put message"), http.StatusInternalServerError
}
return nil, http.StatusOK
}
var _ tools.DataEmitter = (&Heruvym{}).Subscribe
func (h *Heruvym) Subscribe(ctx context.Context) chan interface{} {
sess := jwt_adapter.Get(ctx)
fmt.Println("SESS Subsc", sess)
ticketID := ctx.Value(tools.ContextURLKey).(string)
output := make(chan interface{})
if sess.User == "" {
go func() {
ticket, err := h.dal.GetTicket4Sess(ctx, ticketID, sess.Session)
if err != nil || ticket == nil {
output <- errors.New("no tickets 4 session")
return
}
if err := h.dal.YieldMessages(ctx, ticketID, func(message model.Message) error {
output <- message
fmt.Println("OOOOOOLd")
//if err := h.dal.SetShown(ctx, message.ID, sess.Session); err != nil {
//
// output <- errors.New("cannot show message " + err.Error())
// return err
//}
return nil
}); err != nil {
output <- errors.New("cannot read messages " + err.Error())
}
if err := h.dal.WatchMessages(ctx, ticketID,
func(message model.Message) error {
output <- message
//if err := h.dal.SetShown(ctx, message.ID, sess.Session); err != nil {
// fmt.Println("3", err)
// output <- errors.New("cannot show watch message " + err.Error())
// return err
//}
return nil
}); err != nil {
fmt.Println("4", err)
output <- errors.New("cannot watch messages " + err.Error())
}
}()
} else {
role, err := h.ral.GetProfileRole(ctx, sess.User)
if err != nil {
go h.hasNoRole(output)
}
if role == "admin" || role == "manager" {
go func() {
if err := h.dal.YieldMessages(ctx, ticketID, func(message model.Message) error {
output <- message
//if err := h.dal.SetShown(ctx, message.ID, sess.User); err != nil {
// fmt.Println("2", err)
// output <- errors.New("cannot show message " + err.Error())
// return err
//}
return nil
}); err != nil {
fmt.Println("1", err)
output <- errors.New("cannot read messages " + err.Error())
}
if err := h.dal.WatchMessages(ctx, ticketID,
func(message model.Message) error {
output <- message
//if err := h.dal.SetShown(ctx, message.ID, sess.Session); err != nil {
// fmt.Println("3", err)
// output <- errors.New("cannot show watch message " + err.Error())
// return err
//}
return nil
}); err != nil {
fmt.Println("4", err)
output <- errors.New("cannot watch messages " + err.Error())
}
}()
} else {
go func() {
defer func() {
if v := recover(); v != nil {
fmt.Println("heryvym panic", v)
}
}()
ticket, err := h.dal.GetTicket4User(ctx, ticketID, sess.User)
if err != nil || ticket == nil {
output <- errors.New("no tickets 4 user")
return
}
if err := h.dal.YieldMessages(ctx, ticketID, func(message model.Message) error {
output <- message
//if err := h.dal.SetShown(ctx, message.ID, sess.User); err != nil {
// fmt.Println("2", err)
// output <- errors.New("cannot show message " + err.Error())
// return err
//}
return nil
}); err != nil {
fmt.Println("1", err)
output <- errors.New("cannot read messages " + err.Error())
}
if err := h.dal.WatchMessages(ctx, ticketID,
func(message model.Message) error {
output <- message
//if err := h.dal.SetShown(ctx, message.ID, sess.Session); err != nil {
// fmt.Println("3", err)
// output <- errors.New("cannot show watch message " + err.Error())
// return err
//}
return nil
}); err != nil {
fmt.Println("4", err)
output <- errors.New("cannot watch messages " + err.Error())
}
}()
}
}
return output
}
func (h *Heruvym) handleOwnMessages(output chan interface{}) {
defer close(output)
}
type GetTicketsReq struct {
Amount int64 `json:"amt"`
Page int64 `json:"page"`
Search string `json:"srch"`
Status string `json:"status"`
}
type GetTicketsResp struct {
Data []model.Ticket `json:"data"`
Count int64 `json:"count"`
}
func (h *Heruvym) GetTickets(
ctx context.Context,
request GetTicketsReq) (GetTicketsResp, int) {
result, count, err := h.dal.GetTicketPage(ctx,
request.Status,
request.Search,
request.Amount,
request.Page,
)
if err != nil {
return GetTicketsResp{}, http.StatusNoContent
}
return GetTicketsResp{
Data: *result,
Count: count,
}, http.StatusOK
}
type GetMessagesReq struct {
Amount int64 `json:"amt"`
Page int64 `json:"page"`
Search string `json:"srch"`
TicketID string `json:"ticket"`
}
func (h *Heruvym) GetMessages(
ctx context.Context,
request GetMessagesReq) ([]model.Message, int) {
result, err := h.dal.GetMessagesPage(ctx,
request.Search,
request.TicketID,
request.Amount,
request.Page,
)
if err != nil {
return nil, http.StatusNoContent
}
return result, http.StatusOK
}
type CloseTicketReq struct {
TicketID string `json:"ticket"`
}
type CloseTicketResp struct {
TicketID string `json:"ticket"`
}
func (h *Heruvym) CloseTicket(ctx context.Context, req CloseTicketReq) (*CloseTicketResp, int) {
if err := h.dal.SetTicketStatus(ctx, req.TicketID, model.StateClose); err != nil {
return nil, http.StatusBadRequest
}
if _, err := h.dal.PutMessage(ctx, "close", "close", "close", req.TicketID, []string{}); err != nil {
return nil, http.StatusBadRequest
}
return &CloseTicketResp{
TicketID: req.TicketID,
}, http.StatusOK
}
type VoteReq struct {
TicketID string `json:"ticket"`
Rate int `json:"rate"`
}
func (h *Heruvym) Vote(ctx context.Context, req VoteReq) (error, int) {
if err := h.dal.SetRate(ctx, req.TicketID, req.Rate); err != nil {
return err, http.StatusBadRequest
}
return nil, http.StatusOK
}
type PickReq struct {
TicketID string `json:"ticket"`
}
func (h *Heruvym) Pick(ctx context.Context, req PickReq) (error, int) {
sess := jwt_adapter.Get(ctx)
if err := h.dal.SetAnswerer(ctx, req.TicketID, sess.User); err != nil {
return err, http.StatusBadRequest
}
return nil, http.StatusOK
}
type DelegateReq struct {
TicketID string `json:"ticket"`
AnswererID string `json:"answerer"`
}
func (h *Heruvym) Delegate(ctx context.Context, req DelegateReq) (error, int) {
if err := h.dal.SetAnswerer(ctx, req.TicketID, req.AnswererID); err != nil {
return err, http.StatusBadRequest
}
return nil, http.StatusOK
}
// MB Size constants
const (
MB = 1 << 20
)
type PutFileReq struct {
Ticket string `json:"ticket"`
}
type PutFileResp struct {
Message string `json:"message"`
}
func (h *Heruvym) PutFile(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
if err := r.ParseMultipartForm(10 * MB); err != nil {
if _, err := w.Write([]byte("can not parse multipart " + err.Error())); err != nil {
fmt.Println("CAN NOT WRITE", err)
}
w.WriteHeader(http.StatusBadRequest)
return
}
if r.MultipartForm == nil {
if _, err := w.Write([]byte("no multipart")); err != nil {
fmt.Println("CAN NOT WRITE", err)
}
w.WriteHeader(http.StatusBadRequest)
return
}
if r.MultipartForm.File == nil {
if _, err := w.Write([]byte("no file")); err != nil {
fmt.Println("CAN NOT WRITE", err)
}
w.WriteHeader(http.StatusBadRequest)
return
}
filesCount := len(r.MultipartForm.File)
if filesCount == 0 {
if _, err := w.Write([]byte("no files")); err != nil {
fmt.Println("CAN NOT WRITE", err)
}
w.WriteHeader(http.StatusBadRequest)
return
}
sess := jwt_adapter.Get(r.Context())
if sess == nil {
if _, err := w.Write([]byte("not authorized")); err != nil {
fmt.Println("CAN NOT WRITE", err)
}
w.WriteHeader(http.StatusBadRequest)
return
}
var req PutFileReq
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
if _, err := w.Write([]byte("can not decode " + err.Error())); err != nil {
fmt.Println("CAN NOT WRITE", err)
}
w.WriteHeader(http.StatusBadRequest)
return
}
var (
fileIDs, filenames []string
errFile error
)
wg := new(sync.WaitGroup)
m := new(sync.Mutex)
wg.Add(filesCount)
for name, file := range r.MultipartForm.File {
file := file
name := name
go func() {
defer wg.Done()
freader, err := file[0].Open()
if err != nil {
fmt.Println("can not open ", err.Error())
}
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 := h.bs.PutFile(
r.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 {
if _, err := w.Write([]byte("can not store files " + errFile.Error())); err != nil {
fmt.Println("CAN NOT WRITE", err)
}
w.WriteHeader(http.StatusInternalServerError)
return
}
message, err := h.dal.PutMessage(
r.Context(),
strings.Join(filenames, ", "),
sess.User,
sess.Session,
req.Ticket,
fileIDs,
)
if err != nil {
for _, filename := range filenames {
if err := h.bs.DeleteFile(r.Context(), filename); err != nil {
fmt.Println("can not delete", err)
}
}
if _, err := w.Write([]byte("can not store message " + err.Error())); err != nil {
fmt.Println("CAN NOT WRITE", err)
}
w.WriteHeader(http.StatusInternalServerError)
return
}
resp, err := json.Marshal(&PutFileResp{Message: message.ID})
if err != nil {
if _, err := w.Write([]byte("can not marshal resp " + err.Error())); err != nil {
fmt.Println("CAN NOT WRITE", err)
}
w.WriteHeader(http.StatusInternalServerError)
return
}
if _, err := w.Write(resp); err != nil {
fmt.Println("CAN NOT WRITE", err)
}
w.WriteHeader(http.StatusOK)
}
type PutSCReq struct {
Ticket string `json:"ticket"`
}
type PutSCResp struct {
Message string `json:"message"`
}
func (h *Heruvym) PutSC(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
if err := r.ParseMultipartForm(10 * MB); err != nil {
if _, err := w.Write([]byte("can not parse multipart " + err.Error())); err != nil {
fmt.Println("CAN NOT WRITE", err)
}
w.WriteHeader(http.StatusBadRequest)
return
}
if r.MultipartForm == nil {
if _, err := w.Write([]byte("no multipart")); err != nil {
fmt.Println("CAN NOT WRITE", err)
}
w.WriteHeader(http.StatusBadRequest)
return
}
if r.MultipartForm.File == nil {
if _, err := w.Write([]byte("no file")); err != nil {
fmt.Println("CAN NOT WRITE", err)
}
w.WriteHeader(http.StatusBadRequest)
return
}
filesCount := len(r.MultipartForm.File)
if filesCount == 0 {
if _, err := w.Write([]byte("no files")); err != nil {
fmt.Println("CAN NOT WRITE", err)
}
w.WriteHeader(http.StatusBadRequest)
return
}
sess := jwt_adapter.Get(r.Context())
if sess == nil {
if _, err := w.Write([]byte("not authorized")); err != nil {
fmt.Println("CAN NOT WRITE", err)
}
w.WriteHeader(http.StatusBadRequest)
return
}
var req PutFileReq
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
if _, err := w.Write([]byte("can not decode " + err.Error())); err != nil {
fmt.Println("CAN NOT WRITE", err)
}
w.WriteHeader(http.StatusBadRequest)
return
}
var (
fileIDs, filenames []string
errFile error
)
wg := new(sync.WaitGroup)
m := new(sync.Mutex)
wg.Add(filesCount)
for name, file := range r.MultipartForm.File {
file := file
name := name
go func() {
defer wg.Done()
freader, err := file[0].Open()
if err != nil {
fmt.Println("can not open ", err.Error())
}
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 := h.bs.PutFile(
r.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 {
if _, err := w.Write([]byte("can not store files " + errFile.Error())); err != nil {
fmt.Println("CAN NOT WRITE", err)
}
w.WriteHeader(http.StatusInternalServerError)
return
}
message, err := h.dal.PutSCResponse(
r.Context(),
strings.Join(filenames, ", "),
sess.User,
sess.Session,
req.Ticket,
fileIDs,
)
if err != nil {
for _, filename := range filenames {
if err := h.bs.DeleteFile(r.Context(), filename); err != nil {
fmt.Println("can not delete", err)
}
}
if _, err := w.Write([]byte("can not store message " + err.Error())); err != nil {
fmt.Println("CAN NOT WRITE", err)
}
w.WriteHeader(http.StatusInternalServerError)
return
}
resp, err := json.Marshal(&PutFileResp{Message: message.ID})
if err != nil {
if _, err := w.Write([]byte("can not marshal resp " + err.Error())); err != nil {
fmt.Println("CAN NOT WRITE", err)
}
w.WriteHeader(http.StatusInternalServerError)
return
}
if _, err := w.Write(resp); err != nil {
fmt.Println("CAN NOT WRITE", err)
}
w.WriteHeader(http.StatusOK)
}