Added Dokubox Sync Service and Button Fix #12
This commit is contained in:
@@ -27,233 +27,249 @@ let client: ImapFlow | null = null
|
||||
// -------------------------------------------------------------
|
||||
// IMAP CLIENT INITIALIZEN
|
||||
// -------------------------------------------------------------
|
||||
export async function initDokuboxClient() {
|
||||
client = new ImapFlow({
|
||||
host: secrets.DOKUBOX_IMAP_HOST,
|
||||
port: secrets.DOKUBOX_IMAP_PORT,
|
||||
secure: secrets.DOKUBOX_IMAP_SECURE,
|
||||
auth: {
|
||||
user: secrets.DOKUBOX_IMAP_USER,
|
||||
pass: secrets.DOKUBOX_IMAP_PASSWORD
|
||||
},
|
||||
logger: false
|
||||
})
|
||||
|
||||
console.log("Dokubox E-Mail Client Initialized")
|
||||
|
||||
await client.connect()
|
||||
}
|
||||
|
||||
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// MAIN SYNC FUNCTION (DRIZZLE VERSION)
|
||||
// -------------------------------------------------------------
|
||||
export const syncDokubox = (server: FastifyInstance) =>
|
||||
async () => {
|
||||
|
||||
console.log("Perform Dokubox Sync")
|
||||
export function syncDokuboxService (server: FastifyInstance) {
|
||||
async function initDokuboxClient() {
|
||||
client = new ImapFlow({
|
||||
host: secrets.DOKUBOX_IMAP_HOST,
|
||||
port: secrets.DOKUBOX_IMAP_PORT,
|
||||
secure: secrets.DOKUBOX_IMAP_SECURE,
|
||||
auth: {
|
||||
user: secrets.DOKUBOX_IMAP_USER,
|
||||
pass: secrets.DOKUBOX_IMAP_PASSWORD
|
||||
},
|
||||
logger: false
|
||||
})
|
||||
|
||||
await initDokuboxClient()
|
||||
console.log("Dokubox E-Mail Client Initialized")
|
||||
|
||||
if (!client?.usable) {
|
||||
throw new Error("E-Mail Client not usable")
|
||||
await client.connect()
|
||||
}
|
||||
|
||||
const syncDokubox = async () => {
|
||||
|
||||
console.log("Perform Dokubox Sync")
|
||||
|
||||
await initDokuboxClient()
|
||||
|
||||
if (!client?.usable) {
|
||||
throw new Error("E-Mail Client not usable")
|
||||
}
|
||||
|
||||
// -------------------------------
|
||||
// TENANTS LADEN (DRIZZLE)
|
||||
// -------------------------------
|
||||
const tenantList = await server.db
|
||||
.select({
|
||||
id: tenants.id,
|
||||
name: tenants.name,
|
||||
emailAddresses: tenants.dokuboxEmailAddresses,
|
||||
key: tenants.dokuboxkey
|
||||
})
|
||||
.from(tenants)
|
||||
|
||||
const lock = await client.getMailboxLock("INBOX")
|
||||
|
||||
try {
|
||||
|
||||
for await (let msg of client.fetch({ seen: false }, { envelope: true, source: true })) {
|
||||
|
||||
const parsed = await simpleParser(msg.source)
|
||||
|
||||
const message = {
|
||||
id: msg.uid,
|
||||
subject: parsed.subject,
|
||||
to: parsed.to?.value || [],
|
||||
cc: parsed.cc?.value || [],
|
||||
attachments: parsed.attachments || []
|
||||
}
|
||||
|
||||
// -------------------------------------------------
|
||||
// MAPPING / FIND TENANT
|
||||
// -------------------------------------------------
|
||||
const config = await getMessageConfigDrizzle(server, message, tenantList)
|
||||
|
||||
if (!config) {
|
||||
badMessageDetected = true
|
||||
|
||||
if (!badMessageMessageSent) {
|
||||
badMessageMessageSent = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (message.attachments.length > 0) {
|
||||
for (const attachment of message.attachments) {
|
||||
await saveFile(
|
||||
server,
|
||||
config.tenant,
|
||||
message.id,
|
||||
attachment,
|
||||
config.folder,
|
||||
config.filetype
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!badMessageDetected) {
|
||||
badMessageDetected = false
|
||||
badMessageMessageSent = false
|
||||
}
|
||||
|
||||
await client.messageFlagsAdd({ seen: false }, ["\\Seen"])
|
||||
await client.messageDelete({ seen: true })
|
||||
|
||||
} finally {
|
||||
lock.release()
|
||||
client.close()
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------
|
||||
// TENANTS LADEN (DRIZZLE)
|
||||
// -------------------------------
|
||||
const tenantList = await server.db
|
||||
.select({
|
||||
id: tenants.id,
|
||||
name: tenants.name,
|
||||
emailAddresses: tenants.dokuboxEmailAddresses,
|
||||
key: tenants.dokuboxkey
|
||||
})
|
||||
.from(tenants)
|
||||
const getMessageConfigDrizzle = async (
|
||||
server: FastifyInstance,
|
||||
message,
|
||||
tenantsList: any[]
|
||||
) => {
|
||||
|
||||
const lock = await client.getMailboxLock("INBOX")
|
||||
let possibleKeys: string[] = []
|
||||
|
||||
try {
|
||||
if (message.to) {
|
||||
message.to.forEach((item) =>
|
||||
possibleKeys.push(item.address.split("@")[0].toLowerCase())
|
||||
)
|
||||
}
|
||||
|
||||
for await (let msg of client.fetch({ seen: false }, { envelope: true, source: true })) {
|
||||
if (message.cc) {
|
||||
message.cc.forEach((item) =>
|
||||
possibleKeys.push(item.address.split("@")[0].toLowerCase())
|
||||
)
|
||||
}
|
||||
|
||||
const parsed = await simpleParser(msg.source)
|
||||
// -------------------------------------------
|
||||
// TENANT IDENTIFY
|
||||
// -------------------------------------------
|
||||
let tenant = tenantsList.find((t) => possibleKeys.includes(t.key))
|
||||
|
||||
const message = {
|
||||
id: msg.uid,
|
||||
subject: parsed.subject,
|
||||
to: parsed.to?.value || [],
|
||||
cc: parsed.cc?.value || [],
|
||||
attachments: parsed.attachments || []
|
||||
}
|
||||
if (!tenant && message.to?.length) {
|
||||
const address = message.to[0].address.toLowerCase()
|
||||
|
||||
// -------------------------------------------------
|
||||
// MAPPING / FIND TENANT
|
||||
// -------------------------------------------------
|
||||
const config = await getMessageConfigDrizzle(server, message, tenantList)
|
||||
tenant = tenantsList.find((t) =>
|
||||
(t.emailAddresses || []).map((m) => m.toLowerCase()).includes(address)
|
||||
)
|
||||
}
|
||||
|
||||
if (!config) {
|
||||
badMessageDetected = true
|
||||
if (!tenant) return null
|
||||
|
||||
if (!badMessageMessageSent) {
|
||||
badMessageMessageSent = true
|
||||
}
|
||||
return
|
||||
}
|
||||
// -------------------------------------------
|
||||
// FOLDER + FILETYPE VIA SUBJECT
|
||||
// -------------------------------------------
|
||||
let folderId = null
|
||||
let filetypeId = null
|
||||
|
||||
if (message.attachments.length > 0) {
|
||||
for (const attachment of message.attachments) {
|
||||
await saveFile(
|
||||
server,
|
||||
config.tenant,
|
||||
message.id,
|
||||
attachment,
|
||||
config.folder,
|
||||
config.filetype
|
||||
// -------------------------------------------
|
||||
// Rechnung / Invoice
|
||||
// -------------------------------------------
|
||||
if (message.subject?.match(/(Rechnung|Beleg|Invoice|Quittung)/gi)) {
|
||||
|
||||
const folder = await server.db
|
||||
.select({ id: folders.id })
|
||||
.from(folders)
|
||||
.where(
|
||||
and(
|
||||
eq(folders.tenant, tenant.id),
|
||||
and(
|
||||
eq(folders.function, "incomingInvoices"),
|
||||
//@ts-ignore
|
||||
eq(folders.year, dayjs().format("YYYY"))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
.limit(1)
|
||||
|
||||
if (!badMessageDetected) {
|
||||
badMessageDetected = false
|
||||
badMessageMessageSent = false
|
||||
}
|
||||
folderId = folder[0]?.id ?? null
|
||||
|
||||
await client.messageFlagsAdd({ seen: false }, ["\\Seen"])
|
||||
await client.messageDelete({ seen: true })
|
||||
const tag = await server.db
|
||||
.select({ id: filetags.id })
|
||||
.from(filetags)
|
||||
.where(
|
||||
and(
|
||||
eq(filetags.tenant, tenant.id),
|
||||
eq(filetags.incomingDocumentType, "invoices")
|
||||
)
|
||||
)
|
||||
.limit(1)
|
||||
|
||||
} finally {
|
||||
lock.release()
|
||||
client.close()
|
||||
filetypeId = tag[0]?.id ?? null
|
||||
}
|
||||
|
||||
// -------------------------------------------
|
||||
// Mahnung
|
||||
// -------------------------------------------
|
||||
else if (message.subject?.match(/(Mahnung|Zahlungsaufforderung|Zahlungsverzug)/gi)) {
|
||||
|
||||
const tag = await server.db
|
||||
.select({ id: filetags.id })
|
||||
.from(filetags)
|
||||
.where(
|
||||
and(
|
||||
eq(filetags.tenant, tenant.id),
|
||||
eq(filetags.incomingDocumentType, "reminders")
|
||||
)
|
||||
)
|
||||
.limit(1)
|
||||
|
||||
filetypeId = tag[0]?.id ?? null
|
||||
}
|
||||
|
||||
// -------------------------------------------
|
||||
// Sonstige Dokumente → Deposit Folder
|
||||
// -------------------------------------------
|
||||
else {
|
||||
|
||||
const folder = await server.db
|
||||
.select({ id: folders.id })
|
||||
.from(folders)
|
||||
.where(
|
||||
and(
|
||||
eq(folders.tenant, tenant.id),
|
||||
eq(folders.function, "deposit")
|
||||
)
|
||||
)
|
||||
.limit(1)
|
||||
|
||||
folderId = folder[0]?.id ?? null
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
tenant: tenant.id,
|
||||
folder: folderId,
|
||||
filetype: filetypeId
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
run: async () => {
|
||||
await initDokuboxClient()
|
||||
await syncDokubox()
|
||||
console.log("Service: Dokubox sync finished")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// TENANT ERKENNEN + FOLDER/FILETYPES (DRIZZLE VERSION)
|
||||
// -------------------------------------------------------------
|
||||
const getMessageConfigDrizzle = async (
|
||||
server: FastifyInstance,
|
||||
message,
|
||||
tenantsList: any[]
|
||||
) => {
|
||||
|
||||
let possibleKeys: string[] = []
|
||||
|
||||
if (message.to) {
|
||||
message.to.forEach((item) =>
|
||||
possibleKeys.push(item.address.split("@")[0].toLowerCase())
|
||||
)
|
||||
}
|
||||
|
||||
if (message.cc) {
|
||||
message.cc.forEach((item) =>
|
||||
possibleKeys.push(item.address.split("@")[0].toLowerCase())
|
||||
)
|
||||
}
|
||||
|
||||
// -------------------------------------------
|
||||
// TENANT IDENTIFY
|
||||
// -------------------------------------------
|
||||
let tenant = tenantsList.find((t) => possibleKeys.includes(t.key))
|
||||
|
||||
if (!tenant && message.to?.length) {
|
||||
const address = message.to[0].address.toLowerCase()
|
||||
|
||||
tenant = tenantsList.find((t) =>
|
||||
(t.emailAddresses || []).map((m) => m.toLowerCase()).includes(address)
|
||||
)
|
||||
}
|
||||
|
||||
if (!tenant) return null
|
||||
|
||||
// -------------------------------------------
|
||||
// FOLDER + FILETYPE VIA SUBJECT
|
||||
// -------------------------------------------
|
||||
let folderId = null
|
||||
let filetypeId = null
|
||||
|
||||
// -------------------------------------------
|
||||
// Rechnung / Invoice
|
||||
// -------------------------------------------
|
||||
if (message.subject?.match(/(Rechnung|Beleg|Invoice|Quittung)/gi)) {
|
||||
|
||||
const folder = await server.db
|
||||
.select({ id: folders.id })
|
||||
.from(folders)
|
||||
.where(
|
||||
and(
|
||||
eq(folders.tenant, tenant.id),
|
||||
and(
|
||||
eq(folders.function, "incomingInvoices"),
|
||||
//@ts-ignore
|
||||
eq(folders.year, dayjs().format("YYYY"))
|
||||
)
|
||||
)
|
||||
)
|
||||
.limit(1)
|
||||
|
||||
folderId = folder[0]?.id ?? null
|
||||
|
||||
const tag = await server.db
|
||||
.select({ id: filetags.id })
|
||||
.from(filetags)
|
||||
.where(
|
||||
and(
|
||||
eq(filetags.tenant, tenant.id),
|
||||
eq(filetags.incomingDocumentType, "invoices")
|
||||
)
|
||||
)
|
||||
.limit(1)
|
||||
|
||||
filetypeId = tag[0]?.id ?? null
|
||||
}
|
||||
|
||||
// -------------------------------------------
|
||||
// Mahnung
|
||||
// -------------------------------------------
|
||||
else if (message.subject?.match(/(Mahnung|Zahlungsaufforderung|Zahlungsverzug)/gi)) {
|
||||
|
||||
const tag = await server.db
|
||||
.select({ id: filetags.id })
|
||||
.from(filetags)
|
||||
.where(
|
||||
and(
|
||||
eq(filetags.tenant, tenant.id),
|
||||
eq(filetags.incomingDocumentType, "reminders")
|
||||
)
|
||||
)
|
||||
.limit(1)
|
||||
|
||||
filetypeId = tag[0]?.id ?? null
|
||||
}
|
||||
|
||||
// -------------------------------------------
|
||||
// Sonstige Dokumente → Deposit Folder
|
||||
// -------------------------------------------
|
||||
else {
|
||||
|
||||
const folder = await server.db
|
||||
.select({ id: folders.id })
|
||||
.from(folders)
|
||||
.where(
|
||||
and(
|
||||
eq(folders.tenant, tenant.id),
|
||||
eq(folders.function, "deposit")
|
||||
)
|
||||
)
|
||||
.limit(1)
|
||||
|
||||
folderId = folder[0]?.id ?? null
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
tenant: tenant.id,
|
||||
folder: folderId,
|
||||
filetype: filetypeId
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// /plugins/services.ts
|
||||
import fp from "fastify-plugin";
|
||||
import { bankStatementService } from "../modules/cron/bankstatementsync.service";
|
||||
//import {initDokuboxClient, syncDokubox} from "../modules/cron/dokuboximport.service";
|
||||
import {syncDokuboxService} from "../modules/cron/dokuboximport.service";
|
||||
import { FastifyInstance } from "fastify";
|
||||
import {prepareIncomingInvoices} from "../modules/cron/prepareIncomingInvoices";
|
||||
|
||||
@@ -9,7 +9,7 @@ declare module "fastify" {
|
||||
interface FastifyInstance {
|
||||
services: {
|
||||
bankStatements: ReturnType<typeof bankStatementService>;
|
||||
//dokuboxSync: ReturnType<typeof syncDokubox>;
|
||||
dokuboxSync: ReturnType<typeof syncDokuboxService>;
|
||||
prepareIncomingInvoices: ReturnType<typeof prepareIncomingInvoices>;
|
||||
};
|
||||
}
|
||||
@@ -18,7 +18,7 @@ declare module "fastify" {
|
||||
export default fp(async function servicePlugin(server: FastifyInstance) {
|
||||
server.decorate("services", {
|
||||
bankStatements: bankStatementService(server),
|
||||
//dokuboxSync: syncDokubox(server),
|
||||
dokuboxSync: syncDokuboxService(server),
|
||||
prepareIncomingInvoices: prepareIncomingInvoices(server),
|
||||
});
|
||||
});
|
||||
|
||||
@@ -179,6 +179,11 @@ export default async function functionRoutes(server: FastifyInstance) {
|
||||
await server.services.prepareIncomingInvoices.run(req.user.tenant_id)
|
||||
})
|
||||
|
||||
server.post('/functions/services/syncdokubox', async (req, reply) => {
|
||||
|
||||
await server.services.dokuboxSync.run()
|
||||
})
|
||||
|
||||
|
||||
/*server.post('/print/zpl/preview', async (req, reply) => {
|
||||
const { zpl, widthMm = 50, heightMm = 30, dpmm = 8, asBase64 = false } = req.body as {zpl:string,widthMm:number,heightMm:number,dpmm:number,asBase64:string}
|
||||
|
||||
@@ -11,6 +11,7 @@ const router = useRouter()
|
||||
const route = useRoute()
|
||||
const modal = useModal()
|
||||
const toast = useToast()
|
||||
const { $api } = useNuxtApp()
|
||||
const files = useFiles()
|
||||
|
||||
// --- State ---
|
||||
@@ -222,11 +223,50 @@ defineShortcuts({
|
||||
'arrowdown': () => { if (selectedFileIndex.value < renderedFileList.value.length - 1) selectedFileIndex.value++ },
|
||||
'arrowup': () => { if (selectedFileIndex.value > 0) selectedFileIndex.value-- }
|
||||
})
|
||||
|
||||
const isSyncing = ref(false)
|
||||
|
||||
const syncdokubox = async () => {
|
||||
isSyncing.value = true
|
||||
try {
|
||||
await $api('/api/functions/services/syncdokubox', { method: 'POST' })
|
||||
|
||||
toast.add({
|
||||
title: 'Erfolg',
|
||||
description: 'Dokubox wurde synchronisiert.',
|
||||
icon: 'i-heroicons-check-circle',
|
||||
color: 'green'
|
||||
})
|
||||
|
||||
// Liste neu laden
|
||||
await setupPage()
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
toast.add({
|
||||
title: 'Fehler',
|
||||
description: 'Beim Synchronisieren der Dokubox ist ein Fehler aufgetreten.',
|
||||
icon: 'i-heroicons-exclamation-circle',
|
||||
color: 'red'
|
||||
})
|
||||
} finally {
|
||||
isSyncing.value = false
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UDashboardNavbar title="Dateien">
|
||||
<template #right>
|
||||
<UButton
|
||||
label="Dokubox Sync"
|
||||
icon="i-heroicons-sparkles"
|
||||
color="primary"
|
||||
variant="solid"
|
||||
:loading="isSyncing"
|
||||
@click="syncdokubox"
|
||||
class="mr-2"
|
||||
/>
|
||||
<UInput id="searchinput" v-model="searchString" icon="i-heroicons-magnifying-glass" placeholder="Suche..." class="w-64" />
|
||||
</template>
|
||||
</UDashboardNavbar>
|
||||
|
||||
Reference in New Issue
Block a user