/
ticket.go
247 lines (217 loc) · 7.86 KB
/
ticket.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
package handler
import (
"fmt"
"io"
"log/slog"
"net/http"
"net/smtp"
"os"
"strconv"
"github.com/labstack/echo/v4"
"gorm.io/gorm"
"github.com/alirezaarzehgar/ticketservice/model"
"github.com/alirezaarzehgar/ticketservice/util"
)
var DefaultAssetDir = "./assets"
// UploadAsset godoc
//
// @Summary Upload asset for tickets
// @Description Users can upload asset and attach it's url to their tickets
// @Tags ticket
// @Accept json
// @Produce json
// @Param is_image query string false "flag for check images"
// @Param asset formData string true "asset"
// @Success 200 {object} util.Response
// @Failure 400 {object} util.ResponseError
// @Failure 500 {object} util.ResponseError
//
// @Router /ticket/assets [POST]
func UploadAsset(c echo.Context) error {
var isImage bool
if c.QueryParam("is_image") != "" {
var err error
isImage, err = strconv.ParseBool(c.QueryParam("is_image"))
if err != nil {
return c.JSON(http.StatusBadRequest, util.Response{Alert: util.ALERT_BAD_REQUEST})
}
}
slog.Debug("start saving an asset")
file, err := c.FormFile("asset")
if err != nil {
slog.Debug("asset form field is empty")
return c.JSON(http.StatusBadRequest, util.Response{Alert: util.ALERT_BAD_REQUEST})
}
slog.Debug("recieve file", "data", file.Filename)
src, err := file.Open()
if err != nil {
slog.Debug("cannot open file", "data", err)
return c.JSON(http.StatusInternalServerError, util.Response{Alert: util.ALERT_INTERNAL})
}
defer src.Close()
slog.Debug("open sent file", "data", file.Filename)
if !util.IsValidPath(file.Filename, isImage) {
return c.JSON(http.StatusBadRequest, util.Response{Alert: util.ALERT_BAD_REQUEST})
}
dirpath := util.GetUserDir(util.GetUserId(c))
assetsDir := DefaultAssetDir + "/" + dirpath
if _, err := os.Stat(assetsDir); err != nil {
if err := os.Mkdir(assetsDir, os.ModePerm); err != nil {
slog.Debug("mkdir dirpath", "err", err)
return c.JSON(http.StatusInternalServerError, util.Response{Alert: util.ALERT_INTERNAL})
}
slog.Debug("create dir", "data", assetsDir)
}
filepath := fmt.Sprintf("%s/%s", dirpath, util.GetUniqueName(file.Filename))
dst, err := os.Create(DefaultAssetDir + "/" + filepath)
if err != nil {
slog.Debug("create filepath", "path", filepath, "err", err)
return c.JSON(http.StatusInternalServerError, util.Response{Alert: util.ALERT_INTERNAL})
}
defer dst.Close()
slog.Debug("create asset on path", "path", filepath)
if _, err = io.Copy(dst, src); err != nil {
return err
}
slog.Debug("copy transfered file to assets directory")
return c.JSON(http.StatusOK, util.Response{
Status: true,
Alert: util.ALERT_SUCCESS,
Data: map[string]any{"path": filepath},
})
}
// SendTicket godoc
//
// @Summary Create and send ticket to organization
// @Description Just admins can reply it though email.
// @Tags ticket
// @Accept json
// @Produce json
// @Param org_id path int true "Organization ID"
// @Param title body string true "Title"
// @Param body body string true "Body"
// @Param attachment_url body string false "Attachment Url"
// @Param website_url body string false "website Url"
// @Success 200 {object} util.Response
// @Failure 400 {object} util.ResponseError
//
// @Router /ticket/:org_id [POST]
func SendTicket(c echo.Context) error {
id, err := strconv.Atoi(c.Param("org_id"))
if err != nil || id <= 0 {
slog.Debug("invalid id parameter", "data", err)
return c.JSON(http.StatusBadRequest, util.Response{Alert: util.ALERT_BAD_REQUEST})
}
var ticket model.Ticket
if err := util.ParseBody(c, &ticket, []string{"title", "body"}, nil); err != nil {
return nil
}
ticket.UserID = util.GetUserId(c)
ticket.OrganizationID = uint(id)
slog.Debug("create ticket", "data", ticket)
err = db.Create(&ticket).Error
if err == gorm.ErrForeignKeyViolated {
slog.Debug("user not found", "data", err)
return c.JSON(http.StatusNotFound, util.Response{Alert: util.ALERT_NOT_FOUND})
} else if err != nil {
slog.Debug("db error on create ticket", "data", err)
return c.JSON(http.StatusInternalServerError, util.Response{Alert: util.ALERT_INTERNAL})
}
return c.JSON(http.StatusOK, util.Response{Status: true, Alert: util.ALERT_SUCCESS, Data: ticket})
}
// GetAllTickets godoc
//
// @Summary Get tickets by priviledge
// @Description If you are a user you can see your tickets about an organization.
// @Description If you are an admin you can see tickets of all users for allorganizations.
// @Tags ticket
// @Accept json
// @Produce json
// @Param org_id path int true "Organization ID"
// @Success 200 {object} util.Response
// @Failure 400 {object} util.ResponseError
//
// @Router /ticket/{org_id} [GET]
func GetAllTickets(c echo.Context) error {
var tickets []model.Ticket
userId := util.GetUserId(c)
orgId, err := strconv.Atoi(c.Param("org_id"))
if err != nil || orgId <= 0 {
slog.Debug("invalid id parameter", "data", err)
return c.JSON(http.StatusBadRequest, util.Response{Alert: util.ALERT_BAD_REQUEST})
}
role := util.GetUserRole(c)
slog.Debug("requested role", "role", role)
switch role {
case model.USERS_ROLE_USER:
db.Find(&tickets, "organization_id = ? AND user_id = ?", orgId, userId)
case model.USERS_ROLE_ADMIN:
var permitted int64
oa := &model.OrgAdmin{OrganizationID: uint(orgId), UserID: userId}
db.Where(oa).First(&model.OrgAdmin{}).Count(&permitted)
if permitted < 1 {
slog.Debug("user is not admin of reguested org", "user_id", userId, "org_id", orgId)
return c.JSON(http.StatusUnauthorized, util.Response{Status: false, Alert: util.ALERT_ORG_EDIT_UNAUTHORIZED})
}
db.Find(&tickets, "organization_id = ?", orgId)
case model.USERS_ROLE_SUPER_ADMIN:
db.Find(&tickets, "organization_id = ?", orgId)
}
return c.JSON(http.StatusOK, util.Response{Status: true, Alert: util.ALERT_SUCCESS, Data: tickets})
}
// ReplyToTicket godoc
//
// @Summary Admin can reply to a ticket
// @Description Admins can reply to his organization tickets.
// @Description Super Admins can reply to every ticket.
// @Tags organization
// @Accept json
// @Produce json
// @Param id path int true "Ticket ID"
// @Param subject body int true "Email subject"
// @Param body body int true "Email body"
// @Success 200 {object} util.Response
// @Failure 400 {object} util.ResponseError
//
// @Router /ticket/{id}/mail [POST]
func ReplyToTicket(c echo.Context) error {
ticketId, err := strconv.Atoi(c.Param("id"))
if err != nil || ticketId <= 0 {
slog.Debug("invalid id parameter", "data", err)
return c.JSON(http.StatusBadRequest, util.Response{Alert: util.ALERT_BAD_REQUEST})
}
var sc util.SmtpContent
if err := util.ParseBody(c, &sc, []string{"subject", "body"}, nil); err != nil {
return nil
}
var ticket model.Ticket
r := db.Preload("User").First(&ticket, ticketId)
if r.Error == gorm.ErrRecordNotFound || r.RowsAffected == 0 {
slog.Debug("ticket not found with recieved id", "data", r.Error)
return c.JSON(http.StatusNotFound, util.Response{Alert: util.ALERT_NOT_FOUND})
} else if err != nil {
slog.Debug("database error", "data", err)
return c.JSON(http.StatusInternalServerError, util.Response{Alert: util.ALERT_INTERNAL})
}
slog.Debug("fetched ticket", "ticket", ticket)
ticket.Seen = true
if err = db.Save(&ticket).Error; err != nil {
slog.Debug("database error", "data", err)
return c.JSON(http.StatusInternalServerError, util.Response{Alert: util.ALERT_INTERNAL})
}
slog.Debug("change ticket status to seen", "ticket", ticket)
to := ticket.User.Email
go func() {
conf := util.SmtpConf
msg := fmt.Sprintf("From: %s\r\n"+
"To: %s\r\n"+
"Subject: %s\r\n"+sc.Body,
conf.FromAddress, to, sc.Subject,
)
err = smtp.SendMail(conf.Server, util.SmtpAuth, conf.FromAddress, []string{to}, []byte(msg))
if err != nil {
slog.Info("the system could not send your email", "email", msg, "err", err)
}
}()
return c.JSON(http.StatusOK, util.Response{Status: true, Alert: util.ALERT_SUCCESS})
}