mirror of
https://github.com/abhinavxd/libredesk.git
synced 2025-11-06 15:03:22 +00:00
Compare commits
7 Commits
v0.3.2-alp
...
v0.3.3-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
59951f0829 | ||
|
|
461ae3cf22 | ||
|
|
da5dfdbcde | ||
|
|
9c67c02b08 | ||
|
|
15b200b0db | ||
|
|
f4617c599c | ||
|
|
341d0b7e47 |
@@ -74,7 +74,7 @@ __________________
|
|||||||
- Run `./libredesk --set-system-user-password` to set the password for the System user.
|
- Run `./libredesk --set-system-user-password` to set the password for the System user.
|
||||||
- Run `./libredesk` and visit `http://localhost:9000` and login with username `System` and the password you set using the --set-system-user-password command.
|
- Run `./libredesk` and visit `http://localhost:9000` and login with username `System` and the password you set using the --set-system-user-password command.
|
||||||
|
|
||||||
See [installation docs](https://libredesk.app/docs/installation)
|
See [installation docs](https://libredesk.io/docs/installation)
|
||||||
__________________
|
__________________
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
15
cmd/users.go
15
cmd/users.go
@@ -338,7 +338,7 @@ func handleDeleteAvatar(r *fastglue.Request) error {
|
|||||||
|
|
||||||
// Valid str?
|
// Valid str?
|
||||||
if user.AvatarURL.String == "" {
|
if user.AvatarURL.String == "" {
|
||||||
return r.SendEnvelope(true)
|
return r.SendEnvelope("Avatar deleted successfully.")
|
||||||
}
|
}
|
||||||
|
|
||||||
fileName := filepath.Base(user.AvatarURL.String)
|
fileName := filepath.Base(user.AvatarURL.String)
|
||||||
@@ -347,8 +347,8 @@ func handleDeleteAvatar(r *fastglue.Request) error {
|
|||||||
if err := app.media.Delete(fileName); err != nil {
|
if err := app.media.Delete(fileName); err != nil {
|
||||||
return sendErrorEnvelope(r, err)
|
return sendErrorEnvelope(r, err)
|
||||||
}
|
}
|
||||||
err = app.user.UpdateAvatar(user.ID, "")
|
|
||||||
if err != nil {
|
if err = app.user.UpdateAvatar(user.ID, ""); err != nil {
|
||||||
return sendErrorEnvelope(r, err)
|
return sendErrorEnvelope(r, err)
|
||||||
}
|
}
|
||||||
return r.SendEnvelope("Avatar deleted successfully.")
|
return r.SendEnvelope("Avatar deleted successfully.")
|
||||||
@@ -363,7 +363,7 @@ func handleResetPassword(r *fastglue.Request) error {
|
|||||||
email = string(p.Peek("email"))
|
email = string(p.Peek("email"))
|
||||||
)
|
)
|
||||||
if ok && auser.ID > 0 {
|
if ok && auser.ID > 0 {
|
||||||
return r.SendErrorEnvelope(fasthttp.StatusBadRequest, "User is already logged in", nil, envelope.InputError)
|
return r.SendErrorEnvelope(fasthttp.StatusBadRequest, "User is already logged in, Please logout to reset password.", nil, envelope.InputError)
|
||||||
}
|
}
|
||||||
|
|
||||||
if email == "" {
|
if email == "" {
|
||||||
@@ -372,7 +372,8 @@ func handleResetPassword(r *fastglue.Request) error {
|
|||||||
|
|
||||||
user, err := app.user.GetByEmail(email)
|
user, err := app.user.GetByEmail(email)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return sendErrorEnvelope(r, err)
|
// Send 200 even if user not found, to prevent email enumeration.
|
||||||
|
return r.SendEnvelope("Reset password email sent successfully.")
|
||||||
}
|
}
|
||||||
|
|
||||||
token, err := app.user.SetResetPasswordToken(user.ID)
|
token, err := app.user.SetResetPasswordToken(user.ID)
|
||||||
@@ -396,8 +397,8 @@ func handleResetPassword(r *fastglue.Request) error {
|
|||||||
Content: content,
|
Content: content,
|
||||||
Provider: notifier.ProviderEmail,
|
Provider: notifier.ProviderEmail,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
app.lo.Error("error sending notification message", "error", err)
|
app.lo.Error("error sending password reset email", "error", err)
|
||||||
return r.SendErrorEnvelope(fasthttp.StatusInternalServerError, "Error sending notification message", nil, envelope.GeneralError)
|
return r.SendErrorEnvelope(fasthttp.StatusInternalServerError, "Error sending password reset email", nil, envelope.GeneralError)
|
||||||
}
|
}
|
||||||
|
|
||||||
return r.SendEnvelope("Reset password email sent successfully.")
|
return r.SendEnvelope("Reset password email sent successfully.")
|
||||||
|
|||||||
@@ -18,5 +18,5 @@ export function useSla (dueAt, actualAt) {
|
|||||||
clearInterval(intervalId)
|
clearInterval(intervalId)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
return { sla, updateSla }
|
return sla
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,6 +65,7 @@
|
|||||||
<Input type="number" placeholder="2" v-bind="componentField" />
|
<Input type="number" placeholder="2" v-bind="componentField" />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
|
<FormDescription> Maximum concurrent connections to the server. </FormDescription>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</FormField>
|
</FormField>
|
||||||
|
|
||||||
@@ -76,6 +77,10 @@
|
|||||||
<Input type="text" placeholder="15s" v-bind="componentField" />
|
<Input type="text" placeholder="15s" v-bind="componentField" />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
|
<FormDescription>
|
||||||
|
Time to wait for new activity on a connection before closing it and removing it from the
|
||||||
|
pool (s for second, m for minute)
|
||||||
|
</FormDescription>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</FormField>
|
</FormField>
|
||||||
|
|
||||||
@@ -87,6 +92,10 @@
|
|||||||
<Input type="text" placeholder="5s" v-bind="componentField" />
|
<Input type="text" placeholder="5s" v-bind="componentField" />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
|
<FormDescription>
|
||||||
|
Time to wait for new activity on a connection before closing it and removing it from the
|
||||||
|
pool (s for second, m for minute, h for hour).
|
||||||
|
</FormDescription>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</FormField>
|
</FormField>
|
||||||
|
|
||||||
@@ -139,6 +148,7 @@
|
|||||||
<Input type="number" placeholder="2" v-bind="componentField" />
|
<Input type="number" placeholder="2" v-bind="componentField" />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
|
<FormDescription> Number of times to retry when a message fails. </FormDescription>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</FormField>
|
</FormField>
|
||||||
|
|
||||||
|
|||||||
@@ -51,8 +51,8 @@ export const smtpConfigSchema = z.object({
|
|||||||
auth_protocol: z
|
auth_protocol: z
|
||||||
.enum(['plain', 'login', 'cram', 'none'])
|
.enum(['plain', 'login', 'cram', 'none'])
|
||||||
.describe('Authentication protocol'),
|
.describe('Authentication protocol'),
|
||||||
email_address: z.string().describe('Email address').email().nonempty({
|
email_address: z.string().describe('From email address with name (e.g., "Name <email@example.com>")').nonempty({
|
||||||
message: "Email address is required"
|
message: "From email address is required"
|
||||||
}),
|
}),
|
||||||
max_msg_retries: z
|
max_msg_retries: z
|
||||||
.number({
|
.number({
|
||||||
|
|||||||
@@ -267,7 +267,7 @@ const processSend = async () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
await api.sendMessage(conversationStore.current.uuid, {
|
await api.sendMessage(conversationStore.current.uuid, {
|
||||||
private: messageType.value === 'private',
|
private: messageType.value === 'private_note',
|
||||||
message: message,
|
message: message,
|
||||||
attachments: conversationStore.conversation.mediaFiles.map((file) => file.id),
|
attachments: conversationStore.conversation.mediaFiles.map((file) => file.id),
|
||||||
// Convert email addresses to array and remove empty strings.
|
// Convert email addresses to array and remove empty strings.
|
||||||
|
|||||||
@@ -57,16 +57,18 @@
|
|||||||
|
|
||||||
<div class="flex items-center mt-2 space-x-2">
|
<div class="flex items-center mt-2 space-x-2">
|
||||||
<SlaBadge
|
<SlaBadge
|
||||||
|
v-if="conversation.first_response_due_at"
|
||||||
:dueAt="conversation.first_response_due_at"
|
:dueAt="conversation.first_response_due_at"
|
||||||
:actualAt="conversation.first_reply_at"
|
:actualAt="conversation.first_reply_at"
|
||||||
:label="'FRD'"
|
:label="'FRD'"
|
||||||
:showSLAMet="false"
|
:showExtra="false"
|
||||||
/>
|
/>
|
||||||
<SlaBadge
|
<SlaBadge
|
||||||
|
v-if="conversation.resolution_due_at"
|
||||||
:dueAt="conversation.resolution_due_at"
|
:dueAt="conversation.resolution_due_at"
|
||||||
:actualAt="conversation.resolved_at"
|
:actualAt="conversation.resolved_at"
|
||||||
:label="'RD'"
|
:label="'RD'"
|
||||||
:showSLAMet="false"
|
:showExtra="false"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -27,8 +27,10 @@
|
|||||||
<div class="flex justify-start items-center space-x-2">
|
<div class="flex justify-start items-center space-x-2">
|
||||||
<p class="font-medium">First reply at</p>
|
<p class="font-medium">First reply at</p>
|
||||||
<SlaBadge
|
<SlaBadge
|
||||||
|
v-if="conversation.first_response_due_at"
|
||||||
:dueAt="conversation.first_response_due_at"
|
:dueAt="conversation.first_response_due_at"
|
||||||
:actualAt="conversation.first_reply_at"
|
:actualAt="conversation.first_reply_at"
|
||||||
|
:key="conversation.uuid"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Skeleton v-if="conversationStore.conversation.loading" class="w-32 h-4" />
|
<Skeleton v-if="conversationStore.conversation.loading" class="w-32 h-4" />
|
||||||
@@ -43,7 +45,12 @@
|
|||||||
<div class="flex flex-col gap-1 mb-5">
|
<div class="flex flex-col gap-1 mb-5">
|
||||||
<div class="flex justify-start items-center space-x-2">
|
<div class="flex justify-start items-center space-x-2">
|
||||||
<p class="font-medium">Resolved at</p>
|
<p class="font-medium">Resolved at</p>
|
||||||
<SlaBadge :dueAt="conversation.resolution_due_at" :actualAt="conversation.resolved_at" />
|
<SlaBadge
|
||||||
|
v-if="conversation.resolution_due_at"
|
||||||
|
:dueAt="conversation.resolution_due_at"
|
||||||
|
:actualAt="conversation.resolved_at"
|
||||||
|
:key="conversation.uuid"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Skeleton v-if="conversationStore.conversation.loading" class="w-32 h-4" />
|
<Skeleton v-if="conversationStore.conversation.loading" class="w-32 h-4" />
|
||||||
<div v-else>
|
<div v-else>
|
||||||
|
|||||||
@@ -1,32 +1,33 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-if="dueAt" class="flex justify-start items-center space-x-2">
|
<div v-if="dueAt" class="flex justify-start items-center space-x-2">
|
||||||
<TransitionGroup name="fade">
|
<!-- Overdue-->
|
||||||
<!-- Overdue-->
|
<span v-if="sla?.status === 'overdue'" key="overdue" class="sla-badge box sla-overdue">
|
||||||
<span v-if="sla?.status === 'overdue'" key="overdue" class="sla-badge box sla-overdue">
|
<AlertCircle size="12" class="text-red-800" />
|
||||||
<AlertCircle size="10" class="text-red-800" />
|
<span class="sla-text text-red-800"
|
||||||
<span class="text-xs text-red-800">{{ label }} Overdue</span>
|
>{{ label }} Overdue
|
||||||
|
<span v-if="showExtra">by {{ sla.value }}</span>
|
||||||
</span>
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
<!-- SLA Hit -->
|
<!-- SLA Hit -->
|
||||||
<span
|
<span
|
||||||
v-else-if="sla?.status === 'hit' && showSLAMet"
|
v-else-if="sla?.status === 'hit' && showExtra"
|
||||||
key="sla-hit"
|
key="sla-hit"
|
||||||
class="sla-badge box sla-hit"
|
class="sla-badge box sla-hit"
|
||||||
>
|
>
|
||||||
<CheckCircle size="10" />
|
<CheckCircle size="12" />
|
||||||
<span class="sla-text">{{ label }} SLA met</span>
|
<span class="sla-text">{{ label }} SLA met</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<!-- Remaining -->
|
<!-- Remaining -->
|
||||||
<span
|
<span
|
||||||
v-else-if="sla?.status === 'remaining'"
|
v-else-if="sla?.status === 'remaining'"
|
||||||
key="remaining"
|
key="remaining"
|
||||||
class="sla-badge box sla-remaining"
|
class="sla-badge box sla-remaining"
|
||||||
>
|
>
|
||||||
<Clock size="10" />
|
<Clock size="12" />
|
||||||
<span class="sla-text">{{ label }} {{ sla.value }}</span>
|
<span class="sla-text">{{ label }} {{ sla.value }}</span>
|
||||||
</span>
|
</span>
|
||||||
</TransitionGroup>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -38,12 +39,16 @@ const props = defineProps({
|
|||||||
dueAt: String,
|
dueAt: String,
|
||||||
actualAt: String,
|
actualAt: String,
|
||||||
label: String,
|
label: String,
|
||||||
showSLAMet: {
|
showExtra: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true
|
default: true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const { sla } = useSla(ref(props.dueAt), ref(props.actualAt))
|
|
||||||
|
let sla = null
|
||||||
|
if (props.dueAt) {
|
||||||
|
sla = useSla(ref(props.dueAt), ref(props.actualAt))
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@@ -62,4 +67,8 @@ const { sla } = useSla(ref(props.dueAt), ref(props.actualAt))
|
|||||||
.sla-remaining {
|
.sla-remaining {
|
||||||
@apply bg-yellow-100 text-yellow-800;
|
@apply bg-yellow-100 text-yellow-800;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sla-text {
|
||||||
|
@apply text-[0.65rem];
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -62,9 +62,9 @@ const submitForm = async (values) => {
|
|||||||
}
|
}
|
||||||
await api.updateOIDC(props.id, values)
|
await api.updateOIDC(props.id, values)
|
||||||
toastDescription = 'Provider updated successfully'
|
toastDescription = 'Provider updated successfully'
|
||||||
router.push({ name: 'sso-list' })
|
|
||||||
} else {
|
} else {
|
||||||
await api.createOIDC(values)
|
await api.createOIDC(values)
|
||||||
|
router.push({ name: 'sso-list' })
|
||||||
toastDescription = 'Provider created successfully'
|
toastDescription = 'Provider created successfully'
|
||||||
}
|
}
|
||||||
emitter.emit(EMITTER_EVENTS.SHOW_TOAST, {
|
emitter.emit(EMITTER_EVENTS.SHOW_TOAST, {
|
||||||
|
|||||||
Reference in New Issue
Block a user