mirror of
https://github.com/abhinavxd/libredesk.git
synced 2025-11-04 14:03:19 +00:00
Compare commits
8 Commits
v0.7.0-alp
...
v0.7.2-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e0dc0285a4 | ||
|
|
b971619ea6 | ||
|
|
69accaebef | ||
|
|
27de73536e | ||
|
|
df108a3363 | ||
|
|
266c3dab72 | ||
|
|
bf2c1fff6f | ||
|
|
2930af0c4f |
16
.github/ISSUE_TEMPLATE/confirmed-bug.md
vendored
Normal file
16
.github/ISSUE_TEMPLATE/confirmed-bug.md
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
name: Confirmed Bug Report
|
||||||
|
about: Report a confirmed bug in Libredesk
|
||||||
|
title: "[Bug] <brief summary>"
|
||||||
|
labels: bug
|
||||||
|
assignees: ""
|
||||||
|
---
|
||||||
|
|
||||||
|
**Version:**
|
||||||
|
- libredesk: [eg: v0.7.0]
|
||||||
|
|
||||||
|
**Description of the bug and steps to reproduce:**
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
**Logs / Screenshots:**
|
||||||
|
Attach any relevant logs or screenshots to help diagnose the issue.
|
||||||
16
.github/ISSUE_TEMPLATE/possible-bug.md
vendored
Normal file
16
.github/ISSUE_TEMPLATE/possible-bug.md
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
name: Possible Bug Report
|
||||||
|
about: Something in Libredesk might be broken but needs confirmation
|
||||||
|
title: "[Possible Bug] <brief summary>"
|
||||||
|
labels: bug, needs-investigation
|
||||||
|
assignees: ""
|
||||||
|
---
|
||||||
|
|
||||||
|
**Version:**
|
||||||
|
- libredesk: [eg: v0.7.0]
|
||||||
|
|
||||||
|
**Description of the bug and steps to reproduce:**
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
**Logs / Screenshots:**
|
||||||
|
Attach any relevant logs or screenshots to help diagnose the issue.
|
||||||
@@ -3,7 +3,6 @@ package main
|
|||||||
import (
|
import (
|
||||||
amodels "github.com/abhinavxd/libredesk/internal/auth/models"
|
amodels "github.com/abhinavxd/libredesk/internal/auth/models"
|
||||||
"github.com/abhinavxd/libredesk/internal/envelope"
|
"github.com/abhinavxd/libredesk/internal/envelope"
|
||||||
umodels "github.com/abhinavxd/libredesk/internal/user/models"
|
|
||||||
realip "github.com/ferluci/fast-realip"
|
realip "github.com/ferluci/fast-realip"
|
||||||
"github.com/valyala/fasthttp"
|
"github.com/valyala/fasthttp"
|
||||||
"github.com/zerodha/fastglue"
|
"github.com/zerodha/fastglue"
|
||||||
@@ -42,12 +41,6 @@ func handleLogin(r *fastglue.Request) error {
|
|||||||
return sendErrorEnvelope(r, envelope.NewError(envelope.GeneralError, app.i18n.T("user.accountDisabled"), nil))
|
return sendErrorEnvelope(r, envelope.NewError(envelope.GeneralError, app.i18n.T("user.accountDisabled"), nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set user availability status to online.
|
|
||||||
if err := app.user.UpdateAvailability(user.ID, umodels.Online); err != nil {
|
|
||||||
return sendErrorEnvelope(r, err)
|
|
||||||
}
|
|
||||||
user.AvailabilityStatus = umodels.Online
|
|
||||||
|
|
||||||
if err := app.auth.SaveSession(amodels.User{
|
if err := app.auth.SaveSession(amodels.User{
|
||||||
ID: user.ID,
|
ID: user.ID,
|
||||||
Email: user.Email.String,
|
Email: user.Email.String,
|
||||||
|
|||||||
30
docs/docs/api-getting-started.md
Normal file
30
docs/docs/api-getting-started.md
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
# API getting started
|
||||||
|
|
||||||
|
You can access the Libredesk API to interact with your instance programmatically.
|
||||||
|
|
||||||
|
## Generating API keys
|
||||||
|
|
||||||
|
1. **Edit agent**: Go to Admin → Teammate → Agent → Edit
|
||||||
|
2. **Generate new API key**: An API Key and API Secret will be generated for the agent
|
||||||
|
3. **Save the credentials**: Keep both the API Key and API Secret secure
|
||||||
|
4. **Key management**: You can revoke / regenerate API keys at any time from the same page
|
||||||
|
|
||||||
|
## Using the API
|
||||||
|
|
||||||
|
LibreDesk supports two authentication schemes:
|
||||||
|
|
||||||
|
### Basic authentication
|
||||||
|
```bash
|
||||||
|
curl -X GET "https://your-libredesk-instance.com/api/endpoint" \
|
||||||
|
-H "Authorization: Basic <base64_encoded_key:secret>"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Token authentication
|
||||||
|
```bash
|
||||||
|
curl -X GET "https://your-libredesk-instance.com/api/endpoint" \
|
||||||
|
-H "Authorization: token your_api_key:your_api_secret"
|
||||||
|
```
|
||||||
|
|
||||||
|
## API Documentation
|
||||||
|
|
||||||
|
Complete API documentation with available endpoints and examples coming soon.
|
||||||
@@ -32,6 +32,7 @@ nav:
|
|||||||
- Email Templates: templating.md
|
- Email Templates: templating.md
|
||||||
- SSO Setup: sso.md
|
- SSO Setup: sso.md
|
||||||
- Webhooks: webhooks.md
|
- Webhooks: webhooks.md
|
||||||
|
- API Getting Started: api-getting-started.md
|
||||||
- Contributions:
|
- Contributions:
|
||||||
- Developer Setup: developer-setup.md
|
- Developer Setup: developer-setup.md
|
||||||
- Translate Libredesk: translations.md
|
- Translate Libredesk: translations.md
|
||||||
|
|||||||
@@ -1,105 +1,139 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="max-w-5xl mx-auto p-6 min-h-screen">
|
<div class="max-w-5xl mx-auto p-6 min-h-screen">
|
||||||
<div class="space-y-8">
|
<Tabs :default-value="defaultTab" v-model="activeTab">
|
||||||
<div
|
<TabsList class="grid w-full mb-6" :class="tabsGridClass">
|
||||||
v-for="(items, type) in results"
|
<TabsTrigger v-for="(items, type) in results" :key="type" :value="type" class="capitalize">
|
||||||
:key="type"
|
{{ type }} ({{ items.length }})
|
||||||
class="bg-card rounded shadow overflow-hidden"
|
</TabsTrigger>
|
||||||
>
|
</TabsList>
|
||||||
<!-- Header for each section -->
|
|
||||||
<h2
|
|
||||||
class="bg-primary dark:bg-primary text-lg font-bold text-white dark:text-primary-foreground py-2 px-6 capitalize"
|
|
||||||
>
|
|
||||||
{{ type }}
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<!-- No results message -->
|
<TabsContent v-for="(items, type) in results" :key="type" :value="type" class="mt-0">
|
||||||
<div v-if="items.length === 0" class="p-6 text-gray-500 dark:text-muted-foreground">
|
<div class="bg-background rounded border overflow-hidden">
|
||||||
{{
|
<!-- No results message -->
|
||||||
$t('globals.messages.noResults', {
|
<div v-if="items.length === 0" class="p-8 text-center text-muted-foreground">
|
||||||
name: type
|
<div class="text-lg font-medium mb-2">
|
||||||
})
|
{{
|
||||||
}}
|
$t('globals.messages.noResults', {
|
||||||
</div>
|
name: type
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
<div class="text-sm">{{ $t('search.adjustSearchTerms') }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Results list -->
|
<!-- Results list -->
|
||||||
<div class="divide-y divide-gray-200 dark:divide-border">
|
<div v-else class="divide-y divide-border">
|
||||||
<div
|
<div
|
||||||
v-for="item in items"
|
v-for="item in items"
|
||||||
:key="item.id || item.uuid"
|
:key="item.id || item.uuid"
|
||||||
class="p-6 hover:bg-gray-100 dark:hover:bg-accent transition duration-300 ease-in-out group"
|
class="p-6 hover:bg-accent/50 transition duration-200 ease-in-out group"
|
||||||
>
|
|
||||||
<router-link
|
|
||||||
:to="{
|
|
||||||
name: 'inbox-conversation',
|
|
||||||
params: {
|
|
||||||
uuid: type === 'conversations' ? item.uuid : item.conversation_uuid,
|
|
||||||
type: 'assigned'
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
class="block"
|
|
||||||
>
|
>
|
||||||
<div class="flex justify-between items-start">
|
<router-link
|
||||||
<div class="flex-grow">
|
:to="{
|
||||||
<!-- Reference number -->
|
name: 'inbox-conversation',
|
||||||
<div
|
params: {
|
||||||
class="text-sm font-semibold mb-2 group-hover:text-primary dark:group-hover:text-primary transition duration-300"
|
uuid: type === 'conversations' ? item.uuid : item.conversation_uuid,
|
||||||
>
|
type: 'assigned'
|
||||||
#{{
|
}
|
||||||
type === 'conversations'
|
}"
|
||||||
? item.reference_number
|
class="block"
|
||||||
: item.conversation_reference_number
|
>
|
||||||
}}
|
<div class="flex justify-between items-start">
|
||||||
|
<div class="flex-grow">
|
||||||
|
<!-- Reference number -->
|
||||||
|
<div
|
||||||
|
class="text-sm font-semibold mb-2 text-muted-foreground group-hover:text-primary transition duration-200"
|
||||||
|
>
|
||||||
|
#{{
|
||||||
|
type === 'conversations'
|
||||||
|
? item.reference_number
|
||||||
|
: item.conversation_reference_number
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Content -->
|
||||||
|
<div
|
||||||
|
class="text-foreground font-medium mb-2 text-lg group-hover:text-primary transition duration-200"
|
||||||
|
>
|
||||||
|
{{
|
||||||
|
truncateText(
|
||||||
|
type === 'conversations' ? item.subject : item.text_content,
|
||||||
|
100
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Timestamp -->
|
||||||
|
<div class="text-sm text-muted-foreground flex items-center">
|
||||||
|
<ClockIcon class="h-4 w-4 mr-1" />
|
||||||
|
{{
|
||||||
|
formatDate(
|
||||||
|
type === 'conversations' ? item.created_at : item.conversation_created_at
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Content -->
|
<!-- Right arrow icon -->
|
||||||
<div
|
<div
|
||||||
class="text-gray-900 dark:text-card-foreground font-medium mb-2 text-lg group-hover:text-gray-950 dark:group-hover:text-foreground transition duration-300"
|
class="bg-secondary rounded-full p-2 group-hover:bg-primary transition duration-200"
|
||||||
>
|
>
|
||||||
{{
|
<ChevronRightIcon
|
||||||
truncateText(type === 'conversations' ? item.subject : item.text_content, 100)
|
class="h-5 w-5 text-secondary-foreground group-hover:text-primary-foreground"
|
||||||
}}
|
aria-hidden="true"
|
||||||
</div>
|
/>
|
||||||
|
|
||||||
<!-- Timestamp -->
|
|
||||||
<div class="text-sm text-gray-500 dark:text-muted-foreground flex items-center">
|
|
||||||
<ClockIcon class="h-4 w-4 mr-1" />
|
|
||||||
{{
|
|
||||||
formatDate(
|
|
||||||
type === 'conversations' ? item.created_at : item.conversation_created_at
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</router-link>
|
||||||
<!-- Right arrow icon -->
|
</div>
|
||||||
<div
|
|
||||||
class="bg-gray-200 dark:bg-secondary rounded-full p-2 group-hover:bg-primary dark:group-hover:bg-primary transition duration-300"
|
|
||||||
>
|
|
||||||
<ChevronRightIcon
|
|
||||||
class="h-5 w-5 text-gray-700 dark:text-secondary-foreground group-hover:text-white dark:group-hover:text-primary-foreground"
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</router-link>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</TabsContent>
|
||||||
</div>
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { computed, ref, watch } from 'vue'
|
||||||
import { ChevronRightIcon, ClockIcon } from 'lucide-vue-next'
|
import { ChevronRightIcon, ClockIcon } from 'lucide-vue-next'
|
||||||
import { format, parseISO } from 'date-fns'
|
import { format, parseISO } from 'date-fns'
|
||||||
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||||
|
|
||||||
defineProps({
|
const props = defineProps({
|
||||||
results: {
|
results: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Get the first available tab as default
|
||||||
|
const defaultTab = computed(() => {
|
||||||
|
const types = Object.keys(props.results)
|
||||||
|
return types.length > 0 ? types[0] : ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const activeTab = ref('')
|
||||||
|
|
||||||
|
// Watch for changes in results and set the first tab as active
|
||||||
|
watch(
|
||||||
|
() => props.results,
|
||||||
|
(newResults) => {
|
||||||
|
const types = Object.keys(newResults)
|
||||||
|
if (types.length > 0 && !activeTab.value) {
|
||||||
|
activeTab.value = types[0]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
// Dynamic grid class based on number of tabs
|
||||||
|
const tabsGridClass = computed(() => {
|
||||||
|
const tabCount = Object.keys(props.results).length
|
||||||
|
if (tabCount <= 2) return 'grid-cols-2'
|
||||||
|
if (tabCount <= 3) return 'grid-cols-3'
|
||||||
|
if (tabCount <= 4) return 'grid-cols-4'
|
||||||
|
return 'grid-cols-5'
|
||||||
|
})
|
||||||
|
|
||||||
const formatDate = (dateString) => {
|
const formatDate = (dateString) => {
|
||||||
const date = parseISO(dateString)
|
const date = parseISO(dateString)
|
||||||
return format(date, 'MMM d, yyyy HH:mm')
|
return format(date, 'MMM d, yyyy HH:mm')
|
||||||
|
|||||||
2
go.mod
2
go.mod
@@ -38,7 +38,7 @@ require (
|
|||||||
github.com/zerodha/simplesessions/v3 v3.0.0
|
github.com/zerodha/simplesessions/v3 v3.0.0
|
||||||
golang.org/x/crypto v0.38.0
|
golang.org/x/crypto v0.38.0
|
||||||
golang.org/x/mod v0.17.0
|
golang.org/x/mod v0.17.0
|
||||||
golang.org/x/oauth2 v0.21.0
|
golang.org/x/oauth2 v0.27.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
|||||||
6
go.sum
6
go.sum
@@ -140,8 +140,6 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
|||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/redis/go-redis/v9 v9.5.5 h1:51VEyMF8eOO+NUHFm8fpg+IOc1xFuFOhxs3R+kPu1FM=
|
github.com/redis/go-redis/v9 v9.5.5 h1:51VEyMF8eOO+NUHFm8fpg+IOc1xFuFOhxs3R+kPu1FM=
|
||||||
github.com/redis/go-redis/v9 v9.5.5/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
|
github.com/redis/go-redis/v9 v9.5.5/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
|
||||||
github.com/rhnvrm/simples3 v0.9.0 h1:It6/glyqRTRooRzXcYOuqpKwjGg3lsXgNmeGgxpBtjA=
|
|
||||||
github.com/rhnvrm/simples3 v0.9.0/go.mod h1:Y+3vYm2V7Y4VijFoJHHTrja6OgPrJ2cBti8dPGkC3sA=
|
|
||||||
github.com/rhnvrm/simples3 v0.9.1 h1:pYfEe2wTjx8B2zFzUdy4kZn3I3Otd9ZvzIhHkFR85kE=
|
github.com/rhnvrm/simples3 v0.9.1 h1:pYfEe2wTjx8B2zFzUdy4kZn3I3Otd9ZvzIhHkFR85kE=
|
||||||
github.com/rhnvrm/simples3 v0.9.1/go.mod h1:Y+3vYm2V7Y4VijFoJHHTrja6OgPrJ2cBti8dPGkC3sA=
|
github.com/rhnvrm/simples3 v0.9.1/go.mod h1:Y+3vYm2V7Y4VijFoJHHTrja6OgPrJ2cBti8dPGkC3sA=
|
||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
@@ -211,8 +209,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
|
|||||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
||||||
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
||||||
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
|
golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
|
||||||
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
|||||||
@@ -568,6 +568,7 @@
|
|||||||
"search.noResultsForQuery": "No results found for query `{query}`. Try a different search term.",
|
"search.noResultsForQuery": "No results found for query `{query}`. Try a different search term.",
|
||||||
"search.minQueryLength": " Please enter at least {length} characters to search.",
|
"search.minQueryLength": " Please enter at least {length} characters to search.",
|
||||||
"search.searchBy": "Search by reference number, contact email address or messages in conversations.",
|
"search.searchBy": "Search by reference number, contact email address or messages in conversations.",
|
||||||
|
"search.adjustSearchTerms": "Try adjusting your search terms or filters.",
|
||||||
"sla.overdueBy": "Overdue by",
|
"sla.overdueBy": "Overdue by",
|
||||||
"sla.met": "SLA met",
|
"sla.met": "SLA met",
|
||||||
"view.form.description": "Create and save custom filter views for quick access to your conversations.",
|
"view.form.description": "Create and save custom filter views for quick access to your conversations.",
|
||||||
|
|||||||
@@ -438,6 +438,7 @@ SELECT
|
|||||||
m.sender_type,
|
m.sender_type,
|
||||||
m.sender_id,
|
m.sender_id,
|
||||||
m.meta,
|
m.meta,
|
||||||
|
c.uuid as conversation_uuid,
|
||||||
COALESCE(
|
COALESCE(
|
||||||
json_agg(
|
json_agg(
|
||||||
json_build_object(
|
json_build_object(
|
||||||
@@ -452,10 +453,11 @@ SELECT
|
|||||||
'[]'::json
|
'[]'::json
|
||||||
) AS attachments
|
) AS attachments
|
||||||
FROM conversation_messages m
|
FROM conversation_messages m
|
||||||
|
INNER JOIN conversations c ON c.id = m.conversation_id
|
||||||
LEFT JOIN media ON media.model_type = 'messages' AND media.model_id = m.id
|
LEFT JOIN media ON media.model_type = 'messages' AND media.model_id = m.id
|
||||||
WHERE m.uuid = $1
|
WHERE m.uuid = $1
|
||||||
GROUP BY
|
GROUP BY
|
||||||
m.id, m.created_at, m.updated_at, m.status, m.type, m.content, m.uuid, m.private, m.sender_type
|
m.id, m.created_at, m.updated_at, m.status, m.type, m.content, m.uuid, m.private, m.sender_type, c.uuid
|
||||||
ORDER BY m.created_at;
|
ORDER BY m.created_at;
|
||||||
|
|
||||||
-- name: get-messages
|
-- name: get-messages
|
||||||
|
|||||||
Reference in New Issue
Block a user