Supabase Removals Backend
This commit is contained in:
@@ -110,17 +110,6 @@ export default async function functionRoutes(server: FastifyInstance) {
|
||||
const data = await server.db.select().from(citys).where(eq(citys.zip,zip))
|
||||
|
||||
|
||||
/*const { data, error } = await server.supabase
|
||||
.from('citys')
|
||||
.select()
|
||||
.eq('zip', zip)
|
||||
.maybeSingle()
|
||||
|
||||
if (error) {
|
||||
console.log(error)
|
||||
return reply.code(500).send({ error: 'Database error' })
|
||||
}*/
|
||||
|
||||
if (!data) {
|
||||
return reply.code(404).send({ error: 'ZIP not found' })
|
||||
}
|
||||
@@ -224,4 +213,4 @@ export default async function functionRoutes(server: FastifyInstance) {
|
||||
}
|
||||
})*/
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,9 @@ import { FastifyPluginAsync } from 'fastify'
|
||||
import { createConversation } from '../modules/helpdesk/helpdesk.conversation.service.js'
|
||||
import { addMessage } from '../modules/helpdesk/helpdesk.message.service.js'
|
||||
import { getOrCreateContact } from '../modules/helpdesk/helpdesk.contact.service.js'
|
||||
import {extractDomain, findCustomerOrContactByEmailOrDomain} from "../utils/helpers";
|
||||
import {useNextNumberRangeNumber} from "../utils/functions";
|
||||
import { findCustomerOrContactByEmailOrDomain } from "../utils/helpers";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { helpdesk_conversations, helpdesk_messages } from "../../db/schema";
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// 📧 Interne M2M-Route für eingehende E-Mails
|
||||
@@ -52,12 +53,12 @@ const helpdeskInboundEmailRoutes: FastifyPluginAsync = async (server) => {
|
||||
// 3️⃣ Konversation anhand In-Reply-To suchen
|
||||
let conversationId: string | null = null
|
||||
if (in_reply_to) {
|
||||
const { data: msg } = await server.supabase
|
||||
.from('helpdesk_messages')
|
||||
.select('conversation_id')
|
||||
.eq('external_message_id', in_reply_to)
|
||||
.maybeSingle()
|
||||
conversationId = msg?.conversation_id || null
|
||||
const msg = await server.db
|
||||
.select({ conversationId: helpdesk_messages.conversationId })
|
||||
.from(helpdesk_messages)
|
||||
.where(eq(helpdesk_messages.externalMessageId, in_reply_to))
|
||||
.limit(1)
|
||||
conversationId = msg[0]?.conversationId || null
|
||||
}
|
||||
|
||||
// 4️⃣ Neue Konversation anlegen falls keine existiert
|
||||
@@ -73,12 +74,12 @@ const helpdeskInboundEmailRoutes: FastifyPluginAsync = async (server) => {
|
||||
})
|
||||
conversationId = conversation.id
|
||||
} else {
|
||||
const { data } = await server.supabase
|
||||
.from('helpdesk_conversations')
|
||||
.select('*')
|
||||
.eq('id', conversationId)
|
||||
.single()
|
||||
conversation = data
|
||||
const rows = await server.db
|
||||
.select()
|
||||
.from(helpdesk_conversations)
|
||||
.where(eq(helpdesk_conversations.id, conversationId))
|
||||
.limit(1)
|
||||
conversation = rows[0]
|
||||
}
|
||||
|
||||
// 5️⃣ Nachricht speichern
|
||||
@@ -96,7 +97,7 @@ const helpdeskInboundEmailRoutes: FastifyPluginAsync = async (server) => {
|
||||
return res.status(201).send({
|
||||
success: true,
|
||||
conversation_id: conversationId,
|
||||
ticket_number: conversation.ticket_number,
|
||||
ticket_number: conversation?.ticket_number || conversation?.ticketNumber,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -3,70 +3,9 @@ import { FastifyPluginAsync } from 'fastify'
|
||||
import { createConversation } from '../modules/helpdesk/helpdesk.conversation.service.js'
|
||||
import { addMessage } from '../modules/helpdesk/helpdesk.message.service.js'
|
||||
import { getOrCreateContact } from '../modules/helpdesk/helpdesk.contact.service.js'
|
||||
|
||||
/**
|
||||
* Öffentliche Route zum Empfang eingehender Kontaktformular-Nachrichten.
|
||||
* Authentifizierung: über `public_token` aus helpdesk_channel_instances
|
||||
*/
|
||||
|
||||
function extractDomain(email) {
|
||||
if (!email) return null
|
||||
const parts = email.split("@")
|
||||
return parts.length === 2 ? parts[1].toLowerCase() : null
|
||||
}
|
||||
|
||||
async function findCustomerOrContactByEmailOrDomain(server,fromMail, tenantId) {
|
||||
const sender = fromMail
|
||||
const senderDomain = extractDomain(sender)
|
||||
if (!senderDomain) return null
|
||||
|
||||
|
||||
// 1️⃣ Direkter Match über contacts
|
||||
const { data: contactMatch } = await server.supabase
|
||||
.from("contacts")
|
||||
.select("id, customer")
|
||||
.eq("email", sender)
|
||||
.eq("tenant", tenantId)
|
||||
.maybeSingle()
|
||||
|
||||
if (contactMatch?.customer_id) return {
|
||||
customer: contactMatch.customer,
|
||||
contact: contactMatch.id
|
||||
}
|
||||
|
||||
// 2️⃣ Kunden laden, bei denen E-Mail oder Rechnungsmail passt
|
||||
const { data: customers, error } = await server.supabase
|
||||
.from("customers")
|
||||
.select("id, infoData")
|
||||
.eq("tenant", tenantId)
|
||||
|
||||
if (error) {
|
||||
console.error(`[Helpdesk] Fehler beim Laden der Kunden:`, error.message)
|
||||
return null
|
||||
}
|
||||
|
||||
// 3️⃣ Durch Kunden iterieren und prüfen
|
||||
for (const c of customers || []) {
|
||||
const info = c.infoData || {}
|
||||
const email = info.email?.toLowerCase()
|
||||
const invoiceEmail = info.invoiceEmail?.toLowerCase()
|
||||
|
||||
const emailDomain = extractDomain(email)
|
||||
const invoiceDomain = extractDomain(invoiceEmail)
|
||||
|
||||
// exakter Match oder Domain-Match
|
||||
if (
|
||||
sender === email ||
|
||||
sender === invoiceEmail ||
|
||||
senderDomain === emailDomain ||
|
||||
senderDomain === invoiceDomain
|
||||
) {
|
||||
return {customer: c.id, contact:null}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
import { findCustomerOrContactByEmailOrDomain } from "../utils/helpers";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { helpdesk_channel_instances } from "../../db/schema";
|
||||
|
||||
const helpdeskInboundRoutes: FastifyPluginAsync = async (server) => {
|
||||
// Öffentliche POST-Route
|
||||
@@ -85,17 +24,18 @@ const helpdeskInboundRoutes: FastifyPluginAsync = async (server) => {
|
||||
}
|
||||
|
||||
// 1️⃣ Kanalinstanz anhand des Tokens ermitteln
|
||||
const { data: channel, error: channelError } = await server.supabase
|
||||
.from('helpdesk_channel_instances')
|
||||
.select('*')
|
||||
.eq('public_token', public_token)
|
||||
.single()
|
||||
const channels = await server.db
|
||||
.select()
|
||||
.from(helpdesk_channel_instances)
|
||||
.where(eq(helpdesk_channel_instances.publicToken, public_token))
|
||||
.limit(1)
|
||||
const channel = channels[0]
|
||||
|
||||
if (channelError || !channel) {
|
||||
if (!channel) {
|
||||
return res.status(404).send({ error: 'Invalid channel token' })
|
||||
}
|
||||
|
||||
const tenant_id = channel.tenant_id
|
||||
const tenant_id = channel.tenantId
|
||||
const channel_instance_id = channel.id
|
||||
|
||||
// @ts-ignore
|
||||
|
||||
@@ -5,6 +5,13 @@ import { addMessage, getMessages } from '../modules/helpdesk/helpdesk.message.se
|
||||
import { getOrCreateContact } from '../modules/helpdesk/helpdesk.contact.service.js'
|
||||
import {decrypt, encrypt} from "../utils/crypt";
|
||||
import nodemailer from "nodemailer"
|
||||
import { eq } from "drizzle-orm";
|
||||
import {
|
||||
helpdesk_channel_instances,
|
||||
helpdesk_contacts,
|
||||
helpdesk_conversations,
|
||||
helpdesk_messages,
|
||||
} from "../../db/schema";
|
||||
|
||||
const helpdeskRoutes: FastifyPluginAsync = async (server) => {
|
||||
// 📩 1. Liste aller Konversationen
|
||||
@@ -58,15 +65,30 @@ const helpdeskRoutes: FastifyPluginAsync = async (server) => {
|
||||
const tenant_id = req.user?.tenant_id
|
||||
const {id: conversation_id} = req.params as {id: string}
|
||||
|
||||
const { data, error } = await server.supabase
|
||||
.from('helpdesk_conversations')
|
||||
.select('*, helpdesk_contacts(*)')
|
||||
.eq('tenant_id', tenant_id)
|
||||
.eq('id', conversation_id)
|
||||
.single()
|
||||
const rows = await server.db
|
||||
.select({
|
||||
conversation: helpdesk_conversations,
|
||||
contact: helpdesk_contacts
|
||||
})
|
||||
.from(helpdesk_conversations)
|
||||
.leftJoin(helpdesk_contacts, eq(helpdesk_contacts.id, helpdesk_conversations.contactId))
|
||||
.where(eq(helpdesk_conversations.id, conversation_id))
|
||||
|
||||
if (error) return res.status(404).send({ error: 'Conversation not found' })
|
||||
return res.send(data)
|
||||
const data = rows[0]
|
||||
if (!data || data.conversation.tenantId !== tenant_id) return res.status(404).send({ error: 'Conversation not found' })
|
||||
|
||||
return res.send({
|
||||
...data.conversation,
|
||||
channel_instance_id: data.conversation.channelInstanceId,
|
||||
contact_id: data.conversation.contactId,
|
||||
contact_person_id: data.conversation.contactPersonId,
|
||||
created_at: data.conversation.createdAt,
|
||||
customer_id: data.conversation.customerId,
|
||||
last_message_at: data.conversation.lastMessageAt,
|
||||
tenant_id: data.conversation.tenantId,
|
||||
ticket_number: data.conversation.ticketNumber,
|
||||
helpdesk_contacts: data.contact,
|
||||
})
|
||||
})
|
||||
|
||||
// 🔄 4. Konversation Status ändern
|
||||
@@ -181,36 +203,39 @@ const helpdeskRoutes: FastifyPluginAsync = async (server) => {
|
||||
safeConfig.smtp.pass = encrypt(safeConfig.smtp.pass)
|
||||
}
|
||||
|
||||
// Speichern in Supabase
|
||||
const { data, error } = await server.supabase
|
||||
.from("helpdesk_channel_instances")
|
||||
.insert({
|
||||
tenant_id,
|
||||
type_id,
|
||||
const inserted = await server.db
|
||||
.insert(helpdesk_channel_instances)
|
||||
.values({
|
||||
tenantId: tenant_id,
|
||||
typeId: type_id,
|
||||
name,
|
||||
config: safeConfig,
|
||||
is_active,
|
||||
isActive: is_active,
|
||||
})
|
||||
.select()
|
||||
.single()
|
||||
.returning()
|
||||
|
||||
if (error) throw error
|
||||
const data = inserted[0]
|
||||
if (!data) throw new Error("Konnte Channel nicht erstellen")
|
||||
const responseConfig: any = data.config
|
||||
|
||||
// sensible Felder aus Response entfernen
|
||||
if (data.config?.imap) {
|
||||
delete data.config.imap.host
|
||||
delete data.config.imap.user
|
||||
delete data.config.imap.pass
|
||||
if (responseConfig?.imap) {
|
||||
delete responseConfig.imap.host
|
||||
delete responseConfig.imap.user
|
||||
delete responseConfig.imap.pass
|
||||
}
|
||||
if (data.config?.smtp) {
|
||||
delete data.config.smtp.host
|
||||
delete data.config.smtp.user
|
||||
delete data.config.smtp.pass
|
||||
if (responseConfig?.smtp) {
|
||||
delete responseConfig.smtp.host
|
||||
delete responseConfig.smtp.user
|
||||
delete responseConfig.smtp.pass
|
||||
}
|
||||
|
||||
reply.send({
|
||||
message: "E-Mail-Channel erfolgreich erstellt",
|
||||
channel: data,
|
||||
channel: {
|
||||
...data,
|
||||
config: responseConfig
|
||||
},
|
||||
})
|
||||
} catch (err) {
|
||||
console.error("Fehler bei Channel-Erstellung:", err)
|
||||
@@ -234,29 +259,29 @@ const helpdeskRoutes: FastifyPluginAsync = async (server) => {
|
||||
const { text } = req.body as { text: string }
|
||||
|
||||
// 🔹 Konversation inkl. Channel + Kontakt laden
|
||||
const { data: conv, error: convErr } = await server.supabase
|
||||
.from("helpdesk_conversations")
|
||||
.select(`
|
||||
id,
|
||||
tenant_id,
|
||||
subject,
|
||||
channel_instance_id,
|
||||
helpdesk_contacts(email),
|
||||
helpdesk_channel_instances(config, name),
|
||||
ticket_number
|
||||
`)
|
||||
.eq("id", conversationId)
|
||||
.single()
|
||||
const rows = await server.db
|
||||
.select({
|
||||
conversation: helpdesk_conversations,
|
||||
contact: helpdesk_contacts,
|
||||
channel: helpdesk_channel_instances,
|
||||
})
|
||||
.from(helpdesk_conversations)
|
||||
.leftJoin(helpdesk_contacts, eq(helpdesk_contacts.id, helpdesk_conversations.contactId))
|
||||
.leftJoin(helpdesk_channel_instances, eq(helpdesk_channel_instances.id, helpdesk_conversations.channelInstanceId))
|
||||
.where(eq(helpdesk_conversations.id, conversationId))
|
||||
.limit(1)
|
||||
|
||||
const conv = rows[0]
|
||||
|
||||
console.log(conv)
|
||||
|
||||
if (convErr || !conv) {
|
||||
if (!conv) {
|
||||
reply.status(404).send({ error: "Konversation nicht gefunden" })
|
||||
return
|
||||
}
|
||||
|
||||
const contact = conv.helpdesk_contacts as unknown as {email: string}
|
||||
const channel = conv.helpdesk_channel_instances as unknown as {name: string}
|
||||
const contact = conv.contact as unknown as {email: string}
|
||||
const channel = conv.channel as unknown as {name: string, config: any}
|
||||
|
||||
console.log(contact)
|
||||
if (!contact?.email) {
|
||||
@@ -288,7 +313,7 @@ const helpdeskRoutes: FastifyPluginAsync = async (server) => {
|
||||
const mailOptions = {
|
||||
from: `"${channel?.name}" <${user}>`,
|
||||
to: contact.email,
|
||||
subject: `${conv.ticket_number} | ${conv.subject}` || `${conv.ticket_number} | Antwort vom FEDEO Helpdesk`,
|
||||
subject: `${conv.conversation.ticketNumber} | ${conv.conversation.subject}` || `${conv.conversation.ticketNumber} | Antwort vom FEDEO Helpdesk`,
|
||||
text,
|
||||
}
|
||||
|
||||
@@ -296,24 +321,22 @@ const helpdeskRoutes: FastifyPluginAsync = async (server) => {
|
||||
console.log(`[Helpdesk SMTP] Gesendet an ${contact.email}: ${info.messageId}`)
|
||||
|
||||
// 💾 Nachricht speichern
|
||||
const { error: insertErr } = await server.supabase
|
||||
.from("helpdesk_messages")
|
||||
.insert({
|
||||
tenant_id: conv.tenant_id,
|
||||
conversation_id: conversationId,
|
||||
await server.db
|
||||
.insert(helpdesk_messages)
|
||||
.values({
|
||||
tenantId: conv.conversation.tenantId,
|
||||
conversationId: conversationId,
|
||||
direction: "outgoing",
|
||||
payload: { type: "text", text },
|
||||
external_message_id: info.messageId,
|
||||
received_at: new Date().toISOString(),
|
||||
externalMessageId: info.messageId,
|
||||
receivedAt: new Date(),
|
||||
})
|
||||
|
||||
if (insertErr) throw insertErr
|
||||
|
||||
// 🔁 Konversation aktualisieren
|
||||
await server.supabase
|
||||
.from("helpdesk_conversations")
|
||||
.update({ last_message_at: new Date().toISOString() })
|
||||
.eq("id", conversationId)
|
||||
await server.db
|
||||
.update(helpdesk_conversations)
|
||||
.set({ lastMessageAt: new Date() })
|
||||
.where(eq(helpdesk_conversations.id, conversationId))
|
||||
|
||||
reply.send({
|
||||
message: "E-Mail erfolgreich gesendet",
|
||||
|
||||
@@ -1,12 +1,34 @@
|
||||
// src/routes/resources/history.ts
|
||||
import { FastifyInstance } from "fastify";
|
||||
import { and, asc, eq, inArray } from "drizzle-orm";
|
||||
import { authProfiles, historyitems } from "../../db/schema";
|
||||
|
||||
const columnMap: Record<string, string> = {
|
||||
const columnMap: Record<string, any> = {
|
||||
customers: historyitems.customer,
|
||||
vendors: historyitems.vendor,
|
||||
projects: historyitems.project,
|
||||
plants: historyitems.plant,
|
||||
contacts: historyitems.contact,
|
||||
tasks: historyitems.task,
|
||||
vehicles: historyitems.vehicle,
|
||||
events: historyitems.event,
|
||||
files: historyitems.file,
|
||||
products: historyitems.product,
|
||||
inventoryitems: historyitems.inventoryitem,
|
||||
inventoryitemgroups: historyitems.inventoryitemgroup,
|
||||
checks: historyitems.check,
|
||||
costcentres: historyitems.costcentre,
|
||||
ownaccounts: historyitems.ownaccount,
|
||||
documentboxes: historyitems.documentbox,
|
||||
hourrates: historyitems.hourrate,
|
||||
services: historyitems.service,
|
||||
};
|
||||
|
||||
const insertFieldMap: Record<string, string> = {
|
||||
customers: "customer",
|
||||
vendors: "vendor",
|
||||
projects: "project",
|
||||
plants: "plant",
|
||||
contracts: "contract",
|
||||
contacts: "contact",
|
||||
tasks: "task",
|
||||
vehicles: "vehicle",
|
||||
@@ -15,15 +37,18 @@ const columnMap: Record<string, string> = {
|
||||
products: "product",
|
||||
inventoryitems: "inventoryitem",
|
||||
inventoryitemgroups: "inventoryitemgroup",
|
||||
absencerequests: "absencerequest",
|
||||
checks: "check",
|
||||
costcentres: "costcentre",
|
||||
ownaccounts: "ownaccount",
|
||||
documentboxes: "documentbox",
|
||||
hourrates: "hourrate",
|
||||
services: "service",
|
||||
roles: "role",
|
||||
};
|
||||
}
|
||||
|
||||
const parseId = (value: string) => {
|
||||
if (/^\d+$/.test(value)) return Number(value)
|
||||
return value
|
||||
}
|
||||
|
||||
export default async function resourceHistoryRoutes(server: FastifyInstance) {
|
||||
server.get<{
|
||||
@@ -49,29 +74,36 @@ export default async function resourceHistoryRoutes(server: FastifyInstance) {
|
||||
return reply.code(400).send({ error: `History not supported for resource '${resource}'` });
|
||||
}
|
||||
|
||||
const { data, error } = await server.supabase
|
||||
.from("historyitems")
|
||||
.select("*")
|
||||
.eq(column, id)
|
||||
.order("created_at", { ascending: true });
|
||||
const data = await server.db
|
||||
.select()
|
||||
.from(historyitems)
|
||||
.where(eq(column, parseId(id)))
|
||||
.orderBy(asc(historyitems.createdAt));
|
||||
|
||||
if (error) {
|
||||
server.log.error(error);
|
||||
return reply.code(500).send({ error: "Failed to fetch history" });
|
||||
}
|
||||
const userIds = Array.from(
|
||||
new Set(data.map((item) => item.createdBy).filter(Boolean))
|
||||
) as string[]
|
||||
|
||||
const {data:users, error:usersError} = await server.supabase
|
||||
.from("auth_users")
|
||||
.select("*, auth_profiles(*), tenants!auth_tenant_users(*)")
|
||||
const profiles = userIds.length > 0
|
||||
? await server.db
|
||||
.select()
|
||||
.from(authProfiles)
|
||||
.where(and(
|
||||
eq(authProfiles.tenant_id, req.user?.tenant_id),
|
||||
inArray(authProfiles.user_id, userIds)
|
||||
))
|
||||
: []
|
||||
|
||||
const filteredUsers = (users ||[]).filter(i => i.tenants.find((t:any) => t.id === req.user?.tenant_id))
|
||||
const profileByUserId = new Map(
|
||||
profiles.map((profile) => [profile.user_id, profile])
|
||||
)
|
||||
|
||||
const dataCombined = data.map(historyitem => {
|
||||
return {
|
||||
...historyitem,
|
||||
created_by_profile: filteredUsers.find(i => i.id === historyitem.created_by) ? filteredUsers.find(i => i.id === historyitem.created_by).auth_profiles[0] : null
|
||||
}
|
||||
})
|
||||
const dataCombined = data.map((historyitem) => ({
|
||||
...historyitem,
|
||||
created_at: historyitem.createdAt,
|
||||
created_by: historyitem.createdBy,
|
||||
created_by_profile: historyitem.createdBy ? profileByUserId.get(historyitem.createdBy) || null : null,
|
||||
}))
|
||||
|
||||
|
||||
|
||||
@@ -128,29 +160,33 @@ export default async function resourceHistoryRoutes(server: FastifyInstance) {
|
||||
const userId = (req.user as any)?.user_id;
|
||||
|
||||
|
||||
const fkField = columnMap[resource];
|
||||
const fkField = insertFieldMap[resource];
|
||||
if (!fkField) {
|
||||
return reply.code(400).send({ error: `Unknown resource: ${resource}` });
|
||||
}
|
||||
|
||||
const { data, error } = await server.supabase
|
||||
.from("historyitems")
|
||||
.insert({
|
||||
const inserted = await server.db
|
||||
.insert(historyitems)
|
||||
.values({
|
||||
text,
|
||||
[fkField]: id,
|
||||
[fkField]: parseId(id),
|
||||
oldVal: old_val || null,
|
||||
newVal: new_val || null,
|
||||
config: config || null,
|
||||
tenant: (req.user as any)?.tenant_id,
|
||||
created_by: userId
|
||||
createdBy: userId
|
||||
})
|
||||
.select()
|
||||
.single();
|
||||
.returning()
|
||||
|
||||
if (error) {
|
||||
return reply.code(500).send({ error: error.message });
|
||||
const data = inserted[0]
|
||||
if (!data) {
|
||||
return reply.code(500).send({ error: "Failed to create history entry" });
|
||||
}
|
||||
|
||||
return reply.code(201).send(data);
|
||||
return reply.code(201).send({
|
||||
...data,
|
||||
created_at: data.createdAt,
|
||||
created_by: data.createdBy
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
// routes/notifications.routes.ts
|
||||
import { FastifyInstance } from 'fastify';
|
||||
import { NotificationService, UserDirectory } from '../modules/notification.service';
|
||||
import { eq } from "drizzle-orm";
|
||||
import { authUsers } from "../../db/schema";
|
||||
|
||||
// Beispiel: E-Mail aus eigener User-Tabelle laden
|
||||
const getUserDirectory: UserDirectory = async (server:FastifyInstance, userId, tenantId) => {
|
||||
const { data, error } = await server.supabase
|
||||
.from('auth_users')
|
||||
.select('email')
|
||||
.eq('id', userId)
|
||||
.maybeSingle();
|
||||
if (error || !data) return null;
|
||||
const rows = await server.db
|
||||
.select({ email: authUsers.email })
|
||||
.from(authUsers)
|
||||
.where(eq(authUsers.id, userId))
|
||||
.limit(1)
|
||||
const data = rows[0]
|
||||
if (!data) return null;
|
||||
return { email: data.email };
|
||||
};
|
||||
|
||||
export default async function notificationsRoutes(server: FastifyInstance) {
|
||||
// wichtig: server.supabase ist über app verfügbar
|
||||
|
||||
const svc = new NotificationService(server, getUserDirectory);
|
||||
|
||||
server.post('/notifications/trigger', async (req, reply) => {
|
||||
|
||||
@@ -35,7 +35,7 @@ export default async function resourceRoutesSpecial(server: FastifyInstance) {
|
||||
}
|
||||
|
||||
// ---------------------------------------
|
||||
// 📌 SELECT: wir ignorieren select string (wie Supabase)
|
||||
// 📌 SELECT: select-string wird in dieser Route bewusst ignoriert
|
||||
// Drizzle kann kein dynamisches Select aus String!
|
||||
// Wir geben IMMER alle Spalten zurück → kompatibel zum Frontend
|
||||
// ---------------------------------------
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { FastifyInstance } from 'fastify'
|
||||
import { StaffTimeEntryConnect } from '../../types/staff'
|
||||
import { asc, eq } from "drizzle-orm";
|
||||
import { stafftimenetryconnects } from "../../../db/schema";
|
||||
|
||||
export default async function staffTimeConnectRoutes(server: FastifyInstance) {
|
||||
|
||||
@@ -8,16 +10,21 @@ export default async function staffTimeConnectRoutes(server: FastifyInstance) {
|
||||
'/staff/time/:id/connects',
|
||||
async (req, reply) => {
|
||||
const { id } = req.params
|
||||
const { started_at, stopped_at, project_id, customer_id, task_id, ticket_id, notes } = req.body
|
||||
const { started_at, stopped_at, project_id, notes } = req.body
|
||||
const parsedProjectId = project_id ? Number(project_id) : null
|
||||
|
||||
const { data, error } = await server.supabase
|
||||
.from('staff_time_entry_connects')
|
||||
.insert([{ time_entry_id: id, started_at, stopped_at, project_id, customer_id, task_id, ticket_id, notes }])
|
||||
.select()
|
||||
.maybeSingle()
|
||||
const data = await server.db
|
||||
.insert(stafftimenetryconnects)
|
||||
.values({
|
||||
stafftimeentry: id,
|
||||
started_at: new Date(started_at),
|
||||
stopped_at: new Date(stopped_at),
|
||||
project_id: parsedProjectId,
|
||||
notes
|
||||
})
|
||||
.returning()
|
||||
|
||||
if (error) return reply.code(400).send({ error: error.message })
|
||||
return reply.send(data)
|
||||
return reply.send(data[0])
|
||||
}
|
||||
)
|
||||
|
||||
@@ -26,13 +33,12 @@ export default async function staffTimeConnectRoutes(server: FastifyInstance) {
|
||||
'/staff/time/:id/connects',
|
||||
async (req, reply) => {
|
||||
const { id } = req.params
|
||||
const { data, error } = await server.supabase
|
||||
.from('staff_time_entry_connects')
|
||||
.select('*')
|
||||
.eq('time_entry_id', id)
|
||||
.order('started_at', { ascending: true })
|
||||
const data = await server.db
|
||||
.select()
|
||||
.from(stafftimenetryconnects)
|
||||
.where(eq(stafftimenetryconnects.stafftimeentry, id))
|
||||
.orderBy(asc(stafftimenetryconnects.started_at))
|
||||
|
||||
if (error) return reply.code(400).send({ error: error.message })
|
||||
return reply.send(data)
|
||||
}
|
||||
)
|
||||
@@ -42,15 +48,20 @@ export default async function staffTimeConnectRoutes(server: FastifyInstance) {
|
||||
'/staff/time/connects/:connectId',
|
||||
async (req, reply) => {
|
||||
const { connectId } = req.params
|
||||
const { data, error } = await server.supabase
|
||||
.from('staff_time_entry_connects')
|
||||
.update({ ...req.body, updated_at: new Date().toISOString() })
|
||||
.eq('id', connectId)
|
||||
.select()
|
||||
.maybeSingle()
|
||||
const patchData = { ...req.body } as any
|
||||
if (patchData.started_at) patchData.started_at = new Date(patchData.started_at)
|
||||
if (patchData.stopped_at) patchData.stopped_at = new Date(patchData.stopped_at)
|
||||
if (patchData.project_id !== undefined) {
|
||||
patchData.project_id = patchData.project_id ? Number(patchData.project_id) : null
|
||||
}
|
||||
|
||||
if (error) return reply.code(400).send({ error: error.message })
|
||||
return reply.send(data)
|
||||
const data = await server.db
|
||||
.update(stafftimenetryconnects)
|
||||
.set({ ...patchData, updated_at: new Date() })
|
||||
.where(eq(stafftimenetryconnects.id, connectId))
|
||||
.returning()
|
||||
|
||||
return reply.send(data[0])
|
||||
}
|
||||
)
|
||||
|
||||
@@ -59,12 +70,10 @@ export default async function staffTimeConnectRoutes(server: FastifyInstance) {
|
||||
'/staff/time/connects/:connectId',
|
||||
async (req, reply) => {
|
||||
const { connectId } = req.params
|
||||
const { error } = await server.supabase
|
||||
.from('staff_time_entry_connects')
|
||||
.delete()
|
||||
.eq('id', connectId)
|
||||
await server.db
|
||||
.delete(stafftimenetryconnects)
|
||||
.where(eq(stafftimenetryconnects.id, connectId))
|
||||
|
||||
if (error) return reply.code(400).send({ error: error.message })
|
||||
return reply.send({ success: true })
|
||||
}
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user