Changes
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
import { FastifyInstance } from "fastify";
|
||||
import bcrypt from "bcrypt";
|
||||
import jwt from "jsonwebtoken";
|
||||
import { generateRandomPassword, hashPassword } from "../utils/password"
|
||||
import { sendMail } from "../utils/mailer"
|
||||
import { generateRandomPassword, hashPassword } from "../../utils/password"
|
||||
import { sendMail } from "../../utils/mailer"
|
||||
|
||||
export default async function authRoutes(server: FastifyInstance) {
|
||||
// Registrierung
|
||||
@@ -25,7 +25,7 @@ export default async function meRoutes(server: FastifyInstance) {
|
||||
// 2. Tenants laden (alle Tenants des Users)
|
||||
const { data: tenantLinks, error: tenantLinksError } = await server.supabase
|
||||
.from("auth_users")
|
||||
.select(`*, tenants!auth_tenant_users ( id, name, locked )`)
|
||||
.select(`*, tenants!auth_tenant_users ( id, name,short, locked, extraModules, businessInfo, numberRanges, dokuboxkey )`)
|
||||
.eq("id", authUser.user_id)
|
||||
.single();
|
||||
|
||||
67
src/routes/auth/user.ts
Normal file
67
src/routes/auth/user.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import { FastifyInstance } from "fastify";
|
||||
|
||||
export default async function userRoutes(server: FastifyInstance) {
|
||||
//TODO: PERMISSIONS Rückmeldung beschränken
|
||||
|
||||
server.get("/user/:id", async (req, reply) => {
|
||||
const authUser = req.user // kommt aus JWT (user_id + tenant_id)
|
||||
|
||||
const {id} = req.params
|
||||
|
||||
if (!authUser) {
|
||||
return reply.code(401).send({ error: "Unauthorized" })
|
||||
}
|
||||
|
||||
|
||||
// 1. User laden
|
||||
const { data: user, error: userError } = await server.supabase
|
||||
.from("auth_users")
|
||||
.select("id, email, created_at, must_change_password")
|
||||
.eq("id", id)
|
||||
.single()
|
||||
|
||||
if (userError || !user) {
|
||||
return reply.code(401).send({ error: "User not found" })
|
||||
}
|
||||
|
||||
// 2. Tenants laden (alle Tenants des Users)
|
||||
/*const { data: tenantLinks, error: tenantLinksError } = await server.supabase
|
||||
.from("auth_users")
|
||||
.select(`*, tenants!auth_tenant_users ( id, name, locked )`)
|
||||
.eq("id", authUser.user_id)
|
||||
.single();
|
||||
|
||||
if (tenantLinksError) {
|
||||
|
||||
console.log(tenantLinksError)
|
||||
|
||||
return reply.code(401).send({ error: "Tenant Error" })
|
||||
}
|
||||
|
||||
const tenants = tenantLinks?.tenants*/
|
||||
|
||||
// 3. Aktiven Tenant bestimmen
|
||||
const activeTenant = authUser.tenant_id /*|| tenants[0].id*/
|
||||
|
||||
// 4. Profil für den aktiven Tenant laden
|
||||
let profile = null
|
||||
if (activeTenant) {
|
||||
const { data: profileData } = await server.supabase
|
||||
.from("auth_profiles")
|
||||
.select("*")
|
||||
.eq("user_id", id)
|
||||
.eq("tenant_id", activeTenant)
|
||||
.single()
|
||||
|
||||
profile = profileData
|
||||
}
|
||||
|
||||
// 5. Permissions laden (über Funktion)
|
||||
|
||||
// 6. Response zurückgeben
|
||||
return {
|
||||
user,
|
||||
profile,
|
||||
}
|
||||
})
|
||||
}
|
||||
72
src/routes/banking.ts
Normal file
72
src/routes/banking.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import { FastifyInstance } from "fastify";
|
||||
import jwt from "jsonwebtoken";
|
||||
import {insertHistoryItem} from "../utils/history";
|
||||
|
||||
export default async function bankingRoutes(server: FastifyInstance) {
|
||||
|
||||
//Create Banking Statement
|
||||
server.post("/banking/statements", async (req, reply) => {
|
||||
if (!req.user) {
|
||||
return reply.code(401).send({ error: "Unauthorized" });
|
||||
}
|
||||
|
||||
const body = req.body as { data: string };
|
||||
console.log(body);
|
||||
|
||||
const {data,error} = await server.supabase.from("statementallocations").insert({
|
||||
...body.data,
|
||||
tenant: req.user.tenant_id,
|
||||
}).select()
|
||||
|
||||
await insertHistoryItem(server,{
|
||||
entity: "bankstatements",
|
||||
entityId: data.id,
|
||||
action: "created",
|
||||
created_by: req.user.user_id,
|
||||
tenant_id: req.user.tenant_id,
|
||||
oldVal: null,
|
||||
newVal: data,
|
||||
text: `Buchung erstellt`,
|
||||
});
|
||||
|
||||
if(data && !error){
|
||||
return reply.send(data)
|
||||
}
|
||||
});
|
||||
|
||||
//Delete Banking Statement
|
||||
server.delete("/banking/statements/:id", async (req, reply) => {
|
||||
if (!req.user) {
|
||||
return reply.code(401).send({ error: "Unauthorized" });
|
||||
}
|
||||
|
||||
const { id } = req.params as { id?: string }
|
||||
|
||||
const {data} = await server.supabase.from("statementallocations").select().eq("id",id).single()
|
||||
|
||||
const {error} = await server.supabase.from("statementallocations").delete().eq("id",id)
|
||||
|
||||
if(!error){
|
||||
|
||||
await insertHistoryItem(server,{
|
||||
entity: "bankstatements",
|
||||
entityId: id,
|
||||
action: "deleted",
|
||||
created_by: req.user.user_id,
|
||||
tenant_id: req.user.tenant_id,
|
||||
oldVal: data,
|
||||
newVal: null,
|
||||
text: `Buchung gelöscht`,
|
||||
});
|
||||
|
||||
return reply.send({success:true})
|
||||
} else {
|
||||
return reply.code(500).send({error:"Fehler beim löschen"})
|
||||
}
|
||||
|
||||
|
||||
})
|
||||
|
||||
|
||||
|
||||
}
|
||||
30
src/routes/emailAsUser.ts
Normal file
30
src/routes/emailAsUser.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { FastifyInstance } from "fastify";
|
||||
import {createInvoicePDF} from "../utils/pdf";
|
||||
import {useNextNumberRangeNumber} from "../utils/functions";
|
||||
import {sendMailAsUser} from "../utils/emailengine";
|
||||
import {subtle} from "node:crypto";
|
||||
|
||||
export default async function emailAsUserRoutes(server: FastifyInstance) {
|
||||
server.post("/emailasuser/send", async (req, reply) => {
|
||||
const body = req.body as {
|
||||
to: string
|
||||
cc?: string
|
||||
bcc?: string
|
||||
subject?: string
|
||||
text?: string
|
||||
html?: string
|
||||
attachments?: any,
|
||||
account: string
|
||||
}
|
||||
|
||||
try {
|
||||
reply.send(await sendMailAsUser(body.to,body.subject,body.html,body.text,body.account,body.cc,body.bcc,body.attachments))
|
||||
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
reply.code(500).send({ error: "Failed to send E-Mail as User" })
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
}
|
||||
105
src/routes/exports.ts
Normal file
105
src/routes/exports.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
import { FastifyInstance } from "fastify";
|
||||
import jwt from "jsonwebtoken";
|
||||
import {insertHistoryItem} from "../utils/history";
|
||||
import {buildExportZip} from "../utils/export/datev";
|
||||
import {s3} from "../utils/s3";
|
||||
import {GetObjectCommand, PutObjectCommand} from "@aws-sdk/client-s3"
|
||||
import {getSignedUrl} from "@aws-sdk/s3-request-presigner";
|
||||
import dayjs from "dayjs";
|
||||
import {randomUUID} from "node:crypto";
|
||||
|
||||
const createExport = async (server:FastifyInstance,req:any,startDate,endDate,beraternr,mandantennr) => {
|
||||
console.log(startDate,endDate,beraternr,mandantennr)
|
||||
|
||||
// 1) ZIP erzeugen
|
||||
const buffer = await buildExportZip(server,req.user.tenant_id, startDate, endDate, beraternr, mandantennr)
|
||||
console.log("ZIP created")
|
||||
|
||||
// 2) Dateiname & Key festlegen
|
||||
const fileKey = `${req.user.tenant_id}/exports/Export_${dayjs(startDate).format("YYYY-MM-DD")}_${dayjs(endDate).format("YYYY-MM-DD")}_${randomUUID()}.zip`
|
||||
console.log(fileKey)
|
||||
|
||||
// 3) In S3 hochladen
|
||||
await s3.send(
|
||||
new PutObjectCommand({
|
||||
Bucket: process.env.S3_BUCKET || "FEDEO",
|
||||
Key: fileKey,
|
||||
Body: buffer,
|
||||
ContentType: "application/zip",
|
||||
})
|
||||
)
|
||||
|
||||
// 4) Presigned URL erzeugen (24h gültig)
|
||||
const url = await getSignedUrl(
|
||||
s3,
|
||||
new GetObjectCommand({
|
||||
Bucket: process.env.S3_BUCKET || "FEDEO",
|
||||
Key: fileKey,
|
||||
}),
|
||||
{ expiresIn: 60 * 60 * 24 }
|
||||
)
|
||||
|
||||
console.log(url)
|
||||
|
||||
// 5) In Supabase-DB speichern
|
||||
const { data, error } = await server.supabase
|
||||
.from("exports")
|
||||
.insert([
|
||||
{
|
||||
tenant_id: req.user.tenant_id,
|
||||
start_date: startDate,
|
||||
end_date: endDate,
|
||||
valid_until: dayjs().add(24,"hours").toISOString(),
|
||||
file_path: fileKey,
|
||||
url: url,
|
||||
created_at: new Date().toISOString(),
|
||||
},
|
||||
])
|
||||
.select()
|
||||
.single()
|
||||
|
||||
console.log(data)
|
||||
console.log(error)
|
||||
}
|
||||
|
||||
|
||||
export default async function exportRoutes(server: FastifyInstance) {
|
||||
//Export DATEV
|
||||
server.post("/exports/datev", async (req, reply) => {
|
||||
const { start_date, end_date, beraternr, mandantennr } = req.body as {
|
||||
start_date: string
|
||||
end_date: string
|
||||
beraternr: string
|
||||
mandantennr: string
|
||||
}
|
||||
|
||||
|
||||
|
||||
reply.send({success:true})
|
||||
|
||||
setImmediate(async () => {
|
||||
try {
|
||||
await createExport(server,req,start_date,end_date,beraternr,mandantennr)
|
||||
console.log("Job done ✅")
|
||||
} catch (err) {
|
||||
console.error("Job failed ❌", err)
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
//List Exports Available for Download
|
||||
|
||||
server.get("/exports", async (req,reply) => {
|
||||
const {data,error} = await server.supabase.from("exports").select().eq("tenant_id",req.user.tenant_id)
|
||||
|
||||
console.log(data,error)
|
||||
reply.send(data)
|
||||
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -43,7 +43,6 @@ export default async function fileRoutes(server: FastifyInstance) {
|
||||
|
||||
if (!data.file) return reply.code(400).send({ error: "No file uploaded" })
|
||||
|
||||
console.log("ENDE")
|
||||
|
||||
const {data:createdFileData,error:createdFileError} = await server.supabase
|
||||
.from("files")
|
||||
@@ -207,7 +206,7 @@ export default async function fileRoutes(server: FastifyInstance) {
|
||||
}
|
||||
|
||||
try {
|
||||
const {data:supabaseFileEntries,error} = await server.supabase.from("files").select("*").in("id", ids)
|
||||
const {data:supabaseFileEntries,error} = await server.supabase.from("files").select("*, createddocument(*, customer(*))").in("id", ids)
|
||||
|
||||
|
||||
|
||||
|
||||
42
src/routes/functions.ts
Normal file
42
src/routes/functions.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { FastifyInstance } from "fastify";
|
||||
import {createInvoicePDF} from "../utils/pdf";
|
||||
import {useNextNumberRangeNumber} from "../utils/functions";
|
||||
|
||||
export default async function functionRoutes(server: FastifyInstance) {
|
||||
server.post("/functions/createinvoicepdf", async (req, reply) => {
|
||||
const body = req.body as {
|
||||
invoiceData: any
|
||||
backgroundPath?: string
|
||||
}
|
||||
|
||||
try {
|
||||
const pdf = await createInvoicePDF(
|
||||
server,
|
||||
"base64",
|
||||
body.invoiceData,
|
||||
body.backgroundPath
|
||||
)
|
||||
|
||||
reply.send(pdf) // Fastify wandelt automatisch in JSON
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
reply.code(500).send({ error: "Failed to create PDF" })
|
||||
}
|
||||
})
|
||||
|
||||
server.get(
|
||||
"/functions/usenextnumber/:numberrange",
|
||||
async (req, reply) => {
|
||||
const { numberrange } = req.params as { numberrange: string };
|
||||
const tenant = (req as any).user.tenant_id
|
||||
|
||||
try {
|
||||
const result = await useNextNumberRangeNumber(server,tenant, numberrange)
|
||||
reply.send(result) // JSON automatisch
|
||||
} catch (err) {
|
||||
req.log.error(err)
|
||||
reply.code(500).send({ error: "Failed to generate next number" })
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -60,5 +60,101 @@ export default async function routes(server: FastifyInstance) {
|
||||
return { token };
|
||||
});
|
||||
|
||||
server.get("/tenant/users", async (req, reply) => {
|
||||
const { tenant_id } = req.params as { tenant_id: string };
|
||||
const authUser = req.user // kommt aus JWT (user_id + tenant_id)
|
||||
|
||||
if (!authUser) {
|
||||
return reply.code(401).send({ error: "Unauthorized" })
|
||||
}
|
||||
|
||||
const { data, error } = await server.supabase
|
||||
.from("auth_tenant_users")
|
||||
.select(`
|
||||
user_id,
|
||||
auth_users!tenantusers_user_id_fkey ( id, email, created_at, auth_profiles(*))`)
|
||||
.eq("tenant_id", authUser.tenant_id);
|
||||
|
||||
if (error) {
|
||||
console.log(error);
|
||||
return reply.code(400).send({ error: error.message });
|
||||
}
|
||||
|
||||
let correctedData = data.map(i => {
|
||||
return {
|
||||
id: i.user_id,
|
||||
email: i.auth_users.email,
|
||||
profile: i.auth_users.auth_profiles.find(x => x.tenant_id === authUser.tenant_id),
|
||||
full_name: i.auth_users.auth_profiles.find(x => x.tenant_id === authUser.tenant_id)?.full_name,
|
||||
}
|
||||
})
|
||||
|
||||
return { tenant_id, users: correctedData };
|
||||
});
|
||||
|
||||
server.put("/tenant/numberrange/:numberrange", async (req, reply) => {
|
||||
if (!req.user) {
|
||||
return reply.code(401).send({ error: "Unauthorized" });
|
||||
}
|
||||
const { numberrange } = req.params as { numberrange?: string }
|
||||
|
||||
const body = req.body as { numberRange: object };
|
||||
console.log(body);
|
||||
|
||||
if(!body.numberRange) {
|
||||
return reply.code(400).send({ error: "numberRange required" });
|
||||
}
|
||||
|
||||
const {data:currentTenantData,error:numberRangesError} = await server.supabase.from("tenants").select().eq("id", req.user.tenant_id).single()
|
||||
|
||||
console.log(currentTenantData)
|
||||
console.log(numberRangesError)
|
||||
|
||||
|
||||
let numberRanges = {
|
||||
// @ts-ignore
|
||||
...currentTenantData.numberRanges
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
numberRanges[numberrange] = body.numberRange
|
||||
|
||||
|
||||
console.log(numberRanges)
|
||||
|
||||
const {data,error} = await server.supabase
|
||||
.from("tenants")
|
||||
.update({numberRanges: numberRanges})
|
||||
.eq('id',req.user.tenant_id)
|
||||
.select()
|
||||
|
||||
if(data && !error) {
|
||||
return reply.send(data)
|
||||
}
|
||||
});
|
||||
|
||||
server.put("/tenant/other/:id", async (req, reply) => {
|
||||
if (!req.user) {
|
||||
return reply.code(401).send({ error: "Unauthorized" });
|
||||
}
|
||||
const { id } = req.params as { id?: string }
|
||||
|
||||
const body = req.body as { data: object };
|
||||
console.log(body);
|
||||
|
||||
if(!body.data) {
|
||||
return reply.code(400).send({ error: "data required" });
|
||||
}
|
||||
|
||||
const {data:dataReturn,error} = await server.supabase
|
||||
.from("tenants")
|
||||
.update(body.data)
|
||||
.eq('id',req.user.tenant_id)
|
||||
.select()
|
||||
|
||||
if(dataReturn && !error) {
|
||||
return reply.send(dataReturn)
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user