package service import ( "context" "encoding/json" "errors" "fmt" "heruvym/dal/minio" "heruvym/dal/mongo" "heruvym/jwt_adapter" "heruvym/model" "heruvym/tools" "net/http" "strings" "sync" "github.com/rs/xid" "github.com/themakers/hlog" tb "gopkg.in/tucnak/telebot.v2" ) type Heruvym struct { logger hlog.Logger dal *mongo.DAL bs *minio.BlobStore notifier *tb.Bot } func New( blobs *minio.BlobStore, dataAccessLayer *mongo.DAL, log hlog.Logger, notifier *tb.Bot) *Heruvym { return &Heruvym{ logger: log.Module("Service"), dal: dataAccessLayer, 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"` Sess string `json:"sess"` } 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.StatusBadRequest) return } if request.Message == "" { http.Error(w, "No Message", http.StatusBadRequest) return } ctx := r.Context() session := jwt_adapter.Get(ctx) if session == nil { http.Error(w, "No session", http.StatusUnauthorized) return } var ( ticketID string tickets []model.Ticket role = jwt_adapter.GetRole(ctx) ) if role == "" { tickets, _, err = h.dal.GetTickets4Sess(ctx, session.Id) } if err != nil || len(tickets) == 0 { ticketID, err = h.dal.CreateTicket( ctx, session.Id, session.Id, request.Title, request.Message, []string{}, ) if err != nil { http.Error(w, "CannotCreateTicket", http.StatusInternalServerError) return } if _, err := h.dal.PutMessage(ctx, request.Message, session.Id, session.Id, ticketID, []string{}, ); err != nil { http.Error(w, "CannotCreateMessage", http.StatusInternalServerError) return } } else { ticketID = tickets[0].ID } response, err := json.Marshal(CreateTicketResp{Ticket: ticketID, Sess: session.Id}) 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.Id == "" { go h.unauthorizedTickets(ctx, sess.Id, output) } else { role := jwt_adapter.GetRole(ctx) fmt.Println("ALL TICKETS Sess ", sess.Id, role) if role == "admin" || role == "manager" { go h.allTickets(ctx, output) } else { go h.userTickets(ctx, sess.Id, 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, 0) 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) fmt.Println("PUTMES", sess) request.Files = []string{} message, err := h.dal.PutMessage( ctx, request.Message, sess.Id, sess.Id, request.TicketID, []string{}, ) if err != nil { fmt.Println("PUTMES1", err) return errors.New("can not put message"), http.StatusInternalServerError } go func() { /*if sess.Id != "" { additional, err := h.dal.GetAdditionalData(context.TODO(), sess.Id) fmt.Println("CAN NOT NOTIFY", err) if err == nil && h.notifier != nil { additional, err := h.dal.GetAdditionalData(context.TODO(), sess.Id) fmt.Println("CAN NOT NOTIFY", err) 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 h.notifier != nil { if _, err := h.notifier.Send(tb.ChatID(-1001802261459), 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.Id, sess.Id, 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.Id == "" { go func() { ticket, err := h.dal.GetTicket4Sess(ctx, ticketID, sess.Id) 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 := jwt_adapter.GetRole(ctx) 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.Id) if err != nil || ticket == nil { output <- errors.New("no tickets 4 user") } /*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) { role := jwt_adapter.GetRole(ctx) if role == "admin" { 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 } else { sess := jwt_adapter.Get(ctx) result, count, err := h.dal.YieldUserTickets(ctx, sess.Id, request.Amount, request.Page*request.Amount) 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.Id); 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 req.Ticket = r.MultipartForm.Value["ticket"][0] 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.Id, sess.Id, 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 req.Ticket = r.MultipartForm.Value["ticket"][0] 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.Id, sess.Id, 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) }