mirror of
https://github.com/abhinavxd/libredesk.git
synced 2025-10-23 05:11:57 +00:00
feat: standardize API requests to use JSON instead of form data
This commit is contained in:
17
cmd/ai.go
17
cmd/ai.go
@@ -5,6 +5,11 @@ import (
|
|||||||
"github.com/zerodha/fastglue"
|
"github.com/zerodha/fastglue"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type aiCompletionReq struct {
|
||||||
|
PromptKey string `json:"prompt_key"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
}
|
||||||
|
|
||||||
type providerUpdateReq struct {
|
type providerUpdateReq struct {
|
||||||
Provider string `json:"provider"`
|
Provider string `json:"provider"`
|
||||||
APIKey string `json:"api_key"`
|
APIKey string `json:"api_key"`
|
||||||
@@ -13,11 +18,15 @@ type providerUpdateReq struct {
|
|||||||
// handleAICompletion handles AI completion requests
|
// handleAICompletion handles AI completion requests
|
||||||
func handleAICompletion(r *fastglue.Request) error {
|
func handleAICompletion(r *fastglue.Request) error {
|
||||||
var (
|
var (
|
||||||
app = r.Context.(*App)
|
app = r.Context.(*App)
|
||||||
promptKey = string(r.RequestCtx.PostArgs().Peek("prompt_key"))
|
req = aiCompletionReq{}
|
||||||
content = string(r.RequestCtx.PostArgs().Peek("content"))
|
|
||||||
)
|
)
|
||||||
resp, err := app.ai.Completion(promptKey, content)
|
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := app.ai.Completion(req.PromptKey, req.Content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return sendErrorEnvelope(r, err)
|
return sendErrorEnvelope(r, err)
|
||||||
}
|
}
|
||||||
|
@@ -9,6 +9,10 @@ import (
|
|||||||
"github.com/zerodha/fastglue"
|
"github.com/zerodha/fastglue"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type updateAutomationRuleExecutionModeReq struct {
|
||||||
|
Mode string `json:"mode"`
|
||||||
|
}
|
||||||
|
|
||||||
// handleGetAutomationRules gets all automation rules
|
// handleGetAutomationRules gets all automation rules
|
||||||
func handleGetAutomationRules(r *fastglue.Request) error {
|
func handleGetAutomationRules(r *fastglue.Request) error {
|
||||||
var (
|
var (
|
||||||
@@ -118,14 +122,20 @@ func handleUpdateAutomationRuleWeights(r *fastglue.Request) error {
|
|||||||
// handleUpdateAutomationRuleExecutionMode updates the execution mode of the automation rules for a given type
|
// handleUpdateAutomationRuleExecutionMode updates the execution mode of the automation rules for a given type
|
||||||
func handleUpdateAutomationRuleExecutionMode(r *fastglue.Request) error {
|
func handleUpdateAutomationRuleExecutionMode(r *fastglue.Request) error {
|
||||||
var (
|
var (
|
||||||
app = r.Context.(*App)
|
app = r.Context.(*App)
|
||||||
mode = string(r.RequestCtx.PostArgs().Peek("mode"))
|
req = updateAutomationRuleExecutionModeReq{}
|
||||||
)
|
)
|
||||||
if mode != amodels.ExecutionModeAll && mode != amodels.ExecutionModeFirstMatch {
|
|
||||||
|
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 req.Mode != amodels.ExecutionModeAll && req.Mode != amodels.ExecutionModeFirstMatch {
|
||||||
return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.T("automation.invalidRuleExecutionMode"), nil, envelope.InputError)
|
return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.T("automation.invalidRuleExecutionMode"), nil, envelope.InputError)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only new conversation rules can be updated as they are the only ones that have execution mode.
|
// Only new conversation rules can be updated as they are the only ones that have execution mode.
|
||||||
if err := app.automation.UpdateRuleExecutionMode(amodels.RuleTypeNewConversation, mode); err != nil {
|
if err := app.automation.UpdateRuleExecutionMode(amodels.RuleTypeNewConversation, req.Mode); err != nil {
|
||||||
return sendErrorEnvelope(r, err)
|
return sendErrorEnvelope(r, err)
|
||||||
}
|
}
|
||||||
return r.SendEnvelope(true)
|
return r.SendEnvelope(true)
|
||||||
|
@@ -14,6 +14,14 @@ import (
|
|||||||
"github.com/zerodha/fastglue"
|
"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.
|
// handleGetContacts returns a list of contacts from the database.
|
||||||
func handleGetContacts(r *fastglue.Request) error {
|
func handleGetContacts(r *fastglue.Request) error {
|
||||||
var (
|
var (
|
||||||
@@ -185,12 +193,17 @@ func handleCreateContactNote(r *fastglue.Request) error {
|
|||||||
app = r.Context.(*App)
|
app = r.Context.(*App)
|
||||||
contactID, _ = strconv.Atoi(r.RequestCtx.UserValue("id").(string))
|
contactID, _ = strconv.Atoi(r.RequestCtx.UserValue("id").(string))
|
||||||
auser = r.RequestCtx.UserValue("user").(amodels.User)
|
auser = r.RequestCtx.UserValue("user").(amodels.User)
|
||||||
note = string(r.RequestCtx.PostArgs().Peek("note"))
|
req = createContactNoteReq{}
|
||||||
)
|
)
|
||||||
if len(note) == 0 {
|
|
||||||
|
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)
|
return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.Ts("globals.messages.empty", "name", "note"), nil, envelope.InputError)
|
||||||
}
|
}
|
||||||
if err := app.user.CreateNote(contactID, auser.ID, note); err != nil {
|
if err := app.user.CreateNote(contactID, auser.ID, req.Note); err != nil {
|
||||||
return sendErrorEnvelope(r, err)
|
return sendErrorEnvelope(r, err)
|
||||||
}
|
}
|
||||||
return r.SendEnvelope(true)
|
return r.SendEnvelope(true)
|
||||||
@@ -238,12 +251,18 @@ func handleBlockContact(r *fastglue.Request) error {
|
|||||||
var (
|
var (
|
||||||
app = r.Context.(*App)
|
app = r.Context.(*App)
|
||||||
contactID, _ = strconv.Atoi(r.RequestCtx.UserValue("id").(string))
|
contactID, _ = strconv.Atoi(r.RequestCtx.UserValue("id").(string))
|
||||||
enabled = r.RequestCtx.PostArgs().GetBool("enabled")
|
req = blockContactReq{}
|
||||||
)
|
)
|
||||||
|
|
||||||
if contactID <= 0 {
|
if contactID <= 0 {
|
||||||
return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.Ts("globals.messages.invalid", "name", "`id`"), nil, envelope.InputError)
|
return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.Ts("globals.messages.invalid", "name", "`id`"), nil, envelope.InputError)
|
||||||
}
|
}
|
||||||
if err := app.user.ToggleEnabled(contactID, models.UserTypeContact, enabled); err != nil {
|
|
||||||
|
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 err := app.user.ToggleEnabled(contactID, models.UserTypeContact, req.Enabled); err != nil {
|
||||||
return sendErrorEnvelope(r, err)
|
return sendErrorEnvelope(r, err)
|
||||||
}
|
}
|
||||||
return r.SendEnvelope(true)
|
return r.SendEnvelope(true)
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -19,16 +18,37 @@ import (
|
|||||||
"github.com/zerodha/fastglue"
|
"github.com/zerodha/fastglue"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type assigneeChangeReq struct {
|
||||||
|
AssigneeID int `json:"assignee_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type teamAssigneeChangeReq struct {
|
||||||
|
AssigneeID int `json:"assignee_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type priorityUpdateReq struct {
|
||||||
|
Priority string `json:"priority"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type statusUpdateReq struct {
|
||||||
|
Status string `json:"status"`
|
||||||
|
SnoozedUntil string `json:"snoozed_until,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type tagsUpdateReq struct {
|
||||||
|
Tags []string `json:"tags"`
|
||||||
|
}
|
||||||
|
|
||||||
type createConversationRequest struct {
|
type createConversationRequest struct {
|
||||||
InboxID int `json:"inbox_id" form:"inbox_id"`
|
InboxID int `json:"inbox_id"`
|
||||||
AssignedAgentID int `json:"agent_id" form:"agent_id"`
|
AssignedAgentID int `json:"agent_id"`
|
||||||
AssignedTeamID int `json:"team_id" form:"team_id"`
|
AssignedTeamID int `json:"team_id"`
|
||||||
Email string `json:"contact_email" form:"contact_email"`
|
Email string `json:"contact_email"`
|
||||||
FirstName string `json:"first_name" form:"first_name"`
|
FirstName string `json:"first_name"`
|
||||||
LastName string `json:"last_name" form:"last_name"`
|
LastName string `json:"last_name"`
|
||||||
Subject string `json:"subject" form:"subject"`
|
Subject string `json:"subject"`
|
||||||
Content string `json:"content" form:"content"`
|
Content string `json:"content"`
|
||||||
Attachments []int `json:"attachments" form:"attachments"`
|
Attachments []int `json:"attachments"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleGetAllConversations retrieves all conversations.
|
// handleGetAllConversations retrieves all conversations.
|
||||||
@@ -304,13 +324,15 @@ func handleGetConversationParticipants(r *fastglue.Request) error {
|
|||||||
// handleUpdateUserAssignee updates the user assigned to a conversation.
|
// handleUpdateUserAssignee updates the user assigned to a conversation.
|
||||||
func handleUpdateUserAssignee(r *fastglue.Request) error {
|
func handleUpdateUserAssignee(r *fastglue.Request) error {
|
||||||
var (
|
var (
|
||||||
app = r.Context.(*App)
|
app = r.Context.(*App)
|
||||||
uuid = r.RequestCtx.UserValue("uuid").(string)
|
uuid = r.RequestCtx.UserValue("uuid").(string)
|
||||||
auser = r.RequestCtx.UserValue("user").(amodels.User)
|
auser = r.RequestCtx.UserValue("user").(amodels.User)
|
||||||
assigneeID = r.RequestCtx.PostArgs().GetUintOrZero("assignee_id")
|
req = assigneeChangeReq{}
|
||||||
)
|
)
|
||||||
if assigneeID == 0 {
|
|
||||||
return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.Ts("globals.messages.invalid", "name", "`assignee_id`"), nil, envelope.InputError)
|
if err := r.Decode(&req, "json"); err != nil {
|
||||||
|
app.lo.Error("error decoding assignee change request", "error", err)
|
||||||
|
return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.Ts("globals.messages.errorParsing", "name", "{globals.terms.request}"), nil, envelope.InputError)
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := app.user.GetAgent(auser.ID, "")
|
user, err := app.user.GetAgent(auser.ID, "")
|
||||||
@@ -324,11 +346,11 @@ func handleUpdateUserAssignee(r *fastglue.Request) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Already assigned?
|
// Already assigned?
|
||||||
if conversation.AssignedUserID.Int == assigneeID {
|
if conversation.AssignedUserID.Int == req.AssigneeID {
|
||||||
return r.SendEnvelope(true)
|
return r.SendEnvelope(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := app.conversation.UpdateConversationUserAssignee(uuid, assigneeID, user); err != nil {
|
if err := app.conversation.UpdateConversationUserAssignee(uuid, req.AssigneeID, user); err != nil {
|
||||||
return sendErrorEnvelope(r, err)
|
return sendErrorEnvelope(r, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -341,12 +363,16 @@ func handleUpdateTeamAssignee(r *fastglue.Request) error {
|
|||||||
app = r.Context.(*App)
|
app = r.Context.(*App)
|
||||||
uuid = r.RequestCtx.UserValue("uuid").(string)
|
uuid = r.RequestCtx.UserValue("uuid").(string)
|
||||||
auser = r.RequestCtx.UserValue("user").(amodels.User)
|
auser = r.RequestCtx.UserValue("user").(amodels.User)
|
||||||
|
req = teamAssigneeChangeReq{}
|
||||||
)
|
)
|
||||||
assigneeID, err := r.RequestCtx.PostArgs().GetUint("assignee_id")
|
|
||||||
if err != nil {
|
if err := r.Decode(&req, "json"); err != nil {
|
||||||
return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.Ts("globals.messages.invalid", "name", "`assignee_id`"), nil, envelope.InputError)
|
app.lo.Error("error decoding team assignee change request", "error", err)
|
||||||
|
return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.Ts("globals.messages.errorParsing", "name", "{globals.terms.request}"), nil, envelope.InputError)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assigneeID := req.AssigneeID
|
||||||
|
|
||||||
user, err := app.user.GetAgent(auser.ID, "")
|
user, err := app.user.GetAgent(auser.ID, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return sendErrorEnvelope(r, err)
|
return sendErrorEnvelope(r, err)
|
||||||
@@ -376,11 +402,18 @@ func handleUpdateTeamAssignee(r *fastglue.Request) error {
|
|||||||
// handleUpdateConversationPriority updates the priority of a conversation.
|
// handleUpdateConversationPriority updates the priority of a conversation.
|
||||||
func handleUpdateConversationPriority(r *fastglue.Request) error {
|
func handleUpdateConversationPriority(r *fastglue.Request) error {
|
||||||
var (
|
var (
|
||||||
app = r.Context.(*App)
|
app = r.Context.(*App)
|
||||||
uuid = r.RequestCtx.UserValue("uuid").(string)
|
uuid = r.RequestCtx.UserValue("uuid").(string)
|
||||||
auser = r.RequestCtx.UserValue("user").(amodels.User)
|
auser = r.RequestCtx.UserValue("user").(amodels.User)
|
||||||
priority = string(r.RequestCtx.PostArgs().Peek("priority"))
|
req = priorityUpdateReq{}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if err := r.Decode(&req, "json"); err != nil {
|
||||||
|
app.lo.Error("error decoding priority update request", "error", err)
|
||||||
|
return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.Ts("globals.messages.errorParsing", "name", "{globals.terms.request}"), nil, envelope.InputError)
|
||||||
|
}
|
||||||
|
|
||||||
|
priority := req.Priority
|
||||||
if priority == "" {
|
if priority == "" {
|
||||||
return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.Ts("globals.messages.empty", "name", "`priority`"), nil, envelope.InputError)
|
return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.Ts("globals.messages.empty", "name", "`priority`"), nil, envelope.InputError)
|
||||||
}
|
}
|
||||||
@@ -403,13 +436,20 @@ func handleUpdateConversationPriority(r *fastglue.Request) error {
|
|||||||
// handleUpdateConversationStatus updates the status of a conversation.
|
// handleUpdateConversationStatus updates the status of a conversation.
|
||||||
func handleUpdateConversationStatus(r *fastglue.Request) error {
|
func handleUpdateConversationStatus(r *fastglue.Request) error {
|
||||||
var (
|
var (
|
||||||
app = r.Context.(*App)
|
app = r.Context.(*App)
|
||||||
status = string(r.RequestCtx.PostArgs().Peek("status"))
|
uuid = r.RequestCtx.UserValue("uuid").(string)
|
||||||
snoozedUntil = string(r.RequestCtx.PostArgs().Peek("snoozed_until"))
|
auser = r.RequestCtx.UserValue("user").(amodels.User)
|
||||||
uuid = r.RequestCtx.UserValue("uuid").(string)
|
req = statusUpdateReq{}
|
||||||
auser = r.RequestCtx.UserValue("user").(amodels.User)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if err := r.Decode(&req, "json"); err != nil {
|
||||||
|
app.lo.Error("error decoding status update request", "error", err)
|
||||||
|
return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.Ts("globals.messages.errorParsing", "name", "{globals.terms.request}"), nil, envelope.InputError)
|
||||||
|
}
|
||||||
|
|
||||||
|
status := req.Status
|
||||||
|
snoozedUntil := req.SnoozedUntil
|
||||||
|
|
||||||
// Validate inputs
|
// Validate inputs
|
||||||
if status == "" {
|
if status == "" {
|
||||||
return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.Ts("globals.messages.empty", "name", "`status`"), nil, envelope.InputError)
|
return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.Ts("globals.messages.empty", "name", "`status`"), nil, envelope.InputError)
|
||||||
@@ -463,18 +503,19 @@ func handleUpdateConversationStatus(r *fastglue.Request) error {
|
|||||||
// handleUpdateConversationtags updates conversation tags.
|
// handleUpdateConversationtags updates conversation tags.
|
||||||
func handleUpdateConversationtags(r *fastglue.Request) error {
|
func handleUpdateConversationtags(r *fastglue.Request) error {
|
||||||
var (
|
var (
|
||||||
app = r.Context.(*App)
|
app = r.Context.(*App)
|
||||||
tagNames = []string{}
|
auser = r.RequestCtx.UserValue("user").(amodels.User)
|
||||||
tagJSON = r.RequestCtx.PostArgs().Peek("tags")
|
uuid = r.RequestCtx.UserValue("uuid").(string)
|
||||||
auser = r.RequestCtx.UserValue("user").(amodels.User)
|
req = tagsUpdateReq{}
|
||||||
uuid = r.RequestCtx.UserValue("uuid").(string)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if err := json.Unmarshal(tagJSON, &tagNames); err != nil {
|
if err := r.Decode(&req, "json"); err != nil {
|
||||||
app.lo.Error("error unmarshalling tags JSON", "error", err)
|
app.lo.Error("error decoding tags update request", "error", err)
|
||||||
return r.SendErrorEnvelope(fasthttp.StatusInternalServerError, app.i18n.Ts("globals.messages.errorParsing", "name", "{globals.terms.request}"), nil, envelope.InputError)
|
return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.Ts("globals.messages.errorParsing", "name", "{globals.terms.request}"), nil, envelope.InputError)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tagNames := req.Tags
|
||||||
|
|
||||||
user, err := app.user.GetAgent(auser.ID, "")
|
user, err := app.user.GetAgent(auser.ID, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return sendErrorEnvelope(r, err)
|
return sendErrorEnvelope(r, err)
|
||||||
|
@@ -15,7 +15,7 @@ import (
|
|||||||
// initHandlers initializes the HTTP routes and handlers for the application.
|
// initHandlers initializes the HTTP routes and handlers for the application.
|
||||||
func initHandlers(g *fastglue.Fastglue, hub *ws.Hub) {
|
func initHandlers(g *fastglue.Fastglue, hub *ws.Hub) {
|
||||||
// Authentication.
|
// Authentication.
|
||||||
g.POST("/api/v1/login", handleLogin)
|
g.POST("/api/v1/auth/login", handleLogin)
|
||||||
g.GET("/logout", auth(handleLogout))
|
g.GET("/logout", auth(handleLogout))
|
||||||
g.GET("/api/v1/oidc/{id}/login", handleOIDCLogin)
|
g.GET("/api/v1/oidc/{id}/login", handleOIDCLogin)
|
||||||
g.GET("/api/v1/oidc/{id}/finish", handleOIDCCallback)
|
g.GET("/api/v1/oidc/{id}/finish", handleOIDCCallback)
|
||||||
|
@@ -14,7 +14,7 @@ import (
|
|||||||
|
|
||||||
// authenticateUser handles both API key and session-based authentication
|
// authenticateUser handles both API key and session-based authentication
|
||||||
// Returns the authenticated user or an error
|
// Returns the authenticated user or an error
|
||||||
func authenticateUser(r *fastglue.Request, app *App) (models.User, error) {
|
func authenticateUser(r *fastglue.Request, app *App, checkCsrf bool) (models.User, error) {
|
||||||
var user models.User
|
var user models.User
|
||||||
|
|
||||||
// Check for Authorization header first (API key authentication)
|
// Check for Authorization header first (API key authentication)
|
||||||
@@ -29,13 +29,15 @@ func authenticateUser(r *fastglue.Request, app *App) (models.User, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Session-based authentication
|
// Session-based authentication
|
||||||
cookieToken := string(r.RequestCtx.Request.Header.Cookie("csrf_token"))
|
if checkCsrf {
|
||||||
hdrToken := string(r.RequestCtx.Request.Header.Peek("X-CSRFTOKEN"))
|
cookieToken := string(r.RequestCtx.Request.Header.Cookie("csrf_token"))
|
||||||
|
hdrToken := string(r.RequestCtx.Request.Header.Peek("X-CSRFTOKEN"))
|
||||||
|
|
||||||
// Match CSRF token from cookie and header.
|
// Match CSRF token from cookie and header.
|
||||||
if cookieToken == "" || hdrToken == "" || cookieToken != hdrToken {
|
if cookieToken == "" || hdrToken == "" || cookieToken != hdrToken {
|
||||||
app.lo.Error("csrf token mismatch", "cookie_token", cookieToken, "header_token", hdrToken)
|
app.lo.Error("csrf token mismatch", "cookie_token", cookieToken, "header_token", hdrToken)
|
||||||
return user, envelope.NewError(envelope.PermissionError, app.i18n.T("auth.csrfTokenMismatch"), nil)
|
return user, envelope.NewError(envelope.PermissionError, app.i18n.T("auth.csrfTokenMismatch"), nil)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate session and fetch user.
|
// Validate session and fetch user.
|
||||||
@@ -70,7 +72,7 @@ func tryAuth(handler fastglue.FastRequestHandler) fastglue.FastRequestHandler {
|
|||||||
app := r.Context.(*App)
|
app := r.Context.(*App)
|
||||||
|
|
||||||
// Try to authenticate user using shared authentication logic, but don't return errors
|
// Try to authenticate user using shared authentication logic, but don't return errors
|
||||||
user, err := authenticateUser(r, app)
|
user, err := authenticateUser(r, app, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Authentication failed, but this is optional, so continue without user
|
// Authentication failed, but this is optional, so continue without user
|
||||||
return handler(r)
|
return handler(r)
|
||||||
@@ -95,7 +97,7 @@ func auth(handler fastglue.FastRequestHandler) fastglue.FastRequestHandler {
|
|||||||
var app = r.Context.(*App)
|
var app = r.Context.(*App)
|
||||||
|
|
||||||
// Authenticate user using shared authentication logic
|
// Authenticate user using shared authentication logic
|
||||||
user, err := authenticateUser(r, app)
|
user, err := authenticateUser(r, app, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if envErr, ok := err.(envelope.Error); ok {
|
if envErr, ok := err.(envelope.Error); ok {
|
||||||
if envErr.ErrorType == envelope.PermissionError {
|
if envErr.ErrorType == envelope.PermissionError {
|
||||||
@@ -125,7 +127,7 @@ func perm(handler fastglue.FastRequestHandler, perm string) fastglue.FastRequest
|
|||||||
var app = r.Context.(*App)
|
var app = r.Context.(*App)
|
||||||
|
|
||||||
// Authenticate user using shared authentication logic
|
// Authenticate user using shared authentication logic
|
||||||
user, err := authenticateUser(r, app)
|
user, err := authenticateUser(r, app, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if envErr, ok := err.(envelope.Error); ok {
|
if envErr, ok := err.(envelope.Error); ok {
|
||||||
if envErr.ErrorType == envelope.PermissionError {
|
if envErr.ErrorType == envelope.PermissionError {
|
||||||
|
41
cmd/teams.go
41
cmd/teams.go
@@ -4,8 +4,8 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/abhinavxd/libredesk/internal/envelope"
|
"github.com/abhinavxd/libredesk/internal/envelope"
|
||||||
|
"github.com/abhinavxd/libredesk/internal/team/models"
|
||||||
"github.com/valyala/fasthttp"
|
"github.com/valyala/fasthttp"
|
||||||
"github.com/volatiletech/null/v9"
|
|
||||||
"github.com/zerodha/fastglue"
|
"github.com/zerodha/fastglue"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -52,16 +52,15 @@ func handleGetTeam(r *fastglue.Request) error {
|
|||||||
// handleCreateTeam creates a new team.
|
// handleCreateTeam creates a new team.
|
||||||
func handleCreateTeam(r *fastglue.Request) error {
|
func handleCreateTeam(r *fastglue.Request) error {
|
||||||
var (
|
var (
|
||||||
app = r.Context.(*App)
|
app = r.Context.(*App)
|
||||||
name = string(r.RequestCtx.PostArgs().Peek("name"))
|
req = models.Team{}
|
||||||
timezone = string(r.RequestCtx.PostArgs().Peek("timezone"))
|
|
||||||
emoji = string(r.RequestCtx.PostArgs().Peek("emoji"))
|
|
||||||
conversationAssignmentType = string(r.RequestCtx.PostArgs().Peek("conversation_assignment_type"))
|
|
||||||
businessHrsID, _ = strconv.Atoi(string(r.RequestCtx.PostArgs().Peek("business_hours_id")))
|
|
||||||
slaPolicyID, _ = strconv.Atoi(string(r.RequestCtx.PostArgs().Peek("sla_policy_id")))
|
|
||||||
maxAutoAssignedConversations, _ = strconv.Atoi(string(r.RequestCtx.PostArgs().Peek("max_auto_assigned_conversations")))
|
|
||||||
)
|
)
|
||||||
if err := app.team.Create(name, timezone, conversationAssignmentType, null.NewInt(businessHrsID, businessHrsID != 0), null.NewInt(slaPolicyID, slaPolicyID != 0), emoji, maxAutoAssignedConversations); err != nil {
|
|
||||||
|
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 err := app.team.Create(req.Name, req.Timezone, req.ConversationAssignmentType, req.BusinessHoursID, req.SLAPolicyID, req.Emoji.String, req.MaxAutoAssignedConversations); err != nil {
|
||||||
return sendErrorEnvelope(r, err)
|
return sendErrorEnvelope(r, err)
|
||||||
}
|
}
|
||||||
return r.SendEnvelope(true)
|
return r.SendEnvelope(true)
|
||||||
@@ -70,20 +69,20 @@ func handleCreateTeam(r *fastglue.Request) error {
|
|||||||
// handleUpdateTeam updates an existing team.
|
// handleUpdateTeam updates an existing team.
|
||||||
func handleUpdateTeam(r *fastglue.Request) error {
|
func handleUpdateTeam(r *fastglue.Request) error {
|
||||||
var (
|
var (
|
||||||
app = r.Context.(*App)
|
app = r.Context.(*App)
|
||||||
name = string(r.RequestCtx.PostArgs().Peek("name"))
|
id, _ = strconv.Atoi(r.RequestCtx.UserValue("id").(string))
|
||||||
timezone = string(r.RequestCtx.PostArgs().Peek("timezone"))
|
req = models.Team{}
|
||||||
emoji = string(r.RequestCtx.PostArgs().Peek("emoji"))
|
|
||||||
conversationAssignmentType = string(r.RequestCtx.PostArgs().Peek("conversation_assignment_type"))
|
|
||||||
id, _ = strconv.Atoi(r.RequestCtx.UserValue("id").(string))
|
|
||||||
businessHrsID, _ = strconv.Atoi(string(r.RequestCtx.PostArgs().Peek("business_hours_id")))
|
|
||||||
slaPolicyID, _ = strconv.Atoi(string(r.RequestCtx.PostArgs().Peek("sla_policy_id")))
|
|
||||||
maxAutoAssignedConversations, _ = strconv.Atoi(string(r.RequestCtx.PostArgs().Peek("max_auto_assigned_conversations")))
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if id < 1 {
|
if id < 1 {
|
||||||
return r.SendErrorEnvelope(fasthttp.StatusBadRequest, "Invalid team `id`", nil, envelope.InputError)
|
return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.Ts("globals.messages.invalid", "name", "`id`"), nil, envelope.InputError)
|
||||||
}
|
}
|
||||||
if err := app.team.Update(id, name, timezone, conversationAssignmentType, null.NewInt(businessHrsID, businessHrsID != 0), null.NewInt(slaPolicyID, slaPolicyID != 0), emoji, maxAutoAssignedConversations); err != nil {
|
|
||||||
|
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 err := app.team.Update(id, req.Name, req.Timezone, req.ConversationAssignmentType, req.BusinessHoursID, req.SLAPolicyID, req.Emoji.String, req.MaxAutoAssignedConversations); err != nil {
|
||||||
return sendErrorEnvelope(r, err)
|
return sendErrorEnvelope(r, err)
|
||||||
}
|
}
|
||||||
return r.SendEnvelope(true)
|
return r.SendEnvelope(true)
|
||||||
|
12
cmd/users.go
12
cmd/users.go
@@ -434,20 +434,22 @@ func handleSetPassword(r *fastglue.Request) error {
|
|||||||
var (
|
var (
|
||||||
app = r.Context.(*App)
|
app = r.Context.(*App)
|
||||||
agent, ok = r.RequestCtx.UserValue("user").(amodels.User)
|
agent, ok = r.RequestCtx.UserValue("user").(amodels.User)
|
||||||
p = r.RequestCtx.PostArgs()
|
req = SetPasswordRequest{}
|
||||||
password = string(p.Peek("password"))
|
|
||||||
token = string(p.Peek("token"))
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if ok && agent.ID > 0 {
|
if ok && agent.ID > 0 {
|
||||||
return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.T("user.userAlreadyLoggedIn"), nil, envelope.InputError)
|
return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.T("user.userAlreadyLoggedIn"), nil, envelope.InputError)
|
||||||
}
|
}
|
||||||
|
|
||||||
if password == "" {
|
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 req.Password == "" {
|
||||||
return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.Ts("globals.messages.empty", "name", "{globals.terms.password}"), nil, envelope.InputError)
|
return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.Ts("globals.messages.empty", "name", "{globals.terms.password}"), nil, envelope.InputError)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := app.user.ResetPassword(token, password); err != nil {
|
if err := app.user.ResetPassword(req.Token, req.Password); err != nil {
|
||||||
return sendErrorEnvelope(r, err)
|
return sendErrorEnvelope(r, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -38,7 +38,7 @@ describe('Login Component', () => {
|
|||||||
|
|
||||||
it('should show error for invalid login attempt', () => {
|
it('should show error for invalid login attempt', () => {
|
||||||
// Mock failed login API call
|
// Mock failed login API call
|
||||||
cy.intercept('POST', '**/api/v1/login', {
|
cy.intercept('POST', '**/api/v1/auth/login', {
|
||||||
statusCode: 401,
|
statusCode: 401,
|
||||||
body: {
|
body: {
|
||||||
message: 'Invalid credentials'
|
message: 'Invalid credentials'
|
||||||
@@ -61,7 +61,7 @@ describe('Login Component', () => {
|
|||||||
|
|
||||||
it('should login successfully with correct credentials', () => {
|
it('should login successfully with correct credentials', () => {
|
||||||
// Mock successful login API call
|
// Mock successful login API call
|
||||||
cy.intercept('POST', '**/api/v1/login', {
|
cy.intercept('POST', '**/api/v1/auth/login', {
|
||||||
statusCode: 200,
|
statusCode: 200,
|
||||||
body: {
|
body: {
|
||||||
data: {
|
data: {
|
||||||
@@ -111,7 +111,7 @@ describe('Login Component', () => {
|
|||||||
|
|
||||||
it('should show loading state during login', () => {
|
it('should show loading state during login', () => {
|
||||||
// Mock slow API response
|
// Mock slow API response
|
||||||
cy.intercept('POST', '**/api/v1/login', {
|
cy.intercept('POST', '**/api/v1/auth/login', {
|
||||||
statusCode: 200,
|
statusCode: 200,
|
||||||
body: {
|
body: {
|
||||||
data: {
|
data: {
|
||||||
|
@@ -27,9 +27,13 @@ http.interceptors.request.use((request) => {
|
|||||||
|
|
||||||
// Set content type for POST/PUT requests if the content type is not set.
|
// Set content type for POST/PUT requests if the content type is not set.
|
||||||
if ((request.method === 'post' || request.method === 'put') && !request.headers['Content-Type']) {
|
if ((request.method === 'post' || request.method === 'put') && !request.headers['Content-Type']) {
|
||||||
request.headers['Content-Type'] = 'application/x-www-form-urlencoded'
|
request.headers['Content-Type'] = 'application/json'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.headers['Content-Type'] === 'application/x-www-form-urlencoded') {
|
||||||
request.data = qs.stringify(request.data)
|
request.data = qs.stringify(request.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
return request
|
return request
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -135,7 +139,7 @@ const updateSettings = (key, data) =>
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
const getSettings = (key) => http.get(`/api/v1/settings/${key}`)
|
const getSettings = (key) => http.get(`/api/v1/settings/${key}`)
|
||||||
const login = (data) => http.post(`/api/v1/login`, data, {
|
const login = (data) => http.post(`/api/v1/auth/login`, data, {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
}
|
}
|
||||||
@@ -166,7 +170,11 @@ const updateAutomationRuleWeights = (data) =>
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
const updateAutomationRulesExecutionMode = (data) =>
|
const updateAutomationRulesExecutionMode = (data) =>
|
||||||
http.put(`/api/v1/automations/rules/execution-mode`, data)
|
http.put(`/api/v1/automations/rules/execution-mode`, data, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
})
|
||||||
const getRoles = () => http.get('/api/v1/roles')
|
const getRoles = () => http.get('/api/v1/roles')
|
||||||
const getRole = (id) => http.get(`/api/v1/roles/${id}`)
|
const getRole = (id) => http.get(`/api/v1/roles/${id}`)
|
||||||
const createRole = (data) =>
|
const createRole = (data) =>
|
||||||
@@ -190,11 +198,23 @@ const updateContact = (id, data) =>
|
|||||||
'Content-Type': 'multipart/form-data'
|
'Content-Type': 'multipart/form-data'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const blockContact = (id, data) => http.put(`/api/v1/contacts/${id}/block`, data)
|
const blockContact = (id, data) => http.put(`/api/v1/contacts/${id}/block`, data, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
})
|
||||||
const getTeam = (id) => http.get(`/api/v1/teams/${id}`)
|
const getTeam = (id) => http.get(`/api/v1/teams/${id}`)
|
||||||
const getTeams = () => http.get('/api/v1/teams')
|
const getTeams = () => http.get('/api/v1/teams')
|
||||||
const updateTeam = (id, data) => http.put(`/api/v1/teams/${id}`, data)
|
const updateTeam = (id, data) => http.put(`/api/v1/teams/${id}`, data, {
|
||||||
const createTeam = (data) => http.post('/api/v1/teams', data)
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const createTeam = (data) => http.post('/api/v1/teams', data, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
})
|
||||||
const getTeamsCompact = () => http.get('/api/v1/teams/compact')
|
const getTeamsCompact = () => http.get('/api/v1/teams/compact')
|
||||||
const deleteTeam = (id) => http.delete(`/api/v1/teams/${id}`)
|
const deleteTeam = (id) => http.delete(`/api/v1/teams/${id}`)
|
||||||
const updateUser = (id, data) =>
|
const updateUser = (id, data) =>
|
||||||
@@ -238,9 +258,17 @@ const createUser = (data) =>
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
const getTags = () => http.get('/api/v1/tags')
|
const getTags = () => http.get('/api/v1/tags')
|
||||||
const upsertTags = (uuid, data) => http.post(`/api/v1/conversations/${uuid}/tags`, data)
|
const upsertTags = (uuid, data) => http.post(`/api/v1/conversations/${uuid}/tags`, data, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
})
|
||||||
const updateAssignee = (uuid, assignee_type, data) =>
|
const updateAssignee = (uuid, assignee_type, data) =>
|
||||||
http.put(`/api/v1/conversations/${uuid}/assignee/${assignee_type}`, data)
|
http.put(`/api/v1/conversations/${uuid}/assignee/${assignee_type}`, data, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
})
|
||||||
const removeAssignee = (uuid, assignee_type) =>
|
const removeAssignee = (uuid, assignee_type) =>
|
||||||
http.put(`/api/v1/conversations/${uuid}/assignee/${assignee_type}/remove`)
|
http.put(`/api/v1/conversations/${uuid}/assignee/${assignee_type}/remove`)
|
||||||
const updateContactCustomAttribute = (uuid, data) =>
|
const updateContactCustomAttribute = (uuid, data) =>
|
||||||
@@ -262,9 +290,17 @@ const createConversation = (data) =>
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
const updateConversationStatus = (uuid, data) =>
|
const updateConversationStatus = (uuid, data) =>
|
||||||
http.put(`/api/v1/conversations/${uuid}/status`, data)
|
http.put(`/api/v1/conversations/${uuid}/status`, data, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
})
|
||||||
const updateConversationPriority = (uuid, data) =>
|
const updateConversationPriority = (uuid, data) =>
|
||||||
http.put(`/api/v1/conversations/${uuid}/priority`, data)
|
http.put(`/api/v1/conversations/${uuid}/priority`, data, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
})
|
||||||
const updateAssigneeLastSeen = (uuid) => http.put(`/api/v1/conversations/${uuid}/last-seen`)
|
const updateAssigneeLastSeen = (uuid) => http.put(`/api/v1/conversations/${uuid}/last-seen`)
|
||||||
const getConversationMessage = (cuuid, uuid) =>
|
const getConversationMessage = (cuuid, uuid) =>
|
||||||
http.get(`/api/v1/conversations/${cuuid}/messages/${uuid}`)
|
http.get(`/api/v1/conversations/${cuuid}/messages/${uuid}`)
|
||||||
@@ -350,10 +386,22 @@ const updateView = (id, data) =>
|
|||||||
})
|
})
|
||||||
const deleteView = (id) => http.delete(`/api/v1/views/me/${id}`)
|
const deleteView = (id) => http.delete(`/api/v1/views/me/${id}`)
|
||||||
const getAiPrompts = () => http.get('/api/v1/ai/prompts')
|
const getAiPrompts = () => http.get('/api/v1/ai/prompts')
|
||||||
const aiCompletion = (data) => http.post('/api/v1/ai/completion', data)
|
const aiCompletion = (data) => http.post('/api/v1/ai/completion', data, {
|
||||||
const updateAIProvider = (data) => http.put('/api/v1/ai/provider', data)
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const updateAIProvider = (data) => http.put('/api/v1/ai/provider', data, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
})
|
||||||
const getContactNotes = (id) => http.get(`/api/v1/contacts/${id}/notes`)
|
const getContactNotes = (id) => http.get(`/api/v1/contacts/${id}/notes`)
|
||||||
const createContactNote = (id, data) => http.post(`/api/v1/contacts/${id}/notes`, data)
|
const createContactNote = (id, data) => http.post(`/api/v1/contacts/${id}/notes`, data, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
})
|
||||||
const deleteContactNote = (id, noteId) => http.delete(`/api/v1/contacts/${id}/notes/${noteId}`)
|
const deleteContactNote = (id, noteId) => http.delete(`/api/v1/contacts/${id}/notes/${noteId}`)
|
||||||
const getActivityLogs = (params) => http.get('/api/v1/activity-logs', { params })
|
const getActivityLogs = (params) => http.get('/api/v1/activity-logs', { params })
|
||||||
const getWebhooks = () => http.get('/api/v1/webhooks')
|
const getWebhooks = () => http.get('/api/v1/webhooks')
|
||||||
|
@@ -162,7 +162,7 @@ watch(
|
|||||||
}
|
}
|
||||||
|
|
||||||
conversationStore.upsertTags({
|
conversationStore.upsertTags({
|
||||||
tags: JSON.stringify(newTags)
|
tags: newTags
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
{ immediate: false }
|
{ immediate: false }
|
||||||
@@ -184,13 +184,13 @@ const fetchTags = async () => {
|
|||||||
|
|
||||||
const handleAssignedUserChange = (id) => {
|
const handleAssignedUserChange = (id) => {
|
||||||
conversationStore.updateAssignee('user', {
|
conversationStore.updateAssignee('user', {
|
||||||
assignee_id: id
|
assignee_id: parseInt(id)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleAssignedTeamChange = (id) => {
|
const handleAssignedTeamChange = (id) => {
|
||||||
conversationStore.updateAssignee('team', {
|
conversationStore.updateAssignee('team', {
|
||||||
assignee_id: id
|
assignee_id: parseInt(id)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -23,5 +23,5 @@ type ActivityLog struct {
|
|||||||
TargetModelID int `db:"target_model_id" json:"target_model_id"`
|
TargetModelID int `db:"target_model_id" json:"target_model_id"`
|
||||||
IP string `db:"ip" json:"ip"`
|
IP string `db:"ip" json:"ip"`
|
||||||
|
|
||||||
Total int `db:"total" json:"total"`
|
Total int `db:"total" json:"-"`
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
-- name: get-all
|
-- name: get-all
|
||||||
SELECT id, name, description FROM roles;
|
SELECT id, created_at, updated_at, name, description, permissions FROM roles;
|
||||||
|
|
||||||
-- name: get-role
|
-- name: get-role
|
||||||
SELECT * FROM roles where id = $1;
|
SELECT * FROM roles where id = $1;
|
||||||
|
@@ -23,7 +23,7 @@ WHERE id != $1 AND $4 = TRUE;
|
|||||||
SELECT id, type, name, body, subject FROM templates WHERE is_default is TRUE;
|
SELECT id, type, name, body, subject FROM templates WHERE is_default is TRUE;
|
||||||
|
|
||||||
-- name: get-all
|
-- name: get-all
|
||||||
SELECT id, type, name, is_default, updated_at FROM templates WHERE type = $1 ORDER BY updated_at DESC;
|
SELECT id, created_at, updated_at, type, name, is_default, is_builtin FROM templates WHERE type = $1 ORDER BY updated_at DESC;
|
||||||
|
|
||||||
-- name: get-template
|
-- name: get-template
|
||||||
SELECT id, type, name, body, subject, is_default, type FROM templates WHERE id = $1;
|
SELECT id, type, name, body, subject, is_default, type FROM templates WHERE id = $1;
|
||||||
|
Reference in New Issue
Block a user