mirror of
https://github.com/abhinavxd/libredesk.git
synced 2025-10-23 05:11:57 +00:00
Show a warning in sidebar when a conversation with a visitor is opened. Move string.js utils to shared-ui utils Modify user fetch queries to allow optional filter by multiple user types not just one. Fix capitalizaiton for live chat en translations
294 lines
9.4 KiB
Go
294 lines
9.4 KiB
Go
package main
|
|
|
|
import (
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
|
|
amodels "github.com/abhinavxd/libredesk/internal/auth/models"
|
|
"github.com/abhinavxd/libredesk/internal/envelope"
|
|
"github.com/abhinavxd/libredesk/internal/stringutil"
|
|
"github.com/abhinavxd/libredesk/internal/user/models"
|
|
"github.com/valyala/fasthttp"
|
|
"github.com/volatiletech/null/v9"
|
|
"github.com/zerodha/fastglue"
|
|
)
|
|
|
|
type createContactNoteReq struct {
|
|
Note string `json:"note"`
|
|
}
|
|
|
|
type blockContactReq struct {
|
|
Enabled bool `json:"enabled"`
|
|
}
|
|
|
|
// handleGetContacts returns a list of contacts from the database.
|
|
func handleGetContacts(r *fastglue.Request) error {
|
|
var (
|
|
app = r.Context.(*App)
|
|
order = string(r.RequestCtx.QueryArgs().Peek("order"))
|
|
orderBy = string(r.RequestCtx.QueryArgs().Peek("order_by"))
|
|
filters = string(r.RequestCtx.QueryArgs().Peek("filters"))
|
|
page, _ = strconv.Atoi(string(r.RequestCtx.QueryArgs().Peek("page")))
|
|
pageSize, _ = strconv.Atoi(string(r.RequestCtx.QueryArgs().Peek("page_size")))
|
|
total = 0
|
|
)
|
|
contacts, err := app.user.GetContacts(page, pageSize, order, orderBy, filters)
|
|
if err != nil {
|
|
return sendErrorEnvelope(r, err)
|
|
}
|
|
if len(contacts) > 0 {
|
|
total = contacts[0].Total
|
|
}
|
|
return r.SendEnvelope(envelope.PageResults{
|
|
Results: contacts,
|
|
Total: total,
|
|
PerPage: pageSize,
|
|
TotalPages: (total + pageSize - 1) / pageSize,
|
|
Page: page,
|
|
})
|
|
}
|
|
|
|
// handleGetTags returns a contact from the database.
|
|
func handleGetContact(r *fastglue.Request) error {
|
|
var (
|
|
app = r.Context.(*App)
|
|
id, _ = strconv.Atoi(r.RequestCtx.UserValue("id").(string))
|
|
)
|
|
if id <= 0 {
|
|
return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.Ts("globals.messages.invalid", "name", "`id`"), nil, envelope.InputError)
|
|
}
|
|
c, err := app.user.GetContact(id, "")
|
|
if err != nil {
|
|
return sendErrorEnvelope(r, err)
|
|
}
|
|
return r.SendEnvelope(c)
|
|
}
|
|
|
|
// handleUpdateContact updates a contact in the database.
|
|
func handleUpdateContact(r *fastglue.Request) error {
|
|
var (
|
|
app = r.Context.(*App)
|
|
id, _ = strconv.Atoi(r.RequestCtx.UserValue("id").(string))
|
|
)
|
|
if id <= 0 {
|
|
return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.Ts("globals.messages.invalid", "name", "`id`"), nil, envelope.InputError)
|
|
}
|
|
|
|
contact, err := app.user.GetContact(id, "")
|
|
if err != nil {
|
|
return sendErrorEnvelope(r, err)
|
|
}
|
|
|
|
form, err := r.RequestCtx.MultipartForm()
|
|
if err != nil {
|
|
app.lo.Error("error parsing form data", "error", err)
|
|
return r.SendErrorEnvelope(fasthttp.StatusInternalServerError, app.i18n.Ts("globals.messages.errorParsing", "name", "{globals.terms.request}"), nil, envelope.GeneralError)
|
|
}
|
|
|
|
// Parse form data
|
|
firstName := ""
|
|
if v, ok := form.Value["first_name"]; ok && len(v) > 0 {
|
|
firstName = string(v[0])
|
|
}
|
|
lastName := ""
|
|
if v, ok := form.Value["last_name"]; ok && len(v) > 0 {
|
|
lastName = string(v[0])
|
|
}
|
|
email := ""
|
|
if v, ok := form.Value["email"]; ok && len(v) > 0 {
|
|
email = strings.TrimSpace(string(v[0]))
|
|
}
|
|
phoneNumber := ""
|
|
if v, ok := form.Value["phone_number"]; ok && len(v) > 0 {
|
|
phoneNumber = string(v[0])
|
|
}
|
|
phoneNumberCountryCode := ""
|
|
if v, ok := form.Value["phone_number_country_code"]; ok && len(v) > 0 {
|
|
phoneNumberCountryCode = string(v[0])
|
|
}
|
|
avatarURL := ""
|
|
if v, ok := form.Value["avatar_url"]; ok && len(v) > 0 {
|
|
avatarURL = string(v[0])
|
|
}
|
|
|
|
// Set nulls to empty strings.
|
|
if avatarURL == "null" {
|
|
avatarURL = ""
|
|
}
|
|
if phoneNumberCountryCode == "null" {
|
|
phoneNumberCountryCode = ""
|
|
}
|
|
if phoneNumber == "null" {
|
|
phoneNumber = ""
|
|
}
|
|
|
|
// Validate mandatory fields.
|
|
if email == "" {
|
|
return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.Ts("globals.messages.empty", "name", "email"), nil, envelope.InputError)
|
|
}
|
|
if !stringutil.ValidEmail(email) {
|
|
return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.Ts("globals.messages.invalid", "name", "email"), nil, envelope.InputError)
|
|
}
|
|
if firstName == "" {
|
|
return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.Ts("globals.messages.empty", "name", "first_name"), nil, envelope.InputError)
|
|
}
|
|
|
|
// Another contact with same new email?
|
|
existingContact, _ := app.user.GetContact(0, email)
|
|
if existingContact.ID > 0 && existingContact.ID != id {
|
|
return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.T("contact.alreadyExistsWithEmail"), nil, envelope.InputError)
|
|
}
|
|
|
|
contactToUpdate := models.User{
|
|
FirstName: firstName,
|
|
LastName: lastName,
|
|
Email: null.StringFrom(email),
|
|
AvatarURL: null.NewString(avatarURL, avatarURL != ""),
|
|
PhoneNumber: null.NewString(phoneNumber, phoneNumber != ""),
|
|
PhoneNumberCountryCode: null.NewString(phoneNumberCountryCode, phoneNumberCountryCode != ""),
|
|
}
|
|
|
|
if err := app.user.UpdateContact(id, contactToUpdate); err != nil {
|
|
return sendErrorEnvelope(r, err)
|
|
}
|
|
|
|
// Delete avatar?
|
|
if avatarURL == "" && contact.AvatarURL.Valid {
|
|
fileName := filepath.Base(contact.AvatarURL.String)
|
|
app.media.Delete(fileName)
|
|
contact.AvatarURL.Valid = false
|
|
contact.AvatarURL.String = ""
|
|
}
|
|
|
|
// Upload avatar?
|
|
files, ok := form.File["files"]
|
|
if ok && len(files) > 0 {
|
|
if err := uploadUserAvatar(r, contact, files); err != nil {
|
|
return sendErrorEnvelope(r, err)
|
|
}
|
|
}
|
|
|
|
// Refetch contact and return it
|
|
contact, err = app.user.GetContact(id, "")
|
|
if err != nil {
|
|
return sendErrorEnvelope(r, err)
|
|
}
|
|
return r.SendEnvelope(contact)
|
|
}
|
|
|
|
// handleGetContactNotes returns all notes for a contact.
|
|
func handleGetContactNotes(r *fastglue.Request) error {
|
|
var (
|
|
app = r.Context.(*App)
|
|
contactID, _ = strconv.Atoi(r.RequestCtx.UserValue("id").(string))
|
|
)
|
|
if contactID <= 0 {
|
|
return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.Ts("globals.messages.invalid", "name", "`id`"), nil, envelope.InputError)
|
|
}
|
|
notes, err := app.user.GetNotes(contactID)
|
|
if err != nil {
|
|
return sendErrorEnvelope(r, err)
|
|
}
|
|
return r.SendEnvelope(notes)
|
|
}
|
|
|
|
// handleCreateContactNote creates a note for a contact.
|
|
func handleCreateContactNote(r *fastglue.Request) error {
|
|
var (
|
|
app = r.Context.(*App)
|
|
contactID, _ = strconv.Atoi(r.RequestCtx.UserValue("id").(string))
|
|
auser = r.RequestCtx.UserValue("user").(amodels.User)
|
|
req = createContactNoteReq{}
|
|
)
|
|
if err := r.Decode(&req, "json"); err != nil {
|
|
return sendErrorEnvelope(r, envelope.NewError(envelope.InputError, app.i18n.Ts("globals.messages.errorParsing", "name", "{globals.terms.request}"), nil))
|
|
}
|
|
if len(req.Note) == 0 {
|
|
return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.Ts("globals.messages.empty", "name", "note"), nil, envelope.InputError)
|
|
}
|
|
n, err := app.user.CreateNote(contactID, auser.ID, req.Note)
|
|
if err != nil {
|
|
return sendErrorEnvelope(r, err)
|
|
}
|
|
n, err = app.user.GetNote(n.ID)
|
|
if err != nil {
|
|
return sendErrorEnvelope(r, err)
|
|
}
|
|
return r.SendEnvelope(n)
|
|
}
|
|
|
|
// handleDeleteContactNote deletes a note for a contact.
|
|
func handleDeleteContactNote(r *fastglue.Request) error {
|
|
var (
|
|
app = r.Context.(*App)
|
|
contactID, _ = strconv.Atoi(r.RequestCtx.UserValue("id").(string))
|
|
noteID, _ = strconv.Atoi(r.RequestCtx.UserValue("note_id").(string))
|
|
auser = r.RequestCtx.UserValue("user").(amodels.User)
|
|
)
|
|
if contactID <= 0 {
|
|
return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.Ts("globals.messages.invalid", "name", "`id`"), nil, envelope.InputError)
|
|
}
|
|
if noteID <= 0 {
|
|
return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.Ts("globals.messages.invalid", "name", "`note_id`"), nil, envelope.InputError)
|
|
}
|
|
|
|
agent, err := app.user.GetAgent(auser.ID, "")
|
|
if err != nil {
|
|
return sendErrorEnvelope(r, err)
|
|
}
|
|
|
|
// Allow deletion of only own notes and not those created by others, but also allow `Admin` to delete any note.
|
|
if !agent.HasAdminRole() {
|
|
note, err := app.user.GetNote(noteID)
|
|
if err != nil {
|
|
return sendErrorEnvelope(r, err)
|
|
}
|
|
if note.UserID != auser.ID {
|
|
return r.SendErrorEnvelope(fasthttp.StatusForbidden, app.i18n.Ts("globals.messages.canOnlyDeleteOwn", "name", "{globals.terms.note}"), nil, envelope.InputError)
|
|
}
|
|
}
|
|
|
|
app.lo.Info("deleting contact note", "note_id", noteID, "contact_id", contactID, "actor_id", auser.ID)
|
|
|
|
if err := app.user.DeleteNote(noteID, contactID); err != nil {
|
|
return sendErrorEnvelope(r, err)
|
|
}
|
|
return r.SendEnvelope(true)
|
|
}
|
|
|
|
// handleBlockContact blocks a contact.
|
|
func handleBlockContact(r *fastglue.Request) error {
|
|
var (
|
|
app = r.Context.(*App)
|
|
contactID, _ = strconv.Atoi(r.RequestCtx.UserValue("id").(string))
|
|
auser = r.RequestCtx.UserValue("user").(amodels.User)
|
|
req = blockContactReq{}
|
|
)
|
|
|
|
if contactID <= 0 {
|
|
return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.Ts("globals.messages.invalid", "name", "`id`"), nil, envelope.InputError)
|
|
}
|
|
|
|
if err := r.Decode(&req, "json"); err != nil {
|
|
return sendErrorEnvelope(r, envelope.NewError(envelope.InputError, app.i18n.Ts("globals.messages.errorParsing", "name", "{globals.terms.request}"), nil))
|
|
}
|
|
|
|
app.lo.Info("setting contact block status", "contact_id", contactID, "enabled", req.Enabled, "actor_id", auser.ID)
|
|
|
|
contact, err := app.user.GetContact(contactID, "")
|
|
if err != nil {
|
|
return sendErrorEnvelope(r, err)
|
|
}
|
|
|
|
if err := app.user.ToggleEnabled(contactID, contact.Type, req.Enabled); err != nil {
|
|
return sendErrorEnvelope(r, err)
|
|
}
|
|
|
|
contact, err = app.user.GetContact(contactID, "")
|
|
if err != nil {
|
|
return sendErrorEnvelope(r, err)
|
|
}
|
|
return r.SendEnvelope(contact)
|
|
}
|