This commit is contained in:
@@ -17,6 +17,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@f3ve/vue-markdown-it": "^0.2.3",
|
"@f3ve/vue-markdown-it": "^0.2.3",
|
||||||
|
"@hereconnect/types": "^1.0.0",
|
||||||
"@primevue/forms": "^4.2.1",
|
"@primevue/forms": "^4.2.1",
|
||||||
"@primevue/themes": "^4.2.1",
|
"@primevue/themes": "^4.2.1",
|
||||||
"@tanstack/vue-query": "^5.60.6",
|
"@tanstack/vue-query": "^5.60.6",
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import type {
|
|||||||
QRCodeDocument,
|
QRCodeDocument,
|
||||||
MessageDocument,
|
MessageDocument,
|
||||||
WebPushSubscriptionDocument,
|
WebPushSubscriptionDocument,
|
||||||
} from '@/types/DBDocumentTypes'
|
PublicSettingsDocument,
|
||||||
import type { PublicSettingsDocument } from '@/types/DBDocumentTypes/PublicSettingsDocument'
|
} from '@hereconnect/types'
|
||||||
|
|
||||||
export const qrCodeDbRemote = new PouchDB<QRCodeDocument>(`${import.meta.env.VITE_API_URL}/qr`, {
|
export const qrCodeDbRemote = new PouchDB<QRCodeDocument>(`${import.meta.env.VITE_API_URL}/qr`, {
|
||||||
skip_setup: true,
|
skip_setup: true,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { QRCodeDocument } from '@/types/DBDocumentTypes'
|
import type { QRCodeDocument } from '@hereconnect/types'
|
||||||
import { qrCodeDbLocal, qrCodeDbRemote } from '@/api/dbs'
|
import { qrCodeDbLocal, qrCodeDbRemote } from '@/api/dbs'
|
||||||
import { getUserUuid } from '@/utils/getUserUuid'
|
import { getUserUuid } from '@/utils/getUserUuid'
|
||||||
import { getBrowserUuid } from '@/utils/getBrowserUuid'
|
import { getBrowserUuid } from '@/utils/getBrowserUuid'
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { WebPushSubscriptionDocument } from '@/types/DBDocumentTypes'
|
import type { WebPushSubscriptionDocument } from '@hereconnect/types'
|
||||||
import { publicSettingsDbRemote, webPushSubscriptionsDbRemote } from '@/api/dbs'
|
import { publicSettingsDbRemote, webPushSubscriptionsDbRemote } from '@/api/dbs'
|
||||||
import { getUserUuid } from '@/utils/getUserUuid'
|
import { getUserUuid } from '@/utils/getUserUuid'
|
||||||
import { getBrowserUuid } from '@/utils/getBrowserUuid'
|
import { getBrowserUuid } from '@/utils/getBrowserUuid'
|
||||||
|
|||||||
@@ -40,7 +40,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, onUnmounted, useTemplateRef, watch, nextTick } from 'vue'
|
import { ref, onMounted, onUnmounted, useTemplateRef, watch, nextTick } from 'vue'
|
||||||
import { messagesDbLocal as db, messagesDbRemote as remoteDb } from '@/api/dbs'
|
import { messagesDbLocal as db, messagesDbRemote as remoteDb } from '@/api/dbs'
|
||||||
import { type MessageDocument } from '@/types/DBDocumentTypes'
|
import { type MessageDocument } from '@hereconnect/types'
|
||||||
|
import { getBrowserUuid } from '@/utils/getBrowserUuid'
|
||||||
|
import { getUserUuid } from '@/utils/getUserUuid'
|
||||||
|
|
||||||
const formRef = useTemplateRef('form')
|
const formRef = useTemplateRef('form')
|
||||||
const messagesContainerRef = useTemplateRef('messagesContainer')
|
const messagesContainerRef = useTemplateRef('messagesContainer')
|
||||||
@@ -84,9 +86,11 @@ async function sendMessage() {
|
|||||||
type: 'message',
|
type: 'message',
|
||||||
qr_code_uri,
|
qr_code_uri,
|
||||||
chat,
|
chat,
|
||||||
created_at,
|
|
||||||
from: userRole,
|
from: userRole,
|
||||||
body: newMessage.value.trim(),
|
body: newMessage.value.trim(),
|
||||||
|
browser_uuid: getBrowserUuid(),
|
||||||
|
user_uuid: getUserUuid(),
|
||||||
|
created_at,
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { type QRCodeDocument } from '@/types/DBDocumentTypes'
|
import { type QRCodeDocument } from '@hereconnect/types'
|
||||||
import CreateMessageDeliveryOptionTelegram from '@/components/create/CreateMessageDeliveryOptionTelegram.vue'
|
import CreateMessageDeliveryOptionTelegram from '@/components/create/CreateMessageDeliveryOptionTelegram.vue'
|
||||||
import CreateMessageDeliveryOptionWebPush from '@/components/create/CreateMessageDeliveryOptionWebPush.vue'
|
import CreateMessageDeliveryOptionWebPush from '@/components/create/CreateMessageDeliveryOptionWebPush.vue'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { QRCodeDocument } from '@/types/DBDocumentTypes'
|
import type { QRCodeDocument } from '@hereconnect/types'
|
||||||
import type { DeliveryOption } from '@/constants/deliveryOptions'
|
import type { DeliveryOption } from '@/constants/deliveryOptions'
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { type QRCodeDocument } from '@/types/DBDocumentTypes'
|
import { type QRCodeDocument } from '@hereconnect/types'
|
||||||
import { type DeliveryOption, DELIVERY_METHOD_TELEGRAM } from '@/constants/deliveryOptions'
|
import { type DeliveryOption, DELIVERY_METHOD_TELEGRAM } from '@/constants/deliveryOptions'
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { type QRCodeDocument } from '@/types/DBDocumentTypes'
|
import { type QRCodeDocument } from '@hereconnect/types'
|
||||||
import { type DeliveryOption, DELIVERY_METHOD_WEB_PUSH } from '@/constants/deliveryOptions'
|
import { type DeliveryOption, DELIVERY_METHOD_WEB_PUSH } from '@/constants/deliveryOptions'
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { useQuery } from '@tanstack/vue-query'
|
import { useQuery } from '@tanstack/vue-query'
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { qrCodePlacements } from '@/constants/qrCodePlacements'
|
import { qrCodePlacements } from '@/constants/qrCodePlacements'
|
||||||
import type { QRCodeDocument } from '@/types/DBDocumentTypes'
|
import type { QRCodeDocument } from '@hereconnect/types'
|
||||||
const selectedPlacement = defineModel<null | QRCodeDocument['placement']>()
|
const selectedPlacement = defineModel<null | QRCodeDocument['placement']>()
|
||||||
|
|
||||||
defineEmits(['nextStep'])
|
defineEmits(['nextStep'])
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { nextTick, useTemplateRef, watch } from 'vue'
|
import { nextTick, useTemplateRef, watch } from 'vue'
|
||||||
import { type QRCodeDocument } from '@/types/DBDocumentTypes'
|
import { type QRCodeDocument } from '@hereconnect/types'
|
||||||
import { defaultMessages } from '@/constants/defaultMessages'
|
import { defaultMessages } from '@/constants/defaultMessages'
|
||||||
|
|
||||||
// Пропсы
|
// Пропсы
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref, watch } from 'vue'
|
import { computed, ref, watch } from 'vue'
|
||||||
import type { QRCodeDocument } from '@/types/DBDocumentTypes'
|
import type { QRCodeDocument } from '@hereconnect/types'
|
||||||
|
|
||||||
// Пропсы, которые будут передаваться из родительского компонента
|
// Пропсы, которые будут передаваться из родительского компонента
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { type QRCodeDocument } from '@/types/DBDocumentTypes'
|
import { type QRCodeDocument } from '@hereconnect/types'
|
||||||
defineProps<{ buttonLabel: string }>()
|
defineProps<{ buttonLabel: string }>()
|
||||||
const name = defineModel<QRCodeDocument['name']>('name')
|
const name = defineModel<QRCodeDocument['name']>('name')
|
||||||
const emit = defineEmits(['nextStep'])
|
const emit = defineEmits(['nextStep'])
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { VueMarkdownIt } from '@f3ve/vue-markdown-it'
|
import { VueMarkdownIt } from '@f3ve/vue-markdown-it'
|
||||||
import { useQuery } from '@tanstack/vue-query'
|
import { useQuery } from '@tanstack/vue-query'
|
||||||
import { type QRCodeDocument } from '@/types/DBDocumentTypes'
|
import { type QRCodeDocument } from '@hereconnect/types'
|
||||||
|
|
||||||
// Импортируем все инструкции из директории
|
// Импортируем все инструкции из директории
|
||||||
const importInstructions = import.meta.glob('/src/content/placementInstructions/*.md', {
|
const importInstructions = import.meta.glob('/src/content/placementInstructions/*.md', {
|
||||||
|
|||||||
@@ -48,7 +48,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
import md5 from 'md5'
|
import md5 from 'md5'
|
||||||
import type { ContactInfo } from '@/types/DBDocumentTypes'
|
import type { ContactInfo } from '@hereconnect/types'
|
||||||
import { sanitizeFileName } from '@/utils/sanitizeFileName'
|
import { sanitizeFileName } from '@/utils/sanitizeFileName'
|
||||||
|
|
||||||
const { contactInfo } = defineProps<{ contactInfo: ContactInfo }>()
|
const { contactInfo } = defineProps<{ contactInfo: ContactInfo }>()
|
||||||
|
|||||||
@@ -45,8 +45,10 @@
|
|||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { messagesDbLocal as db } from '@/api/dbs'
|
import { messagesDbLocal as db } from '@/api/dbs'
|
||||||
import type { MessageDocument, QRCodeDocument } from '@/types/DBDocumentTypes'
|
import type { MessageDocument, QRCodeDocument } from '@hereconnect/types'
|
||||||
import { ROUTE_NAMES } from '@/constants/routes'
|
import { ROUTE_NAMES } from '@/constants/routes'
|
||||||
|
import { getUserUuid } from '@/utils/getUserUuid'
|
||||||
|
import { getBrowserUuid } from '@/utils/getBrowserUuid'
|
||||||
|
|
||||||
const { doc } = defineProps<{
|
const { doc } = defineProps<{
|
||||||
doc: QRCodeDocument
|
doc: QRCodeDocument
|
||||||
@@ -76,6 +78,8 @@ async function sendMessage(content: string) {
|
|||||||
chat,
|
chat,
|
||||||
created_at: new Date().toISOString(),
|
created_at: new Date().toISOString(),
|
||||||
from: 'guest',
|
from: 'guest',
|
||||||
|
user_uuid: getUserUuid(),
|
||||||
|
browser_uuid: getBrowserUuid(),
|
||||||
body: content.trim(),
|
body: content.trim(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
import type { BaseDocument } from '@/types/DBDocumentTypes/BaseDocument'
|
|
||||||
|
|
||||||
export interface PublicSettingsDocument extends BaseDocument {
|
|
||||||
_id: string // Формат: "public_settings:<name>"
|
|
||||||
type: 'public_settings'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Название настройки
|
|
||||||
*/
|
|
||||||
name: string
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Значение настройки
|
|
||||||
*/
|
|
||||||
value: string
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import type { BaseDocument } from '@/types/DBDocumentTypes/BaseDocument'
|
|
||||||
|
|
||||||
export interface WebPushSubscriptionDocument extends BaseDocument {
|
|
||||||
_id: string // Формат: "web_push_subscription:<user_uuid>:<browser_uuid>"
|
|
||||||
type: 'web_push_subscription'
|
|
||||||
user_uuid: string // UUID пользователя, связанного с подпиской
|
|
||||||
browser_uuid: string // UUID браузера, связанного с подпиской
|
|
||||||
subscription: PushSubscriptionJSON
|
|
||||||
created_at: string // Дата создания подписки в ISO формате
|
|
||||||
updated_at?: string // Дата последнего обновления подписки
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
export * from './BaseDocument'
|
|
||||||
export * from './QRCodeDocument'
|
|
||||||
export * from './MessageDocument'
|
|
||||||
export * from './UserDocument'
|
|
||||||
export * from './WebPushSubscriptionDocument'
|
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { createQrCodeDocument } from '@/api/qrCode'
|
import { createQrCodeDocument } from '@/api/qrCode'
|
||||||
import type { QRCodeDocument } from '@/types/DBDocumentTypes'
|
import type { QRCodeDocument } from '@hereconnect/types'
|
||||||
import { ROUTE_NAMES } from '@/constants/routes'
|
import { ROUTE_NAMES } from '@/constants/routes'
|
||||||
|
|
||||||
const placement = ref<null | QRCodeDocument['placement']>(null)
|
const placement = ref<null | QRCodeDocument['placement']>(null)
|
||||||
|
|||||||
@@ -1,23 +1,32 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col min-h-full">
|
<div class="flex flex-col min-h-full">
|
||||||
<main class="flex-grow flex items-center justify-center py-6 px-4 md:px-6">
|
<main
|
||||||
|
class="flex-grow flex justify-center py-6 px-4 md:px-6"
|
||||||
|
:class="{
|
||||||
|
'items-center':
|
||||||
|
svgQueryIsLoading || qrCodeQueryIsLoading || svgQueryIsError || qrCodeQueryIsError,
|
||||||
|
}"
|
||||||
|
>
|
||||||
<section class="max-w-prose w-full flex flex-col gap-1">
|
<section class="max-w-prose w-full flex flex-col gap-1">
|
||||||
<h2 class="text-xl font-semibold mb-4">Ваш QR‑код готов</h2>
|
<p v-if="svgQueryIsLoading || qrCodeQueryIsLoading" class="text-gray-500 mt-4">
|
||||||
<p class="text-gray-600 mb-4">Сохраните его и ознакомьтесь с инструкциями по размещению.</p>
|
Загрузка...
|
||||||
|
</p>
|
||||||
<!-- Рендер QR‑кода -->
|
|
||||||
<div v-if="!svgQueryIsLoading && svgQueryData" class="border p-4 bg-white rounded-lg mb-6">
|
|
||||||
<img :src="svgQueryData" alt="QR‑код" />
|
|
||||||
</div>
|
|
||||||
<p v-else-if="svgQueryIsError" class="text-red-500 mt-4">
|
<p v-else-if="svgQueryIsError" class="text-red-500 mt-4">
|
||||||
Ошибка генерации QR‑кода: {{ svgQueryError?.message }}
|
Ошибка генерации QR‑кода: {{ svgQueryError?.message }}
|
||||||
</p>
|
</p>
|
||||||
|
<p v-else-if="qrCodeQueryIsError" class="text-red-500 mt-4">
|
||||||
<!-- Сообщение об ошибке -->
|
|
||||||
<p v-if="qrCodeQueryIsError" class="text-red-500 mt-4">
|
|
||||||
Ошибка загрузки данных QR‑кода: {{ qrCodeQueryError!.message }}
|
Ошибка загрузки данных QR‑кода: {{ qrCodeQueryError!.message }}
|
||||||
</p>
|
</p>
|
||||||
<p v-if="svgQueryIsLoading" class="text-gray-500 mt-4">Загрузка...</p>
|
<template v-else>
|
||||||
|
<h2 class="text-xl font-semibold mb-4">Ваш QR‑код готов</h2>
|
||||||
|
<p class="text-gray-600 mb-4">
|
||||||
|
Сохраните его и ознакомьтесь с инструкциями по размещению.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<!-- Рендер QR‑кода -->
|
||||||
|
<div class="border p-4 bg-white rounded-lg mb-6">
|
||||||
|
<img v-if="!svgQueryIsLoading && svgQueryData" :src="svgQueryData" alt="QR‑код" />
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Кнопки для скачивания -->
|
<!-- Кнопки для скачивания -->
|
||||||
<div
|
<div
|
||||||
@@ -47,6 +56,7 @@
|
|||||||
:placement="qrCodeDoc?.placement"
|
:placement="qrCodeDoc?.placement"
|
||||||
v-if="qrCodeDoc?.placement"
|
v-if="qrCodeDoc?.placement"
|
||||||
/>
|
/>
|
||||||
|
</template>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
@@ -71,7 +81,7 @@ import QRCodeStyling from 'qr-code-styling'
|
|||||||
import { optimizeSvg } from '@/utils/images/svg/optimizeSvg'
|
import { optimizeSvg } from '@/utils/images/svg/optimizeSvg'
|
||||||
import { replaceFillInSvg } from '@/utils/images/svg/replaceFillInSvg'
|
import { replaceFillInSvg } from '@/utils/images/svg/replaceFillInSvg'
|
||||||
import { getQrCodeDocument } from '@/api/qrCode'
|
import { getQrCodeDocument } from '@/api/qrCode'
|
||||||
import { type QRCodeDocument } from '@/types/DBDocumentTypes'
|
import { type QRCodeDocument } from '@hereconnect/types'
|
||||||
import { addLabelToSvg } from '@/utils/images/svg/addLabelToSvg'
|
import { addLabelToSvg } from '@/utils/images/svg/addLabelToSvg'
|
||||||
import logo from '@/assets/logo.svg?raw'
|
import logo from '@/assets/logo.svg?raw'
|
||||||
import { sanitizeFileName } from '@/utils/sanitizeFileName'
|
import { sanitizeFileName } from '@/utils/sanitizeFileName'
|
||||||
@@ -86,11 +96,15 @@ const height = ref(width.value)
|
|||||||
// Хук для загрузки данных QR‑кода
|
// Хук для загрузки данных QR‑кода
|
||||||
const {
|
const {
|
||||||
data: qrCodeDoc,
|
data: qrCodeDoc,
|
||||||
|
isLoading: qrCodeQueryIsLoading,
|
||||||
isError: qrCodeQueryIsError,
|
isError: qrCodeQueryIsError,
|
||||||
error: qrCodeQueryError,
|
error: qrCodeQueryError,
|
||||||
} = useQuery({
|
} = useQuery({
|
||||||
queryKey: ['qrCode', computed(() => qr_code_uri)],
|
queryKey: ['qrCode', computed(() => qr_code_uri)],
|
||||||
queryFn: () => getQrCodeDocument(qr_code_uri),
|
queryFn: async () => {
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 1000))
|
||||||
|
return getQrCodeDocument(qr_code_uri)
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const sanitizedFileName = computed(() => {
|
const sanitizedFileName = computed(() => {
|
||||||
@@ -110,6 +124,8 @@ const {
|
|||||||
enabled: computed(() => !!qrCodeDoc.value?.url),
|
enabled: computed(() => !!qrCodeDoc.value?.url),
|
||||||
retry: false,
|
retry: false,
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 1000))
|
||||||
|
|
||||||
// Заменяем цвет `fill` в логотипе
|
// Заменяем цвет `fill` в логотипе
|
||||||
const updatedLogo = replaceFillInSvg(logo, logoColor.value)
|
const updatedLogo = replaceFillInSvg(logo, logoColor.value)
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { MessageDocument } from '@/types/DBDocumentTypes'
|
import type { MessageDocument } from '@hereconnect/types'
|
||||||
import ChatComponent from '@/components/ChatComponent.vue'
|
import ChatComponent from '@/components/ChatComponent.vue'
|
||||||
defineProps<{ qr_code_uri: MessageDocument['qr_code_uri']; chat: MessageDocument['chat'] }>()
|
defineProps<{ qr_code_uri: MessageDocument['qr_code_uri']; chat: MessageDocument['chat'] }>()
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useQuery } from '@tanstack/vue-query'
|
import { useQuery } from '@tanstack/vue-query'
|
||||||
import { getQrCodeDocument } from '@/api/qrCode'
|
import { getQrCodeDocument } from '@/api/qrCode'
|
||||||
import type { QRCodeDocument } from '@/types/DBDocumentTypes'
|
import type { QRCodeDocument } from '@hereconnect/types'
|
||||||
import QueryRender from '@/components/QueryRender.vue'
|
import QueryRender from '@/components/QueryRender.vue'
|
||||||
|
|
||||||
const { qr_code_uri } = defineProps<{ qr_code_uri: string }>()
|
const { qr_code_uri } = defineProps<{ qr_code_uri: string }>()
|
||||||
|
|||||||
@@ -12,3 +12,9 @@ services:
|
|||||||
- ./services/couchdb/config:/opt/couchdb/etc/local.d
|
- ./services/couchdb/config:/opt/couchdb/etc/local.d
|
||||||
|
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
|
logging:
|
||||||
|
driver: "json-file"
|
||||||
|
options:
|
||||||
|
max-size: "100m"
|
||||||
|
max-file: "10"
|
||||||
Generated
+39
-26
@@ -26,6 +26,7 @@
|
|||||||
"license": "@hereconnect/license",
|
"license": "@hereconnect/license",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@f3ve/vue-markdown-it": "^0.2.3",
|
"@f3ve/vue-markdown-it": "^0.2.3",
|
||||||
|
"@hereconnect/types": "^1.0.0",
|
||||||
"@primevue/forms": "^4.2.1",
|
"@primevue/forms": "^4.2.1",
|
||||||
"@primevue/themes": "^4.2.1",
|
"@primevue/themes": "^4.2.1",
|
||||||
"@tanstack/vue-query": "^5.60.6",
|
"@tanstack/vue-query": "^5.60.6",
|
||||||
@@ -1194,6 +1195,10 @@
|
|||||||
"resolved": "services/message-delivery-method-web-push",
|
"resolved": "services/message-delivery-method-web-push",
|
||||||
"link": true
|
"link": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@hereconnect/types": {
|
||||||
|
"resolved": "packages/types",
|
||||||
|
"link": true
|
||||||
|
},
|
||||||
"node_modules/@humanfs/core": {
|
"node_modules/@humanfs/core": {
|
||||||
"version": "0.19.1",
|
"version": "0.19.1",
|
||||||
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
|
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
|
||||||
@@ -3298,6 +3303,12 @@
|
|||||||
"url": "https://github.com/sponsors/mesqueeb"
|
"url": "https://github.com/sponsors/mesqueeb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/couchdb-changes-stream": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/couchdb-changes-stream/-/couchdb-changes-stream-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-msaurXYnqqAa4s9RmKV14NH+6kK8h7pppoSwTYfR55fdmaYqYLaIuPVoNuWixZaXPEgZ+cHRKIf4PpF1/tu8BQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/cross-spawn": {
|
"node_modules/cross-spawn": {
|
||||||
"version": "7.0.6",
|
"version": "7.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||||
@@ -4911,6 +4922,7 @@
|
|||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/js-yaml": {
|
"node_modules/js-yaml": {
|
||||||
@@ -5179,19 +5191,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/loose-envify": {
|
|
||||||
"version": "1.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
|
||||||
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
|
||||||
"license": "MIT",
|
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
|
||||||
"js-tokens": "^3.0.0 || ^4.0.0"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"loose-envify": "cli.js"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/loupe": {
|
"node_modules/loupe": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.2.tgz",
|
||||||
@@ -6543,19 +6542,6 @@
|
|||||||
],
|
],
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/react": {
|
|
||||||
"version": "18.3.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
|
|
||||||
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
|
||||||
"loose-envify": "^1.1.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/react-query": {
|
"node_modules/react-query": {
|
||||||
"version": "3.39.3",
|
"version": "3.39.3",
|
||||||
"resolved": "https://registry.npmjs.org/react-query/-/react-query-3.39.3.tgz",
|
"resolved": "https://registry.npmjs.org/react-query/-/react-query-3.39.3.tgz",
|
||||||
@@ -8449,6 +8435,30 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"packages/couchdb-changes-stream": {
|
||||||
|
"name": "@hereconnect/couchdb-changes-stream",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"extraneous": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"devDependencies": {
|
||||||
|
"@jest/types": "^29.6.3",
|
||||||
|
"@types/jest": "^29.5.14",
|
||||||
|
"@types/node": "^20.8.0",
|
||||||
|
"eslint": "^8.53.0",
|
||||||
|
"eslint-config-prettier": "^9.0.0",
|
||||||
|
"eslint-plugin-prettier": "^5.0.0",
|
||||||
|
"fast-check": "^3.10.0",
|
||||||
|
"jest": "^29.7.0",
|
||||||
|
"prettier": "^3.1.0",
|
||||||
|
"ts-jest": "^29.2.5",
|
||||||
|
"typescript": "^5.2.2",
|
||||||
|
"vitest": "^0.34.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"packages/types": {
|
||||||
|
"name": "@hereconnect/types",
|
||||||
|
"version": "1.0.0"
|
||||||
|
},
|
||||||
"services/db-migrations": {
|
"services/db-migrations": {
|
||||||
"name": "@hereconnect/db-migrations",
|
"name": "@hereconnect/db-migrations",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
@@ -8470,8 +8480,11 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"license": "@hereconnect/license",
|
"license": "@hereconnect/license",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@hereconnect/types": "^1.0.0",
|
||||||
|
"couchdb-changes-stream": "^1.0.1",
|
||||||
"pouchdb-adapter-http": "^9.0.0",
|
"pouchdb-adapter-http": "^9.0.0",
|
||||||
"pouchdb-core": "^9.0.0",
|
"pouchdb-core": "^9.0.0",
|
||||||
|
"pouchdb-find": "^9.0.0",
|
||||||
"web-push": "^3.6.7"
|
"web-push": "^3.6.7"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|||||||
@@ -21,4 +21,3 @@
|
|||||||
"lint": "npm run lint --workspaces"
|
"lint": "npm run lint --workspaces"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
dist
|
||||||
+6
@@ -23,6 +23,12 @@ export interface MessageDocument extends BaseDocument {
|
|||||||
/** Опциональная геопозиция (широта и долгота) */
|
/** Опциональная геопозиция (широта и долгота) */
|
||||||
location?: { lat: number; lng: number }
|
location?: { lat: number; lng: number }
|
||||||
|
|
||||||
|
/** Идентификатор пользователя, отправившего сообщение */
|
||||||
|
user_uuid: string
|
||||||
|
|
||||||
|
/** Идентификатор браузера, отправившего сообщение */
|
||||||
|
browser_uuid: string
|
||||||
|
|
||||||
/** Вложения, такие как фото */
|
/** Вложения, такие как фото */
|
||||||
// _attachments?: {
|
// _attachments?: {
|
||||||
// [key: string]: Attachment
|
// [key: string]: Attachment
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import type { BaseDocument } from "./BaseDocument";
|
||||||
|
|
||||||
|
export interface PublicSettingsDocument extends BaseDocument {
|
||||||
|
_id: string; // Формат: "public_settings:<name>"
|
||||||
|
type: "public_settings";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Название настройки
|
||||||
|
*/
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Значение настройки
|
||||||
|
*/
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import type { BaseDocument } from "./BaseDocument";
|
||||||
|
|
||||||
|
type MessageDeliveryMethodWebPushVapid = {
|
||||||
|
name: "message_delivery_method_web_push_vapid";
|
||||||
|
value: {
|
||||||
|
subject: string;
|
||||||
|
keys: {
|
||||||
|
publicKey: string;
|
||||||
|
privateKey: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
type MessageDeliveryMethodWebPushGcmApiKey = {
|
||||||
|
name: "message_delivery_method_web_push_gcm_api_key";
|
||||||
|
value: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ServerSettingsDocument = (
|
||||||
|
| MessageDeliveryMethodWebPushVapid
|
||||||
|
| MessageDeliveryMethodWebPushGcmApiKey
|
||||||
|
) & {
|
||||||
|
_id: string; // Формат: "server_settings:<name>"
|
||||||
|
type: "server_settings";
|
||||||
|
} & BaseDocument;
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import type { BaseDocument } from "./BaseDocument";
|
||||||
|
|
||||||
|
export interface WebPushSubscriptionDocument extends BaseDocument {
|
||||||
|
_id: string; // Формат: "web_push_subscription:<user_uuid>:<browser_uuid>"
|
||||||
|
type: "web_push_subscription";
|
||||||
|
user_uuid: string; // UUID пользователя, связанного с подпиской
|
||||||
|
browser_uuid: string; // UUID браузера, связанного с подпиской
|
||||||
|
subscription: PushSubscriptionJSON;
|
||||||
|
created_at: string; // Дата создания подписки в ISO формате
|
||||||
|
updated_at?: string; // Дата последнего обновления подписки
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
export * from "./BaseDocument";
|
||||||
|
export * from "./MessageDocument";
|
||||||
|
export * from "./PublicSettingsDocument";
|
||||||
|
export * from "./QRCodeDocument";
|
||||||
|
export * from "./ServerSettingsDocument";
|
||||||
|
export * from "./UserDocument";
|
||||||
|
export * from "./WebPushSubscriptionDocument";
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./DBDocuments";
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"name": "@hereconnect/types",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "index.ts",
|
||||||
|
"types": "index.ts",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"declaration": true,
|
||||||
|
"emitDeclarationOnly": true,
|
||||||
|
"outDir": "./dist",
|
||||||
|
"strict": true
|
||||||
|
},
|
||||||
|
"include": ["index.ts"]
|
||||||
|
}
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
import PouchDB from "pouchdb-core";
|
|
||||||
import PouchDBAdapterHttp from "pouchdb-adapter-http";
|
|
||||||
|
|
||||||
export default PouchDB.plugin(PouchDBAdapterHttp);
|
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
"name": "@hereconnect/message-delivery-method-web-push",
|
"name": "@hereconnect/message-delivery-method-web-push",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "src/server.ts",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "@hereconnect/license",
|
"license": "@hereconnect/license",
|
||||||
"private": true,
|
"private": true,
|
||||||
@@ -24,13 +24,16 @@
|
|||||||
},
|
},
|
||||||
"contributors": [],
|
"contributors": [],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"generate-keys": "node ./generate-keys.js",
|
"generate-keys": "node ./src/generate-keys.js",
|
||||||
"test-send-notification": "node ./test-send-notification.js",
|
"test-send-notification": "node ./src/test-send-notification.js",
|
||||||
"test-send-notification-cancel": "node ./test-send-notification-cancel.js"
|
"test-send-notification-cancel": "node ./src/test-send-notification-cancel.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@hereconnect/types": "^1.0.0",
|
||||||
|
"couchdb-changes-stream": "^1.0.1",
|
||||||
"pouchdb-adapter-http": "^9.0.0",
|
"pouchdb-adapter-http": "^9.0.0",
|
||||||
"pouchdb-core": "^9.0.0",
|
"pouchdb-core": "^9.0.0",
|
||||||
|
"pouchdb-find": "^9.0.0",
|
||||||
"web-push": "^3.6.7"
|
"web-push": "^3.6.7"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import PouchDB from "pouchdb-core";
|
||||||
|
import PouchDBAdapterHttp from "pouchdb-adapter-http";
|
||||||
|
import PouchDBFindPlugin from 'pouchdb-find'
|
||||||
|
|
||||||
|
export default PouchDB
|
||||||
|
.plugin(PouchDBAdapterHttp)
|
||||||
|
.plugin(PouchDBFindPlugin);
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import PouchDB from "./PouchDB";
|
||||||
|
import type {
|
||||||
|
MessageDocument,
|
||||||
|
QRCodeDocument,
|
||||||
|
ServerSettingsDocument,
|
||||||
|
} from "@hereconnect/types";
|
||||||
|
|
||||||
|
export interface ServiceDoc {
|
||||||
|
_id: string;
|
||||||
|
lastSeq: string | number;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!process.env.API_URL) {
|
||||||
|
throw new Error("Environment variable API_URL is not set");
|
||||||
|
}
|
||||||
|
|
||||||
|
export const messagesDbUrl = `${process.env.API_URL}/messages`;
|
||||||
|
|
||||||
|
export const serverSettingsDb = new PouchDB<ServerSettingsDocument>(
|
||||||
|
`${process.env.API_URL}/server_settings`,
|
||||||
|
{
|
||||||
|
skip_setup: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
export const qrDb = new PouchDB<QRCodeDocument>(`${process.env.API_URL}/qr`, {
|
||||||
|
skip_setup: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const { messagesDb, serviceDb } = (() => {
|
||||||
|
const messagesDB = new PouchDB(messagesDbUrl, {
|
||||||
|
skip_setup: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
messagesDb: messagesDB as PouchDB.Database<MessageDocument>,
|
||||||
|
serviceDb: messagesDB as PouchDB.Database<ServiceDoc>,
|
||||||
|
} as const;
|
||||||
|
})();
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
import webpush from "web-push";
|
||||||
|
|
||||||
|
import { CouchDBChangesStream } from "couchdb-changes-stream";
|
||||||
|
import { qrDb, messagesDbUrl } from "./dbs";
|
||||||
|
import { setupVapidDetails } from "./setupVapidDetails";
|
||||||
|
import type { MessageDocument } from "@hereconnect/types";
|
||||||
|
import { ServiceSeqTracker } from "./serviceSeqTracker";
|
||||||
|
|
||||||
|
const serviceSeqTracker = new ServiceSeqTracker();
|
||||||
|
|
||||||
|
const start = async () => {
|
||||||
|
await setupVapidDetails();
|
||||||
|
const since = await serviceSeqTracker.getSince();
|
||||||
|
|
||||||
|
const changesStream = new CouchDBChangesStream<MessageDocument>(
|
||||||
|
messagesDbUrl,
|
||||||
|
{
|
||||||
|
since,
|
||||||
|
feed: "continuous",
|
||||||
|
include_docs: true,
|
||||||
|
heartbeat: 1000,
|
||||||
|
filter: "_selector",
|
||||||
|
selector: {
|
||||||
|
type: "message",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Gracefully handle SIGINT
|
||||||
|
process.on("SIGINT", async () => {
|
||||||
|
changesStream.stop();
|
||||||
|
process.stdout.write("\n");
|
||||||
|
process.exit(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
console.info("Service started from sequence", since);
|
||||||
|
|
||||||
|
for await (const change of changesStream) {
|
||||||
|
const { seq, doc } = change;
|
||||||
|
|
||||||
|
if (!doc) {
|
||||||
|
console.error("Change missing document:", change);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceSeqTracker.checkNextSeq(seq);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (doc.from === "guest") {
|
||||||
|
const qrDoc = await qrDb.get(`qr:${doc.qr_code_uri}`);
|
||||||
|
if (qrDoc.messageDeliveryMethod !== "webPush") {
|
||||||
|
console.log(`Skip !webPush`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("FROM GUEST");
|
||||||
|
console.log(qrDoc.user_uuid);
|
||||||
|
console.log(qrDoc.browser_uuid);
|
||||||
|
} else if (doc.from === "owner") {
|
||||||
|
console.log("FROM OWNER");
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`Processed change: ${JSON.stringify({ _id: change.id, ref: doc._rev, seq: change.seq })}`,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(
|
||||||
|
`Failed processing: ${JSON.stringify(error)} change: ${JSON.stringify({ id: doc._id, rev: doc._rev, seq: seq })}`,
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
await serviceSeqTracker.saveSeq(seq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
start().catch((error) => {
|
||||||
|
console.error("error", error);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import { serviceDb, type ServiceDoc } from "./dbs";
|
||||||
|
|
||||||
|
const SERVICE_DOC_ID = "_local/service:message-delivery-method-web-push";
|
||||||
|
|
||||||
|
export class ServiceSeqTracker {
|
||||||
|
private serviceDoc: ServiceDoc;
|
||||||
|
|
||||||
|
async getSince() {
|
||||||
|
this.serviceDoc = await serviceDb.get(SERVICE_DOC_ID).catch((error) => {
|
||||||
|
if (error.name === "not_found") {
|
||||||
|
return {
|
||||||
|
_id: SERVICE_DOC_ID,
|
||||||
|
lastSeq: "0",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
throw error; // Re-throw other errors
|
||||||
|
});
|
||||||
|
|
||||||
|
this.serviceDoc.lastSeq = 0;
|
||||||
|
|
||||||
|
return this.serviceDoc.lastSeq;
|
||||||
|
}
|
||||||
|
|
||||||
|
checkNextSeq(seq: number | string) {
|
||||||
|
const next = parseInt(seq as string, 10);
|
||||||
|
const current = parseInt(this.serviceDoc.lastSeq as string, 10);
|
||||||
|
if (next < current) {
|
||||||
|
throw new Error(
|
||||||
|
`Sequence number is too old: ${seq} (${next} < ${current})`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
saveSeq(seq: number | string) {
|
||||||
|
this.serviceDoc.lastSeq = seq;
|
||||||
|
return serviceDb.put(this.serviceDoc);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
import webpush from "web-push";
|
||||||
|
import { serverSettingsDb } from "./dbs";
|
||||||
|
import { ServerSettingsDocument } from "@hereconnect/types";
|
||||||
|
|
||||||
|
function isType<N extends ServerSettingsDocument["name"]>(
|
||||||
|
doc: ServerSettingsDocument,
|
||||||
|
name: N,
|
||||||
|
): doc is ServerSettingsDocument & { name: N } {
|
||||||
|
return doc.name === name;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const setupVapidDetails = async () => {
|
||||||
|
const changes = serverSettingsDb.changes({
|
||||||
|
since: "now",
|
||||||
|
live: true,
|
||||||
|
include_docs: true,
|
||||||
|
doc_ids: [
|
||||||
|
"server_settings:message_delivery_method_web_push_vapid",
|
||||||
|
"public_settings:message_delivery_method_web_push_gcm_api_key",
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
rows: [vapidDetailsRow, gcmApiRow],
|
||||||
|
} = await serverSettingsDb.allDocs({
|
||||||
|
keys: [
|
||||||
|
"server_settings:message_delivery_method_web_push_vapid",
|
||||||
|
"public_settings:message_delivery_method_web_push_gcm_api_key",
|
||||||
|
],
|
||||||
|
include_docs: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (
|
||||||
|
!("doc" in vapidDetailsRow) ||
|
||||||
|
!isType(vapidDetailsRow.doc, "message_delivery_method_web_push_vapid")
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
`VAPID details ${"error" in vapidDetailsRow ? vapidDetailsRow.error : "not exists"}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
"doc" in gcmApiRow &&
|
||||||
|
isType(gcmApiRow.doc, "message_delivery_method_web_push_gcm_api_key")
|
||||||
|
) {
|
||||||
|
webpush.setGCMAPIKey(gcmApiRow.doc.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
webpush.setVapidDetails(
|
||||||
|
vapidDetailsRow.doc.value.subject,
|
||||||
|
vapidDetailsRow.doc.value.keys.publicKey,
|
||||||
|
vapidDetailsRow.doc.value.keys.privateKey,
|
||||||
|
);
|
||||||
|
|
||||||
|
changes.on("change", (change) => {
|
||||||
|
if (!("doc" in change)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (isType(change.doc, "message_delivery_method_web_push_vapid")) {
|
||||||
|
console.log("update vapid details");
|
||||||
|
webpush.setVapidDetails(
|
||||||
|
change.doc.value.subject,
|
||||||
|
change.doc.value.keys.publicKey,
|
||||||
|
change.doc.value.keys.privateKey,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
"doc" in change &&
|
||||||
|
isType(change.doc, "message_delivery_method_web_push_gcm_api_key")
|
||||||
|
) {
|
||||||
|
console.log("update gcm api key");
|
||||||
|
webpush.setGCMAPIKey(change.doc.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user