From aeaba648655141c91221363bc5dc6a3e418d6d5e Mon Sep 17 00:00:00 2001 From: Florian Federspiel Date: Fri, 29 Aug 2025 14:44:56 +0000 Subject: [PATCH 001/149] Initial commit --- README.md | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..5cd6a60 --- /dev/null +++ b/README.md @@ -0,0 +1,93 @@ +# Backend + + + +## Getting started + +To make it easy for you to get started with GitLab, here's a list of recommended next steps. + +Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)! + +## Add your files + +- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files +- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command: + +``` +cd existing_repo +git remote add origin https://git.federspiel.software/fedeo/backend.git +git branch -M main +git push -uf origin main +``` + +## Integrate with your tools + +- [ ] [Set up project integrations](https://git.federspiel.software/fedeo/backend/-/settings/integrations) + +## Collaborate with your team + +- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/) +- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html) +- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically) +- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/) +- [ ] [Set auto-merge](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html) + +## Test and Deploy + +Use the built-in continuous integration in GitLab. + +- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html) +- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing (SAST)](https://docs.gitlab.com/ee/user/application_security/sast/) +- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html) +- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/) +- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html) + +*** + +# Editing this README + +When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thanks to [makeareadme.com](https://www.makeareadme.com/) for this template. + +## Suggestions for a good README + +Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information. + +## Name +Choose a self-explaining name for your project. + +## Description +Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors. + +## Badges +On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge. + +## Visuals +Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method. + +## Installation +Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection. + +## Usage +Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README. + +## Support +Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc. + +## Roadmap +If you have ideas for releases in the future, it is a good idea to list them in the README. + +## Contributing +State if you are open to contributions and what your requirements are for accepting them. + +For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self. + +You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser. + +## Authors and acknowledgment +Show your appreciation to those who have contributed to the project. + +## License +For open source projects, say how it is licensed. + +## Project status +If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers. From 97a095b4229d6805b45814d126eed47aaedf2794 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sun, 31 Aug 2025 18:29:29 +0200 Subject: [PATCH 002/149] Changes --- TODO.md | 32 ++ src/index.ts | 65 ++++ src/plugins/auth.ts | 54 +++ src/plugins/cors.ts | 16 + src/plugins/supabase.ts | 19 + src/plugins/swagger.ts | 29 ++ src/plugins/tenant.ts | 44 +++ src/routes/admin.ts | 94 +++++ src/routes/auth-authenticated.ts | 76 ++++ src/routes/auth.ts | 221 ++++++++++++ src/routes/health.ts | 14 + src/routes/history.ts | 161 +++++++++ src/routes/me.ts | 80 +++++ src/routes/profiles.ts | 57 +++ src/routes/resources.ts | 580 +++++++++++++++++++++++++++++++ src/routes/tenant.ts | 64 ++++ src/utils/diff.ts | 103 ++++++ src/utils/diffTranslations.ts | 165 +++++++++ src/utils/history.ts | 69 ++++ src/utils/mailer.ts | 32 ++ src/utils/password.ts | 15 + 21 files changed, 1990 insertions(+) create mode 100644 TODO.md create mode 100644 src/index.ts create mode 100644 src/plugins/auth.ts create mode 100644 src/plugins/cors.ts create mode 100644 src/plugins/supabase.ts create mode 100644 src/plugins/swagger.ts create mode 100644 src/plugins/tenant.ts create mode 100644 src/routes/admin.ts create mode 100644 src/routes/auth-authenticated.ts create mode 100644 src/routes/auth.ts create mode 100644 src/routes/health.ts create mode 100644 src/routes/history.ts create mode 100644 src/routes/me.ts create mode 100644 src/routes/profiles.ts create mode 100644 src/routes/resources.ts create mode 100644 src/routes/tenant.ts create mode 100644 src/utils/diff.ts create mode 100644 src/utils/diffTranslations.ts create mode 100644 src/utils/history.ts create mode 100644 src/utils/mailer.ts create mode 100644 src/utils/password.ts diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..da98877 --- /dev/null +++ b/TODO.md @@ -0,0 +1,32 @@ +# Projekt To-Do Liste + +## ✅ Erledigt +- JWT-basierte Authentifizierung mit Cookie +- Prefix für Auth-Tabellen (`auth_users`, `auth_roles`, …) +- `/me` liefert User + Rechte (via `auth_get_user_permissions`) +- Basis-Seed für Standardrollen + Rechte eingespielt +- Auth Middleware im Frontend korrigiert (Login-Redirects) +- Nuxt API Plugin unterstützt JWT im Header +- Login-Seite an Nuxt UI Pro (v2) anpassen +- `usePermission()` Composable im Frontend vorbereitet + +--- + +## 🔄 Offene Punkte + +### Backend +- [ ] `/me` erweitern: Rollen + deren Rechte strukturiert zurückgeben (`{ role: "manager", permissions: [...] }`) +- [ ] Loading Flag im Auth-Flow berücksichtigen (damit `me` nicht doppelt feuert) +- [ ] Gemeinsames Schema für Entities (Backend stellt per Endpoint bereit) +- [ ] Soft Delete vereinheitlichen (`archived = true` statt DELETE) +- [ ] Swagger-Doku verbessern (Schemas, Beispiele) + +### Frontend +- [ ] Loading Flag in Middleware/Store einbauen +- [ ] Einheitliches Laden des Schemas beim Start +- [ ] Pinia-Store für Auth/User/Tenant konsolidieren +- [ ] Composable `usePermission(key)` implementieren, um Rechte einfach im Template zu prüfen +- [ ] Entity-Seiten schrittweise auf API-Routen umstellen +- [ ] Page Guards für Routen einbauen (z. B. `/projects/create` nur bei `projects-create`) + +--- \ No newline at end of file diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..c95ded7 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,65 @@ +import Fastify from "fastify"; +import swaggerPlugin from "./plugins/swagger" +import supabasePlugin from "./plugins/supabase"; +import healthRoutes from "./routes/health"; +import meRoutes from "./routes/me"; +import tenantRoutes from "./routes/tenant"; +import tenantPlugin from "./plugins/tenant"; +import authRoutes from "./routes/auth"; +import authRoutesAuthenticated from "./routes/auth-authenticated"; +import authPlugin from "./plugins/auth"; +import adminRoutes from "./routes/admin"; +import corsPlugin from "./plugins/cors"; +import resourceRoutes from "./routes/resources"; +import fastifyCookie from "@fastify/cookie"; +import historyRoutes from "./routes/history"; + +import {sendMail} from "./utils/mailer"; + +async function main() { + const app = Fastify({ logger: true }); + + /*app.addHook("onRequest", (req, reply, done) => { + console.log("Incoming:", req.method, req.url, "Headers:", req.headers) + done() + })*/ + + // Plugins Global verfügbar + await app.register(swaggerPlugin); + await app.register(corsPlugin); + await app.register(supabasePlugin); + await app.register(tenantPlugin); + app.register(fastifyCookie, { + secret: process.env.COOKIE_SECRET || "supersecret", // optional, für signierte Cookies + }) + // Öffentliche Routes + await app.register(authRoutes); + await app.register(healthRoutes); + + + + //Geschützte Routes + + await app.register(async (subApp) => { + await subApp.register(authPlugin); + await subApp.register(authRoutesAuthenticated); + await subApp.register(meRoutes); + await subApp.register(tenantRoutes); + await subApp.register(adminRoutes); + await subApp.register(resourceRoutes); + await subApp.register(historyRoutes); + + },{prefix: "/api"}) + + + // Start + try { + await app.listen({ port: 3100 }); + console.log("🚀 Server läuft auf http://localhost:3100"); + } catch (err) { + app.log.error(err); + process.exit(1); + } +} + +main(); \ No newline at end of file diff --git a/src/plugins/auth.ts b/src/plugins/auth.ts new file mode 100644 index 0000000..68cd018 --- /dev/null +++ b/src/plugins/auth.ts @@ -0,0 +1,54 @@ +import { FastifyInstance } from "fastify"; +import fp from "fastify-plugin"; +import jwt from "jsonwebtoken"; + +export default fp(async (server: FastifyInstance) => { + server.addHook("preHandler", async (req, reply) => { + + try { + // 1) Token aus Cookie lesen + const cookieToken = req.cookies?.token + + // 2) Token aus Header lesen (falls Cookie nicht da ist) + const authHeader = req.headers.authorization + const headerToken = authHeader?.startsWith("Bearer ") + ? authHeader.slice(7) + : null + + let token = null + + if(headerToken !== null && headerToken.length > 10){ + token = headerToken + } else if(cookieToken ){ + token = cookieToken + } + + if (!token) { + return // keine Exception → Route darf z. B. public sein + } + + + const payload = jwt.verify(token, process.env.JWT_SECRET!) as { + user_id: string; + email: string; + tenant_id?: string; + role?: string; + }; + + (req as any).user = payload; + } catch (err) { + return reply.code(401).send({ error: "Invalid or expired token" }); + } + }); +}); + +declare module "fastify" { + interface FastifyRequest { + user?: { + user_id: string; + email: string; + tenant_id?: string; + role?: string; + }; + } +} \ No newline at end of file diff --git a/src/plugins/cors.ts b/src/plugins/cors.ts new file mode 100644 index 0000000..6d806bd --- /dev/null +++ b/src/plugins/cors.ts @@ -0,0 +1,16 @@ +import { FastifyInstance } from "fastify"; +import fp from "fastify-plugin"; +import cors from "@fastify/cors"; + +export default fp(async (server: FastifyInstance) => { + await server.register(cors, { + origin: [ + "http://localhost:3000", // dein Nuxt-Frontend + "http://127.0.0.1:3000", // dein Nuxt-Frontend + ], + methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"], + allowedHeaders: ["Content-Type", "Authorization", "Context"], + exposedHeaders: ["Authorization"], // optional, falls du ihn auch auslesen willst + credentials: true, // wichtig, falls du Cookies nutzt + }); +}); \ No newline at end of file diff --git a/src/plugins/supabase.ts b/src/plugins/supabase.ts new file mode 100644 index 0000000..66172df --- /dev/null +++ b/src/plugins/supabase.ts @@ -0,0 +1,19 @@ +import { FastifyInstance } from "fastify"; +import fp from "fastify-plugin"; +import { createClient, SupabaseClient } from "@supabase/supabase-js"; + +export default fp(async (server: FastifyInstance) => { + const supabaseUrl = process.env.SUPABASE_URL || "https://uwppvcxflrcsibuzsbil.supabase.co"; + const supabaseServiceKey = process.env.SUPABASE_SERVICE_ROLE_KEY || "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InV3cHB2Y3hmbHJjc2lidXpzYmlsIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTcwMDkzODE5NCwiZXhwIjoyMDE2NTE0MTk0fQ.6hOkD1J8XBkVJUm-swv0ngLQ74xrEYr28EEbo0rUrts"; + + const supabase: SupabaseClient = createClient(supabaseUrl, supabaseServiceKey); + + // Fastify um supabase erweitern + server.decorate("supabase", supabase); +}); + +declare module "fastify" { + interface FastifyInstance { + supabase: SupabaseClient; + } +} \ No newline at end of file diff --git a/src/plugins/swagger.ts b/src/plugins/swagger.ts new file mode 100644 index 0000000..7bd32d9 --- /dev/null +++ b/src/plugins/swagger.ts @@ -0,0 +1,29 @@ +import { FastifyInstance } from "fastify"; +import fp from "fastify-plugin"; +import swagger from "@fastify/swagger"; +import swaggerUi from "@fastify/swagger-ui"; + +export default fp(async (server: FastifyInstance) => { + await server.register(swagger, { + mode: "dynamic", // wichtig: generiert echtes OpenAPI JSON + openapi: { + info: { + title: "Multi-Tenant API", + description: "API Dokumentation für dein Backend", + version: "1.0.0", + }, + servers: [{ url: "http://localhost:3000" }], + }, + }); + + await server.register(swaggerUi, { + routePrefix: "/docs", // UI erreichbar unter http://localhost:3000/docs + swagger: { + info: { + title: "Multi-Tenant API", + version: "1.0.0", + }, + }, + exposeRoute: true, + }); +}); \ No newline at end of file diff --git a/src/plugins/tenant.ts b/src/plugins/tenant.ts new file mode 100644 index 0000000..d77fab2 --- /dev/null +++ b/src/plugins/tenant.ts @@ -0,0 +1,44 @@ +import { FastifyInstance, FastifyRequest } from "fastify"; +import fp from "fastify-plugin"; + +export default fp(async (server: FastifyInstance) => { + server.addHook("preHandler", async (req, reply) => { + const host = req.headers.host?.split(":")[0]; // Domain ohne Port + if (!host) { + reply.code(400).send({ error: "Missing host header" }); + return; + } + + console.log(host) + + // Tenant aus DB laden + const { data: tenant } = await server.supabase + .from("tenants") + .select("*") + .eq("portalDomain", host) + .single(); + + + if(!tenant) { + // Multi Tenant Mode + (req as any).tenant = null; + }else { + // Tenant ins Request-Objekt hängen + (req as any).tenant = tenant; + } + + }); +}); + +// Typ-Erweiterung +declare module "fastify" { + interface FastifyRequest { + tenant?: { + id: string; + name: string; + domain?: string; + subdomain?: string; + settings?: Record; + }; + } +} \ No newline at end of file diff --git a/src/routes/admin.ts b/src/routes/admin.ts new file mode 100644 index 0000000..ebf3e49 --- /dev/null +++ b/src/routes/admin.ts @@ -0,0 +1,94 @@ +import { FastifyInstance } from "fastify"; + +export default async function adminRoutes(server: FastifyInstance) { + server.post("/admin/add-user-to-tenant", async (req, reply) => { + const body = req.body as { + user_id: string; + tenant_id: string; + role?: string; + mode?: "single" | "multi"; + }; + + if (!body.user_id || !body.tenant_id) { + return reply.code(400).send({ error: "user_id and tenant_id required" }); + } + + // Default: "multi" + const mode = body.mode ?? "multi"; + + if (mode === "single") { + // Erst alle alten Verknüpfungen löschen + await server.supabase + .from("auth_tenant_users") + .delete() + .eq("user_id", body.user_id); + } + + const { error } = await server.supabase + .from("auth_tenant_users") + .insert({ + tenant_id: body.tenant_id, + user_id: body.user_id, + role: body.role ?? "member", + }); + + if (error) { + return reply.code(400).send({ error: error.message }); + } + + // Neuen Eintrag setzen + + + return { success: true, mode }; + }); + + /** + * Alle Tenants eines Users abfragen + */ + server.get("/admin/user-tenants/:user_id", async (req, reply) => { + const { user_id } = req.params as { user_id: string }; + + if (!user_id) { + return reply.code(400).send({ error: "user_id required" }); + } + + const {data:user, error: userError} = await server.supabase.from("auth_users").select("*,tenants(*)").eq("id", user_id).single(); + + console.log(userError) + console.log(user) + + if(!user) { + return reply.code(400).send({ error: "faulty user_id presented" }); + } else { + return { user_id, tenants: user.tenants }; + } + + }); + + /** + * Alle User eines Tenants abfragen + * TODO: Aktuell nur Multi Tenant + */ + /*server.get("/admin/tenant-users/:tenant_id", async (req, reply) => { + const { tenant_id } = req.params as { tenant_id: string }; + + if (!tenant_id) { + return reply.code(400).send({ error: "tenant_id required" }); + } + + const { data, error } = await server.supabase + .from("auth_tenant_users") + .select(` + user_id, + role, + users ( id, email, created_at ) + `) + .eq("tenant_id", tenant_id); + + if (error) { + return reply.code(400).send({ error: error.message }); + } + + return { tenant_id, users: data }; + });*/ +} \ No newline at end of file diff --git a/src/routes/auth-authenticated.ts b/src/routes/auth-authenticated.ts new file mode 100644 index 0000000..4cd0d0b --- /dev/null +++ b/src/routes/auth-authenticated.ts @@ -0,0 +1,76 @@ +import { FastifyInstance } from "fastify"; +import bcrypt from "bcrypt"; +import jwt from "jsonwebtoken"; +import { generateRandomPassword, hashPassword } from "../utils/password" +import { sendMail } from "../utils/mailer" + +export default async function authRoutesAuthenticated(server: FastifyInstance) { + server.post("/auth/password/change", { + schema: { + tags: ["Auth"], + summary: "Reset Password after forced change", + body: { + type: "object", + required: ["old_password", "new_password"], + properties: { + old_password: { type: "string" }, + new_password: { type: "string" }, + }, + }, + response: { + 200: { + type: "object", + properties: { + success: { type: "boolean" }, + }, + }, + }, + }, + }, async (req, reply) => { + const { old_password, new_password } = req.body as { old_password: string; new_password: string }; + + console.log(req.user) + + const user_id = req.user?.user_id; // kommt aus JWT Middleware + if (!user_id) { + return reply.code(401).send({ error: "Unauthorized" }); + } + + // Nutzer laden + const { data: user, error } = await server.supabase + .from("auth_users") + .select("id, password_hash, must_change_password") + .eq("id", user_id) + .single(); + + if (error || !user) { + return reply.code(404).send({ error: "User not found" }); + } + + // Altes Passwort prüfen + const valid = await bcrypt.compare(old_password, user.password_hash); + if (!valid) { + return reply.code(401).send({ error: "Old password incorrect" }); + } + + // Neues Passwort hashen + const newHash = await bcrypt.hash(new_password, 10); + + // Speichern + Flag zurücksetzen + const { error: updateError } = await server.supabase + .from("auth_users") + .update({ + password_hash: newHash, + must_change_password: false, + updated_at: new Date().toISOString(), + }) + .eq("id", user_id); + + if (updateError) { + console.log(updateError); + return reply.code(500).send({ error: "Password update failed" }); + } + + return { success: true }; + }); +} \ No newline at end of file diff --git a/src/routes/auth.ts b/src/routes/auth.ts new file mode 100644 index 0000000..3e2b601 --- /dev/null +++ b/src/routes/auth.ts @@ -0,0 +1,221 @@ +import { FastifyInstance } from "fastify"; +import bcrypt from "bcrypt"; +import jwt from "jsonwebtoken"; +import { generateRandomPassword, hashPassword } from "../utils/password" +import { sendMail } from "../utils/mailer" + +export default async function authRoutes(server: FastifyInstance) { + // Registrierung + server.post("/auth/register",{ + schema: { + tags: ["Auth"], + summary: "Register User", + body: { + type: "object", + required: ["email", "password"], + properties: { + email: { type: "string", format: "email" }, + password: { type: "string" }, + }, + }, + response: { + 200: { + type: "object", + properties: { + user: { type: "object" }, + }, + }, + }, + }, + }, async (req, reply) => { + const body = req.body as { email: string; password: string }; + + if (!body.email || !body.password) { + return reply.code(400).send({ error: "Email and password required" }); + } + + // Passwort hashen + const passwordHash = await bcrypt.hash(body.password, 10); + + // User speichern + const { data, error } = await server.supabase + .from("auth_users") + .insert({ email: body.email, password_hash: passwordHash }) + .select("id, email") + .single(); + + if (error) { + return reply.code(400).send({ error: error.message }); + } + + return { user: data }; + }); + + // Login + server.post("/auth/login",{ + schema: { + tags: ["Auth"], + summary: "Login User", + body: { + type: "object", + required: ["email", "password"], + properties: { + email: { type: "string", format: "email" }, + password: { type: "string" }, + }, + }, + response: { + 200: { + type: "object", + properties: { + token: { type: "string" }, + }, + }, + }, + }, + }, async (req, reply) => { + const body = req.body as { email: string; password: string }; + + if (!body.email || !body.password) { + return reply.code(400).send({ error: "Email and password required" }); + } + + console.log(req.tenant) + + /** + * Wenn das Tenant Objekt verfügbar ist, befindet sich das Backend im Single Tenant Modus. + * Es werden nur Benutzer zugelassen, welche auschließlich diesem Tenant angehören. + * Das zeigt sich über das im User gesetzte Tenant Feld + * + * */ + let user = null + let error = null + if(req.tenant) { + // User finden + const { data, error } = await server.supabase + .from("auth_users") + .select("*, tenants!auth_tenant_users(*)") + .eq("email", body.email) + + console.log(data) + console.log(error) + + // @ts-ignore + user = (data || []).find(i => i.tenants.find(x => x.id === req.tenant.id)) + console.log(user) + if(error) { + return reply.code(500).send({ error: "Internal Server Error" }); + } + } else { + // User finden + const { data, error } = await server.supabase + .from("auth_users") + .select("*") + .eq("email", body.email) + .single(); + user = data + if(error) { + console.log(error); + return reply.code(500).send({ error: "Internal Server Error" }); + } + } + + if(!user) { + return reply.code(401).send({ error: "Invalid credentials" }); + } else { + + console.log(user); + console.log(body) + + const valid = await bcrypt.compare(body.password, user.password_hash); + if (!valid) { + return reply.code(401).send({ error: "Invalid credentials" }); + } else { + const token = jwt.sign( + { user_id: user.id, email: user.email, tenant_id: req.tenant ? req.tenant.id : null }, + process.env.JWT_SECRET!, + { expiresIn: "3h" } + ); + + reply.setCookie("token", token, { + path: "/", + httpOnly: true, + sameSite: process.env.NODE_ENV === "production" ? "none" : "lax", + secure: process.env.NODE_ENV === "production", // lokal: false, prod: true + maxAge: 60 * 60 * 3, // 3 Stunden + }) + + return { token }; + } + } + }); + + server.post("/auth/logout", { + schema: { + tags: ["Auth"], + summary: "Logout User (löscht Cookie)" + }, + }, async (req, reply) => { + reply.clearCookie("token", { + path: "/", + httpOnly: true, + secure: process.env.NODE_ENV === "production", + sameSite: "lax", + }) + + return { success: true } + }) + + server.post("/auth/password/reset", { + schema: { + tags: ["Auth"], + summary: "Reset Password", + body: { + type: "object", + required: ["email"], + properties: { + email: { type: "string", format: "email" } + } + } + } + }, async (req, reply) => { + const { email } = req.body as { email: string } + + // User finden + const { data: user, error } = await server.supabase + .from("auth_users") + .select("id, email") + .eq("email", email) + .single() + + if (error || !user) { + return reply.code(404).send({ error: "User not found" }) + } + + // Neues Passwort generieren + const plainPassword = generateRandomPassword() + const passwordHash = await hashPassword(plainPassword) + + // In DB updaten + const { error: updateError } = await server.supabase + .from("auth_users") + .update({ password_hash: passwordHash, must_change_password: true }) + .eq("id", user.id) + + if (updateError) { + return reply.code(500).send({ error: "Could not update password" }) + } + + // Mail verschicken + await sendMail( + user.email, + "FEDEO | Dein neues Passwort", + `

Hallo,

+

dein Passwort wurde zurückgesetzt.

+

Neues Passwort: ${plainPassword}

+

Bitte ändere es nach dem Login umgehend.

` + ) + + return { success: true } + }) +} \ No newline at end of file diff --git a/src/routes/health.ts b/src/routes/health.ts new file mode 100644 index 0000000..a1d8e19 --- /dev/null +++ b/src/routes/health.ts @@ -0,0 +1,14 @@ +import { FastifyInstance } from "fastify"; + +export default async function routes(server: FastifyInstance) { + server.get("/ping", async () => { + // Testquery gegen DB + const { data, error } = await server.supabase.from("tenants").select("id").limit(1); + + return { + status: "ok", + db: error ? "not connected" : "connected", + tenant_count: data?.length ?? 0 + }; + }); +} \ No newline at end of file diff --git a/src/routes/history.ts b/src/routes/history.ts new file mode 100644 index 0000000..e6d396d --- /dev/null +++ b/src/routes/history.ts @@ -0,0 +1,161 @@ +// src/routes/resources/history.ts +import { FastifyInstance } from "fastify"; + +const columnMap: Record = { + customers: "customer", + vendors: "vendor", + projects: "project", + plants: "plant", + contracts: "contract", + contacts: "contact", + tasks: "task", + vehicles: "vehicle", + events: "event", + files: "file", + products: "product", + inventoryitems: "inventoryitem", + inventoryitemgroups: "inventoryitemgroup", + absencerequests: "absencerequest", + checks: "check", + costcentres: "costcentre", + ownaccounts: "ownaccount", + documentboxes: "documentbox", + hourrates: "hourrate", + services: "service", + roles: "role", +}; + +export default async function resourceHistoryRoutes(server: FastifyInstance) { + server.get<{ + Params: { resource: string; id: string } + }>("/resource/:resource/:id/history", { + schema: { + tags: ["History"], + summary: "Get history entries for a resource", + params: { + type: "object", + required: ["resource", "id"], + properties: { + resource: { type: "string" }, + id: { type: "string" }, + }, + }, + }, + }, async (req, reply) => { + const { resource, id } = req.params; + + const column = columnMap[resource]; + if (!column) { + 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 }); + + if (error) { + server.log.error(error); + return reply.code(500).send({ error: "Failed to fetch history" }); + } + + const {data:users, error:usersError} = await server.supabase + .from("auth_users") + .select("*, auth_profiles(*), tenants!auth_tenant_users(*)") + + console.log(users) + console.log(usersError) + + const filteredUsers = (users ||[]).filter(i => i.tenants.find((t:any) => t.id === req.user?.tenant_id)) + + + + const dataCombined = data.map(historyitem => { + return { + ...historyitem, + created_by_profile: filteredUsers.find(i => i.id === historyitem.created_by).auth_profiles[0] + } + }) + + + + return dataCombined; + }); + + // Neuen HistoryItem anlegen + server.post<{ + Params: { resource: string; id: string }; + Body: { + text: string; + old_val?: string | null; + new_val?: string | null; + config?: Record; + }; + }>("/resource/:resource/:id/history", { + schema: { + tags: ["History"], + summary: "Create new history entry", + params: { + type: "object", + properties: { + resource: { type: "string" }, + id: { type: "string" } + }, + required: ["resource", "id"] + }, + body: { + type: "object", + properties: { + text: { type: "string" }, + old_val: { type: "string", nullable: true }, + new_val: { type: "string", nullable: true }, + config: { type: "object", nullable: true } + }, + required: ["text"] + }, + response: { + 201: { + type: "object", + properties: { + id: { type: "number" }, + text: { type: "string" }, + created_at: { type: "string" }, + created_by: { type: "string" } + } + } + } + } + }, async (req, reply) => { + const { resource, id } = req.params; + const { text, old_val, new_val, config } = req.body; + + const userId = (req.user as any)?.user_id; + + + const fkField = columnMap[resource]; + if (!fkField) { + return reply.code(400).send({ error: `Unknown resource: ${resource}` }); + } + + const { data, error } = await server.supabase + .from("historyitems") + .insert({ + text, + [fkField]: id, + oldVal: old_val || null, + newVal: new_val || null, + config: config || null, + tenant: (req.user as any)?.tenant_id, + created_by: userId + }) + .select() + .single(); + + if (error) { + return reply.code(500).send({ error: error.message }); + } + + return reply.code(201).send(data); + }); +} diff --git a/src/routes/me.ts b/src/routes/me.ts new file mode 100644 index 0000000..8b12994 --- /dev/null +++ b/src/routes/me.ts @@ -0,0 +1,80 @@ +import { FastifyInstance } from "fastify"; + +export default async function meRoutes(server: FastifyInstance) { + server.get("/me", async (req, reply) => { + const authUser = req.user // kommt aus JWT (user_id + tenant_id) + + if (!authUser) { + return reply.code(401).send({ error: "Unauthorized" }) + } + + const user_id = req.user.user_id + const tenant_id = req.user.tenant_id + + // 1. User laden + const { data: user, error: userError } = await server.supabase + .from("auth_users") + .select("id, email, created_at, must_change_password") + .eq("id", authUser.user_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", user.id) + .eq("tenant_id", activeTenant) + .single() + + profile = profileData + } + + // 5. Permissions laden (über Funktion) + const { data: permissionsData, error: permissionsError } = await server.supabase + .rpc("auth_get_user_permissions", { + uid: user.id, + tid: activeTenant || null + }) + + if(permissionsError) { + console.log(permissionsError) + } + console.log(permissionsData) + + const permissions = permissionsData.map(i => i.permission) || [] + + // 6. Response zurückgeben + return { + user, + tenants, + activeTenant, + profile, + permissions + } + }) +} \ No newline at end of file diff --git a/src/routes/profiles.ts b/src/routes/profiles.ts new file mode 100644 index 0000000..021ef6b --- /dev/null +++ b/src/routes/profiles.ts @@ -0,0 +1,57 @@ +import { FastifyInstance } from "fastify"; + +export default async function authProfilesRoutes(server: FastifyInstance) { + // Ein einzelnes Profil laden (nur im aktuellen Tenant) + server.get<{ + Params: { id: string } + }>("/api/profiles/:id", { + schema: { + tags: ["Auth"], + summary: "Get a profile by user id (only from current tenant)", + params: { + type: "object", + properties: { + id: { type: "string", format: "uuid" } + }, + required: ["id"] + }, + response: { + 200: { + type: "object", + properties: { + id: { type: "string" }, + firstName: { type: "string" }, + lastName: { type: "string" }, + email: { type: "string", nullable: true }, + mobileTel: { type: "string", nullable: true }, + fixedTel: { type: "string", nullable: true }, + role: { type: "string", nullable: true } + } + } + } + } + }, async (req, reply) => { + const { id } = req.params; + const tenantId = (req.user as any)?.tenant_id; + + if (!tenantId) { + return reply.code(400).send({ error: "No tenant selected" }); + } + + const { data, error } = await server.supabase + .from("auth_users") + .select("*, profile(*)") + .eq("id", id) + .eq("tenant", tenantId) + .single(); + + if (error || !data) { + return reply.code(404).send({ error: "User not found or not in tenant" }); + } + + return { + user_id: data.id, + ...data.profile + }; + }); +} \ No newline at end of file diff --git a/src/routes/resources.ts b/src/routes/resources.ts new file mode 100644 index 0000000..9b2a2f4 --- /dev/null +++ b/src/routes/resources.ts @@ -0,0 +1,580 @@ +import { FastifyInstance } from "fastify"; +import {insertHistoryItem } from "../utils/history" +import {diffObjects} from "../utils/diff"; + +const dataTypes: any[] = { + tasks: { + isArchivable: true, + label: "Aufgaben", + labelSingle: "Aufgabe", + isStandardEntity: true, + redirect: true, + historyItemHolder: "task", + supabaseSelectWithInformation: "*, plant(*), project(*), customer(*)", + inputColumns: [ + "Allgemeines", + "Zuweisungen" + ], + showTabs: [{label: 'Informationen'}] + }, + customers: { + isArchivable: true, + label: "Kunden", + labelSingle: "Kunde", + isStandardEntity: true, + redirect:true, + numberRangeHolder: "customerNumber", + historyItemHolder: "customer", + supabaseSortColumn: "customerNumber", + supabaseSelectWithInformation: "*, projects(*), plants(*), contracts(*), contacts(*), createddocuments(*, statementallocations(*)), files(*), events(*)", + inputColumns: [ + "Allgemeines", + "Kontaktdaten" + ], + showTabs: [{label: 'Informationen'},{label: 'Ansprechpartner'},{label: 'Dateien'},{label: 'Ausgangsbelege'},{label: 'Projekte'},{label: 'Objekte'},{label: 'Termine'},{label: 'Verträge'}] + }, + contacts: { + isArchivable: true, + label: "Kontakte", + labelSingle: "Kontakt", + isStandardEntity: true, + redirect:true, + historyItemHolder: "contact", + supabaseSelectWithInformation: "*, customer(*), vendor(*)", + showTabs:[ + { + label: 'Informationen', + } + ] + }, + contracts: { + isArchivable: true, + label: "Verträge", + labelSingle: "Vertrag", + isStandardEntity: true, + numberRangeHolder: "contractNumber", + redirect:true, + inputColumns: [ + "Allgemeines", + "Abrechnung" + ], + supabaseSelectWithInformation: "*, customer(*), files(*)", + showTabs: [{label: 'Informationen'},{label: 'Dateien'}] + }, + absencerequests: { + isArchivable: true, + label: "Abwesenheiten", + labelSingle: "Abwesenheit", + isStandardEntity: true, + supabaseSortColumn:"startDate", + supabaseSortAscending: false, + supabaseSelectWithInformation: "*", + historyItemHolder: "absencerequest", + redirect:true, + showTabs: [{label: 'Informationen'}] + }, + plants: { + isArchivable: true, + label: "Objekte", + labelSingle: "Objekt", + isStandardEntity: true, + redirect:true, + historyItemHolder: "plant", + supabaseSelectWithInformation: "*, customer(id,name)", + showTabs: [ + { + label: "Informationen" + },{ + label: "Projekte" + },{ + label: "Aufgaben" + },{ + label: "Dateien" + }] + }, + products: { + isArchivable: true, + label: "Artikel", + labelSingle: "Artikel", + isStandardEntity: true, + redirect:true, + supabaseSelectWithInformation: "*, unit(name)", + historyItemHolder: "product", + showTabs: [ + { + label: "Informationen" + } + ] + }, + projects: { + isArchivable: true, + label: "Projekte", + labelSingle: "Projekt", + isStandardEntity: true, + redirect:true, + historyItemHolder: "project", + numberRangeHolder: "projectNumber", + supabaseSelectWithInformation: "*, customer(id,name), plant(id,name), projecttype(name, id), tasks(*, project(id,name), customer(id,name), plant(id,name)), files(*), createddocuments(*, statementallocations(*)), events(*), times(*, profile(id, fullName))", + supabaseSortColumn: "projectNumber", + showTabs: [ + { + key: "information", + label: "Informationen" + }, + { + key: "phases", + label: "Phasen" + },{ + key: "tasks", + label: "Aufgaben" + },{ + key: "files", + label: "Dateien" + },{ + label: "Zeiten" + },{ + label: "Ausgangsbelege" + },{ + label: "Termine" + }/*,{ + key: "timetracking", + label: "Zeiterfassung" + },{ + key: "events", + label: "Termine" + },{ + key: "material", + label: "Material" + }*/] + }, + vehicles: { + isArchivable: true, + label: "Fahrzeuge", + labelSingle: "Fahrzeug", + isStandardEntity: true, + redirect:true, + historyItemHolder: "vehicle", + supabaseSelectWithInformation: "*, checks(*), files(*)", + showTabs: [ + { + label: 'Informationen', + }, { + label: 'Dateien', + }, { + label: 'Überprüfungen', + } + ] + }, + vendors: { + isArchivable: true, + label: "Lieferanten", + labelSingle: "Lieferant", + isStandardEntity: true, + redirect:true, + numberRangeHolder: "vendorNumber", + historyItemHolder: "vendor", + supabaseSortColumn: "vendorNumber", + supabaseSelectWithInformation: "*, contacts(*)", + showTabs: [ + { + label: 'Informationen', + },{ + label: 'Ansprechpartner', + }, { + label: 'Dateien', + } + ] + }, + messages: { + label: "Nachrichten", + labelSingle: "Nachricht" + }, + spaces: { + isArchivable: true, + label: "Lagerplätze", + labelSingle: "Lagerplatz", + isStandardEntity: true, + supabaseSelectWithInformation: "*, files(*)", + supabaseSortColumn: "spaceNumber", + redirect: true, + numberRangeHolder: "spaceNumber", + historyItemHolder: "space", + inputColumns: [ + "Allgemeines", + "Ort" + ], + showTabs: [ + { + label: 'Informationen', + }, { + label: 'Dateien', + },{label: 'Inventarartikel'} + ] + }, + users: { + label: "Benutzer", + labelSingle: "Benutzer" + }, + createddocuments: { + isArchivable: true, + label: "Dokumente", + labelSingle: "Dokument", + supabaseSelectWithInformation: "*, files(*), statementallocations(*)", + }, + incominginvoices: { + label: "Eingangsrechnungen", + labelSingle: "Eingangsrechnung", + redirect:true + }, + inventoryitems: { + isArchivable: true, + label: "Inventarartikel", + labelSingle: "Inventarartikel", + isStandardEntity: true, + supabaseSelectWithInformation: "*, files(*), vendor(id,name), currentSpace(id,name)", + redirect: true, + numberRangeHolder: "articleNumber", + historyItemHolder: "inventoryitem", + inputColumns: [ + "Allgemeines", + "Anschaffung" + ], + showTabs: [ + { + label: 'Informationen', + }, { + label: 'Dateien', + } + ] + }, + inventoryitemgroups: { + isArchivable: true, + label: "Inventarartikelgruppen", + labelSingle: "Inventarartikelgruppe", + isStandardEntity: true, + historyItemHolder: "inventoryitemgroup", + supabaseSelectWithInformation: "*", + redirect: true, + showTabs: [ + { + label: 'Informationen', + } + ] + }, + documentboxes: { + isArchivable: true, + label: "Dokumentenboxen", + labelSingle: "Dokumentenbox", + isStandardEntity: true, + supabaseSelectWithInformation: "*, space(*), files(*)", + redirect: true, + numberRangeHolder: "key", + historyItemHolder: "documentbox", + inputColumns: [ + "Allgemeines", + ], + showTabs: [ + { + label: 'Informationen', + }, { + label: 'Dateien', + } + ] + }, + services: { + isArchivable: true, + label: "Leistungen", + labelSingle: "Leistung", + isStandardEntity: true, + redirect: true, + supabaseSelectWithInformation: "*, unit(*)", + historyItemHolder: "service", + showTabs: [ + { + label: 'Informationen', + } + ] + }, + hourrates: { + isArchivable: true, + label: "Stundensätze", + labelSingle: "Stundensatz", + isStandardEntity: true, + redirect: true, + supabaseSelectWithInformation: "*", + historyItemHolder: "hourrate", + showTabs: [ + { + label: 'Informationen', + } + ] + }, + events: { + isArchivable: true, + label: "Termine", + labelSingle: "Termin", + isStandardEntity: true, + historyItemHolder: "event", + supabaseSelectWithInformation: "*, project(id,name), customer(*)", + redirect: true, + showTabs: [ + { + label: 'Informationen',} + ] + }, + profiles: { + label: "Mitarbeiter", + labelSingle: "Mitarbeiter", + redirect: true, + historyItemHolder: "profile" + }, + workingtimes: { + isArchivable: true, + label: "Anwesenheiten", + labelSingle: "Anwesenheit", + redirect: true, + redirectToList: true + }, + texttemplates: { + isArchivable: true, + label: "Textvorlagen", + labelSingle: "Textvorlage" + }, + bankstatements: { + isArchivable: true, + label: "Kontobewegungen", + labelSingle: "Kontobewegung", + historyItemHolder: "bankStatement", + }, + statementallocations: { + label: "Bankzuweisungen", + labelSingle: "Bankzuweisung" + }, + productcategories: { + isArchivable: true, + label: "Artikelkategorien", + labelSingle: "Artikelkategorie", + isStandardEntity: true, + redirect: true, + supabaseSelectWithInformation: "*", + showTabs: [ + { + label: 'Informationen', + } + ] + }, + servicecategories: { + isArchivable: true, + label: "Leistungskategorien", + labelSingle: "Leistungskategorie", + isStandardEntity: true, + redirect: true, + supabaseSelectWithInformation: "*", + showTabs: [ + { + label: 'Informationen', + } + ] + }, + trackingtrips: { + label: "Fahrten", + labelSingle: "Fahrt", + redirect: true, + historyItemHolder: "trackingtrip", + }, + projecttypes: { + isArchivable: true, + label: "Projekttypen", + labelSingle: "Projekttyp", + redirect: true, + historyItemHolder: "projecttype" + }, + checks: { + isArchivable: true, + label: "Überprüfungen", + labelSingle: "Überprüfung", + isStandardEntity: true, + supabaseSelectWithInformation: "*, vehicle(id,licensePlate), profile(id, fullName), inventoryitem(name), files(*)", + redirect: true, + historyItemHolder: "check", + showTabs: [ + { + label: 'Informationen', + }, {label: 'Dateien'}, {label: 'Ausführungen'}] + }, + roles: { + label: "Rollen", + labelSingle: "Rolle", + redirect:true, + historyItemHolder: "role", + filters: [], + templateColumns: [ + { + key: "name", + label: "Name" + }, { + key: "description", + label: "Beschreibung" + } + ] + }, + costcentres: { + isArchivable: true, + label: "Kostenstellen", + labelSingle: "Kostenstelle", + isStandardEntity: true, + redirect:true, + numberRangeHolder: "number", + historyItemHolder: "costcentre", + supabaseSortColumn: "number", + supabaseSelectWithInformation: "*, project(*), vehicle(*), inventoryitem(*)", + showTabs: [{label: 'Informationen'},{label: 'Auswertung Kostenstelle'}] + }, + ownaccounts: { + isArchivable: true, + label: "zusätzliche Buchungskonten", + labelSingle: "zusätzliches Buchungskonto", + isStandardEntity: true, + redirect:true, + historyItemHolder: "ownaccount", + supabaseSortColumn: "number", + supabaseSelectWithInformation: "*, statementallocations(*, bs_id(*))", + showTabs: [{label: 'Informationen'},{label: 'Buchungen'}] + }, +} + +export default async function resourceRoutes(server: FastifyInstance) { + // Liste + server.get("/resource/:resource", async (req, reply) => { + if (!req.user?.tenant_id) { + return reply.code(400).send({ error: "No tenant selected" }); + } + + const { resource } = req.params as { resource: string }; + + const { data, error } = await server.supabase + .from(resource) + //@ts-ignore + .select(dataTypes[resource].supabaseSelectWithInformation) + .eq("tenant", req.user.tenant_id) + .eq("archived", false); // nur aktive zurückgeben + + if (error) { + return reply.code(400).send({ error: error.message }); + } + + return data; + }); + + // Detail + server.get("/resource/:resource/:id/:with_information?", async (req, reply) => { + if (!req.user?.tenant_id) { + return reply.code(400).send({ error: "No tenant selected" }); + } + + const { resource, id, with_information } = req.params as { resource: string; id: string, with_information: boolean }; + + // @ts-ignore + const { data, error } = await server.supabase.from(resource).select(with_information ? dataTypes[resource].supabaseSelectWithInformation : "*") + .eq("id", id) + .eq("tenant", req.user.tenant_id) + .eq("archived", false) // nur aktive holen + .single(); + + if (error || !data) { + return reply.code(404).send({ error: "Not found" }); + } + + return data; + }); + + // Create + server.post("/resource/:resource", async (req, reply) => { + if (!req.user?.tenant_id) { + return reply.code(400).send({ error: "No tenant selected" }); + } + + const { resource } = req.params as { resource: string }; + const body = req.body as Record; + + const dataType = dataTypes[resource]; + + const { data, error } = await server.supabase + .from(resource) + .insert({ + ...body, + tenant: req.user.tenant_id, + archived: false, // Standardwert + }) + .select("*") + .single(); + + if (error) { + return reply.code(400).send({ error: error.message }); + } + + await insertHistoryItem(server,{ + entity: resource, + entityId: data.id, + action: "created", + created_by: req.user.user_id, + tenant_id: req.user.tenant_id, + oldVal: null, + newVal: data, + text: `${dataType.labelSingle} erstellt`, + }); + + return data; + }); + + // UPDATE (inkl. Soft-Delete/Archive) + server.put("/resource/:resource/:id", async (req, reply) => { + console.log("hi") + const { resource, id } = req.params as { resource: string; id: string } + const body = req.body as Record + + const tenantId = (req.user as any)?.tenant_id + const userId = (req.user as any)?.user_id + + if (!tenantId || !userId) { + return reply.code(401).send({ error: "Unauthorized" }) + } + + // vorherige Version für History laden + const { data: oldItem } = await server.supabase + .from(resource) + .select("*") + .eq("id", id) + .eq("tenant", tenantId) + .single() + + const { data:newItem, error } = await server.supabase + .from(resource) + .update({ ...body, updated_at: new Date().toISOString(), updated_by: userId }) + .eq("id", id) + .eq("tenant", tenantId) + .select() + .single() + + if (error) return reply.code(500).send({ error }) + + const diffs = diffObjects(oldItem, newItem); + + + + for (const d of diffs) { + await insertHistoryItem(server,{ + entity: resource, + entityId: id, + action: d.type, + created_by: userId, + tenant_id: tenantId, + oldVal: d.oldValue ? String(d.oldValue) : null, + newVal: d.newValue ? String(d.newValue) : null, + text: `Feld "${d.label}" ${d.typeLabel}: ${d.oldValue ?? ""} → ${d.newValue ?? ""}`, + }); + } + + return newItem + }) +} \ No newline at end of file diff --git a/src/routes/tenant.ts b/src/routes/tenant.ts new file mode 100644 index 0000000..fff65a5 --- /dev/null +++ b/src/routes/tenant.ts @@ -0,0 +1,64 @@ +import { FastifyInstance } from "fastify"; +import jwt from "jsonwebtoken"; + +export default async function routes(server: FastifyInstance) { + server.get("/tenant", async (req) => { + if(req.tenant) { + return { + message: `Hallo vom Tenant ${req.tenant?.name}`, + tenant_id: req.tenant?.id, + }; + } else { + return { + message: `Server ist in MultiTenant Mode. Sie bekommen alles für Sie verfügbare`, + }; + } + + + + }); + + server.post("/tenant/switch", async (req, reply) => { + if (!req.user) { + return reply.code(401).send({ error: "Unauthorized" }); + } + + const body = req.body as { tenant_id: string }; + console.log(body); + + // prüfen ob user im Tenant Mitglied ist + const { data: tenantUser, error } = await server.supabase + .from("auth_tenant_users") + .select("*") + .eq("user_id", req.user.user_id) + .eq("tenant_id", body.tenant_id) + .single(); + + if (error || !tenantUser) { + return reply.code(403).send({ error: "Not a member of this tenant" }); + } + + // neues JWT mit tenant_id ausstellen + const token = jwt.sign( + { + user_id: req.user.user_id, + email: req.user.email, + tenant_id: body.tenant_id, + }, + process.env.JWT_SECRET!, + { expiresIn: "1h" } + ); + + reply.setCookie("token", token, { + path: "/", + httpOnly: true, + sameSite: process.env.NODE_ENV === "production" ? "none" : "lax", + secure: process.env.NODE_ENV === "production", // lokal: false, prod: true + maxAge: 60 * 60 * 3, // 3 Stunden + }) + + return { token }; + }); + + +} \ No newline at end of file diff --git a/src/utils/diff.ts b/src/utils/diff.ts new file mode 100644 index 0000000..947372c --- /dev/null +++ b/src/utils/diff.ts @@ -0,0 +1,103 @@ + +import {diffTranslations} from "./diffTranslations"; + +export type DiffChange = { + key: string; + label: string; + oldValue: any; + newValue: any; + type: "created" | "updated" | "deleted" | "unchanged"; + typeLabel: "erstellt" | "geändert" | "gelöscht" | "unverändert"; +}; + +const IGNORED_KEYS = new Set([ + "updated_at", + "updated_by", + "created_at", + "created_by", + "id", + "phases" +]); + +/** + * Vergleicht zwei Objekte und gibt die Änderungen zurück. + * @param obj1 Altes Objekt + * @param obj2 Neues Objekt + * @param ctx Lookup-Objekte (z. B. { projects, customers, vendors, profiles, plants }) + */ +export function diffObjects( + obj1: Record, + obj2: Record, + ctx: Record = {} +): DiffChange[] { + const diffs: DiffChange[] = []; + + const allKeys = new Set([ + ...Object.keys(obj1 || {}), + ...Object.keys(obj2 || {}), + ]); + + for (const key of allKeys) { + if (IGNORED_KEYS.has(key)) continue; // Felder überspringen + + const oldVal = obj1?.[key]; + const newVal = obj2?.[key]; + + console.log(oldVal, key, newVal); + + // Wenn beides null/undefined → ignorieren + if ( + (oldVal === null || oldVal === undefined || oldVal === "" || JSON.stringify(oldVal) === "[]") && + (newVal === null || newVal === undefined || newVal === "" || JSON.stringify(newVal) === "[]") + ) { + continue; + } + + let type: DiffChange["type"] = "unchanged"; + let typeLabel: DiffChange["typeLabel"] = "unverändert"; + if (oldVal === newVal) { + type = "unchanged"; + typeLabel = "unverändert"; + } else if (oldVal === undefined) { + type = "created"; + typeLabel = "erstellt" + } else if (newVal === undefined) { + type = "deleted"; + typeLabel = "gelöscht" + } else { + type = "updated"; + typeLabel = "geändert" + } + + if (type === "unchanged") continue; + + const translation = diffTranslations[key]; + let label = key; + let resolvedOld = oldVal; + let resolvedNew = newVal; + + if (translation) { + label = translation.label; + if (translation.resolve) { + const { oldVal: resOld, newVal: resNew } = translation.resolve( + oldVal, + newVal, + ctx + ); + resolvedOld = resOld; + resolvedNew = resNew; + } + } + + diffs.push({ + key, + label, + typeLabel, + oldValue: resolvedOld ?? "-", + newValue: resolvedNew ?? "-", + type, + }); + } + + return diffs; +} \ No newline at end of file diff --git a/src/utils/diffTranslations.ts b/src/utils/diffTranslations.ts new file mode 100644 index 0000000..290ef38 --- /dev/null +++ b/src/utils/diffTranslations.ts @@ -0,0 +1,165 @@ +import dayjs from "dayjs"; + +type ValueResolver = ( + oldVal: any, + newVal: any, + ctx?: Record +) => { oldVal: any; newVal: any }; + +export const diffTranslations: Record< + string, + { label: string; resolve?: ValueResolver } +> = { + project: { + label: "Projekt", + resolve: (o, n, ctx) => ({ + oldVal: o ? ctx?.projects?.find((i: any) => i.id === o)?.name ?? "-" : "-", + newVal: n ? ctx?.projects?.find((i: any) => i.id === n)?.name ?? "-" : "-", + }), + }, + title: { label: "Titel" }, + type: { label: "Typ" }, + notes: { label: "Notizen" }, + link: { label: "Link" }, + + start: { + label: "Start", + resolve: (o, n) => ({ + oldVal: o ? dayjs(o).format("DD.MM.YYYY HH:mm") : "-", + newVal: n ? dayjs(n).format("DD.MM.YYYY HH:mm") : "-", + }), + }, + end: { + label: "Ende", + resolve: (o, n) => ({ + oldVal: o ? dayjs(o).format("DD.MM.YYYY HH:mm") : "-", + newVal: n ? dayjs(n).format("DD.MM.YYYY HH:mm") : "-", + }), + }, + birthday: { + label: "Geburtstag", + resolve: (o, n) => ({ + oldVal: o ? dayjs(o).format("DD.MM.YYYY") : "-", + newVal: n ? dayjs(n).format("DD.MM.YYYY") : "-", + }), + }, + resources: { + label: "Resourcen", + resolve: (o, n) => ({ + oldVal: Array.isArray(o) ? o.map((i: any) => i.title).join(", ") : "-", + newVal: Array.isArray(n) ? n.map((i: any) => i.title).join(", ") : "-", + }), + }, + + customerNumber: { label: "Kundennummer" }, + active: { + label: "Aktiv", + resolve: (o, n) => ({ + oldVal: o === true ? "Aktiv" : "Gesperrt", + newVal: n === true ? "Aktiv" : "Gesperrt", + }), + }, + isCompany: { + label: "Firmenkunde", + resolve: (o, n) => ({ + oldVal: o === true ? "Firma" : "Privatkunde", + newVal: n === true ? "Firma" : "Privatkunde", + }), + }, + special: { label: "Adresszusatz" }, + street: { label: "Straße & Hausnummer" }, + city: { label: "Ort" }, + zip: { label: "Postleitzahl" }, + country: { label: "Land" }, + web: { label: "Webseite" }, + email: { label: "E-Mail" }, + tel: { label: "Telefon" }, + ustid: { label: "USt-ID" }, + role: { label: "Rolle" }, + phoneHome: { label: "Festnetz" }, + phoneMobile: { label: "Mobiltelefon" }, + salutation: { label: "Anrede" }, + firstName: { label: "Vorname" }, + lastName: { label: "Nachname" }, + name: { label: "Name" }, + nameAddition: { label: "Name Zusatz" }, + approved: { label: "Genehmigt" }, + manufacturer: { label: "Hersteller" }, + purchasePrice: { label: "Kaufpreis" }, + purchaseDate: { label: "Kaufdatum" }, + serialNumber: { label: "Seriennummer" }, + usePlanning: { label: "In Plantafel verwenden" }, + currentSpace: { label: "Lagerplatz" }, + + customer: { + label: "Kunde", + resolve: (o, n, ctx) => ({ + oldVal: o ? ctx?.customers?.find((i: any) => i.id === o)?.name ?? "-" : "-", + newVal: n ? ctx?.customers?.find((i: any) => i.id === n)?.name ?? "-" : "-", + }), + }, + vendor: { + label: "Lieferant", + resolve: (o, n, ctx) => ({ + oldVal: o ? ctx?.vendors?.find((i: any) => i.id === o)?.name ?? "-" : "-", + newVal: n ? ctx?.vendors?.find((i: any) => i.id === n)?.name ?? "-" : "-", + }), + }, + + description: { label: "Beschreibung" }, + categorie: { label: "Kategorie" }, + + profile: { + label: "Mitarbeiter", + resolve: (o, n, ctx) => ({ + oldVal: o ? ctx?.profiles?.find((i: any) => i.id === o)?.fullName ?? "-" : "-", + newVal: n ? ctx?.profiles?.find((i: any) => i.id === n)?.fullName ?? "-" : "-", + }), + }, + plant: { + label: "Objekt", + resolve: (o, n, ctx) => ({ + oldVal: o ? ctx?.plants?.find((i: any) => i.id === o)?.name ?? "-" : "-", + newVal: n ? ctx?.plants?.find((i: any) => i.id === n)?.name ?? "-" : "-", + }), + }, + + annualPaidLeaveDays: { label: "Urlaubstage" }, + employeeNumber: { label: "Mitarbeiternummer" }, + weeklyWorkingDays: { label: "Wöchentliche Arbeitstage" }, + weeklyWorkingHours: { label: "Wöchentliche Arbeitszeit" }, + customerRef: { label: "Referenz des Kunden" }, + + licensePlate: { label: "Kennzeichen" }, + tankSize: { label: "Tankvolumen" }, + towingCapacity: { label: "Anhängelast" }, + color: { label: "Farbe" }, + customPaymentDays: { label: "Zahlungsziel in Tagen" }, + customSurchargePercentage: { label: "Individueller Aufschlag" }, + powerInKW: { label: "Leistung" }, + + driver: { + label: "Fahrer", + resolve: (o, n, ctx) => ({ + oldVal: o ? ctx?.profiles?.find((i: any) => i.id === o)?.fullName ?? "-" : "-", + newVal: n ? ctx?.profiles?.find((i: any) => i.id === n)?.fullName ?? "-" : "-", + }), + }, + + projecttype: { label: "Projekttyp" }, + + fixed: { + label: "Festgeschrieben", + resolve: (o, n) => ({ + oldVal: o === true ? "Ja" : "Nein", + newVal: n === true ? "Ja" : "Nein", + }), + }, + archived: { + label: "Archiviert", + resolve: (o, n) => ({ + oldVal: o === true ? "Ja" : "Nein", + newVal: n === true ? "Ja" : "Nein", + }), + }, +}; diff --git a/src/utils/history.ts b/src/utils/history.ts new file mode 100644 index 0000000..5cc39b0 --- /dev/null +++ b/src/utils/history.ts @@ -0,0 +1,69 @@ +import { FastifyInstance } from "fastify" + +export async function insertHistoryItem( + server: FastifyInstance, + params: { + tenant_id: number + created_by: string | null + entity: string + entityId: string | number + action: "created" | "updated" | "unchanged" | "deleted" | "archived" + oldVal?: any + newVal?: any + text?: string + } +) { + const textMap = { + created: `Neuer Eintrag in ${params.entity} erstellt`, + updated: `Eintrag in ${params.entity} geändert`, + archived: `Eintrag in ${params.entity} archiviert`, + deleted: `Eintrag in ${params.entity} gelöscht` + } + + const columnMap: Record = { + customers: "customer", + vendors: "vendor", + projects: "project", + plants: "plant", + contacts: "contact", + inventoryitems: "inventoryitem", + products: "product", + profiles: "profile", + absencerequests: "absencerequest", + events: "event", + tasks: "task", + vehicles: "vehicle", + costcentres: "costcentre", + ownaccounts: "ownaccount", + documentboxes: "documentbox", + hourrates: "hourrate", + services: "service", + roles: "role", + checks: "check", + spaces: "space", + trackingtrips: "trackingtrip", + createddocuments: "createddocument", + inventoryitemgroups: "inventoryitemgroup" + } + + const fkColumn = columnMap[params.entity] + if (!fkColumn) { + server.log.warn(`Keine History-Spalte für Entity: ${params.entity}`) + return + } + + const entry = { + tenant: params.tenant_id, + created_by: params.created_by, + text: params.text || textMap[params.action], + action: params.action, + [fkColumn]: params.entityId, + oldVal: params.oldVal ? JSON.stringify(params.oldVal) : null, + newVal: params.newVal ? JSON.stringify(params.newVal) : null + } + + const { error } = await server.supabase.from("historyitems").insert([entry]) + if (error) { // @ts-ignore + console.log(error) + } +} diff --git a/src/utils/mailer.ts b/src/utils/mailer.ts new file mode 100644 index 0000000..34f94e1 --- /dev/null +++ b/src/utils/mailer.ts @@ -0,0 +1,32 @@ +import nodemailer from "nodemailer" + +const transporter = nodemailer.createTransport({ + host: process.env.SMTP_HOST, + port: Number(process.env.SMTP_PORT) || 587, + secure: process.env.SMTP_SSL === "true", // true für 465, false für andere Ports + auth: { + user: process.env.SMTP_USER, + pass: process.env.SMTP_PASS, + }, +}) + +export async function sendMail( + to: string, + subject: string, + html: string +): Promise<{ success: boolean; info?: any; error?: any }> { + try { + const info = await transporter.sendMail({ + from: process.env.MAIL_FROM, + to, + subject, + html, + }) + + // Nodemailer liefert eine Info-Response zurück + return { success: true, info } + } catch (err) { + console.error("❌ Fehler beim Mailversand:", err) + return { success: false, error: err } + } +} \ No newline at end of file diff --git a/src/utils/password.ts b/src/utils/password.ts new file mode 100644 index 0000000..11851ae --- /dev/null +++ b/src/utils/password.ts @@ -0,0 +1,15 @@ +import bcrypt from "bcrypt" + +export function generateRandomPassword(length = 12): string { + const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*" + let password = "" + for (let i = 0; i < length; i++) { + password += chars.charAt(Math.floor(Math.random() * chars.length)) + } + return password +} + +export async function hashPassword(password: string): Promise { + const saltRounds = 10 + return bcrypt.hash(password, saltRounds) +} \ No newline at end of file From 7c4272ffe9547e28e6bae165b2d851a01678ec92 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Tue, 2 Sep 2025 18:47:44 +0200 Subject: [PATCH 003/149] Changes --- src/index.ts | 2 + src/plugins/cors.ts | 2 +- src/routes/files.ts | 240 ++++++++++++++++++++++++++++++++++++++++ src/routes/history.ts | 4 +- src/routes/resources.ts | 26 ++++- src/routes/tenant.ts | 2 +- src/utils/s3.ts | 11 ++ src/utils/sort.ts | 39 +++++++ 8 files changed, 319 insertions(+), 7 deletions(-) create mode 100644 src/routes/files.ts create mode 100644 src/utils/s3.ts create mode 100644 src/utils/sort.ts diff --git a/src/index.ts b/src/index.ts index c95ded7..c1a4fa7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -13,6 +13,7 @@ import corsPlugin from "./plugins/cors"; import resourceRoutes from "./routes/resources"; import fastifyCookie from "@fastify/cookie"; import historyRoutes from "./routes/history"; +import fileRoutes from "./routes/files"; import {sendMail} from "./utils/mailer"; @@ -48,6 +49,7 @@ async function main() { await subApp.register(adminRoutes); await subApp.register(resourceRoutes); await subApp.register(historyRoutes); + await subApp.register(fileRoutes); },{prefix: "/api"}) diff --git a/src/plugins/cors.ts b/src/plugins/cors.ts index 6d806bd..1a0fa5a 100644 --- a/src/plugins/cors.ts +++ b/src/plugins/cors.ts @@ -10,7 +10,7 @@ export default fp(async (server: FastifyInstance) => { ], methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"], allowedHeaders: ["Content-Type", "Authorization", "Context"], - exposedHeaders: ["Authorization"], // optional, falls du ihn auch auslesen willst + exposedHeaders: ["Authorization", "Content-Disposition", "Content-Type", "Content-Length"], // optional, falls du ihn auch auslesen willst credentials: true, // wichtig, falls du Cookies nutzt }); }); \ No newline at end of file diff --git a/src/routes/files.ts b/src/routes/files.ts new file mode 100644 index 0000000..4da37fc --- /dev/null +++ b/src/routes/files.ts @@ -0,0 +1,240 @@ +import { FastifyInstance } from "fastify" +import multipart from "@fastify/multipart" +import { s3 } from "../utils/s3" +import {GetObjectCommand, PutObjectCommand} from "@aws-sdk/client-s3" +import {getSignedUrl} from "@aws-sdk/s3-request-presigner"; +import archiver from "archiver" + +export default async function fileRoutes(server: FastifyInstance) { + await server.register(multipart,{ + limits: { + fileSize: 20 * 1024 * 1024, // 20 MB + } + }) + + server.post("/files/upload", async (req, reply) => { + const tenantId = req.user?.tenant_id + if (!tenantId) return reply.code(401).send({ error: "Unauthorized" }) + + const data:any = await req.file() + const fileBuffer = await data.toBuffer() + + console.log(data) + + /*const parts = req.parts() // mehrere FormData-Felder + + console.log(parts) + + let file: any + let meta: any + + for await (const part of parts) { + //console.log(part) + // @ts-ignore + if (part.file) { + file = part + + // @ts-ignore + meta = JSON.parse(part.fields?.meta?.value) + } + }*/ + + let meta = JSON.parse(data.fields?.meta?.value) + + 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") + .insert({ + tenant: tenantId, + }) + .select() + .single() + + if(createdFileError) { + console.log(createdFileError) + return reply.code(500).send({ error: "Internal Server Error" }) + } else if(createdFileData && data.file) { + const fileKey = `${tenantId}/filesbyid/${createdFileData.id}/${data.filename}` + + await s3.send(new PutObjectCommand({ + Bucket: process.env.S3_BUCKET || "FEDEO", + Key: fileKey, + Body: fileBuffer, + ContentType: data.mimetype, + })) + + //Update File with Corresponding Path + const {data:updateFileData, error:updateFileError} = await server.supabase + .from("files") + .update({ + ...meta, + path: fileKey, + }) + .eq("id", createdFileData.id) + + if(updateFileError) { + console.log(updateFileError) + return reply.code(500).send({ error: "Internal Server Error" }) + + } else { + /*const {data:tagData, error:tagError} = await server.supabase + .from("filetagmembers") + .insert(tags.map(tag => { + return { + file_id: createdFileData.id, + tag_id: tag + } + }))*/ + + return { id: createdFileData.id, filename: data.filename, path: fileKey } + + } + + } + }) + + server.post("/files/download/:id?", async (req, reply) => { + const { id } = req.params as { id?: string } + + const ids = req.body?.ids || [] + + try { + if (id) { + // 🔹 Einzeldownload + const { data, error } = await server.supabase + .from("files") + .select("*") + .eq("id", id) + .single() + + if (error || !data) { + return reply.code(404).send({ error: "File not found" }) + } + + const command = new GetObjectCommand({ + Bucket: process.env.S3_BUCKET || "FEDEO", + Key: data.path, + }) + + const { Body, ContentType } = await s3.send(command) + + const chunks: any[] = [] + // @ts-ignore + for await (const chunk of Body) { + chunks.push(chunk) + } + const buffer = Buffer.concat(chunks) + + reply.header("Content-Type", ContentType || "application/octet-stream") + reply.header( + "Content-Disposition", + `attachment; filename="${data.path.split("/").pop()}"` + ) + return reply.send(buffer) + } + + console.log(ids) + + if (Array.isArray(ids) && ids.length > 0) { + // 🔹 Multi-Download → ZIP zurückgeben + const { data: supabaseFiles, error } = await server.supabase + .from("files") + .select("*") + .in("id", ids) + + if (error || !supabaseFiles?.length) { + return reply.code(404).send({ error: "Files not found" }) + } + + console.log(supabaseFiles) + + reply.header("Content-Type", "application/zip") + reply.header("Content-Disposition", "attachment; filename=dateien.zip") + + const archive = archiver("zip", { zlib: { level: 9 } }) + archive.on("warning", console.warn) + + for (const entry of supabaseFiles) { + const command = new GetObjectCommand({ + Bucket: process.env.S3_BUCKET || "FEDEO", + Key: entry.path, + }) + + const { Body } = await s3.send(command) + const filename = entry.path.split("/").pop() || entry.id + console.log(filename) + archive.append(Body as any, { name: filename }) + } + + await archive.finalize() + return reply.send(archive) + } + + return reply.code(400).send({ error: "No id or ids provided" }) + } catch (err) { + console.log(err) + reply.code(500).send({ error: "Download failed" }) + } + }) + + server.post("/files/presigned/:id?", async (req, reply) => { + const { id } = req.params as { key: string }; + const { ids } = req.body as { keys: string[] } + + if(id) { + try { + const {data,error} = await server.supabase.from("files").select("*").eq("id", id).single() + + const command = new GetObjectCommand({ + Bucket: process.env.S3_BUCKET || "FEDEO", + Key: data.path, + }); + + // URL für 15 Minuten gültig + const url = await getSignedUrl(s3, command, { expiresIn: 900 }); + + return { ...data, url }; + } catch (err) { + req.log.error(err); + reply.code(500).send({ error: "Could not generate presigned URL" }); + } + } else { + if (!Array.isArray(ids) || ids.length === 0) { + return reply.code(400).send({ error: "No file keys provided" }) + } + + try { + const {data:supabaseFileEntries,error} = await server.supabase.from("files").select("*").in("id", ids) + + + + const urls = await Promise.all( + ids.map(async (id) => { + + let key = supabaseFileEntries.find(i => i.id === id).path + + + const command = new GetObjectCommand({ + Bucket: process.env.S3_BUCKET || "FEDEO", + Key: key, + }) + + const url = await getSignedUrl(s3, command, { expiresIn: 900 }) // 15 min gültig + + return {...supabaseFileEntries.find(i => i.id === id), url} + }) + ) + + return { files: urls } + } catch (err) { + req.log.error(err) + reply.code(500).send({ error: "Could not generate presigned URLs" }) + } + } + + + }) +} \ No newline at end of file diff --git a/src/routes/history.ts b/src/routes/history.ts index e6d396d..2f29ce8 100644 --- a/src/routes/history.ts +++ b/src/routes/history.ts @@ -69,12 +69,12 @@ export default async function resourceHistoryRoutes(server: FastifyInstance) { const filteredUsers = (users ||[]).filter(i => i.tenants.find((t:any) => t.id === req.user?.tenant_id)) - + console.log(filteredUsers) const dataCombined = data.map(historyitem => { return { ...historyitem, - created_by_profile: filteredUsers.find(i => i.id === historyitem.created_by).auth_profiles[0] + created_by_profile: filteredUsers.find(i => i.id === historyitem.created_by) ? filteredUsers.find(i => i.id === historyitem.created_by).auth_profiles[0] : null } }) diff --git a/src/routes/resources.ts b/src/routes/resources.ts index 9b2a2f4..6a7d596 100644 --- a/src/routes/resources.ts +++ b/src/routes/resources.ts @@ -1,6 +1,7 @@ import { FastifyInstance } from "fastify"; import {insertHistoryItem } from "../utils/history" import {diffObjects} from "../utils/diff"; +import {sortData} from "../utils/sort"; const dataTypes: any[] = { tasks: { @@ -221,6 +222,18 @@ const dataTypes: any[] = { labelSingle: "Dokument", supabaseSelectWithInformation: "*, files(*), statementallocations(*)", }, + files: { + isArchivable: true, + label: "Dateien", + labelSingle: "Datei", + supabaseSelectWithInformation: "*", + }, + folders: { + isArchivable: true, + label: "Ordner", + labelSingle: "Ordner", + supabaseSelectWithInformation: "*", + }, incominginvoices: { label: "Eingangsrechnungen", labelSingle: "Eingangsrechnung", @@ -452,18 +465,25 @@ export default async function resourceRoutes(server: FastifyInstance) { const { resource } = req.params as { resource: string }; + const {select, sort, asc } = req.query as { select?: string, sort?: string, asc?: string } + console.log(select, sort, asc) + + const { data, error } = await server.supabase .from(resource) //@ts-ignore - .select(dataTypes[resource].supabaseSelectWithInformation) + .select(select || dataTypes[resource].supabaseSelectWithInformation) .eq("tenant", req.user.tenant_id) - .eq("archived", false); // nur aktive zurückgeben + .eq("archived", false) + + const sorted =sortData(data,sort,asc === "true" ? true : false) if (error) { + console.log(error) return reply.code(400).send({ error: error.message }); } - return data; + return sorted; }); // Detail diff --git a/src/routes/tenant.ts b/src/routes/tenant.ts index fff65a5..24c0c66 100644 --- a/src/routes/tenant.ts +++ b/src/routes/tenant.ts @@ -46,7 +46,7 @@ export default async function routes(server: FastifyInstance) { tenant_id: body.tenant_id, }, process.env.JWT_SECRET!, - { expiresIn: "1h" } + { expiresIn: "3h" } ); reply.setCookie("token", token, { diff --git a/src/utils/s3.ts b/src/utils/s3.ts new file mode 100644 index 0000000..a419fbb --- /dev/null +++ b/src/utils/s3.ts @@ -0,0 +1,11 @@ +import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3" + +export const s3 = new S3Client({ + endpoint: process.env.S3_ENDPOINT || "https://fedeo.nbg1.your-objectstorage.com", // z. B. http://localhost:9000 für MinIO + region: process.env.S3_REGION || "eu-central", + credentials: { + accessKeyId: process.env.S3_ACCESS_KEY || "RYOMQRW8KSTY3UQX7RPJ", + secretAccessKey: process.env.S3_SECRET_KEY || "aZ33xBv47sPPsHuFKeHSDiLagjqF7nShnuGkj7B1", + }, + forcePathStyle: true, // wichtig für MinIO +}) \ No newline at end of file diff --git a/src/utils/sort.ts b/src/utils/sort.ts new file mode 100644 index 0000000..1237d62 --- /dev/null +++ b/src/utils/sort.ts @@ -0,0 +1,39 @@ +/** + * Sortiert ein Array von Objekten anhand einer Spalte. + * + * @param data Array von Objekten + * @param column Sortierspalte (Property-Name im Objekt) + * @param ascending true = aufsteigend, false = absteigend + */ +export function sortData>( + data: T[], + column?: keyof T, + ascending: boolean = true +): T[] { + if (!column) return data + + return [...data].sort((a, b) => { + const valA = a[column] + const valB = b[column] + + // null/undefined nach hinten + if (valA == null && valB != null) return 1 + if (valB == null && valA != null) return -1 + if (valA == null && valB == null) return 0 + + // Zahlenvergleich + if (typeof valA === "number" && typeof valB === "number") { + return ascending ? valA - valB : valB - valA + } + + // Datumsvergleich + if (valA instanceof Date && valB instanceof Date) { + return ascending ? valA.getTime() - valB.getTime() : valB.getTime() - valA.getTime() + } + + // Fallback: Stringvergleich + return ascending + ? String(valA).localeCompare(String(valB)) + : String(valB).localeCompare(String(valA)) + }) +} \ No newline at end of file From dc385b84224df618b1b928336d5eee79d8811e6e Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Tue, 2 Sep 2025 19:11:05 +0200 Subject: [PATCH 004/149] Changes --- src/index.ts | 2 ++ src/routes/resources.ts | 9 +++++---- src/routes/resourcesSpecial.ts | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 4 deletions(-) create mode 100644 src/routes/resourcesSpecial.ts diff --git a/src/index.ts b/src/index.ts index c1a4fa7..ab73502 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,6 +11,7 @@ import authPlugin from "./plugins/auth"; import adminRoutes from "./routes/admin"; import corsPlugin from "./plugins/cors"; import resourceRoutes from "./routes/resources"; +import resourceRoutesSpecial from "./routes/resourcesSpecial"; import fastifyCookie from "@fastify/cookie"; import historyRoutes from "./routes/history"; import fileRoutes from "./routes/files"; @@ -48,6 +49,7 @@ async function main() { await subApp.register(tenantRoutes); await subApp.register(adminRoutes); await subApp.register(resourceRoutes); + await subApp.register(resourceRoutesSpecial); await subApp.register(historyRoutes); await subApp.register(fileRoutes); diff --git a/src/routes/resources.ts b/src/routes/resources.ts index 6a7d596..d48ec9e 100644 --- a/src/routes/resources.ts +++ b/src/routes/resources.ts @@ -475,14 +475,13 @@ export default async function resourceRoutes(server: FastifyInstance) { .select(select || dataTypes[resource].supabaseSelectWithInformation) .eq("tenant", req.user.tenant_id) .eq("archived", false) - - const sorted =sortData(data,sort,asc === "true" ? true : false) - if (error) { console.log(error) return reply.code(400).send({ error: error.message }); } + const sorted =sortData(data,sort,asc === "true" ? true : false) + return sorted; }); @@ -493,9 +492,11 @@ export default async function resourceRoutes(server: FastifyInstance) { } const { resource, id, with_information } = req.params as { resource: string; id: string, with_information: boolean }; + const {select } = req.query as { select?: string } + // @ts-ignore - const { data, error } = await server.supabase.from(resource).select(with_information ? dataTypes[resource].supabaseSelectWithInformation : "*") + const { data, error } = await server.supabase.from(resource).select(select ? select : (with_information ? dataTypes[resource].supabaseSelectWithInformation : "*")) .eq("id", id) .eq("tenant", req.user.tenant_id) .eq("archived", false) // nur aktive holen diff --git a/src/routes/resourcesSpecial.ts b/src/routes/resourcesSpecial.ts new file mode 100644 index 0000000..d50a33d --- /dev/null +++ b/src/routes/resourcesSpecial.ts @@ -0,0 +1,33 @@ +import {FastifyInstance} from "fastify"; +import {sortData} from "../utils/sort"; + + +export default async function resourceRoutesSpecial(server: FastifyInstance) { + // Liste + server.get("/resource-special/:resource", async (req, reply) => { + if (!req.user?.tenant_id) { + return reply.code(400).send({ error: "No tenant selected" }); + } + + const { resource } = req.params as { resource: string }; + + if(!["accounts","units","countrys"].includes(resource)) return reply.code(400).send({ error: "No corrected special resource selected" }); + + const {select, sort, asc } = req.query as { select?: string, sort?: string, asc?: string } + console.log(select, sort, asc) + + + const { data, error } = await server.supabase + .from(resource) + //@ts-ignore + .select(select || "*") + if (error) { + console.log(error) + return reply.code(400).send({ error: error.message }); + } + + const sorted =sortData(data,sort,asc === "true" ? true : false) + + return sorted; + }); +} \ No newline at end of file From c98394b5bfdb4775136dae1002aad92ee19dbc9f Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Fri, 12 Sep 2025 18:29:13 +0200 Subject: [PATCH 005/149] Changes --- src/index.ts | 16 +- src/routes/{ => auth}/auth-authenticated.ts | 0 src/routes/{ => auth}/auth.ts | 4 +- src/routes/{ => auth}/me.ts | 2 +- src/routes/auth/user.ts | 67 ++ src/routes/banking.ts | 72 ++ src/routes/emailAsUser.ts | 30 + src/routes/exports.ts | 105 +++ src/routes/files.ts | 3 +- src/routes/functions.ts | 42 + src/routes/tenant.ts | 96 +++ src/utils/emailengine.ts | 45 ++ src/utils/export/datev.ts | 382 +++++++++ src/utils/functions.ts | 23 + src/utils/history.ts | 3 +- src/utils/pdf.ts | 854 ++++++++++++++++++++ src/utils/stringRendering.ts | 51 ++ 17 files changed, 1786 insertions(+), 9 deletions(-) rename src/routes/{ => auth}/auth-authenticated.ts (100%) rename src/routes/{ => auth}/auth.ts (98%) rename src/routes/{ => auth}/me.ts (94%) create mode 100644 src/routes/auth/user.ts create mode 100644 src/routes/banking.ts create mode 100644 src/routes/emailAsUser.ts create mode 100644 src/routes/exports.ts create mode 100644 src/routes/functions.ts create mode 100644 src/utils/emailengine.ts create mode 100644 src/utils/export/datev.ts create mode 100644 src/utils/functions.ts create mode 100644 src/utils/pdf.ts create mode 100644 src/utils/stringRendering.ts diff --git a/src/index.ts b/src/index.ts index ab73502..9b7697f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,11 +2,11 @@ import Fastify from "fastify"; import swaggerPlugin from "./plugins/swagger" import supabasePlugin from "./plugins/supabase"; import healthRoutes from "./routes/health"; -import meRoutes from "./routes/me"; +import meRoutes from "./routes/auth/me"; import tenantRoutes from "./routes/tenant"; import tenantPlugin from "./plugins/tenant"; -import authRoutes from "./routes/auth"; -import authRoutesAuthenticated from "./routes/auth-authenticated"; +import authRoutes from "./routes/auth/auth"; +import authRoutesAuthenticated from "./routes/auth/auth-authenticated"; import authPlugin from "./plugins/auth"; import adminRoutes from "./routes/admin"; import corsPlugin from "./plugins/cors"; @@ -15,6 +15,11 @@ import resourceRoutesSpecial from "./routes/resourcesSpecial"; import fastifyCookie from "@fastify/cookie"; import historyRoutes from "./routes/history"; import fileRoutes from "./routes/files"; +import userRoutes from "./routes/auth/user" +import functionRoutes from "./routes/functions"; +import bankingRoutes from "./routes/banking"; +import exportRoutes from "./routes/exports" +import emailAsUserRoutes from "./routes/emailAsUser"; import {sendMail} from "./utils/mailer"; @@ -52,6 +57,11 @@ async function main() { await subApp.register(resourceRoutesSpecial); await subApp.register(historyRoutes); await subApp.register(fileRoutes); + await subApp.register(userRoutes); + await subApp.register(functionRoutes); + await subApp.register(bankingRoutes); + await subApp.register(exportRoutes); + await subApp.register(emailAsUserRoutes); },{prefix: "/api"}) diff --git a/src/routes/auth-authenticated.ts b/src/routes/auth/auth-authenticated.ts similarity index 100% rename from src/routes/auth-authenticated.ts rename to src/routes/auth/auth-authenticated.ts diff --git a/src/routes/auth.ts b/src/routes/auth/auth.ts similarity index 98% rename from src/routes/auth.ts rename to src/routes/auth/auth.ts index 3e2b601..5e70e86 100644 --- a/src/routes/auth.ts +++ b/src/routes/auth/auth.ts @@ -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 diff --git a/src/routes/me.ts b/src/routes/auth/me.ts similarity index 94% rename from src/routes/me.ts rename to src/routes/auth/me.ts index 8b12994..510ea89 100644 --- a/src/routes/me.ts +++ b/src/routes/auth/me.ts @@ -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(); diff --git a/src/routes/auth/user.ts b/src/routes/auth/user.ts new file mode 100644 index 0000000..687ea15 --- /dev/null +++ b/src/routes/auth/user.ts @@ -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, + } + }) +} \ No newline at end of file diff --git a/src/routes/banking.ts b/src/routes/banking.ts new file mode 100644 index 0000000..a85fed4 --- /dev/null +++ b/src/routes/banking.ts @@ -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"}) + } + + + }) + + + +} \ No newline at end of file diff --git a/src/routes/emailAsUser.ts b/src/routes/emailAsUser.ts new file mode 100644 index 0000000..9755e45 --- /dev/null +++ b/src/routes/emailAsUser.ts @@ -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" }) + } + }) + + +} \ No newline at end of file diff --git a/src/routes/exports.ts b/src/routes/exports.ts new file mode 100644 index 0000000..eb10186 --- /dev/null +++ b/src/routes/exports.ts @@ -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) + + }) + + + + + +} \ No newline at end of file diff --git a/src/routes/files.ts b/src/routes/files.ts index 4da37fc..8e2f2da 100644 --- a/src/routes/files.ts +++ b/src/routes/files.ts @@ -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) diff --git a/src/routes/functions.ts b/src/routes/functions.ts new file mode 100644 index 0000000..b3618c0 --- /dev/null +++ b/src/routes/functions.ts @@ -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" }) + } + } + ) +} \ No newline at end of file diff --git a/src/routes/tenant.ts b/src/routes/tenant.ts index 24c0c66..6ad68d7 100644 --- a/src/routes/tenant.ts +++ b/src/routes/tenant.ts @@ -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) + } + }); } \ No newline at end of file diff --git a/src/utils/emailengine.ts b/src/utils/emailengine.ts new file mode 100644 index 0000000..705a399 --- /dev/null +++ b/src/utils/emailengine.ts @@ -0,0 +1,45 @@ +import axios from "axios" + +const AxiosEE = axios.create({ + baseURL: process.env.EMAILENGINE_URL ||"https://ee.fedeo.io/v1", + headers: { + Authorization: `Bearer ${process.env.EMAILENGINE_TOKEN || "dcd8209bc5371c728f9ec951600afcfc74e8c391a7e984b2a6df9c4665dc7ad6"}`, + Accept: "application/json", + }, +}) + + + +export async function sendMailAsUser( + to: string, + subject: string, + html: string, + text: string, + account: string, + cc: string, + bcc: string, + attachments: any, +): Promise<{ success: boolean; info?: any; error?: any }> { + try { + const sendData = { + to: to.split(";").map(i => { return {address: i}}), + cc: cc ? cc.split(";").map((i:any) => { return {address: i}}) : null, + bcc: bcc ? bcc.split(";").map((i:any) => { return {address: i}}) : null, + subject, + text, + html, + attachments + } + + if(sendData.cc === null) delete sendData.cc + if(sendData.bcc === null) delete sendData.bcc + + const {data} = await AxiosEE.post(`/account/${account}/submit`, sendData) + + return { success: true, info: data } + + } catch (err) { + console.error("❌ Fehler beim Mailversand:", err) + return { success: false, error: err } + } +} \ No newline at end of file diff --git a/src/utils/export/datev.ts b/src/utils/export/datev.ts new file mode 100644 index 0000000..c8d5298 --- /dev/null +++ b/src/utils/export/datev.ts @@ -0,0 +1,382 @@ +import xmlbuilder from "xmlbuilder"; +import dayjs from "dayjs"; +import isBetween from "dayjs/plugin/isBetween.js" +import {BlobWriter, Data64URIReader, TextReader, TextWriter, ZipWriter} from "@zip.js/zip.js"; +import {FastifyInstance} from "fastify"; +import {GetObjectCommand} from "@aws-sdk/client-s3"; +import {s3} from "../s3"; +dayjs.extend(isBetween) + +const getCreatedDocumentTotal = (item) => { + let totalNet = 0 + let total19 = 0 + let total7 = 0 + + item.rows.forEach(row => { + if(!['pagebreak','title','text'].includes(row.mode)){ + let rowPrice = Number(Number(row.quantity) * Number(row.price) * (1 - Number(row.discountPercent) /100) ).toFixed(3) + totalNet = totalNet + Number(rowPrice) + + if(row.taxPercent === 19) { + total19 = total19 + Number(rowPrice * 0.19) + } else if(row.taxPercent === 7) { + total7 = total7 + Number(rowPrice * 0.07) + } + } + }) + + let totalGross = Number(totalNet.toFixed(2)) + Number(total19.toFixed(2)) + Number(total7.toFixed(2)) + + + + return { + totalNet: totalNet, + total19: total19, + total7: total7, + totalGross: totalGross, + } +} + +const escapeString = (str) => { + + str = (str ||"") + .replaceAll("\n","") + .replaceAll(";","") + .replaceAll(/\r/g,"") + .replaceAll(/"/g,"") + .replaceAll(/ü/g,"ue") + .replaceAll(/ä/g,"ae") + .replaceAll(/ö/g,"oe") + return str +} + +const displayCurrency = (input, onlyAbs = false) => { + + if(onlyAbs) { + return Math.abs(input).toFixed(2).replace(".",",") + } else { + return input.toFixed(2).replace(".",",") + } +} + +export async function buildExportZip(server: FastifyInstance, tenant: number, startDate: string, endDate: string, beraternr: string, mandantennr: string): Promise { + + try { + const zipFileWriter = new BlobWriter() + const zipWriter = new ZipWriter(zipFileWriter) + + + + //Basic Information + + let header = `"EXTF";700;21;"Buchungsstapel";13;${dayjs().format("YYYYMMDDHHmmssSSS")};;"FE";"Florian Federspiel";;${beraternr};${mandantennr};20250101;4;${dayjs(startDate).format("YYYYMMDD")};${dayjs(endDate).format("YYYYMMDD")};"Buchungsstapel";"FF";1;0;1;"EUR";;"";;;"03";;;"";""` + + let colHeaders = `Umsatz;Soll-/Haben-Kennzeichen;WKZ Umsatz;Kurs;Basisumsatz;WKZ Basisumsatz;Konto;Gegenkonto;BU-Schluessel;Belegdatum;Belegfeld 1;Belegfeld 2;Skonto;Buchungstext;Postensperre;Diverse Adressnummer;Geschaeftspartnerbank;Sachverhalt;Zinssperre;Beleglink;Beleginfo - Art 1;Beleginfo - Inhalt 1;Beleginfo - Art 2;Beleginfo - Inhalt 2;Beleginfo - Art 3;Beleginfo - Inhalt 3;Beleginfo - Art 4;Beleginfo - Inhalt 4;Beleginfo - Art 5;Beleginfo - Inhalt 5;Beleginfo - Art 6;Beleginfo - Inhalt 6;Beleginfo - Art 7;Beleginfo - Inhalt 7;Beleginfo - Art 8;Beleginfo - Inhalt 8;KOST1 - Kostenstelle;KOST2 - Kostenstelle;Kost Menge;EU-Land u. USt-IdNr. (Bestimmung);EU-Steuersatz (Bestimmung);Abw. Versteuerungsart;Sachverhalt L+L;Funktionsergaenzung L+L;BU 49 Hauptfunktionstyp;BU 49 Hauptfunktionsnummer;BU 49 Funktionsergaenzung;Zusatzinformation - Art 1;Zusatzinformation - Inhalt 1;Zusatzinformation - Art 2;Zusatzinformation - Inhalt 2;Zusatzinformation - Art 3;Zusatzinformation - Inhalt 3;Zusatzinformation - Art 4;Zusatzinformation - Inhalt 4;Zusatzinformation - Art 5;Zusatzinformation - Inhalt 5;Zusatzinformation - Art 6;Zusatzinformation - Inhalt 6;Zusatzinformation - Art 7;Zusatzinformation - Inhalt 7;Zusatzinformation - Art 8;Zusatzinformation - Inhalt 8;Zusatzinformation - Art 9;Zusatzinformation - Inhalt 9;Zusatzinformation - Art 10;Zusatzinformation - Inhalt 10;Zusatzinformation - Art 11;Zusatzinformation - Inhalt 11;Zusatzinformation - Art 12;Zusatzinformation - Inhalt 12;Zusatzinformation - Art 13;Zusatzinformation - Inhalt 13;Zusatzinformation - Art 14;Zusatzinformation - Inhalt 14;Zusatzinformation - Art 15;Zusatzinformation - Inhalt 15;Zusatzinformation - Art 16;Zusatzinformation - Inhalt 16;Zusatzinformation - Art 17;Zusatzinformation - Inhalt 17;Zusatzinformation - Art 18;Zusatzinformation - Inhalt 18;Zusatzinformation - Art 19;Zusatzinformation - Inhalt 19;Zusatzinformation - Art 20;Zusatzinformation - Inhalt 20;Stueck;Gewicht;Zahlweise;Zahlweise;Veranlagungsjahr;Zugeordnete Faelligkeit;Skontotyp;Auftragsnummer;Buchungstyp;USt-Schluessel (Anzahlungen);EU-Mitgliedstaat (Anzahlungen);Sachverhalt L+L (Anzahlungen);EU-Steuersatz (Anzahlungen);Erloeskonto (Anzahlungen);Herkunft-Kz;Leerfeld;KOST-Datum;SEPA-Mandatsreferenz;Skontosperre;Gesellschaftername;Beteiligtennummer;Identifikationsnummer;Zeichnernummer;Postensperre bis;Bezeichnung SoBil-Sachverhalt;Kennzeichen SoBil-Buchung;Festschreibung;Leistungsdatum;Datum Zuord. Steuerperiode;Faelligkeit;Generalumkehr;Steuersatz;Land;Abrechnungsreferenz;BVV-Position;EU-Mitgliedstaat u. UStID(Ursprung);EU-Steuersatz(Ursprung);Abw. Skontokonto` + + //Get Bookings + const {data:statementallocationsRaw,error: statementallocationsError} = await server.supabase.from("statementallocations").select('*, account(*), bs_id(*, account(*)), cd_id(*,customer(*)), ii_id(*, vendor(*)), vendor(*), customer(*), ownaccount(*)').eq("tenant", tenant); + let {data:createddocumentsRaw,error: createddocumentsError} = await server.supabase.from("createddocuments").select('*,customer(*)').eq("tenant", tenant).in("type",["invoices","advanceInvoices","cancellationInvoices"]).eq("state","Gebucht") + let {data:incominginvoicesRaw,error: incominginvoicesError} = await server.supabase.from("incominginvoices").select('*, vendor(*)').eq("tenant", tenant) + const {data:accounts} = await server.supabase.from("accounts").select() + const {data:tenantData} = await server.supabase.from("tenants").select().eq("id",tenant).single() + + let createddocuments = createddocumentsRaw.filter(i => dayjs(i.documentDate).isBetween(startDate,endDate,"day","[]")) + let incominginvoices = incominginvoicesRaw.filter(i => dayjs(i.date).isBetween(startDate,endDate,"day","[]")) + let statementallocations = statementallocationsRaw.filter(i => dayjs(i.bs_id.date).isBetween(startDate,endDate,"day","[]")) + + + const {data:filesCreateddocuments, error: filesErrorCD} = await server.supabase.from("files").select().eq("tenant",tenant).or(`createddocument.in.(${createddocuments.map(i => i.id).join(",")})`) + const {data:filesIncomingInvoices, error: filesErrorII} = await server.supabase.from("files").select().eq("tenant",tenant).or(`incominginvoice.in.(${incominginvoices.map(i => i.id).join(",")})`) + + const downloadFile = async (bucketName, filePath, downloadFilePath,fileId) => { + const command = new GetObjectCommand({ + Bucket: process.env.S3_BUCKET || "FEDEO", + Key: filePath, + }) + + const { Body, ContentType } = await s3.send(command) + + const chunks: any[] = [] + // @ts-ignore + for await (const chunk of Body) { + chunks.push(chunk) + } + const buffer = Buffer.concat(chunks) + + const dataURL = `data:application/pdf;base64,${buffer.toString('base64')}` + + const dataURLReader = new Data64URIReader(dataURL) + await zipWriter.add(`${fileId}.${downloadFilePath.split(".").pop()}`, dataURLReader) + + //await fs.writeFile(`./output/${fileId}.${downloadFilePath.split(".").pop()}`, buffer, () => {}); + console.log(`File added to Zip`); + }; + + for (const file of filesCreateddocuments) { + await downloadFile("filesdev",file.path,`./output/files/${file.path.split("/")[file.path.split("/").length - 1]}`,file.id); + } + for (const file of filesIncomingInvoices) { + await downloadFile("filesdev",file.path,`./output/files/${file.path.split("/")[file.path.split("/").length - 1]}`,file.id); + } + + let bookingLines = [] + + createddocuments.forEach(createddocument => { + + let file = filesCreateddocuments.find(i => i.createddocument === createddocument.id); + + let total = 0 + let typeString = "" + + if(createddocument.type === "invoices") { + total = getCreatedDocumentTotal(createddocument).totalGross + + console.log() + if(createddocument.usedAdvanceInvoices.length > 0){ + createddocument.usedAdvanceInvoices.forEach(usedAdvanceInvoice => { + total -= getCreatedDocumentTotal(createddocumentsRaw.find(i => i.id === usedAdvanceInvoice)).totalGross + }) + } + + console.log(total) + + typeString = "AR" + } else if(createddocument.type === "advanceInvoices") { + total = getCreatedDocumentTotal(createddocument).totalGross + typeString = "ARAbschlag" + } else if(createddocument.type === "cancellationInvoices") { + total = getCreatedDocumentTotal(createddocument).totalGross + typeString = "ARStorno" + } + + let shSelector = "S" + if(Math.sign(total) === 1) { + shSelector = "S" + } else if (Math.sign(total) === -1) { + shSelector = "H" + } + + bookingLines.push(`${displayCurrency(total,true)};"${shSelector}";;;;;${createddocument.customer.customerNumber};8400;"";${dayjs(createddocument.documentDate).format("DDMM")};"${createddocument.documentNumber}";;;"${`${typeString} ${createddocument.documentNumber} - ${createddocument.customer.name}`.substring(0,59)}";;;;;;${file ? `"BEDI ""${file.id}"""` : ""};"Geschäftspartner";"${createddocument.customer.name}";"Kundennummer";"${createddocument.customer.customerNumber}";"Belegnummer";"${createddocument.documentNumber}";"Leistungsdatum";"${dayjs(createddocument.deliveryDate).format("DD.MM.YYYY")}";"Belegdatum";"${dayjs(createddocument.documentDate).format("DD.MM.YYYY")}";;;;;;;;;;"";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0;;;;"";;;;;;;`) + + }) + + incominginvoices.forEach(incominginvoice => { + console.log(incominginvoice.id); + incominginvoice.accounts.forEach(account => { + + let file = filesIncomingInvoices.find(i => i.incominginvoice === incominginvoice.id); + + + let accountData = accounts.find(i => i.id === account.account) + let buschluessel = 9 + + if(account.taxType === '19'){ + buschluessel = 9 + } else if(account.taxType === 'null') { + buschluessel = "" + } else if(account.taxType === '7') { + buschluessel = "8" + } else if(account.taxType === '19I') { + buschluessel = "19" + } else if(account.taxType === '7I') { + buschluessel = "18" + } else { + buschluessel = "-" + } + + let shSelector = "S" + let amountGross = account.amountGross ? account.amountGross : account.amountNet + account.amountTax + + + if(Math.sign(amountGross) === 1) { + shSelector = "S" + } else if(Math.sign(amountGross) === -1) { + shSelector = "H" + } + + let text = `ER ${incominginvoice.reference}: ${escapeString(incominginvoice.description)}`.substring(0,59) + console.log(incominginvoice) + bookingLines.push(`${Math.abs(amountGross).toFixed(2).replace(".",",")};"${shSelector}";;;;;${accountData.number};${incominginvoice.vendor.vendorNumber};"${buschluessel}";${dayjs(incominginvoice.date).format("DDMM")};"${incominginvoice.reference}";;;"${text}";;;;;;${file ? `"BEDI ""${file.id}"""` : ""};"Geschäftspartner";"${incominginvoice.vendor.name}";"Kundennummer";"${incominginvoice.vendor.vendorNumber}";"Belegnummer";"${incominginvoice.reference}";"Leistungsdatum";"${dayjs(incominginvoice.date).format("DD.MM.YYYY")}";"Belegdatum";"${dayjs(incominginvoice.date).format("DD.MM.YYYY")}";;;;;;;;;;"";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0;;;;"";;;;;;;`) + }) + + + }) + + statementallocations.forEach(statementallocation => { + + let shSelector = "S" + + if(Math.sign(statementallocation.amount) === 1) { + shSelector = "S" + } else if(Math.sign(statementallocation.amount) === -1) { + shSelector = "H" + } + + if(statementallocation.cd_id) { + bookingLines.push(`${displayCurrency(statementallocation.amount,true)};"H";;;;;${statementallocation.cd_id.customer.customerNumber};${statementallocation.bs_id.account.datevNumber};"3";${dayjs(statementallocation.cd_id.documentDate).format("DDMM")};"${statementallocation.cd_id.documentNumber}";;;"${`ZE${statementallocation.description}${escapeString(statementallocation.bs_id.text)}`.substring(0,59)}";;;;;;;"Geschäftspartner";"${statementallocation.cd_id.customer.name}";"Kundennummer";"${statementallocation.cd_id.customer.customerNumber}";"Belegnummer";"${statementallocation.cd_id.documentNumber}";"Leistungsdatum";"${dayjs(statementallocation.cd_id.deliveryDate).format("DD.MM.YYYY")}";"Belegdatum";"${dayjs(statementallocation.cd_id.documentDate).format("DD.MM.YYYY")}";;;;;;;;;;"";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0;;;;"";;;;;;;`) + } else if(statementallocation.ii_id) { + bookingLines.push(`${displayCurrency(statementallocation.amount,true)};"${shSelector}";;;;;${statementallocation.bs_id.account.datevNumber};${statementallocation.ii_id.vendor.vendorNumber};"";${dayjs(statementallocation.ii_id.date).format("DDMM")};"${statementallocation.ii_id.reference}";;;"${`ZA${statementallocation.description} ${escapeString(statementallocation.bs_id.text)} `.substring(0,59)}";;;;;;;"Geschäftspartner";"${statementallocation.ii_id.vendor.name}";"Kundennummer";"${statementallocation.ii_id.vendor.vendorNumber}";"Belegnummer";"${statementallocation.ii_id.reference}";"Leistungsdatum";"${dayjs(statementallocation.ii_id.date).format("DD.MM.YYYY")}";"Belegdatum";"${dayjs(statementallocation.ii_id.date).format("DD.MM.YYYY")}";;;;;;;;;;"";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0;;;;"";;;;;;;`) + } else if(statementallocation.account) { + bookingLines.push(`${displayCurrency(statementallocation.amount,true)};"${shSelector}";;;;;${statementallocation.bs_id.account.datevNumber};${statementallocation.account.number};"";${dayjs(statementallocation.bs_id.date).format("DDMM")};"";;;"${`${Math.sign(statementallocation.amount) > 0 ? "ZE" : "ZA"} ${statementallocation.account.number} - ${escapeString(statementallocation.account.label)}${escapeString(statementallocation.description)}${statementallocation.bs_id.text}`.substring(0,59)}";;;;;;;"Geschäftspartner";"${statementallocation.bs_id.credName}";"Kundennummer";"";"Belegnummer";"";"Leistungsdatum";"";"Belegdatum";"${dayjs(statementallocation.bs_id.date).format("DD.MM.YYYY")}";;;;;;;;;;"";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0;;;;"";;;;;;;`) + } else if(statementallocation.vendor) { + bookingLines.push(`${displayCurrency(statementallocation.amount,true)};"${shSelector}";;;;;${statementallocation.bs_id.account.datevNumber};${statementallocation.vendor.vendorNumber};"";${dayjs(statementallocation.bs_id.date).format("DDMM")};"";;;"${`${Math.sign(statementallocation.amount) > 0 ? "ZE" : "ZA"} ${statementallocation.vendor.vendorNumber} - ${escapeString(statementallocation.vendor.name)}${escapeString(statementallocation.description)}${statementallocation.bs_id.text}`.substring(0,59)}";;;;;;;"Geschäftspartner";"${statementallocation.vendor.name}";"Kundennummer";"";"Belegnummer";"";"Leistungsdatum";"";"Belegdatum";"${dayjs(statementallocation.bs_id.date).format("DD.MM.YYYY")}";;;;;;;;;;"";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0;;;;"";;;;;;;`) + } else if(statementallocation.customer) { + bookingLines.push(`${displayCurrency(statementallocation.amount,true)};"${shSelector}";;;;;${statementallocation.bs_id.account.datevNumber};${statementallocation.customer.customerNumber};"";${dayjs(statementallocation.bs_id.date).format("DDMM")};"";;;"${`${Math.sign(statementallocation.amount) > 0 ? "ZE" : "ZA"} ${statementallocation.customer.customerNumber} - ${escapeString(statementallocation.customer.name)}${escapeString(statementallocation.description)}${statementallocation.bs_id.text}`.substring(0,59)}";;;;;;;"Geschäftspartner";"${statementallocation.customer.name}";"Kundennummer";"";"Belegnummer";"";"Leistungsdatum";"";"Belegdatum";"${dayjs(statementallocation.bs_id.date).format("DD.MM.YYYY")}";;;;;;;;;;"";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0;;;;"";;;;;;;`) + } else if(statementallocation.ownaccount) { + bookingLines.push(`${displayCurrency(statementallocation.amount,true)};"${shSelector}";;;;;${statementallocation.bs_id.account.datevNumber};${statementallocation.ownaccount.number};"";${dayjs(statementallocation.bs_id.date).format("DDMM")};"";;;"${`${Math.sign(statementallocation.amount) > 0 ? "ZE" : "ZA"} ${statementallocation.ownaccount.number} - ${escapeString(statementallocation.ownaccount.name)}${escapeString(statementallocation.description)}${statementallocation.bs_id.text}`.substring(0,59)}";;;;;;;"Geschäftspartner";"${statementallocation.ownaccount.name}";"Kundennummer";"";"Belegnummer";"";"Leistungsdatum";"";"Belegdatum";"${dayjs(statementallocation.bs_id.date).format("DD.MM.YYYY")}";;;;;;;;;;"";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0;;;;"";;;;;;;`) + } + + + }) + + + let csvString = `${header}\n${colHeaders}\n`; + bookingLines.forEach(line => { + csvString += `${line}\n`; + }) + + const buchungsstapelReader = new TextReader(csvString) + await zipWriter.add(`EXTF_Buchungsstapel_von_${dayjs(startDate).format("DDMMYYYY")}_bis_${dayjs(endDate).format("DDMMYYYY")}.csv`, buchungsstapelReader) + + /*fs.writeFile(`output/EXTF_Buchungsstapel_von_${dayjs(startDate).format("DDMMYYYY")}_bis_${dayjs(endDate).format("DDMMYYYY")}.csv`, csvString, 'utf8', function (err) { + if (err) { + console.log('Some error occured - file either not saved or corrupted file saved.'); + console.log(err); + } else{ + console.log('It\'s saved!'); + } + });*/ + + // Kreditoren/Debitoren + let headerStammdaten = `"EXTF";700;16;"Debitoren/Kreditoren";5;${dayjs().format("YYYYMMDDHHmmssSSS")};;"FE";"Florian Federspiel";;${beraternr};${mandantennr};20250101;4;${dayjs(startDate).format("YYYYMMDD")};${dayjs(endDate).format("YYYYMMDD")};"Debitoren & Kreditoren";"FF";1;0;1;"EUR";;"";;;"03";;;"";""` + + let colHeadersStammdaten = `Konto;Name (Adressattyp Unternehmen);Unternehmensgegenstand;Name (Adressattyp natuerl. Person);Vorname (Adressattyp natuerl. Person);Name (Adressattyp keine Angabe);Adressatentyp;Kurzbezeichnung;EU-Land;EU-UStID;Anrede;Titel/Akad. Grad;Adelstitel;Namensvorsatz;Adressart;Strasse;Postfach;Postleitzahl;Ort;Land;Versandzusatz;Adresszusatz;Abweichende Anrede;Abw. Zustellbezeichnung 1;Abw. Zustellbezeichnung 2;Kennz. Korrespondenzadresse;Adresse Gueltig von;Adresse Gueltig bis;Telefon;Bemerkung (Telefon);Telefon GL;Bemerkung (Telefon GL);E-Mail;Bemerkung (E-Mail);Internet;Bemerkung (Internet);Fax;Bemerkung (Fax);Sonstige;Bemerkung (Sonstige);Bankleitzahl 1;Bankbezeichnung 1;Bankkonto-Nummer 1;Laenderkennzeichen 1;IBAN-Nr. 1;Leerfeld;SWIFT-Code 1;Abw. Kontoinhaber 1;Kennz. Haupt-Bankverb. 1;Bankverb. 1 Gueltig von;Bankverb. 1 Gueltig bis;Bankleitzahl 2;Bankbezeichnung 2;Bankkonto-Nummer 2;Laenderkennzeichen 2;IBAN-Nr. 2;Leerfeld;SWIFT-Code 2;Abw. Kontoinhaber 2;Kennz. Haupt-Bankverb. 2;Bankverb. 2 gueltig von;Bankverb. 2 gueltig bis;Bankleitzahl 3;Bankbezeichnung 3;Bankkonto-Nummer 3;Laenderkennzeichen 3;IBAN-Nr. 3;Leerfeld;SWIFT-Code 3;Abw. Kontoinhaber 3;Kennz. Haupt-Bankverb. 3;Bankverb. 3 gueltig von;Bankverb. 3 gueltig bis;Bankleitzahl 4;Bankbezeichnung 4;Bankkonto-Nummer 4;Laenderkennzeichen 4;IBAN-Nr. 4;Leerfeld;SWIFT-Code 4;Abw. Kontoinhaber 4;Kennz. Haupt-Bankverb. 4;Bankverb. 4 gueltig von;Bankverb. 4 gueltig bis;Bankleitzahl 5;Bankbezeichnung 5;Bankkonto-Nummer 5;Laenderkennzeichen 5;IBAN-Nr. 5;Leerfeld;SWIFT-Code 5;Abw. Kontoinhaber 5;Kennz. Haupt-Bankverb. 5;Bankverb. 5 gueltig von;Bankverb. 5 gueltig bis;Leerfeld;Briefanrede;Grussformel;Kunden-/Lief.-Nr.;Steuernummer;Sprache;Ansprechpartner;Vertreter;Sachbearbeiter;Diverse-Konto;Ausgabeziel;Waehrungssteuerung;Kreditlimit (Debitor);Zahlungsbedingung;Faelligkeit in Tagen (Debitor);Skonto in Prozent (Debitor);Kreditoren-Ziel 1 Tg.;Kreditoren-Skonto 1 %;Kreditoren-Ziel 2 Tg.;Kreditoren-Skonto 2 %;Kreditoren-Ziel 3 Brutto Tg.;Kreditoren-Ziel 4 Tg.;Kreditoren-Skonto 4 %;Kreditoren-Ziel 5 Tg.;Kreditoren-Skonto 5 %;Mahnung;Kontoauszug;Mahntext 1;Mahntext 2;Mahntext 3;Kontoauszugstext;Mahnlimit Betrag;Mahnlimit %;Zinsberechnung;Mahnzinssatz 1;Mahnzinssatz 2;Mahnzinssatz 3;Lastschrift;Verfahren;Mandantenbank;Zahlungstraeger;Indiv. Feld 1;Indiv. Feld 2;Indiv. Feld 3;Indiv. Feld 4;Indiv. Feld 5;Indiv. Feld 6;Indiv. Feld 7;Indiv. Feld 8;Indiv. Feld 9;Indiv. Feld 10;Indiv. Feld 11;Indiv. Feld 12;Indiv. Feld 13;Indiv. Feld 14;Indiv. Feld 15;Abweichende Anrede (Rechnungsadresse);Adressart (Rechnungsadresse);Strasse (Rechnungsadresse);Postfach (Rechnungsadresse);Postleitzahl (Rechnungsadresse);Ort (Rechnungsadresse);Land (Rechnungsadresse);Versandzusatz (Rechnungsadresse);Adresszusatz (Rechnungsadresse);Abw. Zustellbezeichnung 1 (Rechnungsadresse);Abw. Zustellbezeichnung 2 (Rechnungsadresse);Adresse Gueltig von (Rechnungsadresse);Adresse Gueltig bis (Rechnungsadresse);Bankleitzahl 6;Bankbezeichnung 6;Bankkonto-Nummer 6;Laenderkennzeichen 6;IBAN-Nr. 6;Leerfeld;SWIFT-Code 6;Abw. Kontoinhaber 6;Kennz. Haupt-Bankverb. 6;Bankverb. 6 gueltig von;Bankverb. 6 gueltig bis;Bankleitzahl 7;Bankbezeichnung 7;Bankkonto-Nummer 7;Laenderkennzeichen 7;IBAN-Nr. 7;Leerfeld;SWIFT-Code 7;Abw. Kontoinhaber 7;Kennz. Haupt-Bankverb. 7;Bankverb. 7 gueltig von;Bankverb. 7 gueltig bis;Bankleitzahl 8;Bankbezeichnung 8;Bankkonto-Nummer 8;Laenderkennzeichen 8;IBAN-Nr. 8;Leerfeld;SWIFT-Code 8;Abw. Kontoinhaber 8;Kennz. Haupt-Bankverb. 8;Bankverb. 8 gueltig von;Bankverb. 8 gueltig bis;Bankleitzahl 9;Bankbezeichnung 9;Bankkonto-Nummer 9;Laenderkennzeichen 9;IBAN-Nr. 9;Leerfeld;SWIFT-Code 9;Abw. Kontoinhaber 9;Kennz. Haupt-Bankverb. 9;Bankverb. 9 gueltig von;Bankverb. 9 gueltig bis;Bankleitzahl 10;Bankbezeichnung 10;Bankkonto-Nummer 10;Laenderkennzeichen 10;IBAN-Nr. 10;Leerfeld;SWIFT-Code 10;Abw. Kontoinhaber 10;Kennz. Haupt-Bankverb. 10;Bankverb 10 Gueltig von;Bankverb 10 Gueltig bis;Nummer Fremdsystem;Insolvent;SEPA-Mandatsreferenz 1;SEPA-Mandatsreferenz 2;SEPA-Mandatsreferenz 3;SEPA-Mandatsreferenz 4;SEPA-Mandatsreferenz 5;SEPA-Mandatsreferenz 6;SEPA-Mandatsreferenz 7;SEPA-Mandatsreferenz 8;SEPA-Mandatsreferenz 9;SEPA-Mandatsreferenz 10;Verknuepftes OPOS-Konto;Mahnsperre bis;Lastschriftsperre bis;Zahlungssperre bis;Gebuehrenberechnung;Mahngebuehr 1;Mahngebuehr 2;Mahngebuehr 3;Pauschalberechnung;Verzugspauschale 1;Verzugspauschale 2;Verzugspauschale 3;Alternativer Suchname;Status;Anschrift manuell geaendert (Korrespondenzadresse);Anschrift individuell (Korrespondenzadresse);Anschrift manuell geaendert (Rechnungsadresse);Anschrift individuell (Rechnungsadresse);Fristberechnung bei Debitor;Mahnfrist 1;Mahnfrist 2;Mahnfrist 3;Letzte Frist` + const {data:customers} = await server.supabase.from("customers").select().eq("tenant",tenant).order("customerNumber") + const {data:vendors} = await server.supabase.from("vendors").select().eq("tenant",tenant).order("vendorNumber") + + let bookinglinesStammdaten = [] + + customers.forEach(customer => { + bookinglinesStammdaten.push(`${customer.customerNumber};"${customer.isCompany ? customer.name.substring(0,48): ''}";;"${!customer.isCompany ? (customer.lastname ? customer.lastname : customer.name) : ''}";"${!customer.isCompany ? (customer.firstname ? customer.firstname : '') : ''}";;${customer.isCompany ? 2 : 1};;;;;;;;"STR";"${customer.infoData.street ? customer.infoData.street : ''}";;"${customer.infoData.zip ? customer.infoData.zip : ''}";"${customer.infoData.city ? customer.infoData.city : ''}";;;"${customer.infoData.special ? customer.infoData.special : ''}";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;`) + + }) + + vendors.forEach(vendor => { + bookinglinesStammdaten.push(`${vendor.vendorNumber};"${vendor.name.substring(0,48)}";;;;;2;;;;;;;;"STR";"${vendor.infoData.street ? vendor.infoData.street : ''}";;"${vendor.infoData.zip ? vendor.infoData.zip : ''}";"${vendor.infoData.city ? vendor.infoData.city : ''}";;;"${vendor.infoData.special ? vendor.infoData.special : ''}";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;`) + + }) + + let csvStringStammdaten = `${headerStammdaten}\n${colHeadersStammdaten}\n`; + bookinglinesStammdaten.forEach(line => { + csvStringStammdaten += `${line}\n`; + }) + + const stammdatenReader = new TextReader(csvStringStammdaten) + await zipWriter.add(`EXTF_Stammdaten_von_${dayjs(startDate).format("DDMMYYYY")}_bis_${dayjs(endDate).format("DDMMYYYY")}.csv`, stammdatenReader) + + + + /*fs.writeFile(`output/EXTF_Stammdaten_von_${dayjs(startDate).format("DDMMYYYY")}_bis_${dayjs(endDate).format("DDMMYYYY")}.csv`, csvStringStammdaten, 'utf8', function (err) { + if (err) { + console.log('Some error occured - file either not saved or corrupted file saved.'); + console.log(err); + } else{ + console.log('It\'s saved!'); + } + });*/ + + //Sachkonten + let headerSachkonten = `"EXTF";700;20;"Kontenbeschriftungen";3;${dayjs().format("YYYYMMDDHHmmssSSS")};;"FE";"Florian Federspiel";;${beraternr};${mandantennr};20250101;4;${dayjs(startDate).format("YYYYMMDD")};${dayjs(endDate).format("YYYYMMDD")};"Sachkonten";"FF";1;0;1;"EUR";;"";;;"03";;;"";""` + + let colHeadersSachkonten = `Konto;Kontenbeschriftung;Sprach-ID;Kontenbeschriftung lang` + const {data:bankaccounts} = await server.supabase.from("bankaccounts").select().eq("tenant",tenant).order("datevNumber") + + let bookinglinesSachkonten = [] + + bankaccounts.forEach(bankaccount => { + bookinglinesSachkonten.push(`${bankaccount.datevNumber};"${bankaccount.name}";"de-DE";`) + + }) + + let csvStringSachkonten = `${headerSachkonten}\n${colHeadersSachkonten}\n`; + bookinglinesSachkonten.forEach(line => { + csvStringSachkonten += `${line}\n`; + }) + + const sachkontenReader = new TextReader(csvStringSachkonten) + await zipWriter.add(`EXTF_Sachkonten_von_${dayjs(startDate).format("DDMMYYYY")}_bis_${dayjs(endDate).format("DDMMYYYY")}.csv`, sachkontenReader) + + /*fs.writeFile(`output/EXTF_Sachkonten_von_${dayjs(startDate).format("DDMMYYYY")}_bis_${dayjs(endDate).format("DDMMYYYY")}.csv`, csvStringSachkonten, 'utf8', function (err) { + if (err) { + console.log('Some error occured - file either not saved or corrupted file saved.'); + console.log(err); + } else{ + console.log('It\'s saved!'); + } + });*/ + + + let obj = { + archive: { + '@version':"5.0", + "@generatingSystem":"fedeo.de", + "@xsi:schemaLocation":"http://xml.datev.de/bedi/tps/document/v05.0 Document_v050.xsd", + "@xmlns":"http://xml.datev.de/bedi/tps/document/v05.0", + "@xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance", + header: { + date: dayjs().format("YYYY-MM-DDTHH:mm:ss") + }, + content: { + document: [] + } + } + } + + filesCreateddocuments.forEach(file => { + obj.archive.content.document.push({ + "@guid": file.id, + extension: { + "@xsi:type":"File", + "@name":`${file.id}.pdf` + } + }) + }) + + filesIncomingInvoices.forEach(file => { + obj.archive.content.document.push({ + "@guid": file.id, + extension: { + "@xsi:type":"File", + "@name":`${file.id}.pdf` + } + }) + }) + + let doc = xmlbuilder.create(obj, {encoding: 'UTF-8', standalone: true}) + + //console.log(doc.end({pretty: true})); + + const documentsReader = new TextReader(doc.end({pretty: true})) + await zipWriter.add(`document.xml`, documentsReader) + + + + + /*function toBuffer(arrayBuffer) { + const buffer = Buffer.alloc(arrayBuffer.byteLength); + const view = new Uint8Array(arrayBuffer); + for (let i = 0; i < buffer.length; ++i) { + buffer[i] = view[i]; + } + return buffer; + }*/ + + + const arrayBuffer = await (await zipWriter.close()).arrayBuffer() + return Buffer.from(arrayBuffer) + } catch(error) { + console.log(error) + } + + +} \ No newline at end of file diff --git a/src/utils/functions.ts b/src/utils/functions.ts new file mode 100644 index 0000000..e5fdda8 --- /dev/null +++ b/src/utils/functions.ts @@ -0,0 +1,23 @@ +import {FastifyInstance} from "fastify"; + +export const useNextNumberRangeNumber = async (server:FastifyInstance, tenantId:number,numberRange)=> { + const {data:tenant} = await server.supabase.from("tenants").select().eq("id",tenantId).single() + + const numberRanges = tenant.numberRanges + + const usedNumber = (numberRanges[numberRange].prefix ? numberRanges[numberRange].prefix : "") + numberRanges[numberRange].nextNumber + (numberRanges[numberRange].suffix ? numberRanges[numberRange].suffix : "") + + let newNumberRange = numberRanges + + newNumberRange[numberRange].nextNumber += 1 + + const {error} = await server.supabase.from("tenants").update({numberRanges: newNumberRange}).eq("id",tenantId) + + if(error) { + console.log(error) + } else { + return { + usedNumber + } + } +} \ No newline at end of file diff --git a/src/utils/history.ts b/src/utils/history.ts index 5cc39b0..477c666 100644 --- a/src/utils/history.ts +++ b/src/utils/history.ts @@ -43,7 +43,8 @@ export async function insertHistoryItem( spaces: "space", trackingtrips: "trackingtrip", createddocuments: "createddocument", - inventoryitemgroups: "inventoryitemgroup" + inventoryitemgroups: "inventoryitemgroup", + bankstatements: "bankstatement" } const fkColumn = columnMap[params.entity] diff --git a/src/utils/pdf.ts b/src/utils/pdf.ts new file mode 100644 index 0000000..6e1681a --- /dev/null +++ b/src/utils/pdf.ts @@ -0,0 +1,854 @@ +import {PDFDocument, StandardFonts, rgb} from "pdf-lib" +import dayjs from "dayjs" +import {renderAsCurrency, splitStringBySpace} from "./stringRendering"; +import {FastifyInstance} from "fastify"; + +const getCoordinatesForPDFLib = (x:number ,y:number, page:any) => { + /* + * @param x the wanted X Parameter in Millimeters from Top Left + * @param y the wanted Y Parameter in Millimeters from Top Left + * @param page the page Object + * + * @returns x,y object + * */ + + + let retX = x * 2.83 + let retY = page.getHeight()-(y*2.83) + + return { + x: retX, + y: retY + } +} + + + + + +export const createInvoicePDF = async (server:FastifyInstance, returnMode, invoiceData, backgroundPath:string) => { + + console.log(returnMode, invoiceData, backgroundPath) + + const genPDF = async (invoiceData, backgroundSourceBuffer) => { + const pdfDoc = await PDFDocument.create() + + const font = await pdfDoc.embedFont(StandardFonts.Helvetica) + const fontBold = await pdfDoc.embedFont(StandardFonts.HelveticaBold) + + let pages = [] + let pageCounter = 1 + + + //const backgroundPdfSourceBuffer = await fetch("/Briefpapier.pdf").then((res) => res.arrayBuffer()) + const backgroudPdf = await PDFDocument.load(backgroundSourceBuffer) + + const firstPageBackground = await pdfDoc.embedPage(backgroudPdf.getPages()[0]) + const secondPageBackground = await pdfDoc.embedPage(backgroudPdf.getPages()[backgroudPdf.getPages().length > 1 ? 1 : 0]) + + // + const page1 = pdfDoc.addPage() + + // + + page1.drawPage(firstPageBackground, { + x: 0, + y: 0, + }) + // + pages.push(page1) + // + + + //Falzmarke 1 + pages[pageCounter - 1].drawLine({ + start: getCoordinatesForPDFLib(0, 105, page1), + end: getCoordinatesForPDFLib(5, 105, page1), + thickness: 0.2, + color: rgb(0, 0, 0), + opacity: 1 + }) + + //Lochmarke + pages[pageCounter - 1].drawLine({ + start: getCoordinatesForPDFLib(0, 148.5, page1), + end: getCoordinatesForPDFLib(5, 148.5, page1), + thickness: 0.2, + color: rgb(0, 0, 0), + opacity: 1 + }) + + //Falzmarke 2 + pages[pageCounter - 1].drawLine({ + start: getCoordinatesForPDFLib(0, 210, page1), + end: getCoordinatesForPDFLib(5, 210, page1), + thickness: 0.2, + color: rgb(0, 0, 0), + opacity: 1 + }) + + + /*page1.drawLine({ + start: getCoordinatesForPDFLib(20,45,page1), + end: getCoordinatesForPDFLib(105,45,page1), + thickness: 0.5, + color: rgb(0,0,0), + opacity: 1 + })*/ + + if (!invoiceData.addressLine) console.log("Missing Addressline") + + pages[pageCounter - 1].drawText(invoiceData.adressLine, { + ...getCoordinatesForPDFLib(21, 48, page1), + size: 6, + color: rgb(0, 0, 0), + lineHeight: 6, + opacity: 1, + maxWidth: 240 + }) + + + /*page1.drawLine({ + start: getCoordinatesForPDFLib(20,50,page1), + end: getCoordinatesForPDFLib(105,50,page1), + thickness: 0.5, + color: rgb(0,0,0), + opacity: 1 + })*/ + + let partLinesAdded = 0 + invoiceData.recipient.forEach((info, index) => { + + + let maxSplitLength = 35 + let splittedContent = splitStringBySpace(info, maxSplitLength) + + + splittedContent.forEach((part, partIndex) => { + if (partIndex === 0) { + pages[pageCounter - 1].drawText(part, { + ...getCoordinatesForPDFLib(21, 55 + index * 5 + partLinesAdded * 5, page1), + size: 10, + color: rgb(0, 0, 0), + lineHeight: 10, + opacity: 1, + maxWidth: 240 + }) + } else { + partLinesAdded++ + + pages[pageCounter - 1].drawText(part, { + ...getCoordinatesForPDFLib(21, 55 + index * 5 + partLinesAdded * 5, page1), + size: 10, + color: rgb(0, 0, 0), + lineHeight: 10, + opacity: 1, + maxWidth: 240 + }) + } + + /*if(partIndex > 0) partLinesAdded++ + + pages[pageCounter - 1].drawText(part, { + y: getCoordinatesForPDFLib(21,55+index*5+partLinesAdded*5, page1).y, + x: getCoordinatesForPDFLib(21,55+index*5+partLinesAdded*5,page1).x + 230 - font.widthOfTextAtSize(part,10), + size:10, + color:rgb(0,0,0), + lineHeight:10, + opacity: 1, + maxWidth: 240 + })*/ + + }) + }) + + //Rechts + + partLinesAdded = 0 + + invoiceData.info.forEach((info, index) => { + + let maxSplitLength = 34 + let splittedContent = splitStringBySpace(info.content, maxSplitLength) + + + splittedContent.forEach((part, partIndex) => { + if (partIndex === 0) { + pages[pageCounter - 1].drawText(info.label, { + ...getCoordinatesForPDFLib(116, 55 + index * 5 + partLinesAdded * 5, page1), + size: 10, + color: rgb(0, 0, 0), + lineHeight: 10, + opacity: 1, + maxWidth: 240 + }) + } + + if (partIndex > 0) partLinesAdded++ + + pages[pageCounter - 1].drawText(part, { + y: getCoordinatesForPDFLib(116, 55 + index * 5 + partLinesAdded * 5, page1).y, + x: getCoordinatesForPDFLib(116, 55 + index * 5 + partLinesAdded * 5, page1).x + 230 - font.widthOfTextAtSize(part, 10), + size: 10, + color: rgb(0, 0, 0), + lineHeight: 10, + opacity: 1, + maxWidth: 240 + }) + + }) + }) + + + /*page1.drawLine({ + start: getCoordinatesForPDFLib(125,90,page1), + end: getCoordinatesForPDFLib(200,90,page1), + thickness: 0.5, + color: rgb(0,0,0), + opacity: 1 + })*/ + + //Title + + /*page1.drawLine({ + start: getCoordinatesForPDFLib(20,95,page1), + end: getCoordinatesForPDFLib(200,95,page1), + thickness: 0.5, + color: rgb(0,0,0), + opacity: 1 + })*/ + + if (!invoiceData.title) console.log("Missing Title") + + pages[pageCounter - 1].drawText(invoiceData.title, { + ...getCoordinatesForPDFLib(20, 100, page1), + size: 13, + color: rgb(0, 0, 0), + lineHeight: 15, + opacity: 1, + maxWidth: 500 + }) + + /*page1.drawLine({ + start: getCoordinatesForPDFLib(20,105,page1), + end: getCoordinatesForPDFLib(200,105,page1), + thickness: 0.5, + color: rgb(0,0,0), + opacity: 1 + })*/ + if (!invoiceData.description) console.log("Missing Description") + + if (invoiceData.description) { + pages[pageCounter - 1].drawText(invoiceData.description, { + ...getCoordinatesForPDFLib(20, 112, page1), + size: 13, + color: rgb(0, 0, 0), + lineHeight: 15, + opacity: 1, + maxWidth: 500 + }) + } + + if (!invoiceData.startText) console.log("Missing StartText") + + + pages[pageCounter - 1].drawText(invoiceData.startText, { + ...getCoordinatesForPDFLib(20, 119, page1), + size: 10, + color: rgb(0, 0, 0), + lineHeight: 10, + opacity: 1, + maxWidth: 500 + }) + + /*page1.drawLine({ + start: getCoordinatesForPDFLib(20,115,page1), + end: getCoordinatesForPDFLib(200,115,page1), + thickness: 0.5, + color: rgb(0,0,0), + opacity: 1 + })*/ + + pages[pageCounter - 1].drawLine({ + start: getCoordinatesForPDFLib(20, 140, page1), + end: getCoordinatesForPDFLib(199, 140, page1), + thickness: 0.1, + color: rgb(0, 0, 0), + opacity: 1, + }) + + /*pages[pageCounter - 1].drawRectangle({ + ...getCoordinatesForPDFLib(20,140, page1), + width: 180 * 2.83, + height: 8 * 2.83, + color: rgb(0,0,0), + opacity: 0, + borderWidth: 0.1 + })*/ + + //Header + + pages[pageCounter - 1].drawText("Pos", { + ...getCoordinatesForPDFLib(21, 137, page1), + size: 12, + color: rgb(0, 0, 0), + lineHeight: 12, + opacity: 1, + maxWidth: 240, + font: fontBold + }) + + pages[pageCounter - 1].drawText("Menge", { + ...getCoordinatesForPDFLib(35, 137, page1), + size: 12, + color: rgb(0, 0, 0), + lineHeight: 12, + opacity: 1, + maxWidth: 240, + font: fontBold + }) + + pages[pageCounter - 1].drawText("Bezeichnung", { + ...getCoordinatesForPDFLib(52, 137, page1), + size: 12, + color: rgb(0, 0, 0), + lineHeight: 12, + opacity: 1, + maxWidth: 240, + font: fontBold + }) + + if (invoiceData.type !== "deliveryNotes") { + pages[pageCounter - 1].drawText("Steuer", { + ...getCoordinatesForPDFLib(135, 137, page1), + size: 12, + color: rgb(0, 0, 0), + lineHeight: 12, + opacity: 1, + maxWidth: 240, + font: fontBold + }) + + pages[pageCounter - 1].drawText("Einheitspreis", { + ...getCoordinatesForPDFLib(150, 137, page1), + size: 12, + color: rgb(0, 0, 0), + lineHeight: 12, + opacity: 1, + maxWidth: 240, + font: fontBold + }) + + pages[pageCounter - 1].drawText("Gesamt", { + y: getCoordinatesForPDFLib(25, 137, page1).y, + x: getCoordinatesForPDFLib(25, 137, page1).x + 490 - fontBold.widthOfTextAtSize("Gesamt", 12), + size: 12, + color: rgb(0, 0, 0), + lineHeight: 12, + opacity: 1, + maxWidth: 240, + font: fontBold + }) + } + + + let rowHeight = 145.5 + + let pageIndex = 0 + + + invoiceData.rows.forEach((row, index) => { + + if (!["pagebreak", "title", "text"].includes(row.mode)) { + + + if (!row.pos) console.log("Missing Row Pos") + + pages[pageCounter - 1].drawText(String(row.pos), { + ...getCoordinatesForPDFLib(21, rowHeight, page1), + size: 10, + color: rgb(0, 0, 0), + lineHeight: 10, + opacity: 1, + maxWidth: 240 + }) + + if (!row.quantity) console.log("Missing Row Quantity") + if (!row.unit) console.log("Missing Row Unit") + + pages[pageCounter - 1].drawText((row.optional || row.alternative) ? `(${row.quantity} ${row.unit})` : `${row.quantity} ${row.unit}`, { + ...getCoordinatesForPDFLib(35, rowHeight, page1), + size: 10, + color: rgb(0, 0, 0), + lineHeight: 10, + opacity: 1, + maxWidth: 240 + }) + + let rowTextLines = 0 + + if (invoiceData.type !== "deliveryNotes") { + pages[pageCounter - 1].drawText(splitStringBySpace(row.text, 35).join("\n"), { + ...getCoordinatesForPDFLib(52, rowHeight, page1), + size: 10, + color: rgb(0, 0, 0), + lineHeight: 10, + opacity: 1, + font: fontBold + }) + rowTextLines = splitStringBySpace(row.text, 35).length + + } else { + pages[pageCounter - 1].drawText(splitStringBySpace(row.text, 80).join("\n"), { + ...getCoordinatesForPDFLib(52, rowHeight, page1), + size: 10, + color: rgb(0, 0, 0), + lineHeight: 10, + opacity: 1, + font: fontBold + }) + + rowTextLines = splitStringBySpace(row.text, 80).length + } + + let rowDescriptionLines = 0 + + if (row.descriptionText) { + if (invoiceData.type !== "deliveryNotes") { + rowDescriptionLines = splitStringBySpace(row.descriptionText, 60).length + pages[pageCounter - 1].drawText(splitStringBySpace(row.descriptionText, 60).join("\n"), { + ...getCoordinatesForPDFLib(52, rowHeight + (rowTextLines * 5), page1), + size: 10, + color: rgb(0, 0, 0), + lineHeight: 10, + opacity: 1, + }) + + } else { + rowDescriptionLines = splitStringBySpace(row.descriptionText, 80).length + pages[pageCounter - 1].drawText(splitStringBySpace(row.descriptionText, 80).join("\n"), { + ...getCoordinatesForPDFLib(52, rowHeight + (rowTextLines * 5), page1), + size: 10, + color: rgb(0, 0, 0), + lineHeight: 10, + opacity: 1, + }) + } + } + + + if (invoiceData.type !== "deliveryNotes") { + pages[pageCounter - 1].drawText(`${row.taxPercent} %`, { + ...getCoordinatesForPDFLib(135, rowHeight, page1), + size: 10, + color: rgb(0, 0, 0), + lineHeight: 10, + opacity: 1, + maxWidth: 240 + }) + pages[pageCounter - 1].drawText(row.price, { + ...getCoordinatesForPDFLib(150, rowHeight, page1), + size: 10, + color: rgb(0, 0, 0), + lineHeight: 10, + opacity: 1, + maxWidth: 240 + }) + + + pages[pageCounter - 1].drawText((row.optional || row.alternative) ? `(${row.rowAmount})` : row.rowAmount, { + y: getCoordinatesForPDFLib(25, rowHeight, page1).y, + x: getCoordinatesForPDFLib(25, rowHeight, page1).x + 490 - font.widthOfTextAtSize((row.optional || row.alternative) ? `(${row.rowAmount})` : row.rowAmount, 10), + size: 10, + color: rgb(0, 0, 0), + lineHeight: 10, + opacity: 1, + maxWidth: 240, + }) + + if (row.discountPercent > 0) { + + let text = row.discountText + + if (row.optional) text = `Optional - ${text}` + if (row.alternative) text = `Alternativ - ${text}` + + + pages[pageCounter - 1].drawText(text, { + y: getCoordinatesForPDFLib(25, rowHeight + 5, page1).y, + x: getCoordinatesForPDFLib(25, rowHeight + 5, page1).x + 490 - font.widthOfTextAtSize(text, 8), + size: 8, + color: rgb(0, 0, 0), + lineHeight: 10, + opacity: 1, + maxWidth: 240, + }) + } else if (row.optional) { + pages[pageCounter - 1].drawText("Optional", { + y: getCoordinatesForPDFLib(25, rowHeight + 5, page1).y, + x: getCoordinatesForPDFLib(25, rowHeight + 5, page1).x + 490 - font.widthOfTextAtSize("Optional", 8), + size: 8, + color: rgb(0, 0, 0), + lineHeight: 10, + opacity: 1, + maxWidth: 240, + }) + } else if (row.alternative) { + pages[pageCounter - 1].drawText("Alternativ", { + y: getCoordinatesForPDFLib(25, rowHeight + 5, page1).y, + x: getCoordinatesForPDFLib(25, rowHeight + 5, page1).x + 490 - font.widthOfTextAtSize("Alternativ", 8), + size: 8, + color: rgb(0, 0, 0), + lineHeight: 10, + opacity: 1, + maxWidth: 240, + }) + } + } + + if (row.descriptionText) { + rowHeight += rowDescriptionLines * 4.5 + rowTextLines * 5.5 + } else if (row.discountPercent) { + rowHeight += (rowTextLines + 1) * 5.5 + } else if (row.optional || row.alternative) { + rowHeight += (rowTextLines + 1) * 5.5 + } else { + rowHeight += rowTextLines * 5.5 + } + + + pageIndex += 1 + + + } else if (row.mode === 'pagebreak') { + + console.log(invoiceData.rows[index + 1]) + + if (invoiceData.rows[index + 1].mode === 'title') { + let transferSumText = `Übertrag: ${invoiceData.total.titleSumsTransfer[Object.keys(invoiceData.total.titleSums)[invoiceData.rows[index + 1].pos - 2]]}` + pages[pageCounter - 1].drawText(transferSumText, { + y: getCoordinatesForPDFLib(21, rowHeight - 2, page1).y, + x: getCoordinatesForPDFLib(21, rowHeight - 2, page1).x + 500 - fontBold.widthOfTextAtSize(transferSumText, 10), + size: 10, + color: rgb(0, 0, 0), + lineHeight: 10, + opacity: 1, + maxWidth: 240, + font: fontBold + }) + } + + + const page = pdfDoc.addPage() + + page.drawPage(secondPageBackground, { + x: 0, + y: 0, + }) + + //Falzmarke 1 + page.drawLine({ + start: getCoordinatesForPDFLib(0, 105, page1), + end: getCoordinatesForPDFLib(7, 105, page1), + thickness: 0.25, + color: rgb(0, 0, 0), + opacity: 1 + }) + + //Lochmarke + page.drawLine({ + start: getCoordinatesForPDFLib(0, 148.5, page1), + end: getCoordinatesForPDFLib(7, 148.5, page1), + thickness: 0.25, + color: rgb(0, 0, 0), + opacity: 1 + }) + + //Falzmarke 2 + page.drawLine({ + start: getCoordinatesForPDFLib(0, 210, page1), + end: getCoordinatesForPDFLib(7, 210, page1), + thickness: 0.25, + color: rgb(0, 0, 0), + opacity: 1 + }) + + page.drawText("Pos", { + ...getCoordinatesForPDFLib(21, 22, page1), + size: 12, + color: rgb(0, 0, 0), + lineHeight: 12, + opacity: 1, + maxWidth: 240, + font: fontBold + }) + + page.drawText("Menge", { + ...getCoordinatesForPDFLib(35, 22, page1), + size: 12, + color: rgb(0, 0, 0), + lineHeight: 12, + opacity: 1, + maxWidth: 240, + font: fontBold + }) + + page.drawText("Bezeichnung", { + ...getCoordinatesForPDFLib(52, 22, page1), + size: 12, + color: rgb(0, 0, 0), + lineHeight: 12, + opacity: 1, + maxWidth: 240, + font: fontBold + }) + + if (invoiceData.type !== "deliveryNotes") { + page.drawText("Steuer", { + ...getCoordinatesForPDFLib(135, 22, page1), + size: 12, + color: rgb(0, 0, 0), + lineHeight: 12, + opacity: 1, + maxWidth: 240, + font: fontBold + }) + + page.drawText("Einheitspreis", { + ...getCoordinatesForPDFLib(150, 22, page1), + size: 12, + color: rgb(0, 0, 0), + lineHeight: 12, + opacity: 1, + maxWidth: 240, + font: fontBold + }) + + page.drawText("Gesamt", { + y: getCoordinatesForPDFLib(25, 22, page1).y, + x: getCoordinatesForPDFLib(25, 22, page1).x + 490 - fontBold.widthOfTextAtSize("Gesamt", 12), + size: 12, + color: rgb(0, 0, 0), + lineHeight: 12, + opacity: 1, + maxWidth: 240, + font: fontBold + }) + } + + pageCounter += 1; + pageIndex = 0; + rowHeight = 30; + + pages.push(page) + + } else if (row.mode === 'title') { + if (index === 0 || pageIndex === 0) { + rowHeight += 3 + } else { + let transferSumText = `Übertrag: ${invoiceData.total.titleSumsTransfer[Object.keys(invoiceData.total.titleSums)[row.pos - 2]]}` + pages[pageCounter - 1].drawText(transferSumText, { + y: getCoordinatesForPDFLib(21, rowHeight - 2, page1).y, + x: getCoordinatesForPDFLib(21, rowHeight - 2, page1).x + 500 - fontBold.widthOfTextAtSize(transferSumText, 10), + size: 10, + color: rgb(0, 0, 0), + lineHeight: 10, + opacity: 1, + maxWidth: 240, + font: fontBold + }) + + pages[pageCounter - 1].drawLine({ + start: getCoordinatesForPDFLib(20, rowHeight, page1), + end: getCoordinatesForPDFLib(199, rowHeight, page1), + thickness: 0.2, + color: rgb(0, 0, 0), + opacity: 1, + }) + rowHeight += 5 + } + + pages[pageCounter - 1].drawText(String(row.pos), { + ...getCoordinatesForPDFLib(21, rowHeight, page1), + size: 10, + color: rgb(0, 0, 0), + lineHeight: 10, + opacity: 1, + maxWidth: 240, + font: fontBold + }) + + pages[pageCounter - 1].drawText(splitStringBySpace(row.text, 60).join("\n"), { + ...getCoordinatesForPDFLib(35, rowHeight, page1), + size: 12, + color: rgb(0, 0, 0), + lineHeight: 12, + opacity: 1, + maxWidth: 500, + font: fontBold + }) + + rowHeight += splitStringBySpace(row.text, 60).length * 4.5 + } else if (row.mode === 'text') { + if (index === 0 || pageIndex === 0) { + rowHeight += 3 + } + + if (row.descriptionText) { + pages[pageCounter - 1].drawText(splitStringBySpace(row.descriptionText, 70).join("\n"), { + ...getCoordinatesForPDFLib(35, rowHeight, page1), + size: 10, + color: rgb(0, 0, 0), + lineHeight: 10, + opacity: 1, + }) + + rowHeight += (splitStringBySpace(row.descriptionText, 70) || []).length * 4 + rowHeight += 4 + } + } + console.log(rowHeight) + }) + + + let endTextDiff = 35 + + if (invoiceData.type !== "deliveryNotes") { + pages[pageCounter - 1].drawLine({ + start: getCoordinatesForPDFLib(20, rowHeight, page1), + end: getCoordinatesForPDFLib(198, rowHeight, page1), + thickness: 0.2, + color: rgb(0, 0, 0), + opacity: 1, + }) + rowHeight += 6 + + + if (Object.keys(invoiceData.total.titleSums).length > 0) { + Object.keys(invoiceData.total.titleSums).forEach((key, index) => { + pages[pageCounter - 1].drawText(splitStringBySpace(key, 60).join("\n"), { + ...getCoordinatesForPDFLib(21, rowHeight, page1), + size: 11, + color: rgb(0, 0, 0), + lineHeight: 11, + opacity: 1, + font: fontBold + }) + + pages[pageCounter - 1].drawText(invoiceData.total.titleSums[key], { + y: getCoordinatesForPDFLib(21, rowHeight, page1).y, + x: getCoordinatesForPDFLib(21, rowHeight, page1).x + 500 - fontBold.widthOfTextAtSize(invoiceData.total.titleSums[key], 11), + size: 11, + color: rgb(0, 0, 0), + lineHeight: 11, + opacity: 1, + maxWidth: 240, + font: fontBold + }) + + rowHeight += splitStringBySpace(key, 60).length * 5 + }) + + /*let titleSumsArray = Object.keys(invoiceData.total.titleSums) + titleSumsArray.forEach(sum => { + let length = splitStringBySpace(sum,60).length + rowHeight += length *6 + })*/ + + //rowHeight += Object.keys(invoiceData.total.titleSums) + + pages[pageCounter - 1].drawLine({ + start: getCoordinatesForPDFLib(20, rowHeight, page1), + end: getCoordinatesForPDFLib(198, rowHeight, page1), + thickness: 0.2, + color: rgb(0, 0, 0), + opacity: 1, + }) + + rowHeight += 5 + } + + invoiceData.totalArray.forEach((item, index) => { + pages[pageCounter - 1].drawText(item.label, { + ...getCoordinatesForPDFLib(21, rowHeight + 8 * index, page1), + size: 11, + color: rgb(0, 0, 0), + lineHeight: 11, + opacity: 1, + maxWidth: 240, + font: fontBold + }) + + pages[pageCounter - 1].drawText(item.content, { + y: getCoordinatesForPDFLib(21, rowHeight + 8 * index, page1).y, + x: getCoordinatesForPDFLib(21, rowHeight + 8 * index, page1).x + 500 - fontBold.widthOfTextAtSize(item.content, 11), + size: 11, + color: rgb(0, 0, 0), + lineHeight: 11, + opacity: 1, + maxWidth: 240, + font: fontBold + }) + }) + + if (invoiceData.taxType !== "13b UStG" && invoiceData.taxType !== "19 UStG" && invoiceData.taxType !== "12.3 UStG") { + + } else { + if (invoiceData.taxType === "13b UStG") { + pages[pageCounter - 1].drawText("Die Umsatzsteuer für diese Leistung schuldet nach §13b UStG der Leistungsempfänger", { + ...getCoordinatesForPDFLib(21, rowHeight + invoiceData.totalArray.length * 8, page1), + size: 10, + color: rgb(0, 0, 0), + lineHeight: 10, + opacity: 1, + maxWidth: 500 + }) + } else if (invoiceData.taxType === "19 UStG") { + pages[pageCounter - 1].drawText("Als Kleinunternehmer im Sinne von § 19 Abs. 1 UStG wird keine Umsatzsteuer berechnet.", { + ...getCoordinatesForPDFLib(20, rowHeight + invoiceData.totalArray.length * 8, page1), + size: 10, + color: rgb(0, 0, 0), + lineHeight: 10, + opacity: 1, + maxWidth: 500 + }) + } else if (invoiceData.taxType === "12.3 UStG") { + pages[pageCounter - 1].drawText("Umsatzsteuer befreite Lieferung/Leistung für PV-Anlagen gemäß § 12 Absatz 3 UStG.", { + ...getCoordinatesForPDFLib(20, rowHeight + invoiceData.totalArray.length * 8, page1), + size: 10, + color: rgb(0, 0, 0), + lineHeight: 10, + opacity: 1, + maxWidth: 500 + }) + } + + + } + + + pages[pageCounter - 1].drawText(invoiceData.endText, { + ...getCoordinatesForPDFLib(21, rowHeight + endTextDiff + (invoiceData.totalArray.length - 3) * 8, page1), + size: 10, + color: rgb(0, 0, 0), + lineHeight: 10, + opacity: 1, + maxWidth: 500 + }) + + return await pdfDoc.saveAsBase64() + } + + } + + const {data:backgroundPDFData,error:backgroundPDFError} = await server.supabase.storage.from("files").download(backgroundPath) + + const pdfBytes = await genPDF(invoiceData, await backgroundPDFData.arrayBuffer()) + + if(returnMode === "base64"){ + return { + mimeType: 'application/pdf', + base64: pdfBytes + } + } +} \ No newline at end of file diff --git a/src/utils/stringRendering.ts b/src/utils/stringRendering.ts new file mode 100644 index 0000000..72dbad1 --- /dev/null +++ b/src/utils/stringRendering.ts @@ -0,0 +1,51 @@ + +export const renderAsCurrency = (value: string | number,currencyString = "€") => { + return `${Number(value).toFixed(2).replace(".",",")} ${currencyString}` +} + +export const splitStringBySpace = (input:string,maxSplitLength:number,removeLinebreaks = false) => { + + if(removeLinebreaks) { + input = input.replaceAll("\n","") + } + + let splitStrings: string[] = [] + + input.split("\n").forEach(string => { + splitStrings.push(string) + }) + + let returnSplitStrings: string[] = [] + + splitStrings.forEach(string => { + let regex = / /gi, result, indices = []; + while ( (result = regex.exec(string)) ) { + indices.push(result.index); + } + + let lastIndex = 0 + + if(string.length > maxSplitLength) { + let tempStrings = [] + + for (let i = maxSplitLength; i < string.length; i = i + maxSplitLength) { + let nearestIndex = indices.length > 0 ? indices.reduce(function(prev, curr) { + return (Math.abs(curr - i) < Math.abs(prev - i) ? curr : prev); + }) : i + + tempStrings.push(string.substring(lastIndex,nearestIndex)) + + lastIndex = indices.length > 0 ? nearestIndex + 1 : nearestIndex + } + + tempStrings.push(string.substring(lastIndex,input.length)) + + returnSplitStrings.push(...tempStrings) + + } else { + returnSplitStrings.push(string) + } + }) + + return returnSplitStrings +} \ No newline at end of file From fc46e807e7b7436038a5cbc79657e7389c3c1734 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Fri, 12 Sep 2025 18:38:13 +0200 Subject: [PATCH 006/149] Added Running Config --- .gitignore | 5 + .gitlab-ci.yml | 18 + Dockerfile | 20 + docker-compose.yml | 24 + package-lock.json | 5292 ++++++++++++++++++++++++++++++++++++++++++++ package.json | 48 + tsconfig.json | 18 + 7 files changed, 5425 insertions(+) create mode 100644 .gitignore create mode 100644 .gitlab-ci.yml create mode 100644 Dockerfile create mode 100644 docker-compose.yml create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..126419d --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +node_modules +# Keep environment variables out of version control +.env + +/src/generated/prisma diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..499db90 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,18 @@ +before_script: + - docker info + +stages: + - build + +build-backend: + stage: build + tags: + - shell + - docker-daemon + variables: + IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG + script: + - echo $IMAGE_TAG + - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY + - docker build -t $IMAGE_TAG . + - docker push $IMAGE_TAG diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..a8477c2 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,20 @@ +# Basis-Image mit Node.js +FROM node:20-alpine + +# Arbeitsverzeichnis im Container +WORKDIR /usr/src/app + +# package.json und package-lock.json zuerst kopieren +COPY package*.json ./ + +# Dependencies installieren (nur Produktion, falls nötig) +RUN npm install --omit=dev + +# Restlichen Quellcode kopieren +COPY . . + +# Port setzen (Fastify läuft standardmäßig auf 3000) +EXPOSE 3000 + +# Startkommando +CMD ["npm", "start"] \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..ab0b02c --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,24 @@ +services: + backend: + image: reg.federspiel.software/fedeo/backend:main + restart: always + + environment: + JWT_SECRET: "pd6Hp76xtN8R9t6WkagH2FnW8wDWx" + COOKIE_SECRET: "X9mieXp98PCXkFWja66xnDNaJhVKq" + MAIL_FROM: "FEDEO Notify " + SMTP_HOST: "mail.your-server.de" + SMTP_PASS: "NDor3A9QLbc87V39" + SMTP_PORT: 465 + SMTP_SSL: true + SMTP_USER: "notify@fedeo.de" + SUPABASE_SERVICE_ROLE_KEY: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InV3cHB2Y3hmbHJjc2lidXpzYmlsIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTcwMDkzODE5NCwiZXhwIjoyMDE2NTE0MTk0fQ.6hOkD1J8XBkVJUm-swv0ngLQ74xrEYr28EEbo0rUrts" + SUPABASE_URL: "https://uwppvcxflrcsibuzsbil.supabase.co" + PORT: 3100 + S3_BUCKET: "FEDEO" + S3_SECRET_KEY: "aZ33xBv47sPPsHuFKeHSDiLagjqF7nShnuGkj7B1" + S3_ACCESS_KEY: "RYOMQRW8KSTY3UQX7RPJ" + S3_REGION: "eu-central" + S3_ENDPOINT: "https://fedeo.nbg1.your-objectstorage.com" + EMAILENGINE_TOKEN: "dcd8209bc5371c728f9ec951600afcfc74e8c391a7e984b2a6df9c4665dc7ad6" + EMAILENGINE_URL: "https://ee.fedeo.io/v1" diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..a8ee19d --- /dev/null +++ b/package-lock.json @@ -0,0 +1,5292 @@ +{ + "name": "backend", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "backend", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@aws-sdk/client-s3": "^3.879.0", + "@aws-sdk/s3-request-presigner": "^3.879.0", + "@fastify/cookie": "^11.0.2", + "@fastify/cors": "^11.1.0", + "@fastify/multipart": "^9.0.3", + "@fastify/swagger": "^9.5.1", + "@fastify/swagger-ui": "^5.2.3", + "@prisma/client": "^6.15.0", + "@supabase/supabase-js": "^2.56.1", + "@zip.js/zip.js": "^2.7.73", + "archiver": "^7.0.1", + "axios": "^1.12.1", + "bcrypt": "^6.0.0", + "dayjs": "^1.11.18", + "fastify": "^5.5.0", + "fastify-plugin": "^5.0.1", + "jsonwebtoken": "^9.0.2", + "nodemailer": "^7.0.6", + "pdf-lib": "^1.17.1", + "xmlbuilder": "^15.1.1" + }, + "devDependencies": { + "@types/bcrypt": "^6.0.0", + "@types/jsonwebtoken": "^9.0.10", + "@types/node": "^24.3.0", + "prisma": "^6.15.0", + "tsx": "^4.20.5", + "typescript": "^5.9.2" + } + }, + "node_modules/@aws-crypto/crc32": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", + "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/crc32c": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz", + "integrity": "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha1-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-5.2.0.tgz", + "integrity": "sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", + "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", + "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3": { + "version": "3.879.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.879.0.tgz", + "integrity": "sha512-1bD2Do/OdCIzl72ncHKYamDhPijUErLYpuLvciyYD4Ywt4cVLHjWtVIqb22XOOHYYHE3NqHMd4uRhvXMlsBRoQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha1-browser": "5.2.0", + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.879.0", + "@aws-sdk/credential-provider-node": "3.879.0", + "@aws-sdk/middleware-bucket-endpoint": "3.873.0", + "@aws-sdk/middleware-expect-continue": "3.873.0", + "@aws-sdk/middleware-flexible-checksums": "3.879.0", + "@aws-sdk/middleware-host-header": "3.873.0", + "@aws-sdk/middleware-location-constraint": "3.873.0", + "@aws-sdk/middleware-logger": "3.876.0", + "@aws-sdk/middleware-recursion-detection": "3.873.0", + "@aws-sdk/middleware-sdk-s3": "3.879.0", + "@aws-sdk/middleware-ssec": "3.873.0", + "@aws-sdk/middleware-user-agent": "3.879.0", + "@aws-sdk/region-config-resolver": "3.873.0", + "@aws-sdk/signature-v4-multi-region": "3.879.0", + "@aws-sdk/types": "3.862.0", + "@aws-sdk/util-endpoints": "3.879.0", + "@aws-sdk/util-user-agent-browser": "3.873.0", + "@aws-sdk/util-user-agent-node": "3.879.0", + "@aws-sdk/xml-builder": "3.873.0", + "@smithy/config-resolver": "^4.1.5", + "@smithy/core": "^3.9.0", + "@smithy/eventstream-serde-browser": "^4.0.5", + "@smithy/eventstream-serde-config-resolver": "^4.1.3", + "@smithy/eventstream-serde-node": "^4.0.5", + "@smithy/fetch-http-handler": "^5.1.1", + "@smithy/hash-blob-browser": "^4.0.5", + "@smithy/hash-node": "^4.0.5", + "@smithy/hash-stream-node": "^4.0.5", + "@smithy/invalid-dependency": "^4.0.5", + "@smithy/md5-js": "^4.0.5", + "@smithy/middleware-content-length": "^4.0.5", + "@smithy/middleware-endpoint": "^4.1.19", + "@smithy/middleware-retry": "^4.1.20", + "@smithy/middleware-serde": "^4.0.9", + "@smithy/middleware-stack": "^4.0.5", + "@smithy/node-config-provider": "^4.1.4", + "@smithy/node-http-handler": "^4.1.1", + "@smithy/protocol-http": "^5.1.3", + "@smithy/smithy-client": "^4.5.0", + "@smithy/types": "^4.3.2", + "@smithy/url-parser": "^4.0.5", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.27", + "@smithy/util-defaults-mode-node": "^4.0.27", + "@smithy/util-endpoints": "^3.0.7", + "@smithy/util-middleware": "^4.0.5", + "@smithy/util-retry": "^4.0.7", + "@smithy/util-stream": "^4.2.4", + "@smithy/util-utf8": "^4.0.0", + "@smithy/util-waiter": "^4.0.7", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sso": { + "version": "3.879.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.879.0.tgz", + "integrity": "sha512-+Pc3OYFpRYpKLKRreovPM63FPPud1/SF9vemwIJfz6KwsBCJdvg7vYD1xLSIp5DVZLeetgf4reCyAA5ImBfZuw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.879.0", + "@aws-sdk/middleware-host-header": "3.873.0", + "@aws-sdk/middleware-logger": "3.876.0", + "@aws-sdk/middleware-recursion-detection": "3.873.0", + "@aws-sdk/middleware-user-agent": "3.879.0", + "@aws-sdk/region-config-resolver": "3.873.0", + "@aws-sdk/types": "3.862.0", + "@aws-sdk/util-endpoints": "3.879.0", + "@aws-sdk/util-user-agent-browser": "3.873.0", + "@aws-sdk/util-user-agent-node": "3.879.0", + "@smithy/config-resolver": "^4.1.5", + "@smithy/core": "^3.9.0", + "@smithy/fetch-http-handler": "^5.1.1", + "@smithy/hash-node": "^4.0.5", + "@smithy/invalid-dependency": "^4.0.5", + "@smithy/middleware-content-length": "^4.0.5", + "@smithy/middleware-endpoint": "^4.1.19", + "@smithy/middleware-retry": "^4.1.20", + "@smithy/middleware-serde": "^4.0.9", + "@smithy/middleware-stack": "^4.0.5", + "@smithy/node-config-provider": "^4.1.4", + "@smithy/node-http-handler": "^4.1.1", + "@smithy/protocol-http": "^5.1.3", + "@smithy/smithy-client": "^4.5.0", + "@smithy/types": "^4.3.2", + "@smithy/url-parser": "^4.0.5", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.27", + "@smithy/util-defaults-mode-node": "^4.0.27", + "@smithy/util-endpoints": "^3.0.7", + "@smithy/util-middleware": "^4.0.5", + "@smithy/util-retry": "^4.0.7", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.879.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.879.0.tgz", + "integrity": "sha512-AhNmLCrx980LsK+SfPXGh7YqTyZxsK0Qmy18mWmkfY0TSq7WLaSDB5zdQbgbnQCACCHy8DUYXbi4KsjlIhv3PA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.862.0", + "@aws-sdk/xml-builder": "3.873.0", + "@smithy/core": "^3.9.0", + "@smithy/node-config-provider": "^4.1.4", + "@smithy/property-provider": "^4.0.5", + "@smithy/protocol-http": "^5.1.3", + "@smithy/signature-v4": "^5.1.3", + "@smithy/smithy-client": "^4.5.0", + "@smithy/types": "^4.3.2", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-middleware": "^4.0.5", + "@smithy/util-utf8": "^4.0.0", + "fast-xml-parser": "5.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.879.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.879.0.tgz", + "integrity": "sha512-JgG7A8SSbr5IiCYL8kk39Y9chdSB5GPwBorDW8V8mr19G9L+qd6ohED4fAocoNFaDnYJ5wGAHhCfSJjzcsPBVQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.879.0", + "@aws-sdk/types": "3.862.0", + "@smithy/property-provider": "^4.0.5", + "@smithy/types": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.879.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.879.0.tgz", + "integrity": "sha512-2hM5ByLpyK+qORUexjtYyDZsgxVCCUiJQZRMGkNXFEGz6zTpbjfTIWoh3zRgWHEBiqyPIyfEy50eIF69WshcuA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.879.0", + "@aws-sdk/types": "3.862.0", + "@smithy/fetch-http-handler": "^5.1.1", + "@smithy/node-http-handler": "^4.1.1", + "@smithy/property-provider": "^4.0.5", + "@smithy/protocol-http": "^5.1.3", + "@smithy/smithy-client": "^4.5.0", + "@smithy/types": "^4.3.2", + "@smithy/util-stream": "^4.2.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.879.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.879.0.tgz", + "integrity": "sha512-07M8zfb73KmMBqVO5/V3Ea9kqDspMX0fO0kaI1bsjWI6ngnMye8jCE0/sIhmkVAI0aU709VA0g+Bzlopnw9EoQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.879.0", + "@aws-sdk/credential-provider-env": "3.879.0", + "@aws-sdk/credential-provider-http": "3.879.0", + "@aws-sdk/credential-provider-process": "3.879.0", + "@aws-sdk/credential-provider-sso": "3.879.0", + "@aws-sdk/credential-provider-web-identity": "3.879.0", + "@aws-sdk/nested-clients": "3.879.0", + "@aws-sdk/types": "3.862.0", + "@smithy/credential-provider-imds": "^4.0.7", + "@smithy/property-provider": "^4.0.5", + "@smithy/shared-ini-file-loader": "^4.0.5", + "@smithy/types": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.879.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.879.0.tgz", + "integrity": "sha512-FYaAqJbnSTrVL2iZkNDj2hj5087yMv2RN2GA8DJhe7iOJjzhzRojrtlfpWeJg6IhK0sBKDH+YXbdeexCzUJvtA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.879.0", + "@aws-sdk/credential-provider-http": "3.879.0", + "@aws-sdk/credential-provider-ini": "3.879.0", + "@aws-sdk/credential-provider-process": "3.879.0", + "@aws-sdk/credential-provider-sso": "3.879.0", + "@aws-sdk/credential-provider-web-identity": "3.879.0", + "@aws-sdk/types": "3.862.0", + "@smithy/credential-provider-imds": "^4.0.7", + "@smithy/property-provider": "^4.0.5", + "@smithy/shared-ini-file-loader": "^4.0.5", + "@smithy/types": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.879.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.879.0.tgz", + "integrity": "sha512-7r360x1VyEt35Sm1JFOzww2WpnfJNBbvvnzoyLt7WRfK0S/AfsuWhu5ltJ80QvJ0R3AiSNbG+q/btG2IHhDYPQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.879.0", + "@aws-sdk/types": "3.862.0", + "@smithy/property-provider": "^4.0.5", + "@smithy/shared-ini-file-loader": "^4.0.5", + "@smithy/types": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.879.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.879.0.tgz", + "integrity": "sha512-gd27B0NsgtKlaPNARj4IX7F7US5NuU691rGm0EUSkDsM7TctvJULighKoHzPxDQlrDbVI11PW4WtKS/Zg5zPlQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.879.0", + "@aws-sdk/core": "3.879.0", + "@aws-sdk/token-providers": "3.879.0", + "@aws-sdk/types": "3.862.0", + "@smithy/property-provider": "^4.0.5", + "@smithy/shared-ini-file-loader": "^4.0.5", + "@smithy/types": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.879.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.879.0.tgz", + "integrity": "sha512-Jy4uPFfGzHk1Mxy+/Wr43vuw9yXsE2yiF4e4598vc3aJfO0YtA2nSfbKD3PNKRORwXbeKqWPfph9SCKQpWoxEg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.879.0", + "@aws-sdk/nested-clients": "3.879.0", + "@aws-sdk/types": "3.862.0", + "@smithy/property-provider": "^4.0.5", + "@smithy/types": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-bucket-endpoint": { + "version": "3.873.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.873.0.tgz", + "integrity": "sha512-b4bvr0QdADeTUs+lPc9Z48kXzbKHXQKgTvxx/jXDgSW9tv4KmYPO1gIj6Z9dcrBkRWQuUtSW3Tu2S5n6pe+zeg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.862.0", + "@aws-sdk/util-arn-parser": "3.873.0", + "@smithy/node-config-provider": "^4.1.4", + "@smithy/protocol-http": "^5.1.3", + "@smithy/types": "^4.3.2", + "@smithy/util-config-provider": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-expect-continue": { + "version": "3.873.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.873.0.tgz", + "integrity": "sha512-GIqoc8WgRcf/opBOZXFLmplJQKwOMjiOMmDz9gQkaJ8FiVJoAp8EGVmK2TOWZMQUYsavvHYsHaor5R2xwPoGVg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.862.0", + "@smithy/protocol-http": "^5.1.3", + "@smithy/types": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-flexible-checksums": { + "version": "3.879.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.879.0.tgz", + "integrity": "sha512-U1rcWToy2rlQPQLsx5h73uTC1XYo/JpnlJGCc3Iw7b1qrK8Mke4+rgMPKCfnXELD5TTazGrbT03frxH4Y1Ycvw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@aws-crypto/crc32c": "5.2.0", + "@aws-crypto/util": "5.2.0", + "@aws-sdk/core": "3.879.0", + "@aws-sdk/types": "3.862.0", + "@smithy/is-array-buffer": "^4.0.0", + "@smithy/node-config-provider": "^4.1.4", + "@smithy/protocol-http": "^5.1.3", + "@smithy/types": "^4.3.2", + "@smithy/util-middleware": "^4.0.5", + "@smithy/util-stream": "^4.2.4", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.873.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.873.0.tgz", + "integrity": "sha512-KZ/W1uruWtMOs7D5j3KquOxzCnV79KQW9MjJFZM/M0l6KI8J6V3718MXxFHsTjUE4fpdV6SeCNLV1lwGygsjJA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.862.0", + "@smithy/protocol-http": "^5.1.3", + "@smithy/types": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-location-constraint": { + "version": "3.873.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.873.0.tgz", + "integrity": "sha512-r+hIaORsW/8rq6wieDordXnA/eAu7xAPLue2InhoEX6ML7irP52BgiibHLpt9R0psiCzIHhju8qqKa4pJOrmiw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.862.0", + "@smithy/types": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.876.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.876.0.tgz", + "integrity": "sha512-cpWJhOuMSyz9oV25Z/CMHCBTgafDCbv7fHR80nlRrPdPZ8ETNsahwRgltXP1QJJ8r3X/c1kwpOR7tc+RabVzNA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.862.0", + "@smithy/types": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.873.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.873.0.tgz", + "integrity": "sha512-OtgY8EXOzRdEWR//WfPkA/fXl0+WwE8hq0y9iw2caNyKPtca85dzrrZWnPqyBK/cpImosrpR1iKMYr41XshsCg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.862.0", + "@smithy/protocol-http": "^5.1.3", + "@smithy/types": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3": { + "version": "3.879.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.879.0.tgz", + "integrity": "sha512-ZTpLr2AbZcCsEzu18YCtB8Tp8tjAWHT0ccfwy3HiL6g9ncuSMW+7BVi1hDYmBidFwpPbnnIMtM0db3pDMR6/WA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.879.0", + "@aws-sdk/types": "3.862.0", + "@aws-sdk/util-arn-parser": "3.873.0", + "@smithy/core": "^3.9.0", + "@smithy/node-config-provider": "^4.1.4", + "@smithy/protocol-http": "^5.1.3", + "@smithy/signature-v4": "^5.1.3", + "@smithy/smithy-client": "^4.5.0", + "@smithy/types": "^4.3.2", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.5", + "@smithy/util-stream": "^4.2.4", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-ssec": { + "version": "3.873.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.873.0.tgz", + "integrity": "sha512-AF55J94BoiuzN7g3hahy0dXTVZahVi8XxRBLgzNp6yQf0KTng+hb/V9UQZVYY1GZaDczvvvnqC54RGe9OZZ9zQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.862.0", + "@smithy/types": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.879.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.879.0.tgz", + "integrity": "sha512-DDSV8228lQxeMAFKnigkd0fHzzn5aauZMYC3CSj6e5/qE7+9OwpkUcjHfb7HZ9KWG6L2/70aKZXHqiJ4xKhOZw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.879.0", + "@aws-sdk/types": "3.862.0", + "@aws-sdk/util-endpoints": "3.879.0", + "@smithy/core": "^3.9.0", + "@smithy/protocol-http": "^5.1.3", + "@smithy/types": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients": { + "version": "3.879.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.879.0.tgz", + "integrity": "sha512-7+n9NpIz9QtKYnxmw1fHi9C8o0GrX8LbBR4D50c7bH6Iq5+XdSuL5AFOWWQ5cMD0JhqYYJhK/fJsVau3nUtC4g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.879.0", + "@aws-sdk/middleware-host-header": "3.873.0", + "@aws-sdk/middleware-logger": "3.876.0", + "@aws-sdk/middleware-recursion-detection": "3.873.0", + "@aws-sdk/middleware-user-agent": "3.879.0", + "@aws-sdk/region-config-resolver": "3.873.0", + "@aws-sdk/types": "3.862.0", + "@aws-sdk/util-endpoints": "3.879.0", + "@aws-sdk/util-user-agent-browser": "3.873.0", + "@aws-sdk/util-user-agent-node": "3.879.0", + "@smithy/config-resolver": "^4.1.5", + "@smithy/core": "^3.9.0", + "@smithy/fetch-http-handler": "^5.1.1", + "@smithy/hash-node": "^4.0.5", + "@smithy/invalid-dependency": "^4.0.5", + "@smithy/middleware-content-length": "^4.0.5", + "@smithy/middleware-endpoint": "^4.1.19", + "@smithy/middleware-retry": "^4.1.20", + "@smithy/middleware-serde": "^4.0.9", + "@smithy/middleware-stack": "^4.0.5", + "@smithy/node-config-provider": "^4.1.4", + "@smithy/node-http-handler": "^4.1.1", + "@smithy/protocol-http": "^5.1.3", + "@smithy/smithy-client": "^4.5.0", + "@smithy/types": "^4.3.2", + "@smithy/url-parser": "^4.0.5", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.27", + "@smithy/util-defaults-mode-node": "^4.0.27", + "@smithy/util-endpoints": "^3.0.7", + "@smithy/util-middleware": "^4.0.5", + "@smithy/util-retry": "^4.0.7", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.873.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.873.0.tgz", + "integrity": "sha512-q9sPoef+BBG6PJnc4x60vK/bfVwvRWsPgcoQyIra057S/QGjq5VkjvNk6H8xedf6vnKlXNBwq9BaANBXnldUJg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.862.0", + "@smithy/node-config-provider": "^4.1.4", + "@smithy/types": "^4.3.2", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/s3-request-presigner": { + "version": "3.879.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.879.0.tgz", + "integrity": "sha512-WNUrY4UW1ZAkBiSq9HnhJcG/1NdrEy37DDxqE8u0OdIZHhbgU1x1r4iXgQssAZhV6D+Ib70oiQGtPSH/lXeMKg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/signature-v4-multi-region": "3.879.0", + "@aws-sdk/types": "3.862.0", + "@aws-sdk/util-format-url": "3.873.0", + "@smithy/middleware-endpoint": "^4.1.19", + "@smithy/protocol-http": "^5.1.3", + "@smithy/smithy-client": "^4.5.0", + "@smithy/types": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.879.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.879.0.tgz", + "integrity": "sha512-MDsw0EWOHyKac75X3gD8tLWtmPuRliS/s4IhWRhsdDCU13wewHIs5IlA5B65kT6ISf49yEIalEH3FHUSVqdmIQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-sdk-s3": "3.879.0", + "@aws-sdk/types": "3.862.0", + "@smithy/protocol-http": "^5.1.3", + "@smithy/signature-v4": "^5.1.3", + "@smithy/types": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.879.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.879.0.tgz", + "integrity": "sha512-47J7sCwXdnw9plRZNAGVkNEOlSiLb/kR2slnDIHRK9NB/ECKsoqgz5OZQJ9E2f0yqOs8zSNJjn3T01KxpgW8Qw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.879.0", + "@aws-sdk/nested-clients": "3.879.0", + "@aws-sdk/types": "3.862.0", + "@smithy/property-provider": "^4.0.5", + "@smithy/shared-ini-file-loader": "^4.0.5", + "@smithy/types": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.862.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.862.0.tgz", + "integrity": "sha512-Bei+RL0cDxxV+lW2UezLbCYYNeJm6Nzee0TpW0FfyTRBhH9C1XQh4+x+IClriXvgBnRquTMMYsmJfvx8iyLKrg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-arn-parser": { + "version": "3.873.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.873.0.tgz", + "integrity": "sha512-qag+VTqnJWDn8zTAXX4wiVioa0hZDQMtbZcGRERVnLar4/3/VIKBhxX2XibNQXFu1ufgcRn4YntT/XEPecFWcg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.879.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.879.0.tgz", + "integrity": "sha512-aVAJwGecYoEmbEFju3127TyJDF9qJsKDUUTRMDuS8tGn+QiWQFnfInmbt+el9GU1gEJupNTXV+E3e74y51fb7A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.862.0", + "@smithy/types": "^4.3.2", + "@smithy/url-parser": "^4.0.5", + "@smithy/util-endpoints": "^3.0.7", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-format-url": { + "version": "3.873.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-format-url/-/util-format-url-3.873.0.tgz", + "integrity": "sha512-v//b9jFnhzTKKV3HFTw2MakdM22uBAs2lBov51BWmFXuFtSTdBLrR7zgfetQPE3PVkFai0cmtJQPdc3MX+T/cQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.862.0", + "@smithy/querystring-builder": "^4.0.5", + "@smithy/types": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.873.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.873.0.tgz", + "integrity": "sha512-xcVhZF6svjM5Rj89T1WzkjQmrTF6dpR2UvIHPMTnSZoNe6CixejPZ6f0JJ2kAhO8H+dUHwNBlsUgOTIKiK/Syg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.873.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.873.0.tgz", + "integrity": "sha512-AcRdbK6o19yehEcywI43blIBhOCSo6UgyWcuOJX5CFF8k39xm1ILCjQlRRjchLAxWrm0lU0Q7XV90RiMMFMZtA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.862.0", + "@smithy/types": "^4.3.2", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.879.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.879.0.tgz", + "integrity": "sha512-A5KGc1S+CJRzYnuxJQQmH1BtGsz46AgyHkqReKfGiNQA8ET/9y9LQ5t2ABqnSBHHIh3+MiCcQSkUZ0S3rTodrQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.879.0", + "@aws-sdk/types": "3.862.0", + "@smithy/node-config-provider": "^4.1.4", + "@smithy/types": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/xml-builder": { + "version": "3.873.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.873.0.tgz", + "integrity": "sha512-kLO7k7cGJ6KaHiExSJWojZurF7SnGMDHXRuQunFnEoD0n1yB6Lqy/S/zHiQ7oJnBhPr9q0TW9qFkrsZb1Uc54w==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", + "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz", + "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz", + "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz", + "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz", + "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz", + "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz", + "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz", + "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz", + "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz", + "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz", + "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz", + "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz", + "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz", + "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz", + "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz", + "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz", + "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz", + "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz", + "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz", + "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz", + "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz", + "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz", + "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz", + "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz", + "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz", + "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@fastify/accept-negotiator": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@fastify/accept-negotiator/-/accept-negotiator-2.0.1.tgz", + "integrity": "sha512-/c/TW2bO/v9JeEgoD/g1G5GxGeCF1Hafdf79WPmUlgYiBXummY0oX3VVq4yFkKKVBKDNlaDUYoab7g38RpPqCQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, + "node_modules/@fastify/ajv-compiler": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-4.0.2.tgz", + "integrity": "sha512-Rkiu/8wIjpsf46Rr+Fitd3HRP+VsxUFDDeag0hs9L0ksfnwx2g7SPQQTFL0E8Qv+rfXzQOxBJnjUB9ITUDjfWQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "ajv": "^8.12.0", + "ajv-formats": "^3.0.1", + "fast-uri": "^3.0.0" + } + }, + "node_modules/@fastify/busboy": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.2.0.tgz", + "integrity": "sha512-m9FVDXU3GT2ITSe0UaMA5rU3QkfC/UXtCU8y0gSN/GugTqtVldOBWIB5V6V3sbmenVZUIpU6f+mPEO2+m5iTaA==", + "license": "MIT" + }, + "node_modules/@fastify/cookie": { + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/@fastify/cookie/-/cookie-11.0.2.tgz", + "integrity": "sha512-GWdwdGlgJxyvNv+QcKiGNevSspMQXncjMZ1J8IvuDQk0jvkzgWWZFNC2En3s+nHndZBGV8IbLwOI/sxCZw/mzA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "cookie": "^1.0.0", + "fastify-plugin": "^5.0.0" + } + }, + "node_modules/@fastify/cors": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/@fastify/cors/-/cors-11.1.0.tgz", + "integrity": "sha512-sUw8ed8wP2SouWZTIbA7V2OQtMNpLj2W6qJOYhNdcmINTu6gsxVYXjQiM9mdi8UUDlcoDDJ/W2syPo1WB2QjYA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "fastify-plugin": "^5.0.0", + "toad-cache": "^3.7.0" + } + }, + "node_modules/@fastify/deepmerge": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@fastify/deepmerge/-/deepmerge-2.0.2.tgz", + "integrity": "sha512-3wuLdX5iiiYeZWP6bQrjqhrcvBIf0NHbQH1Ur1WbHvoiuTYUEItgygea3zs8aHpiitn0lOB8gX20u1qO+FDm7Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, + "node_modules/@fastify/error": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@fastify/error/-/error-4.2.0.tgz", + "integrity": "sha512-RSo3sVDXfHskiBZKBPRgnQTtIqpi/7zhJOEmAxCiBcM7d0uwdGdxLlsCaLzGs8v8NnxIRlfG0N51p5yFaOentQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, + "node_modules/@fastify/fast-json-stringify-compiler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-5.0.3.tgz", + "integrity": "sha512-uik7yYHkLr6fxd8hJSZ8c+xF4WafPK+XzneQDPU+D10r5X19GW8lJcom2YijX2+qtFF1ENJlHXKFM9ouXNJYgQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "fast-json-stringify": "^6.0.0" + } + }, + "node_modules/@fastify/forwarded": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@fastify/forwarded/-/forwarded-3.0.0.tgz", + "integrity": "sha512-kJExsp4JCms7ipzg7SJ3y8DwmePaELHxKYtg+tZow+k0znUTf3cb+npgyqm8+ATZOdmfgfydIebPDWM172wfyA==", + "license": "MIT" + }, + "node_modules/@fastify/merge-json-schemas": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@fastify/merge-json-schemas/-/merge-json-schemas-0.2.1.tgz", + "integrity": "sha512-OA3KGBCy6KtIvLf8DINC5880o5iBlDX4SxzLQS8HorJAbqluzLRn80UXU0bxZn7UOFhFgpRJDasfwn9nG4FG4A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/@fastify/multipart": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/@fastify/multipart/-/multipart-9.0.3.tgz", + "integrity": "sha512-pJogxQCrT12/6I5Fh6jr3narwcymA0pv4B0jbC7c6Bl9wnrxomEUnV0d26w6gUls7gSXmhG8JGRMmHFIPsxt1g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "@fastify/busboy": "^3.0.0", + "@fastify/deepmerge": "^2.0.0", + "@fastify/error": "^4.0.0", + "fastify-plugin": "^5.0.0", + "secure-json-parse": "^3.0.0" + } + }, + "node_modules/@fastify/multipart/node_modules/secure-json-parse": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-3.0.2.tgz", + "integrity": "sha512-H6nS2o8bWfpFEV6U38sOSjS7bTbdgbCGU9wEM6W14P5H0QOsz94KCusifV44GpHDTu2nqZbuDNhTzu+mjDSw1w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/@fastify/proxy-addr": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@fastify/proxy-addr/-/proxy-addr-5.0.0.tgz", + "integrity": "sha512-37qVVA1qZ5sgH7KpHkkC4z9SK6StIsIcOmpjvMPXNb3vx2GQxhZocogVYbr2PbbeLCQxYIPDok307xEvRZOzGA==", + "license": "MIT", + "dependencies": { + "@fastify/forwarded": "^3.0.0", + "ipaddr.js": "^2.1.0" + } + }, + "node_modules/@fastify/send": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@fastify/send/-/send-4.1.0.tgz", + "integrity": "sha512-TMYeQLCBSy2TOFmV95hQWkiTYgC/SEx7vMdV+wnZVX4tt8VBLKzmH8vV9OzJehV0+XBfg+WxPMt5wp+JBUKsVw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "@lukeed/ms": "^2.0.2", + "escape-html": "~1.0.3", + "fast-decode-uri-component": "^1.0.1", + "http-errors": "^2.0.0", + "mime": "^3" + } + }, + "node_modules/@fastify/static": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@fastify/static/-/static-8.2.0.tgz", + "integrity": "sha512-PejC/DtT7p1yo3p+W7LiUtLMsV8fEvxAK15sozHy9t8kwo5r0uLYmhV/inURmGz1SkHZFz/8CNtHLPyhKcx4SQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "@fastify/accept-negotiator": "^2.0.0", + "@fastify/send": "^4.0.0", + "content-disposition": "^0.5.4", + "fastify-plugin": "^5.0.0", + "fastq": "^1.17.1", + "glob": "^11.0.0" + } + }, + "node_modules/@fastify/swagger": { + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/@fastify/swagger/-/swagger-9.5.1.tgz", + "integrity": "sha512-EGjYLA7vDmCPK7XViAYMF6y4+K3XUy5soVTVxsyXolNe/Svb4nFQxvtuQvvoQb2Gzc9pxiF3+ZQN/iZDHhKtTg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "fastify-plugin": "^5.0.0", + "json-schema-resolver": "^3.0.0", + "openapi-types": "^12.1.3", + "rfdc": "^1.3.1", + "yaml": "^2.4.2" + } + }, + "node_modules/@fastify/swagger-ui": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@fastify/swagger-ui/-/swagger-ui-5.2.3.tgz", + "integrity": "sha512-e7ivEJi9EpFcxTONqICx4llbpB2jmlI+LI1NQ/mR7QGQnyDOqZybPK572zJtcdHZW4YyYTBHcP3a03f1pOh0SA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "@fastify/static": "^8.0.0", + "fastify-plugin": "^5.0.0", + "openapi-types": "^12.1.3", + "rfdc": "^1.3.1", + "yaml": "^2.4.1" + } + }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@lukeed/ms": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@lukeed/ms/-/ms-2.0.2.tgz", + "integrity": "sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@pdf-lib/standard-fonts": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@pdf-lib/standard-fonts/-/standard-fonts-1.0.0.tgz", + "integrity": "sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==", + "license": "MIT", + "dependencies": { + "pako": "^1.0.6" + } + }, + "node_modules/@pdf-lib/upng": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@pdf-lib/upng/-/upng-1.0.1.tgz", + "integrity": "sha512-dQK2FUMQtowVP00mtIksrlZhdFXQZPC+taih1q4CvPZ5vqdxR/LKBaFg0oAfzd1GlHZXXSPdQfzQnt+ViGvEIQ==", + "license": "MIT", + "dependencies": { + "pako": "^1.0.10" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@prisma/client": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.15.0.tgz", + "integrity": "sha512-wR2LXUbOH4cL/WToatI/Y2c7uzni76oNFND7+23ypLllBmIS8e3ZHhO+nud9iXSXKFt1SoM3fTZvHawg63emZw==", + "hasInstallScript": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "peerDependencies": { + "prisma": "*", + "typescript": ">=5.1.0" + }, + "peerDependenciesMeta": { + "prisma": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/@prisma/config": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.15.0.tgz", + "integrity": "sha512-KMEoec9b2u6zX0EbSEx/dRpx1oNLjqJEBZYyK0S3TTIbZ7GEGoVyGyFRk4C72+A38cuPLbfQGQvgOD+gBErKlA==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "c12": "3.1.0", + "deepmerge-ts": "7.1.5", + "effect": "3.16.12", + "empathic": "2.0.0" + } + }, + "node_modules/@prisma/debug": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.15.0.tgz", + "integrity": "sha512-y7cSeLuQmyt+A3hstAs6tsuAiVXSnw9T55ra77z0nbNkA8Lcq9rNcQg6PI00by/+WnE/aMRJ/W7sZWn2cgIy1g==", + "devOptional": true, + "license": "Apache-2.0" + }, + "node_modules/@prisma/engines": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.15.0.tgz", + "integrity": "sha512-opITiR5ddFJ1N2iqa7mkRlohCZqVSsHhRcc29QXeldMljOf4FSellLT0J5goVb64EzRTKcIDeIsJBgmilNcKxA==", + "devOptional": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.15.0", + "@prisma/engines-version": "6.15.0-5.85179d7826409ee107a6ba334b5e305ae3fba9fb", + "@prisma/fetch-engine": "6.15.0", + "@prisma/get-platform": "6.15.0" + } + }, + "node_modules/@prisma/engines-version": { + "version": "6.15.0-5.85179d7826409ee107a6ba334b5e305ae3fba9fb", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.15.0-5.85179d7826409ee107a6ba334b5e305ae3fba9fb.tgz", + "integrity": "sha512-a/46aK5j6L3ePwilZYEgYDPrhBQ/n4gYjLxT5YncUTJJNRnTCVjPF86QdzUOLRdYjCLfhtZp9aum90W0J+trrg==", + "devOptional": true, + "license": "Apache-2.0" + }, + "node_modules/@prisma/fetch-engine": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.15.0.tgz", + "integrity": "sha512-xcT5f6b+OWBq6vTUnRCc7qL+Im570CtwvgSj+0MTSGA1o9UDSKZ/WANvwtiRXdbYWECpyC3CukoG3A04VTAPHw==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.15.0", + "@prisma/engines-version": "6.15.0-5.85179d7826409ee107a6ba334b5e305ae3fba9fb", + "@prisma/get-platform": "6.15.0" + } + }, + "node_modules/@prisma/get-platform": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.15.0.tgz", + "integrity": "sha512-Jbb+Xbxyp05NSR1x2epabetHiXvpO8tdN2YNoWoA/ZsbYyxxu/CO/ROBauIFuMXs3Ti+W7N7SJtWsHGaWte9Rg==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.15.0" + } + }, + "node_modules/@smithy/abort-controller": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.5.tgz", + "integrity": "sha512-jcrqdTQurIrBbUm4W2YdLVMQDoL0sA9DTxYd2s+R/y+2U9NLOP7Xf/YqfSg1FZhlZIYEnvk2mwbyvIfdLEPo8g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/chunked-blob-reader": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-5.0.0.tgz", + "integrity": "sha512-+sKqDBQqb036hh4NPaUiEkYFkTUGYzRsn3EuFhyfQfMy6oGHEUJDurLP9Ufb5dasr/XiAmPNMr6wa9afjQB+Gw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/chunked-blob-reader-native": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-4.0.0.tgz", + "integrity": "sha512-R9wM2yPmfEMsUmlMlIgSzOyICs0x9uu7UTHoccMyt7BWw8shcGM8HqB355+BZCPBcySvbTYMs62EgEQkNxz2ig==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-base64": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/config-resolver": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.1.5.tgz", + "integrity": "sha512-viuHMxBAqydkB0AfWwHIdwf/PRH2z5KHGUzqyRtS/Wv+n3IHI993Sk76VCA7dD/+GzgGOmlJDITfPcJC1nIVIw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.1.4", + "@smithy/types": "^4.3.2", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/core": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.9.0.tgz", + "integrity": "sha512-B/GknvCfS3llXd/b++hcrwIuqnEozQDnRL4sBmOac5/z/dr0/yG1PURNPOyU4Lsiy1IyTj8scPxVqRs5dYWf6A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-serde": "^4.0.9", + "@smithy/protocol-http": "^5.1.3", + "@smithy/types": "^4.3.2", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-middleware": "^4.0.5", + "@smithy/util-stream": "^4.2.4", + "@smithy/util-utf8": "^4.0.0", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.0.7.tgz", + "integrity": "sha512-dDzrMXA8d8riFNiPvytxn0mNwR4B3h8lgrQ5UjAGu6T9z/kRg/Xncf4tEQHE/+t25sY8IH3CowcmWi+1U5B1Gw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.1.4", + "@smithy/property-provider": "^4.0.5", + "@smithy/types": "^4.3.2", + "@smithy/url-parser": "^4.0.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-codec": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.0.5.tgz", + "integrity": "sha512-miEUN+nz2UTNoRYRhRqVTJCx7jMeILdAurStT2XoS+mhokkmz1xAPp95DFW9Gxt4iF2VBqpeF9HbTQ3kY1viOA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^4.3.2", + "@smithy/util-hex-encoding": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-browser": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.0.5.tgz", + "integrity": "sha512-LCUQUVTbM6HFKzImYlSB9w4xafZmpdmZsOh9rIl7riPC3osCgGFVP+wwvYVw6pXda9PPT9TcEZxaq3XE81EdJQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.0.5", + "@smithy/types": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-config-resolver": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.1.3.tgz", + "integrity": "sha512-yTTzw2jZjn/MbHu1pURbHdpjGbCuMHWncNBpJnQAPxOVnFUAbSIUSwafiphVDjNV93TdBJWmeVAds7yl5QCkcA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-node": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.0.5.tgz", + "integrity": "sha512-lGS10urI4CNzz6YlTe5EYG0YOpsSp3ra8MXyco4aqSkQDuyZPIw2hcaxDU82OUVtK7UY9hrSvgWtpsW5D4rb4g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.0.5", + "@smithy/types": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-universal": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.0.5.tgz", + "integrity": "sha512-JFnmu4SU36YYw3DIBVao3FsJh4Uw65vVDIqlWT4LzR6gXA0F3KP0IXFKKJrhaVzCBhAuMsrUUaT5I+/4ZhF7aw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-codec": "^4.0.5", + "@smithy/types": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.1.1.tgz", + "integrity": "sha512-61WjM0PWmZJR+SnmzaKI7t7G0UkkNFboDpzIdzSoy7TByUzlxo18Qlh9s71qug4AY4hlH/CwXdubMtkcNEb/sQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.1.3", + "@smithy/querystring-builder": "^4.0.5", + "@smithy/types": "^4.3.2", + "@smithy/util-base64": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-blob-browser": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-4.0.5.tgz", + "integrity": "sha512-F7MmCd3FH/Q2edhcKd+qulWkwfChHbc9nhguBlVjSUE6hVHhec3q6uPQ+0u69S6ppvLtR3eStfCuEKMXBXhvvA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/chunked-blob-reader": "^5.0.0", + "@smithy/chunked-blob-reader-native": "^4.0.0", + "@smithy/types": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-node": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.0.5.tgz", + "integrity": "sha512-cv1HHkKhpyRb6ahD8Vcfb2Hgz67vNIXEp2vnhzfxLFGRukLCNEA5QdsorbUEzXma1Rco0u3rx5VTqbM06GcZqQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.2", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-stream-node": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-4.0.5.tgz", + "integrity": "sha512-IJuDS3+VfWB67UC0GU0uYBG/TA30w+PlOaSo0GPm9UHS88A6rCP6uZxNjNYiyRtOcjv7TXn/60cW8ox1yuZsLg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.2", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/invalid-dependency": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.0.5.tgz", + "integrity": "sha512-IVnb78Qtf7EJpoEVo7qJ8BEXQwgC4n3igeJNNKEj/MLYtapnx8A67Zt/J3RXAj2xSO1910zk0LdFiygSemuLow==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/is-array-buffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.0.0.tgz", + "integrity": "sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/md5-js": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-4.0.5.tgz", + "integrity": "sha512-8n2XCwdUbGr8W/XhMTaxILkVlw2QebkVTn5tm3HOcbPbOpWg89zr6dPXsH8xbeTsbTXlJvlJNTQsKAIoqQGbdA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.2", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-content-length": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.0.5.tgz", + "integrity": "sha512-l1jlNZoYzoCC7p0zCtBDE5OBXZ95yMKlRlftooE5jPWQn4YBPLgsp+oeHp7iMHaTGoUdFqmHOPa8c9G3gBsRpQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.1.3", + "@smithy/types": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-endpoint": { + "version": "4.1.19", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.1.19.tgz", + "integrity": "sha512-EAlEPncqo03siNZJ9Tm6adKCQ+sw5fNU8ncxWwaH0zTCwMPsgmERTi6CEKaermZdgJb+4Yvh0NFm36HeO4PGgQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.9.0", + "@smithy/middleware-serde": "^4.0.9", + "@smithy/node-config-provider": "^4.1.4", + "@smithy/shared-ini-file-loader": "^4.0.5", + "@smithy/types": "^4.3.2", + "@smithy/url-parser": "^4.0.5", + "@smithy/util-middleware": "^4.0.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-retry": { + "version": "4.1.20", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.1.20.tgz", + "integrity": "sha512-T3maNEm3Masae99eFdx1Q7PIqBBEVOvRd5hralqKZNeIivnoGNx5OFtI3DiZ5gCjUkl0mNondlzSXeVxkinh7Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.1.4", + "@smithy/protocol-http": "^5.1.3", + "@smithy/service-error-classification": "^4.0.7", + "@smithy/smithy-client": "^4.5.0", + "@smithy/types": "^4.3.2", + "@smithy/util-middleware": "^4.0.5", + "@smithy/util-retry": "^4.0.7", + "@types/uuid": "^9.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-serde": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.0.9.tgz", + "integrity": "sha512-uAFFR4dpeoJPGz8x9mhxp+RPjo5wW0QEEIPPPbLXiRRWeCATf/Km3gKIVR5vaP8bN1kgsPhcEeh+IZvUlBv6Xg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.1.3", + "@smithy/types": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-stack": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.0.5.tgz", + "integrity": "sha512-/yoHDXZPh3ocRVyeWQFvC44u8seu3eYzZRveCMfgMOBcNKnAmOvjbL9+Cp5XKSIi9iYA9PECUuW2teDAk8T+OQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-config-provider": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.1.4.tgz", + "integrity": "sha512-+UDQV/k42jLEPPHSn39l0Bmc4sB1xtdI9Gd47fzo/0PbXzJ7ylgaOByVjF5EeQIumkepnrJyfx86dPa9p47Y+w==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.0.5", + "@smithy/shared-ini-file-loader": "^4.0.5", + "@smithy/types": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.1.1.tgz", + "integrity": "sha512-RHnlHqFpoVdjSPPiYy/t40Zovf3BBHc2oemgD7VsVTFFZrU5erFFe0n52OANZZ/5sbshgD93sOh5r6I35Xmpaw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^4.0.5", + "@smithy/protocol-http": "^5.1.3", + "@smithy/querystring-builder": "^4.0.5", + "@smithy/types": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/property-provider": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.0.5.tgz", + "integrity": "sha512-R/bswf59T/n9ZgfgUICAZoWYKBHcsVDurAGX88zsiUtOTA/xUAPyiT+qkNCPwFn43pZqN84M4MiUsbSGQmgFIQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/protocol-http": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.1.3.tgz", + "integrity": "sha512-fCJd2ZR7D22XhDY0l+92pUag/7je2BztPRQ01gU5bMChcyI0rlly7QFibnYHzcxDvccMjlpM/Q1ev8ceRIb48w==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-builder": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.0.5.tgz", + "integrity": "sha512-NJeSCU57piZ56c+/wY+AbAw6rxCCAOZLCIniRE7wqvndqxcKKDOXzwWjrY7wGKEISfhL9gBbAaWWgHsUGedk+A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.2", + "@smithy/util-uri-escape": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-parser": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.0.5.tgz", + "integrity": "sha512-6SV7md2CzNG/WUeTjVe6Dj8noH32r4MnUeFKZrnVYsQxpGSIcphAanQMayi8jJLZAWm6pdM9ZXvKCpWOsIGg0w==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/service-error-classification": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.0.7.tgz", + "integrity": "sha512-XvRHOipqpwNhEjDf2L5gJowZEm5nsxC16pAZOeEcsygdjv9A2jdOh3YoDQvOXBGTsaJk6mNWtzWalOB9976Wlg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/shared-ini-file-loader": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.0.5.tgz", + "integrity": "sha512-YVVwehRDuehgoXdEL4r1tAAzdaDgaC9EQvhK0lEbfnbrd0bd5+CTQumbdPryX3J2shT7ZqQE+jPW4lmNBAB8JQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.1.3.tgz", + "integrity": "sha512-mARDSXSEgllNzMw6N+mC+r1AQlEBO3meEAkR/UlfAgnMzJUB3goRBWgip1EAMG99wh36MDqzo86SfIX5Y+VEaw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "@smithy/protocol-http": "^5.1.3", + "@smithy/types": "^4.3.2", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-middleware": "^4.0.5", + "@smithy/util-uri-escape": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/smithy-client": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.5.0.tgz", + "integrity": "sha512-ZSdE3vl0MuVbEwJBxSftm0J5nL/gw76xp5WF13zW9cN18MFuFXD5/LV0QD8P+sCU5bSWGyy6CTgUupE1HhOo1A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.9.0", + "@smithy/middleware-endpoint": "^4.1.19", + "@smithy/middleware-stack": "^4.0.5", + "@smithy/protocol-http": "^5.1.3", + "@smithy/types": "^4.3.2", + "@smithy/util-stream": "^4.2.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.3.2.tgz", + "integrity": "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/url-parser": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.0.5.tgz", + "integrity": "sha512-j+733Um7f1/DXjYhCbvNXABV53NyCRRA54C7bNEIxNPs0YjfRxeMKjjgm2jvTYrciZyCjsicHwQ6Q0ylo+NAUw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/querystring-parser": "^4.0.5", + "@smithy/types": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-base64": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.0.0.tgz", + "integrity": "sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-browser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.0.0.tgz", + "integrity": "sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-node": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.0.0.tgz", + "integrity": "sha512-q0iDP3VsZzqJyje8xJWEJCNIu3lktUGVoSy1KB0UWym2CL1siV3artm+u1DFYTLejpsrdGyCSWBdGNjJzfDPjg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.0.0.tgz", + "integrity": "sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-config-provider": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.0.0.tgz", + "integrity": "sha512-L1RBVzLyfE8OXH+1hsJ8p+acNUSirQnWQ6/EgpchV88G6zGBTDPdXiiExei6Z1wR2RxYvxY/XLw6AMNCCt8H3w==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.0.27", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.27.tgz", + "integrity": "sha512-i/Fu6AFT5014VJNgWxKomBJP/GB5uuOsM4iHdcmplLm8B1eAqnRItw4lT2qpdO+mf+6TFmf6dGcggGLAVMZJsQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.0.5", + "@smithy/smithy-client": "^4.5.0", + "@smithy/types": "^4.3.2", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-node": { + "version": "4.0.27", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.27.tgz", + "integrity": "sha512-3W0qClMyxl/ELqTA39aNw1N+pN0IjpXT7lPFvZ8zTxqVFP7XCpACB9QufmN4FQtd39xbgS7/Lekn7LmDa63I5w==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/config-resolver": "^4.1.5", + "@smithy/credential-provider-imds": "^4.0.7", + "@smithy/node-config-provider": "^4.1.4", + "@smithy/property-provider": "^4.0.5", + "@smithy/smithy-client": "^4.5.0", + "@smithy/types": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-endpoints": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.0.7.tgz", + "integrity": "sha512-klGBP+RpBp6V5JbrY2C/VKnHXn3d5V2YrifZbmMY8os7M6m8wdYFoO6w/fe5VkP+YVwrEktW3IWYaSQVNZJ8oQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.1.4", + "@smithy/types": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-hex-encoding": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.0.0.tgz", + "integrity": "sha512-Yk5mLhHtfIgW2W2WQZWSg5kuMZCVbvhFmC7rV4IO2QqnZdbEFPmQnCcGMAX2z/8Qj3B9hYYNjZOhWym+RwhePw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-middleware": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.0.5.tgz", + "integrity": "sha512-N40PfqsZHRSsByGB81HhSo+uvMxEHT+9e255S53pfBw/wI6WKDI7Jw9oyu5tJTLwZzV5DsMha3ji8jk9dsHmQQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-retry": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.0.7.tgz", + "integrity": "sha512-TTO6rt0ppK70alZpkjwy+3nQlTiqNfoXja+qwuAchIEAIoSZW8Qyd76dvBv3I5bCpE38APafG23Y/u270NspiQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^4.0.7", + "@smithy/types": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-stream": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.2.4.tgz", + "integrity": "sha512-vSKnvNZX2BXzl0U2RgCLOwWaAP9x/ddd/XobPK02pCbzRm5s55M53uwb1rl/Ts7RXZvdJZerPkA+en2FDghLuQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/fetch-http-handler": "^5.1.1", + "@smithy/node-http-handler": "^4.1.1", + "@smithy/types": "^4.3.2", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-uri-escape": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.0.0.tgz", + "integrity": "sha512-77yfbCbQMtgtTylO9itEAdpPXSog3ZxMe09AEhm0dU0NLTalV70ghDZFR+Nfi1C60jnJoh/Re4090/DuZh2Omg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-utf8": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.0.0.tgz", + "integrity": "sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-waiter": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.0.7.tgz", + "integrity": "sha512-mYqtQXPmrwvUljaHyGxYUIIRI3qjBTEb/f5QFi3A6VlxhpmZd5mWXn9W+qUkf2pVE1Hv3SqxefiZOPGdxmO64A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^4.0.5", + "@smithy/types": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@standard-schema/spec": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", + "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@supabase/auth-js": { + "version": "2.71.1", + "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.71.1.tgz", + "integrity": "sha512-mMIQHBRc+SKpZFRB2qtupuzulaUhFYupNyxqDj5Jp/LyPvcWvjaJzZzObv6URtL/O6lPxkanASnotGtNpS3H2Q==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/functions-js": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.4.5.tgz", + "integrity": "sha512-v5GSqb9zbosquTo6gBwIiq7W9eQ7rE5QazsK/ezNiQXdCbY+bH8D9qEaBIkhVvX4ZRW5rP03gEfw5yw9tiq4EQ==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/node-fetch": { + "version": "2.6.15", + "resolved": "https://registry.npmjs.org/@supabase/node-fetch/-/node-fetch-2.6.15.tgz", + "integrity": "sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + } + }, + "node_modules/@supabase/postgrest-js": { + "version": "1.21.3", + "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-1.21.3.tgz", + "integrity": "sha512-rg3DmmZQKEVCreXq6Am29hMVe1CzemXyIWVYyyua69y6XubfP+DzGfLxME/1uvdgwqdoaPbtjBDpEBhqxq1ZwA==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/realtime-js": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.15.4.tgz", + "integrity": "sha512-e/FYIWjvQJHOCNACWehnKvg26zosju3694k0NMUNb+JGLdvHJzEa29ZVVLmawd2kvx4hdbv8mxSqfttRnH3+DA==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.13", + "@types/phoenix": "^1.6.6", + "@types/ws": "^8.18.1", + "ws": "^8.18.2" + } + }, + "node_modules/@supabase/storage-js": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.11.0.tgz", + "integrity": "sha512-Y+kx/wDgd4oasAgoAq0bsbQojwQ+ejIif8uczZ9qufRHWFLMU5cODT+ApHsSrDufqUcVKt+eyxtOXSkeh2v9ww==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/supabase-js": { + "version": "2.56.1", + "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.56.1.tgz", + "integrity": "sha512-cb/kS0d6G/qbcmUFItkqVrQbxQHWXzfRZuoiSDv/QiU6RbGNTn73XjjvmbBCZ4MMHs+5teihjhpEVluqbXISEg==", + "license": "MIT", + "dependencies": { + "@supabase/auth-js": "2.71.1", + "@supabase/functions-js": "2.4.5", + "@supabase/node-fetch": "2.6.15", + "@supabase/postgrest-js": "1.21.3", + "@supabase/realtime-js": "2.15.4", + "@supabase/storage-js": "^2.10.4" + } + }, + "node_modules/@types/bcrypt": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-6.0.0.tgz", + "integrity": "sha512-/oJGukuH3D2+D+3H4JWLaAsJ/ji86dhRidzZ/Od7H/i8g+aCmvkeCc6Ni/f9uxGLSQVCRZkX2/lqEFG2BvWtlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.10", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz", + "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ms": "*", + "@types/node": "*" + } + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.0.tgz", + "integrity": "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.10.0" + } + }, + "node_modules/@types/phoenix": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.6.tgz", + "integrity": "sha512-PIzZZlEppgrpoT2QgbnDU+MMzuR6BbCjllj0bM70lWoejMeNJAxCchxnv7J3XFkI8MpygtRpzXrIlmWUBclP5A==", + "license": "MIT" + }, + "node_modules/@types/uuid": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", + "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", + "license": "MIT" + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@zip.js/zip.js": { + "version": "2.7.73", + "resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.7.73.tgz", + "integrity": "sha512-I2UP8/rdQE5hTtVVL08B7P8XuwXiKuuMUPjNuFOVL/9b+8IsExR9S5jz2H58u0rJjU4M1BikLgqEMG8gZJZVBw==", + "license": "BSD-3-Clause", + "engines": { + "bun": ">=0.7.0", + "deno": ">=1.0.0", + "node": ">=16.5.0" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/abstract-logging": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz", + "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==", + "license": "MIT" + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ansi-regex": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz", + "integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/archiver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-7.0.1.tgz", + "integrity": "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==", + "license": "MIT", + "dependencies": { + "archiver-utils": "^5.0.2", + "async": "^3.2.4", + "buffer-crc32": "^1.0.0", + "readable-stream": "^4.0.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^3.0.0", + "zip-stream": "^6.0.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/archiver-utils": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-5.0.2.tgz", + "integrity": "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==", + "license": "MIT", + "dependencies": { + "glob": "^10.0.0", + "graceful-fs": "^4.2.0", + "is-stream": "^2.0.1", + "lazystream": "^1.0.0", + "lodash": "^4.17.15", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/archiver-utils/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/archiver-utils/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/archiver-utils/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/archiver-utils/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/archiver-utils/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/atomic-sleep": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/avvio": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/avvio/-/avvio-9.1.0.tgz", + "integrity": "sha512-fYASnYi600CsH/j9EQov7lECAniYiBFiiAtBNuZYLA2leLe9qOvZzqYHFjtIj6gD2VMoMLP14834LFWvr4IfDw==", + "license": "MIT", + "dependencies": { + "@fastify/error": "^4.0.0", + "fastq": "^1.17.1" + } + }, + "node_modules/axios": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.1.tgz", + "integrity": "sha512-Kn4kbSXpkFHCGE6rBFNwIv0GQs4AvDT80jlveJDKFxjbTYMUeB4QtsdPCv6H8Cm19Je7IU6VFtRl2zWZI0rudQ==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/b4a": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz", + "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==", + "license": "Apache-2.0" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/bare-events": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.6.1.tgz", + "integrity": "sha512-AuTJkq9XmE6Vk0FJVNq5QxETrSA/vKHarWVBG5l/JbdCL1prJemiyJqUS0jrlXO0MftuPq4m3YVYhoNc5+aE/g==", + "license": "Apache-2.0", + "optional": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bcrypt": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-6.0.0.tgz", + "integrity": "sha512-cU8v/EGSrnH+HnxV2z0J7/blxH8gq7Xh2JFT6Aroax7UohdmiJJlxApMxtKfuI7z68NvvVcmR78k2LbT6efhRg==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^8.3.0", + "node-gyp-build": "^4.8.4" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/bowser": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.12.1.tgz", + "integrity": "sha512-z4rE2Gxh7tvshQ4hluIT7XcFrgLIQaw9X3A+kTTRdovCz5PMukm/0QC/BKSYPj3omF5Qfypn9O/c5kgpmvYUCw==", + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-crc32": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", + "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "node_modules/c12": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/c12/-/c12-3.1.0.tgz", + "integrity": "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.3", + "confbox": "^0.2.2", + "defu": "^6.1.4", + "dotenv": "^16.6.1", + "exsolve": "^1.0.7", + "giget": "^2.0.0", + "jiti": "^2.4.2", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "perfect-debounce": "^1.0.0", + "pkg-types": "^2.2.0", + "rc9": "^2.1.2" + }, + "peerDependencies": { + "magicast": "^0.3.5" + }, + "peerDependenciesMeta": { + "magicast": { + "optional": true + } + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/citty": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", + "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "consola": "^3.2.3" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/compress-commons": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-6.0.2.tgz", + "integrity": "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==", + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "crc32-stream": "^6.0.0", + "is-stream": "^2.0.1", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/confbox": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz", + "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/crc32-stream": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-6.0.0.tgz", + "integrity": "sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==", + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/dayjs": { + "version": "1.11.18", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.18.tgz", + "integrity": "sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deepmerge-ts": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz", + "integrity": "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==", + "devOptional": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/defu": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", + "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/destr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", + "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "devOptional": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/effect": { + "version": "3.16.12", + "resolved": "https://registry.npmjs.org/effect/-/effect-3.16.12.tgz", + "integrity": "sha512-N39iBk0K71F9nb442TLbTkjl24FLUzuvx2i1I2RsEAQsdAdUTuUoW0vlfUXgkMTUOnYqKnWcFfqw4hK4Pw27hg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "fast-check": "^3.23.1" + } + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/empathic": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/empathic/-/empathic-2.0.0.tgz", + "integrity": "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", + "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.9", + "@esbuild/android-arm": "0.25.9", + "@esbuild/android-arm64": "0.25.9", + "@esbuild/android-x64": "0.25.9", + "@esbuild/darwin-arm64": "0.25.9", + "@esbuild/darwin-x64": "0.25.9", + "@esbuild/freebsd-arm64": "0.25.9", + "@esbuild/freebsd-x64": "0.25.9", + "@esbuild/linux-arm": "0.25.9", + "@esbuild/linux-arm64": "0.25.9", + "@esbuild/linux-ia32": "0.25.9", + "@esbuild/linux-loong64": "0.25.9", + "@esbuild/linux-mips64el": "0.25.9", + "@esbuild/linux-ppc64": "0.25.9", + "@esbuild/linux-riscv64": "0.25.9", + "@esbuild/linux-s390x": "0.25.9", + "@esbuild/linux-x64": "0.25.9", + "@esbuild/netbsd-arm64": "0.25.9", + "@esbuild/netbsd-x64": "0.25.9", + "@esbuild/openbsd-arm64": "0.25.9", + "@esbuild/openbsd-x64": "0.25.9", + "@esbuild/openharmony-arm64": "0.25.9", + "@esbuild/sunos-x64": "0.25.9", + "@esbuild/win32-arm64": "0.25.9", + "@esbuild/win32-ia32": "0.25.9", + "@esbuild/win32-x64": "0.25.9" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/exsolve": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.7.tgz", + "integrity": "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/fast-check": { + "version": "3.23.2", + "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.23.2.tgz", + "integrity": "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==", + "devOptional": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT", + "dependencies": { + "pure-rand": "^6.1.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/fast-decode-uri-component": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz", + "integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==", + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "license": "MIT" + }, + "node_modules/fast-json-stringify": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-6.0.1.tgz", + "integrity": "sha512-s7SJE83QKBZwg54dIbD5rCtzOBVD43V1ReWXXYqBgwCwHLYAAT0RQc/FmrQglXqWPpz6omtryJQOau5jI4Nrvg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "@fastify/merge-json-schemas": "^0.2.0", + "ajv": "^8.12.0", + "ajv-formats": "^3.0.1", + "fast-uri": "^3.0.0", + "json-schema-ref-resolver": "^2.0.0", + "rfdc": "^1.2.0" + } + }, + "node_modules/fast-querystring": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fast-querystring/-/fast-querystring-1.1.2.tgz", + "integrity": "sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==", + "license": "MIT", + "dependencies": { + "fast-decode-uri-component": "^1.0.1" + } + }, + "node_modules/fast-redact": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz", + "integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fast-xml-parser": { + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz", + "integrity": "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^2.1.0" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/fastify": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/fastify/-/fastify-5.5.0.tgz", + "integrity": "sha512-ZWSWlzj3K/DcULCnCjEiC2zn2FBPdlZsSA/pnPa/dbUfLvxkD/Nqmb0XXMXLrWkeM4uQPUvjdJpwtXmTfriXqw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "@fastify/ajv-compiler": "^4.0.0", + "@fastify/error": "^4.0.0", + "@fastify/fast-json-stringify-compiler": "^5.0.0", + "@fastify/proxy-addr": "^5.0.0", + "abstract-logging": "^2.0.1", + "avvio": "^9.0.0", + "fast-json-stringify": "^6.0.0", + "find-my-way": "^9.0.0", + "light-my-request": "^6.0.0", + "pino": "^9.0.0", + "process-warning": "^5.0.0", + "rfdc": "^1.3.1", + "secure-json-parse": "^4.0.0", + "semver": "^7.6.0", + "toad-cache": "^3.7.0" + } + }, + "node_modules/fastify-plugin": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/fastify-plugin/-/fastify-plugin-5.0.1.tgz", + "integrity": "sha512-HCxs+YnRaWzCl+cWRYFnHmeRFyR5GVnJTAaCJQiYzQSDwK9MgJdyAsuL3nh0EWRCYMgQ5MeziymvmAhUHYHDUQ==", + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/find-my-way": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-9.3.0.tgz", + "integrity": "sha512-eRoFWQw+Yv2tuYlK2pjFS2jGXSxSppAs3hSQjfxVKxM5amECzIgYYc1FEI8ZmhSh/Ig+FrKEz43NLRKJjYCZVg==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-querystring": "^1.0.0", + "safe-regex2": "^5.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-tsconfig": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz", + "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/giget": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz", + "integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "citty": "^0.1.6", + "consola": "^3.4.0", + "defu": "^6.1.4", + "node-fetch-native": "^1.6.6", + "nypm": "^0.6.0", + "pathe": "^2.0.3" + }, + "bin": { + "giget": "dist/cli.mjs" + } + }, + "node_modules/glob": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz", + "integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.0.3", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", + "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", + "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jiti": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz", + "integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==", + "devOptional": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/json-schema-ref-resolver": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/json-schema-ref-resolver/-/json-schema-ref-resolver-2.0.1.tgz", + "integrity": "sha512-HG0SIB9X4J8bwbxCbnd5FfPEbcXAJYTi1pBJeP/QPON+w8ovSME8iRG+ElHNxZNX2Qh6eYn1GdzJFS4cDFfx0Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/json-schema-resolver": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-schema-resolver/-/json-schema-resolver-3.0.0.tgz", + "integrity": "sha512-HqMnbz0tz2DaEJ3ntsqtx3ezzZyDE7G56A/pPY/NGmrPu76UzsWquOpHFRAf5beTNXoH2LU5cQePVvRli1nchA==", + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "fast-uri": "^3.0.5", + "rfdc": "^1.1.4" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/Eomm/json-schema-resolver?sponsor=1" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "license": "MIT", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jwa": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "license": "MIT", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "license": "MIT", + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lazystream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/lazystream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/lazystream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/light-my-request": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-6.6.0.tgz", + "integrity": "sha512-CHYbu8RtboSIoVsHZ6Ye4cj4Aw/yg2oAFimlF7mNvfDV192LR7nDiKtSIfCuLT7KokPSTn/9kfVLm5OGN0A28A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause", + "dependencies": { + "cookie": "^1.0.1", + "process-warning": "^4.0.0", + "set-cookie-parser": "^2.6.0" + } + }, + "node_modules/light-my-request/node_modules/process-warning": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-4.0.1.tgz", + "integrity": "sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz", + "integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==", + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", + "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", + "license": "ISC", + "dependencies": { + "@isaacs/brace-expansion": "^5.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/node-addon-api": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.5.0.tgz", + "integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==", + "license": "MIT", + "engines": { + "node": "^18 || ^20 || >= 21" + } + }, + "node_modules/node-fetch-native": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", + "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/nodemailer": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.6.tgz", + "integrity": "sha512-F44uVzgwo49xboqbFgBGkRaiMgtoBrBEWCVincJPK9+S9Adkzt/wXCLKbf7dxucmxfTI5gHGB+bEmdyzN6QKjw==", + "license": "MIT-0", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nypm": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.1.tgz", + "integrity": "sha512-hlacBiRiv1k9hZFiphPUkfSQ/ZfQzZDzC+8z0wL3lvDAOUu/2NnChkKuMoMjNur/9OpKuz2QsIeiPVN0xM5Q0w==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "citty": "^0.1.6", + "consola": "^3.4.2", + "pathe": "^2.0.3", + "pkg-types": "^2.2.0", + "tinyexec": "^1.0.1" + }, + "bin": { + "nypm": "dist/cli.mjs" + }, + "engines": { + "node": "^14.16.0 || >=16.10.0" + } + }, + "node_modules/ohash": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", + "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/on-exit-leak-free": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/openapi-types": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", + "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==", + "license": "MIT" + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/pdf-lib": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/pdf-lib/-/pdf-lib-1.17.1.tgz", + "integrity": "sha512-V/mpyJAoTsN4cnP31vc0wfNA1+p20evqqnap0KLoRUN0Yk/p3wN52DOEsL4oBFcLdb76hlpKPtzJIgo67j/XLw==", + "license": "MIT", + "dependencies": { + "@pdf-lib/standard-fonts": "^1.0.0", + "@pdf-lib/upng": "^1.0.1", + "pako": "^1.0.11", + "tslib": "^1.11.1" + } + }, + "node_modules/pdf-lib/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "license": "0BSD" + }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/pino": { + "version": "9.9.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-9.9.0.tgz", + "integrity": "sha512-zxsRIQG9HzG+jEljmvmZupOMDUQ0Jpj0yAgE28jQvvrdYTlEaiGwelJpdndMl/MBuRr70heIj83QyqJUWaU8mQ==", + "license": "MIT", + "dependencies": { + "atomic-sleep": "^1.0.0", + "fast-redact": "^3.1.1", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^2.0.0", + "pino-std-serializers": "^7.0.0", + "process-warning": "^5.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^4.0.1", + "thread-stream": "^3.0.0" + }, + "bin": { + "pino": "bin.js" + } + }, + "node_modules/pino-abstract-transport": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz", + "integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==", + "license": "MIT", + "dependencies": { + "split2": "^4.0.0" + } + }, + "node_modules/pino-std-serializers": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz", + "integrity": "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==", + "license": "MIT" + }, + "node_modules/pkg-types": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", + "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.2.2", + "exsolve": "^1.0.7", + "pathe": "^2.0.3" + } + }, + "node_modules/prisma": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.15.0.tgz", + "integrity": "sha512-E6RCgOt+kUVtjtZgLQDBJ6md2tDItLJNExwI0XJeBc1FKL+Vwb+ovxXxuok9r8oBgsOXBA33fGDuE/0qDdCWqQ==", + "devOptional": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/config": "6.15.0", + "@prisma/engines": "6.15.0" + }, + "bin": { + "prisma": "build/index.js" + }, + "engines": { + "node": ">=18.18" + }, + "peerDependencies": { + "typescript": ">=5.1.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/process-warning": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz", + "integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "devOptional": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "node_modules/quick-format-unescaped": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", + "license": "MIT" + }, + "node_modules/rc9": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz", + "integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "defu": "^6.1.4", + "destr": "^2.0.3" + } + }, + "node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/readdir-glob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.1.0" + } + }, + "node_modules/readdir-glob/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/real-require": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", + "license": "MIT", + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/ret": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.5.0.tgz", + "integrity": "sha512-I1XxrZSQ+oErkRR4jYbAyEEu2I0avBvvMM5JN+6EBprOGRCs63ENqZ3vjavq8fBw2+62G5LF5XelKwuJpcvcxw==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "license": "MIT" + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-regex2": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-5.0.0.tgz", + "integrity": "sha512-YwJwe5a51WlK7KbOJREPdjNrpViQBI3p4T50lfwPuDhZnE3XGVTlGvi+aolc5+RvxDD6bnUmjVsU9n1eboLUYw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "ret": "~0.5.0" + } + }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/secure-json-parse": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-4.0.0.tgz", + "integrity": "sha512-dxtLJO6sc35jWidmLxo7ij+Eg48PM/kleBsxpC8QJE0qJICe+KawkDQmvCMZUr9u7WKVHgMW6vy3fQ7zMiFZMA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", + "license": "MIT" + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sonic-boom": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz", + "integrity": "sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==", + "license": "MIT", + "dependencies": { + "atomic-sleep": "^1.0.0" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/streamx": { + "version": "2.22.1", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.22.1.tgz", + "integrity": "sha512-znKXEBxfatz2GBNK02kRnCXjV+AA4kjZIUxeWSr3UGirZMJfTE9uiwKHobnbgxWyL/JWro8tTq+vOqAK1/qbSA==", + "license": "MIT", + "dependencies": { + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" + }, + "optionalDependencies": { + "bare-events": "^2.2.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strnum": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.1.tgz", + "integrity": "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, + "node_modules/tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", + "license": "MIT", + "dependencies": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/text-decoder": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", + "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" + } + }, + "node_modules/thread-stream": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz", + "integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==", + "license": "MIT", + "dependencies": { + "real-require": "^0.2.0" + } + }, + "node_modules/tinyexec": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.1.tgz", + "integrity": "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/toad-cache": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/toad-cache/-/toad-cache-3.7.0.tgz", + "integrity": "sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/tsx": { + "version": "4.20.5", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.5.tgz", + "integrity": "sha512-+wKjMNU9w/EaQayHXb7WA7ZaHY6hN8WgfvHNQ3t1PnU91/7O8TcTnIhCDYTZwnt8JsO9IBqZ30Ln1r7pPF52Aw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.25.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/typescript": { + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", + "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==", + "license": "MIT" + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xmlbuilder": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", + "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", + "license": "MIT", + "engines": { + "node": ">=8.0" + } + }, + "node_modules/yaml": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", + "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + } + }, + "node_modules/zip-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-6.0.1.tgz", + "integrity": "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==", + "license": "MIT", + "dependencies": { + "archiver-utils": "^5.0.0", + "compress-commons": "^6.0.2", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..c47b02b --- /dev/null +++ b/package.json @@ -0,0 +1,48 @@ +{ + "name": "backend", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "dev": "tsx watch src/index.ts", + "build": "tsc", + "start": "node dist/index.js" + }, + "repository": { + "type": "git", + "url": "https://git.federspiel.software/fedeo/backend.git" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "@aws-sdk/client-s3": "^3.879.0", + "@aws-sdk/s3-request-presigner": "^3.879.0", + "@fastify/cookie": "^11.0.2", + "@fastify/cors": "^11.1.0", + "@fastify/multipart": "^9.0.3", + "@fastify/swagger": "^9.5.1", + "@fastify/swagger-ui": "^5.2.3", + "@prisma/client": "^6.15.0", + "@supabase/supabase-js": "^2.56.1", + "@zip.js/zip.js": "^2.7.73", + "archiver": "^7.0.1", + "axios": "^1.12.1", + "bcrypt": "^6.0.0", + "dayjs": "^1.11.18", + "fastify": "^5.5.0", + "fastify-plugin": "^5.0.1", + "jsonwebtoken": "^9.0.2", + "nodemailer": "^7.0.6", + "pdf-lib": "^1.17.1", + "xmlbuilder": "^15.1.1" + }, + "devDependencies": { + "@types/bcrypt": "^6.0.0", + "@types/jsonwebtoken": "^9.0.10", + "@types/node": "^24.3.0", + "prisma": "^6.15.0", + "tsx": "^4.20.5", + "typescript": "^5.9.2" + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..94b2972 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "isolatedModules": true, + "outDir": "dist", + "types": ["node"] + }, + "include": ["src"], + "exclude": ["node_modules", "dist"] +} \ No newline at end of file From 0f03deeadd97647cf17ba8423cd575e8106a3137 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Fri, 12 Sep 2025 18:59:08 +0200 Subject: [PATCH 007/149] Changed Dockerfile --- Dockerfile | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/Dockerfile b/Dockerfile index a8477c2..b338f0a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,20 +1,29 @@ -# Basis-Image mit Node.js -FROM node:20-alpine - -# Arbeitsverzeichnis im Container +# Basis-Image +FROM node:20-alpine AS base WORKDIR /usr/src/app -# package.json und package-lock.json zuerst kopieren +# Dependencies installieren (dev deps für Build erforderlich) COPY package*.json ./ +RUN npm install -# Dependencies installieren (nur Produktion, falls nötig) -RUN npm install --omit=dev - -# Restlichen Quellcode kopieren +# Quellcode kopieren COPY . . -# Port setzen (Fastify läuft standardmäßig auf 3000) -EXPOSE 3000 +# Build ausführen (TypeScript -> dist) +RUN npm run build -# Startkommando +# --------- Production Stage --------- +FROM node:20-alpine AS production +WORKDIR /usr/src/app + +# Nur production dependencies installieren +COPY package*.json ./ +RUN npm install --omit=dev + + + +# Port freigeben +EXPOSE 3100 + +# App starten CMD ["npm", "start"] \ No newline at end of file From 6d190d25018a8a64353c3ab78d9ae6280cc477ad Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Fri, 12 Sep 2025 19:13:50 +0200 Subject: [PATCH 008/149] Changed Dockerfile --- Dockerfile | 2 -- 1 file changed, 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index b338f0a..ac7ff2a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,8 +20,6 @@ WORKDIR /usr/src/app COPY package*.json ./ RUN npm install --omit=dev - - # Port freigeben EXPOSE 3100 From 800b7d883eec2d84b8f8fc30176bdd98a396a3e3 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Fri, 12 Sep 2025 19:16:37 +0200 Subject: [PATCH 009/149] Changed Dockerfile --- Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Dockerfile b/Dockerfile index ac7ff2a..7b4b42a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,6 +20,8 @@ WORKDIR /usr/src/app COPY package*.json ./ RUN npm install --omit=dev +COPY --from=base /usr/src/app/dist ./dist + # Port freigeben EXPOSE 3100 From d3104bb5a21ebe2c4fc04a9af834ea23e9644bb4 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Fri, 12 Sep 2025 19:18:51 +0200 Subject: [PATCH 010/149] Changed tsconfig --- tsconfig.json | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/tsconfig.json b/tsconfig.json index 94b2972..4b5cee7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,17 +1,13 @@ { "compilerOptions": { - "target": "ES2020", - "module": "NodeNext", - "moduleResolution": "NodeNext", - "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - "strict": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, - "resolveJsonModule": true, - "isolatedModules": true, + "target": "ES2021", + "lib": ["ES2021"], + "module": "commonjs", "outDir": "dist", - "types": ["node"] + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true }, "include": ["src"], "exclude": ["node_modules", "dist"] From a9d0a060b80979537b4e06e8783571c5a5f09550 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Fri, 12 Sep 2025 19:21:24 +0200 Subject: [PATCH 011/149] Changed tsconfig --- tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tsconfig.json b/tsconfig.json index 4b5cee7..13ebea8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,7 +4,7 @@ "lib": ["ES2021"], "module": "commonjs", "outDir": "dist", - "strict": true, + "strict": false, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true From 1dfc1fd2c1510777f73cbf61bf2f618959d3e3f3 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Fri, 12 Sep 2025 19:22:05 +0200 Subject: [PATCH 012/149] Changed tsconfig --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c47b02b..0655ac5 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "main": "index.js", "scripts": { "dev": "tsx watch src/index.ts", - "build": "tsc", + "build": "tsc --transpileOnly", "start": "node dist/index.js" }, "repository": { From 522af71f9bb35a9fd3584578b5f1bbdd1154571a Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Fri, 12 Sep 2025 19:23:18 +0200 Subject: [PATCH 013/149] Changed tsconfig --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0655ac5..c47b02b 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "main": "index.js", "scripts": { "dev": "tsx watch src/index.ts", - "build": "tsc --transpileOnly", + "build": "tsc", "start": "node dist/index.js" }, "repository": { From f35d375c5fc5b8ba56dc09f9c0baccd87152f26d Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Fri, 12 Sep 2025 19:27:21 +0200 Subject: [PATCH 014/149] Changed Dockerfile --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 7b4b42a..0a79052 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,7 +10,7 @@ RUN npm install COPY . . # Build ausführen (TypeScript -> dist) -RUN npm run build +RUN npx tsc --transpileOnly # --------- Production Stage --------- FROM node:20-alpine AS production From 9b6af75e988bf16a91cd34a06ae003fed3fbc102 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Fri, 12 Sep 2025 19:28:35 +0200 Subject: [PATCH 015/149] Changed Dockerfile --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 0a79052..12566db 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,7 +10,7 @@ RUN npm install COPY . . # Build ausführen (TypeScript -> dist) -RUN npx tsc --transpileOnly +RUN npx tsc --skipLibCheck --noEmitOnError false # --------- Production Stage --------- FROM node:20-alpine AS production From af1bf82c7503299eb988cda965b0af85cdf9a082 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Fri, 12 Sep 2025 19:30:01 +0200 Subject: [PATCH 016/149] Changed Dockerfile --- tsconfig.json | 1 + 1 file changed, 1 insertion(+) diff --git a/tsconfig.json b/tsconfig.json index 13ebea8..bd70a7e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,6 +7,7 @@ "strict": false, "esModuleInterop": true, "skipLibCheck": true, + "noEmitOnError": false, "forceConsistentCasingInFileNames": true }, "include": ["src"], From 0fe16ad79eb01751180574cc4eaa7932eae81926 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Fri, 12 Sep 2025 19:40:53 +0200 Subject: [PATCH 017/149] Changed types --- src/plugins/auth.ts | 2 +- src/plugins/swagger.ts | 1 + src/routes/auth/auth-authenticated.ts | 8 +++++--- src/routes/auth/auth.ts | 7 +++++++ src/routes/auth/user.ts | 2 +- src/routes/banking.ts | 2 ++ src/routes/files.ts | 7 ++++--- src/routes/resources.ts | 2 ++ src/routes/resourcesSpecial.ts | 1 + src/routes/tenant.ts | 5 +++++ src/utils/export/datev.ts | 6 ++++-- src/utils/sort.ts | 3 ++- 12 files changed, 35 insertions(+), 11 deletions(-) diff --git a/src/plugins/auth.ts b/src/plugins/auth.ts index 68cd018..eb92f5d 100644 --- a/src/plugins/auth.ts +++ b/src/plugins/auth.ts @@ -47,7 +47,7 @@ declare module "fastify" { user?: { user_id: string; email: string; - tenant_id?: string; + tenant_id?: number; role?: string; }; } diff --git a/src/plugins/swagger.ts b/src/plugins/swagger.ts index 7bd32d9..b529ea8 100644 --- a/src/plugins/swagger.ts +++ b/src/plugins/swagger.ts @@ -16,6 +16,7 @@ export default fp(async (server: FastifyInstance) => { }, }); + // @ts-ignore await server.register(swaggerUi, { routePrefix: "/docs", // UI erreichbar unter http://localhost:3000/docs swagger: { diff --git a/src/routes/auth/auth-authenticated.ts b/src/routes/auth/auth-authenticated.ts index 4cd0d0b..93e254c 100644 --- a/src/routes/auth/auth-authenticated.ts +++ b/src/routes/auth/auth-authenticated.ts @@ -1,8 +1,6 @@ import { FastifyInstance } from "fastify"; import bcrypt from "bcrypt"; -import jwt from "jsonwebtoken"; -import { generateRandomPassword, hashPassword } from "../utils/password" -import { sendMail } from "../utils/mailer" + export default async function authRoutesAuthenticated(server: FastifyInstance) { server.post("/auth/password/change", { @@ -33,6 +31,7 @@ export default async function authRoutesAuthenticated(server: FastifyInstance) { const user_id = req.user?.user_id; // kommt aus JWT Middleware if (!user_id) { + // @ts-ignore return reply.code(401).send({ error: "Unauthorized" }); } @@ -44,12 +43,14 @@ export default async function authRoutesAuthenticated(server: FastifyInstance) { .single(); if (error || !user) { + // @ts-ignore return reply.code(404).send({ error: "User not found" }); } // Altes Passwort prüfen const valid = await bcrypt.compare(old_password, user.password_hash); if (!valid) { + // @ts-ignore return reply.code(401).send({ error: "Old password incorrect" }); } @@ -68,6 +69,7 @@ export default async function authRoutesAuthenticated(server: FastifyInstance) { if (updateError) { console.log(updateError); + // @ts-ignore return reply.code(500).send({ error: "Password update failed" }); } diff --git a/src/routes/auth/auth.ts b/src/routes/auth/auth.ts index 5e70e86..d0ac34e 100644 --- a/src/routes/auth/auth.ts +++ b/src/routes/auth/auth.ts @@ -31,6 +31,7 @@ export default async function authRoutes(server: FastifyInstance) { const body = req.body as { email: string; password: string }; if (!body.email || !body.password) { + // @ts-ignore return reply.code(400).send({ error: "Email and password required" }); } @@ -45,6 +46,7 @@ export default async function authRoutes(server: FastifyInstance) { .single(); if (error) { + // @ts-ignore return reply.code(400).send({ error: error.message }); } @@ -77,6 +79,7 @@ export default async function authRoutes(server: FastifyInstance) { const body = req.body as { email: string; password: string }; if (!body.email || !body.password) { + // @ts-ignore return reply.code(400).send({ error: "Email and password required" }); } @@ -104,6 +107,7 @@ export default async function authRoutes(server: FastifyInstance) { user = (data || []).find(i => i.tenants.find(x => x.id === req.tenant.id)) console.log(user) if(error) { + // @ts-ignore return reply.code(500).send({ error: "Internal Server Error" }); } } else { @@ -116,11 +120,13 @@ export default async function authRoutes(server: FastifyInstance) { user = data if(error) { console.log(error); + // @ts-ignore return reply.code(500).send({ error: "Internal Server Error" }); } } if(!user) { + // @ts-ignore return reply.code(401).send({ error: "Invalid credentials" }); } else { @@ -129,6 +135,7 @@ export default async function authRoutes(server: FastifyInstance) { const valid = await bcrypt.compare(body.password, user.password_hash); if (!valid) { + // @ts-ignore return reply.code(401).send({ error: "Invalid credentials" }); } else { const token = jwt.sign( diff --git a/src/routes/auth/user.ts b/src/routes/auth/user.ts index 687ea15..78ac0e6 100644 --- a/src/routes/auth/user.ts +++ b/src/routes/auth/user.ts @@ -6,7 +6,7 @@ export default async function userRoutes(server: FastifyInstance) { server.get("/user/:id", async (req, reply) => { const authUser = req.user // kommt aus JWT (user_id + tenant_id) - const {id} = req.params + const { id } = req.params as { id?: string } if (!authUser) { return reply.code(401).send({ error: "Unauthorized" }) diff --git a/src/routes/banking.ts b/src/routes/banking.ts index a85fed4..1e88cee 100644 --- a/src/routes/banking.ts +++ b/src/routes/banking.ts @@ -14,12 +14,14 @@ export default async function bankingRoutes(server: FastifyInstance) { console.log(body); const {data,error} = await server.supabase.from("statementallocations").insert({ + //@ts-ignore ...body.data, tenant: req.user.tenant_id, }).select() await insertHistoryItem(server,{ entity: "bankstatements", + //@ts-ignore entityId: data.id, action: "created", created_by: req.user.user_id, diff --git a/src/routes/files.ts b/src/routes/files.ts index 8e2f2da..beea652 100644 --- a/src/routes/files.ts +++ b/src/routes/files.ts @@ -98,7 +98,8 @@ export default async function fileRoutes(server: FastifyInstance) { server.post("/files/download/:id?", async (req, reply) => { const { id } = req.params as { id?: string } - const ids = req.body?.ids || [] + const { ids } = req.body as { ids: string[] } + try { if (id) { @@ -180,8 +181,8 @@ export default async function fileRoutes(server: FastifyInstance) { }) server.post("/files/presigned/:id?", async (req, reply) => { - const { id } = req.params as { key: string }; - const { ids } = req.body as { keys: string[] } + const { id } = req.params as { id: string }; + const { ids } = req.body as { ids: string[] } if(id) { try { diff --git a/src/routes/resources.ts b/src/routes/resources.ts index d48ec9e..b159c1a 100644 --- a/src/routes/resources.ts +++ b/src/routes/resources.ts @@ -3,7 +3,9 @@ import {insertHistoryItem } from "../utils/history" import {diffObjects} from "../utils/diff"; import {sortData} from "../utils/sort"; + const dataTypes: any[] = { + // @ts-ignore tasks: { isArchivable: true, label: "Aufgaben", diff --git a/src/routes/resourcesSpecial.ts b/src/routes/resourcesSpecial.ts index d50a33d..9fae6ac 100644 --- a/src/routes/resourcesSpecial.ts +++ b/src/routes/resourcesSpecial.ts @@ -26,6 +26,7 @@ export default async function resourceRoutesSpecial(server: FastifyInstance) { return reply.code(400).send({ error: error.message }); } + // @ts-ignore const sorted =sortData(data,sort,asc === "true" ? true : false) return sorted; diff --git a/src/routes/tenant.ts b/src/routes/tenant.ts index 6ad68d7..86e4c04 100644 --- a/src/routes/tenant.ts +++ b/src/routes/tenant.ts @@ -81,10 +81,15 @@ export default async function routes(server: FastifyInstance) { } let correctedData = data.map(i => { + + return { id: i.user_id, + // @ts-ignore email: i.auth_users.email, + // @ts-ignore profile: i.auth_users.auth_profiles.find(x => x.tenant_id === authUser.tenant_id), + // @ts-ignore full_name: i.auth_users.auth_profiles.find(x => x.tenant_id === authUser.tenant_id)?.full_name, } }) diff --git a/src/utils/export/datev.ts b/src/utils/export/datev.ts index c8d5298..3365fc8 100644 --- a/src/utils/export/datev.ts +++ b/src/utils/export/datev.ts @@ -18,8 +18,10 @@ const getCreatedDocumentTotal = (item) => { totalNet = totalNet + Number(rowPrice) if(row.taxPercent === 19) { + // @ts-ignore total19 = total19 + Number(rowPrice * 0.19) } else if(row.taxPercent === 7) { + // @ts-ignore total7 = total7 + Number(rowPrice * 0.07) } } @@ -168,10 +170,10 @@ export async function buildExportZip(server: FastifyInstance, tenant: number, st let accountData = accounts.find(i => i.id === account.account) - let buschluessel = 9 + let buschluessel: string = "9" if(account.taxType === '19'){ - buschluessel = 9 + buschluessel = "9" } else if(account.taxType === 'null') { buschluessel = "" } else if(account.taxType === '7') { diff --git a/src/utils/sort.ts b/src/utils/sort.ts index 1237d62..4cb28ef 100644 --- a/src/utils/sort.ts +++ b/src/utils/sort.ts @@ -7,7 +7,7 @@ */ export function sortData>( data: T[], - column?: keyof T, + column?: keyof T | null, ascending: boolean = true ): T[] { if (!column) return data @@ -27,6 +27,7 @@ export function sortData>( } // Datumsvergleich + // @ts-ignore if (valA instanceof Date && valB instanceof Date) { return ascending ? valA.getTime() - valB.getTime() : valB.getTime() - valA.getTime() } From d45d16a8108d603a927ffb16c1b7e05df069960f Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Fri, 12 Sep 2025 19:50:34 +0200 Subject: [PATCH 018/149] Changed host --- src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 9b7697f..ca1615a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -68,7 +68,7 @@ async function main() { // Start try { - await app.listen({ port: 3100 }); + await app.listen({ port: 3100: host: "0.0.0.0" }); console.log("🚀 Server läuft auf http://localhost:3100"); } catch (err) { app.log.error(err); From 8f7ce314fb300853b27ee784b34d5b78de79a0f4 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Fri, 12 Sep 2025 19:51:05 +0200 Subject: [PATCH 019/149] Changed host --- src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index ca1615a..f49981e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -68,7 +68,7 @@ async function main() { // Start try { - await app.listen({ port: 3100: host: "0.0.0.0" }); + await app.listen({ port: 3100, host: "0.0.0.0" }); console.log("🚀 Server läuft auf http://localhost:3100"); } catch (err) { app.log.error(err); From 91a62f88d28bdd892a48ffbd7a096542635b2f36 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sat, 13 Sep 2025 16:23:53 +0200 Subject: [PATCH 020/149] Added Cors Hosts --- src/plugins/cors.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugins/cors.ts b/src/plugins/cors.ts index 1a0fa5a..b05b84a 100644 --- a/src/plugins/cors.ts +++ b/src/plugins/cors.ts @@ -7,6 +7,8 @@ export default fp(async (server: FastifyInstance) => { origin: [ "http://localhost:3000", // dein Nuxt-Frontend "http://127.0.0.1:3000", // dein Nuxt-Frontend + "https://beta.fedeo.de", // dein Nuxt-Frontend + "https://app.fedeo.de", // dein Nuxt-Frontend ], methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"], allowedHeaders: ["Content-Type", "Authorization", "Context"], From 717eaf0851ce11d750b6a37a3f8d7f1274b1dddc Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sat, 13 Sep 2025 18:10:43 +0200 Subject: [PATCH 021/149] Added Cors Hosts --- src/routes/auth/user.ts | 41 +++++++++++++++++++++++++++++++++++++++++ src/utils/pdf.ts | 4 ++-- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/src/routes/auth/user.ts b/src/routes/auth/user.ts index 78ac0e6..7239922 100644 --- a/src/routes/auth/user.ts +++ b/src/routes/auth/user.ts @@ -64,4 +64,45 @@ export default async function userRoutes(server: FastifyInstance) { profile, } }) + + server.put("/user/:id/profile", async (req, reply) => { + + const { id } = req.params as { id?: string } + + const { data } = req.body as { data?: object } + + // 4. Profil für den aktiven Tenant laden + let profile = null + if (req.user.tenant_id) { + const { data: profileData } = await server.supabase + .from("auth_profiles") + .select("*") + .eq("user_id", req.user.user_id) + .eq("tenant_id", req.user.tenant_id) + .single() + + profile = profileData + } + + console.log(data) + + //Update Profile + const { data: updatedProfileData, error: updateError } = await server.supabase + .from("auth_profiles") + .update(data) + .eq("user_id", id) + .eq("id", profile?.id) + .select("*") + .single() + + console.log(updateError) + console.log(updatedProfileData) + + // 5. Permissions laden (über Funktion) + + // 6. Response zurückgeben + return { + data, + } + }) } \ No newline at end of file diff --git a/src/utils/pdf.ts b/src/utils/pdf.ts index 6e1681a..904bbbe 100644 --- a/src/utils/pdf.ts +++ b/src/utils/pdf.ts @@ -805,7 +805,7 @@ export const createInvoicePDF = async (server:FastifyInstance, returnMode, invoi }) } else if (invoiceData.taxType === "19 UStG") { pages[pageCounter - 1].drawText("Als Kleinunternehmer im Sinne von § 19 Abs. 1 UStG wird keine Umsatzsteuer berechnet.", { - ...getCoordinatesForPDFLib(20, rowHeight + invoiceData.totalArray.length * 8, page1), + ...getCoordinatesForPDFLib(21, rowHeight + invoiceData.totalArray.length * 8, page1), size: 10, color: rgb(0, 0, 0), lineHeight: 10, @@ -814,7 +814,7 @@ export const createInvoicePDF = async (server:FastifyInstance, returnMode, invoi }) } else if (invoiceData.taxType === "12.3 UStG") { pages[pageCounter - 1].drawText("Umsatzsteuer befreite Lieferung/Leistung für PV-Anlagen gemäß § 12 Absatz 3 UStG.", { - ...getCoordinatesForPDFLib(20, rowHeight + invoiceData.totalArray.length * 8, page1), + ...getCoordinatesForPDFLib(21, rowHeight + invoiceData.totalArray.length * 8, page1), size: 10, color: rgb(0, 0, 0), lineHeight: 10, From d3f70942b233d612c5abcb570f9b06a89e071293 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Tue, 16 Sep 2025 18:34:48 +0200 Subject: [PATCH 022/149] Changed Resources with Info --- src/routes/resources.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/resources.ts b/src/routes/resources.ts index b159c1a..41e8158 100644 --- a/src/routes/resources.ts +++ b/src/routes/resources.ts @@ -498,7 +498,7 @@ export default async function resourceRoutes(server: FastifyInstance) { // @ts-ignore - const { data, error } = await server.supabase.from(resource).select(select ? select : (with_information ? dataTypes[resource].supabaseSelectWithInformation : "*")) + const { data, error } = await server.supabase.from(resource).select(with_information ? dataTypes[resource].supabaseSelectWithInformation : (select ? select : "*")) .eq("id", id) .eq("tenant", req.user.tenant_id) .eq("archived", false) // nur aktive holen From b67e4cc6d2d051121588ce5e2567bdb3bc0b7916 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Thu, 18 Sep 2025 17:13:45 +0200 Subject: [PATCH 023/149] Fixed Files Loading --- src/routes/files.ts | 50 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/src/routes/files.ts b/src/routes/files.ts index beea652..c94a4ab 100644 --- a/src/routes/files.ts +++ b/src/routes/files.ts @@ -95,6 +95,33 @@ export default async function fileRoutes(server: FastifyInstance) { } }) + server.get("/files/:id?", async (req, reply) => { + const { id } = req.params as { id?: string } + + if(id) { + try { + const {data,error} = await server.supabase.from("files").select("*").eq("id", id).single() + + return {...data} + } catch (err) { + req.log.error(err); + reply.code(500).send({ error: "Could not generate presigned URL" }); + } + } else { + try { + const {data:supabaseFileEntries,error} = await server.supabase.from("files").select("*, createddocument(*, customer(*))").eq("tenant",req.user.tenant_id) + + + + return { files: supabaseFileEntries } + } catch (err) { + req.log.error(err) + reply.code(500).send({ error: "Could not generate presigned URLs" }) + } + } + + }) + server.post("/files/download/:id?", async (req, reply) => { const { id } = req.params as { id?: string } @@ -207,15 +234,25 @@ export default async function fileRoutes(server: FastifyInstance) { } try { - const {data:supabaseFileEntries,error} = await server.supabase.from("files").select("*, createddocument(*, customer(*))").in("id", ids) + const {data:supabaseFileEntries,error} = await server.supabase.from("files").select("*, createddocument(*, customer(*))").eq("tenant",req.user.tenant_id).is("archived",false) + + console.log(error) + + let filteredFiles = supabaseFileEntries.filter(i => ids.includes(i.id)) + filteredFiles = filteredFiles.filter(i => i.path) + + console.log(filteredFiles.filter(i => !i.path)) - const urls = await Promise.all( + let urls = await Promise.all( ids.map(async (id) => { + let file = filteredFiles.find(i => i.id === id) - let key = supabaseFileEntries.find(i => i.id === id).path + if(!file) return + let key = file.path + if(!key) console.log(file) const command = new GetObjectCommand({ Bucket: process.env.S3_BUCKET || "FEDEO", @@ -224,13 +261,16 @@ export default async function fileRoutes(server: FastifyInstance) { const url = await getSignedUrl(s3, command, { expiresIn: 900 }) // 15 min gültig - return {...supabaseFileEntries.find(i => i.id === id), url} + + return {...filteredFiles.find(i => i.id === id), url} }) ) + urls = urls.filter(i => i) + return { files: urls } } catch (err) { - req.log.error(err) + console.log(err) reply.code(500).send({ error: "Could not generate presigned URLs" }) } } From 64d45696556cbc71769940ce45024f9c6ab3ff20 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Fri, 19 Sep 2025 17:08:43 +0200 Subject: [PATCH 024/149] Removed Archived Filtering --- src/routes/resources.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/routes/resources.ts b/src/routes/resources.ts index 41e8158..3c4d578 100644 --- a/src/routes/resources.ts +++ b/src/routes/resources.ts @@ -476,7 +476,6 @@ export default async function resourceRoutes(server: FastifyInstance) { //@ts-ignore .select(select || dataTypes[resource].supabaseSelectWithInformation) .eq("tenant", req.user.tenant_id) - .eq("archived", false) if (error) { console.log(error) return reply.code(400).send({ error: error.message }); From 07b914675d4479c602acfba07f48003ccca64d0b Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Fri, 19 Sep 2025 17:08:59 +0200 Subject: [PATCH 025/149] Fixed Error when no IDs are present --- src/routes/files.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/files.ts b/src/routes/files.ts index c94a4ab..cba3da0 100644 --- a/src/routes/files.ts +++ b/src/routes/files.ts @@ -125,7 +125,7 @@ export default async function fileRoutes(server: FastifyInstance) { server.post("/files/download/:id?", async (req, reply) => { const { id } = req.params as { id?: string } - const { ids } = req.body as { ids: string[] } + const ids = req.body?.ids || [] try { From 8480ce1512324b2d62fd1c1d5b8fefeb4f784cdd Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sat, 20 Sep 2025 19:07:03 +0200 Subject: [PATCH 026/149] Fixed Error when no IDs are present --- src/routes/files.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/routes/files.ts b/src/routes/files.ts index cba3da0..5ec8d04 100644 --- a/src/routes/files.ts +++ b/src/routes/files.ts @@ -125,6 +125,7 @@ export default async function fileRoutes(server: FastifyInstance) { server.post("/files/download/:id?", async (req, reply) => { const { id } = req.params as { id?: string } + // @ts-ignore const ids = req.body?.ids || [] From 0ae247ac98ba108a491ce4ce46d8af02b5a28709 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Wed, 24 Sep 2025 16:55:18 +0200 Subject: [PATCH 027/149] Added Capacitor CORS --- src/plugins/cors.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/cors.ts b/src/plugins/cors.ts index b05b84a..dd45375 100644 --- a/src/plugins/cors.ts +++ b/src/plugins/cors.ts @@ -9,6 +9,7 @@ export default fp(async (server: FastifyInstance) => { "http://127.0.0.1:3000", // dein Nuxt-Frontend "https://beta.fedeo.de", // dein Nuxt-Frontend "https://app.fedeo.de", // dein Nuxt-Frontend + "capacitor://localhost", // dein Nuxt-Frontend ], methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"], allowedHeaders: ["Content-Type", "Authorization", "Context"], From 4d9b1f1dff3124a9187ba9358809c4c28f683308 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Fri, 26 Sep 2025 18:13:17 +0200 Subject: [PATCH 028/149] Removed Archived --- src/routes/resources.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/routes/resources.ts b/src/routes/resources.ts index 3c4d578..5891747 100644 --- a/src/routes/resources.ts +++ b/src/routes/resources.ts @@ -500,7 +500,6 @@ export default async function resourceRoutes(server: FastifyInstance) { const { data, error } = await server.supabase.from(resource).select(with_information ? dataTypes[resource].supabaseSelectWithInformation : (select ? select : "*")) .eq("id", id) .eq("tenant", req.user.tenant_id) - .eq("archived", false) // nur aktive holen .single(); if (error || !data) { From 83fc24be0c1e93d7987c87ec4a942e7fdb1e0fed Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sun, 28 Sep 2025 17:43:21 +0200 Subject: [PATCH 029/149] Introduced Secrets Manager --- package-lock.json | 1 + package.json | 1 + src/index.ts | 8 +++++--- src/plugins/auth.ts | 3 ++- src/plugins/supabase.ts | 6 +++--- src/routes/auth/auth.ts | 3 ++- src/routes/exports.ts | 5 +++-- src/routes/files.ts | 11 ++++++----- src/routes/tenant.ts | 3 ++- src/utils/export/datev.ts | 3 ++- src/utils/mailer.ts | 13 +++++++------ src/utils/s3.ts | 25 ++++++++++++++++--------- src/utils/secrets.ts | 29 +++++++++++++++++++++++++++++ 13 files changed, 79 insertions(+), 32 deletions(-) create mode 100644 src/utils/secrets.ts diff --git a/package-lock.json b/package-lock.json index a8ee19d..8e8963d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "@fastify/multipart": "^9.0.3", "@fastify/swagger": "^9.5.1", "@fastify/swagger-ui": "^5.2.3", + "@infisical/sdk": "^4.0.6", "@prisma/client": "^6.15.0", "@supabase/supabase-js": "^2.56.1", "@zip.js/zip.js": "^2.7.73", diff --git a/package.json b/package.json index c47b02b..6788b5b 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "@fastify/multipart": "^9.0.3", "@fastify/swagger": "^9.5.1", "@fastify/swagger-ui": "^5.2.3", + "@infisical/sdk": "^4.0.6", "@prisma/client": "^6.15.0", "@supabase/supabase-js": "^2.56.1", "@zip.js/zip.js": "^2.7.73", diff --git a/src/index.ts b/src/index.ts index f49981e..87f6fba 100644 --- a/src/index.ts +++ b/src/index.ts @@ -22,9 +22,11 @@ import exportRoutes from "./routes/exports" import emailAsUserRoutes from "./routes/emailAsUser"; import {sendMail} from "./utils/mailer"; +import {loadSecrets, secrets} from "./utils/secrets"; async function main() { const app = Fastify({ logger: true }); + await loadSecrets(); /*app.addHook("onRequest", (req, reply, done) => { console.log("Incoming:", req.method, req.url, "Headers:", req.headers) @@ -37,7 +39,7 @@ async function main() { await app.register(supabasePlugin); await app.register(tenantPlugin); app.register(fastifyCookie, { - secret: process.env.COOKIE_SECRET || "supersecret", // optional, für signierte Cookies + secret: secrets.COOKIE_SECRET, }) // Öffentliche Routes await app.register(authRoutes); @@ -68,8 +70,8 @@ async function main() { // Start try { - await app.listen({ port: 3100, host: "0.0.0.0" }); - console.log("🚀 Server läuft auf http://localhost:3100"); + await app.listen({ port: secrets.PORT, host: secrets.HOST }); + console.log(`🚀 Server läuft auf http://${secrets.HOST}:${secrets.PORT}`); } catch (err) { app.log.error(err); process.exit(1); diff --git a/src/plugins/auth.ts b/src/plugins/auth.ts index eb92f5d..fc2642c 100644 --- a/src/plugins/auth.ts +++ b/src/plugins/auth.ts @@ -1,6 +1,7 @@ import { FastifyInstance } from "fastify"; import fp from "fastify-plugin"; import jwt from "jsonwebtoken"; +import {secrets} from "../utils/secrets"; export default fp(async (server: FastifyInstance) => { server.addHook("preHandler", async (req, reply) => { @@ -28,7 +29,7 @@ export default fp(async (server: FastifyInstance) => { } - const payload = jwt.verify(token, process.env.JWT_SECRET!) as { + const payload = jwt.verify(token, secrets.JWT_SECRET!) as { user_id: string; email: string; tenant_id?: string; diff --git a/src/plugins/supabase.ts b/src/plugins/supabase.ts index 66172df..eb5ca98 100644 --- a/src/plugins/supabase.ts +++ b/src/plugins/supabase.ts @@ -1,11 +1,11 @@ import { FastifyInstance } from "fastify"; import fp from "fastify-plugin"; import { createClient, SupabaseClient } from "@supabase/supabase-js"; +import {secrets} from "../utils/secrets"; export default fp(async (server: FastifyInstance) => { - const supabaseUrl = process.env.SUPABASE_URL || "https://uwppvcxflrcsibuzsbil.supabase.co"; - const supabaseServiceKey = process.env.SUPABASE_SERVICE_ROLE_KEY || "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InV3cHB2Y3hmbHJjc2lidXpzYmlsIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTcwMDkzODE5NCwiZXhwIjoyMDE2NTE0MTk0fQ.6hOkD1J8XBkVJUm-swv0ngLQ74xrEYr28EEbo0rUrts"; - + const supabaseUrl = secrets.SUPABASE_URL + const supabaseServiceKey = secrets.SUPABASE_SERVICE_ROLE_KEY const supabase: SupabaseClient = createClient(supabaseUrl, supabaseServiceKey); // Fastify um supabase erweitern diff --git a/src/routes/auth/auth.ts b/src/routes/auth/auth.ts index d0ac34e..86cf6d6 100644 --- a/src/routes/auth/auth.ts +++ b/src/routes/auth/auth.ts @@ -3,6 +3,7 @@ import bcrypt from "bcrypt"; import jwt from "jsonwebtoken"; import { generateRandomPassword, hashPassword } from "../../utils/password" import { sendMail } from "../../utils/mailer" +import {secrets} from "../../utils/secrets"; export default async function authRoutes(server: FastifyInstance) { // Registrierung @@ -140,7 +141,7 @@ export default async function authRoutes(server: FastifyInstance) { } else { const token = jwt.sign( { user_id: user.id, email: user.email, tenant_id: req.tenant ? req.tenant.id : null }, - process.env.JWT_SECRET!, + secrets.JWT_SECRET!, { expiresIn: "3h" } ); diff --git a/src/routes/exports.ts b/src/routes/exports.ts index eb10186..89d041b 100644 --- a/src/routes/exports.ts +++ b/src/routes/exports.ts @@ -7,6 +7,7 @@ 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"; +import {secrets} from "../utils/secrets"; const createExport = async (server:FastifyInstance,req:any,startDate,endDate,beraternr,mandantennr) => { console.log(startDate,endDate,beraternr,mandantennr) @@ -22,7 +23,7 @@ const createExport = async (server:FastifyInstance,req:any,startDate,endDate,ber // 3) In S3 hochladen await s3.send( new PutObjectCommand({ - Bucket: process.env.S3_BUCKET || "FEDEO", + Bucket: secrets.S3_BUCKET, Key: fileKey, Body: buffer, ContentType: "application/zip", @@ -33,7 +34,7 @@ const createExport = async (server:FastifyInstance,req:any,startDate,endDate,ber const url = await getSignedUrl( s3, new GetObjectCommand({ - Bucket: process.env.S3_BUCKET || "FEDEO", + Bucket: secrets.S3_BUCKET, Key: fileKey, }), { expiresIn: 60 * 60 * 24 } diff --git a/src/routes/files.ts b/src/routes/files.ts index 5ec8d04..c18529c 100644 --- a/src/routes/files.ts +++ b/src/routes/files.ts @@ -4,6 +4,7 @@ import { s3 } from "../utils/s3" import {GetObjectCommand, PutObjectCommand} from "@aws-sdk/client-s3" import {getSignedUrl} from "@aws-sdk/s3-request-presigner"; import archiver from "archiver" +import {secrets} from "../utils/secrets" export default async function fileRoutes(server: FastifyInstance) { await server.register(multipart,{ @@ -59,7 +60,7 @@ export default async function fileRoutes(server: FastifyInstance) { const fileKey = `${tenantId}/filesbyid/${createdFileData.id}/${data.filename}` await s3.send(new PutObjectCommand({ - Bucket: process.env.S3_BUCKET || "FEDEO", + Bucket: secrets.S3_BUCKET, Key: fileKey, Body: fileBuffer, ContentType: data.mimetype, @@ -143,7 +144,7 @@ export default async function fileRoutes(server: FastifyInstance) { } const command = new GetObjectCommand({ - Bucket: process.env.S3_BUCKET || "FEDEO", + Bucket: secrets.S3_BUCKET, Key: data.path, }) @@ -187,7 +188,7 @@ export default async function fileRoutes(server: FastifyInstance) { for (const entry of supabaseFiles) { const command = new GetObjectCommand({ - Bucket: process.env.S3_BUCKET || "FEDEO", + Bucket: secrets.S3_BUCKET, Key: entry.path, }) @@ -217,7 +218,7 @@ export default async function fileRoutes(server: FastifyInstance) { const {data,error} = await server.supabase.from("files").select("*").eq("id", id).single() const command = new GetObjectCommand({ - Bucket: process.env.S3_BUCKET || "FEDEO", + Bucket: secrets.S3_BUCKET, Key: data.path, }); @@ -256,7 +257,7 @@ export default async function fileRoutes(server: FastifyInstance) { if(!key) console.log(file) const command = new GetObjectCommand({ - Bucket: process.env.S3_BUCKET || "FEDEO", + Bucket: secrets.S3_BUCKET, Key: key, }) diff --git a/src/routes/tenant.ts b/src/routes/tenant.ts index 86e4c04..0739e04 100644 --- a/src/routes/tenant.ts +++ b/src/routes/tenant.ts @@ -1,5 +1,6 @@ import { FastifyInstance } from "fastify"; import jwt from "jsonwebtoken"; +import {secrets} from "../utils/secrets"; export default async function routes(server: FastifyInstance) { server.get("/tenant", async (req) => { @@ -45,7 +46,7 @@ export default async function routes(server: FastifyInstance) { email: req.user.email, tenant_id: body.tenant_id, }, - process.env.JWT_SECRET!, + secrets.JWT_SECRET!, { expiresIn: "3h" } ); diff --git a/src/utils/export/datev.ts b/src/utils/export/datev.ts index 3365fc8..647d27c 100644 --- a/src/utils/export/datev.ts +++ b/src/utils/export/datev.ts @@ -5,6 +5,7 @@ import {BlobWriter, Data64URIReader, TextReader, TextWriter, ZipWriter} from "@z import {FastifyInstance} from "fastify"; import {GetObjectCommand} from "@aws-sdk/client-s3"; import {s3} from "../s3"; +import {secrets} from "../secrets"; dayjs.extend(isBetween) const getCreatedDocumentTotal = (item) => { @@ -92,7 +93,7 @@ export async function buildExportZip(server: FastifyInstance, tenant: number, st const downloadFile = async (bucketName, filePath, downloadFilePath,fileId) => { const command = new GetObjectCommand({ - Bucket: process.env.S3_BUCKET || "FEDEO", + Bucket: secrets.S3_BUCKET, Key: filePath, }) diff --git a/src/utils/mailer.ts b/src/utils/mailer.ts index 34f94e1..143b64d 100644 --- a/src/utils/mailer.ts +++ b/src/utils/mailer.ts @@ -1,12 +1,13 @@ import nodemailer from "nodemailer" +import {secrets} from "./secrets" const transporter = nodemailer.createTransport({ - host: process.env.SMTP_HOST, - port: Number(process.env.SMTP_PORT) || 587, - secure: process.env.SMTP_SSL === "true", // true für 465, false für andere Ports + host: secrets.MAILER_SMTP_HOST, + port: Number(secrets.MAILER_SMTP_PORT) || 587, + secure: secrets.MAILER_SMTP_SSL === "true", // true für 465, false für andere Ports auth: { - user: process.env.SMTP_USER, - pass: process.env.SMTP_PASS, + user: secrets.MAILER_SMTP_USER, + pass: secrets.MAILER_SMTP_PASS, }, }) @@ -17,7 +18,7 @@ export async function sendMail( ): Promise<{ success: boolean; info?: any; error?: any }> { try { const info = await transporter.sendMail({ - from: process.env.MAIL_FROM, + from: secrets.MAILER_FROM, to, subject, html, diff --git a/src/utils/s3.ts b/src/utils/s3.ts index a419fbb..7a33d1c 100644 --- a/src/utils/s3.ts +++ b/src/utils/s3.ts @@ -1,11 +1,18 @@ import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3" +import {secrets} from "./secrets"; -export const s3 = new S3Client({ - endpoint: process.env.S3_ENDPOINT || "https://fedeo.nbg1.your-objectstorage.com", // z. B. http://localhost:9000 für MinIO - region: process.env.S3_REGION || "eu-central", - credentials: { - accessKeyId: process.env.S3_ACCESS_KEY || "RYOMQRW8KSTY3UQX7RPJ", - secretAccessKey: process.env.S3_SECRET_KEY || "aZ33xBv47sPPsHuFKeHSDiLagjqF7nShnuGkj7B1", - }, - forcePathStyle: true, // wichtig für MinIO -}) \ No newline at end of file + + +export let s3 = null + +export const initS3 = async () => { + s3 = new S3Client({ + endpoint: secrets.S3_ENDPOINT, // z. B. http://localhost:9000 für MinIO + region: secrets.S3_REGION, + credentials: { + accessKeyId: secrets.S3_ACCESS_KEY, + secretAccessKey: secrets.S3_SECRET_KEY, + }, + forcePathStyle: true, // wichtig für MinIO + }) +} diff --git a/src/utils/secrets.ts b/src/utils/secrets.ts new file mode 100644 index 0000000..84447ff --- /dev/null +++ b/src/utils/secrets.ts @@ -0,0 +1,29 @@ +import {InfisicalSDK} from "@infisical/sdk" + +const client = new InfisicalSDK({ + siteUrl: "https://secrets.fedeo.io" +}) + + + +export let secrets = {} + +export async function loadSecrets () { + + await client.auth().universalAuth.login({ + clientId: process.env.INFISICAL_CLIENT_ID, + clientSecret: process.env.INFISICAL_CLIENT_SECRET, + }); + + const allSecrets = await client.secrets().listSecrets({ + environment: "dev", // stg, dev, prod, or custom environment slugs + projectId: "39774094-2aaf-49fb-a213-d6b2c10f6144" + }); + + allSecrets.secrets.forEach(secret => { + secrets[secret.secretKey] = secret.secretValue + }) + + console.log("✅ Secrets aus Infisical geladen"); +} + From 7482e974f263a940dd57875678af4906b0fed9c6 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sun, 28 Sep 2025 17:43:44 +0200 Subject: [PATCH 030/149] Introduced Crypt Functions --- package-lock.json | 4344 +++++++++++++++++++++++++++++++++++++++++++- package.json | 1 + src/utils/crypt.ts | 39 + 3 files changed, 4383 insertions(+), 1 deletion(-) create mode 100644 src/utils/crypt.ts diff --git a/package-lock.json b/package-lock.json index 8e8963d..c4fffea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,6 +23,7 @@ "archiver": "^7.0.1", "axios": "^1.12.1", "bcrypt": "^6.0.0", + "crypto": "^1.0.1", "dayjs": "^1.11.18", "fastify": "^5.5.0", "fastify-plugin": "^5.0.1", @@ -242,6 +243,1018 @@ "node": ">=14.0.0" } }, + "node_modules/@aws-sdk/client-cognito-identity": { + "version": "3.600.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.600.0.tgz", + "integrity": "sha512-8dYsnDLiD0rjujRiZZl0E57heUkHqMSFZHBi0YMs57SM8ODPxK3tahwDYZtS7bqanvFKZwGy+o9jIcij7jBOlA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.600.0", + "@aws-sdk/client-sts": "3.600.0", + "@aws-sdk/core": "3.598.0", + "@aws-sdk/credential-provider-node": "3.600.0", + "@aws-sdk/middleware-host-header": "3.598.0", + "@aws-sdk/middleware-logger": "3.598.0", + "@aws-sdk/middleware-recursion-detection": "3.598.0", + "@aws-sdk/middleware-user-agent": "3.598.0", + "@aws-sdk/region-config-resolver": "3.598.0", + "@aws-sdk/types": "3.598.0", + "@aws-sdk/util-endpoints": "3.598.0", + "@aws-sdk/util-user-agent-browser": "3.598.0", + "@aws-sdk/util-user-agent-node": "3.598.0", + "@smithy/config-resolver": "^3.0.2", + "@smithy/core": "^2.2.1", + "@smithy/fetch-http-handler": "^3.0.2", + "@smithy/hash-node": "^3.0.1", + "@smithy/invalid-dependency": "^3.0.1", + "@smithy/middleware-content-length": "^3.0.1", + "@smithy/middleware-endpoint": "^3.0.2", + "@smithy/middleware-retry": "^3.0.4", + "@smithy/middleware-serde": "^3.0.1", + "@smithy/middleware-stack": "^3.0.1", + "@smithy/node-config-provider": "^3.1.1", + "@smithy/node-http-handler": "^3.0.1", + "@smithy/protocol-http": "^4.0.1", + "@smithy/smithy-client": "^3.1.2", + "@smithy/types": "^3.1.0", + "@smithy/url-parser": "^3.0.1", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.4", + "@smithy/util-defaults-mode-node": "^3.0.4", + "@smithy/util-endpoints": "^2.0.2", + "@smithy/util-middleware": "^3.0.1", + "@smithy/util-retry": "^3.0.1", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/client-sso": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.598.0.tgz", + "integrity": "sha512-nOI5lqPYa+YZlrrzwAJywJSw3MKVjvu6Ge2fCqQUNYMfxFB0NAaDFnl0EPjXi+sEbtCuz/uWE77poHbqiZ+7Iw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.598.0", + "@aws-sdk/middleware-host-header": "3.598.0", + "@aws-sdk/middleware-logger": "3.598.0", + "@aws-sdk/middleware-recursion-detection": "3.598.0", + "@aws-sdk/middleware-user-agent": "3.598.0", + "@aws-sdk/region-config-resolver": "3.598.0", + "@aws-sdk/types": "3.598.0", + "@aws-sdk/util-endpoints": "3.598.0", + "@aws-sdk/util-user-agent-browser": "3.598.0", + "@aws-sdk/util-user-agent-node": "3.598.0", + "@smithy/config-resolver": "^3.0.2", + "@smithy/core": "^2.2.1", + "@smithy/fetch-http-handler": "^3.0.2", + "@smithy/hash-node": "^3.0.1", + "@smithy/invalid-dependency": "^3.0.1", + "@smithy/middleware-content-length": "^3.0.1", + "@smithy/middleware-endpoint": "^3.0.2", + "@smithy/middleware-retry": "^3.0.4", + "@smithy/middleware-serde": "^3.0.1", + "@smithy/middleware-stack": "^3.0.1", + "@smithy/node-config-provider": "^3.1.1", + "@smithy/node-http-handler": "^3.0.1", + "@smithy/protocol-http": "^4.0.1", + "@smithy/smithy-client": "^3.1.2", + "@smithy/types": "^3.1.0", + "@smithy/url-parser": "^3.0.1", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.4", + "@smithy/util-defaults-mode-node": "^3.0.4", + "@smithy/util-endpoints": "^2.0.2", + "@smithy/util-middleware": "^3.0.1", + "@smithy/util-retry": "^3.0.1", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/core": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.598.0.tgz", + "integrity": "sha512-HaSjt7puO5Cc7cOlrXFCW0rtA0BM9lvzjl56x0A20Pt+0wxXGeTOZZOkXQIepbrFkV2e/HYukuT9e99vXDm59g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^2.2.1", + "@smithy/protocol-http": "^4.0.1", + "@smithy/signature-v4": "^3.1.0", + "@smithy/smithy-client": "^3.1.2", + "@smithy/types": "^3.1.0", + "fast-xml-parser": "4.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.598.0.tgz", + "integrity": "sha512-vi1khgn7yXzLCcgSIzQrrtd2ilUM0dWodxj3PQ6BLfP0O+q1imO3hG1nq7DVyJtq7rFHs6+9N8G4mYvTkxby2w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/property-provider": "^3.1.1", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.598.0.tgz", + "integrity": "sha512-N7cIafi4HVlQvEgvZSo1G4T9qb/JMLGMdBsDCT5XkeJrF0aptQWzTFH0jIdZcLrMYvzPcuEyO3yCBe6cy/ba0g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/fetch-http-handler": "^3.0.2", + "@smithy/node-http-handler": "^3.0.1", + "@smithy/property-provider": "^3.1.1", + "@smithy/protocol-http": "^4.0.1", + "@smithy/smithy-client": "^3.1.2", + "@smithy/types": "^3.1.0", + "@smithy/util-stream": "^3.0.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.600.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.600.0.tgz", + "integrity": "sha512-1pC7MPMYD45J7yFjA90SxpR0yaSvy+yZiq23aXhAPZLYgJBAxHLu0s0mDCk/piWGPh8+UGur5K0bVdx4B1D5hw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.598.0", + "@aws-sdk/credential-provider-http": "3.598.0", + "@aws-sdk/credential-provider-ini": "3.598.0", + "@aws-sdk/credential-provider-process": "3.598.0", + "@aws-sdk/credential-provider-sso": "3.598.0", + "@aws-sdk/credential-provider-web-identity": "3.598.0", + "@aws-sdk/types": "3.598.0", + "@smithy/credential-provider-imds": "^3.1.1", + "@smithy/property-provider": "^3.1.1", + "@smithy/shared-ini-file-loader": "^3.1.1", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-node/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.598.0.tgz", + "integrity": "sha512-/ppcIVUbRwDIwJDoYfp90X3+AuJo2mvE52Y1t2VSrvUovYn6N4v95/vXj6LS8CNDhz2jvEJYmu+0cTMHdhI6eA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.598.0", + "@aws-sdk/credential-provider-http": "3.598.0", + "@aws-sdk/credential-provider-process": "3.598.0", + "@aws-sdk/credential-provider-sso": "3.598.0", + "@aws-sdk/credential-provider-web-identity": "3.598.0", + "@aws-sdk/types": "3.598.0", + "@smithy/credential-provider-imds": "^3.1.1", + "@smithy/property-provider": "^3.1.1", + "@smithy/shared-ini-file-loader": "^3.1.1", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.598.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-node/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.598.0.tgz", + "integrity": "sha512-GV5GdiMbz5Tz9JO4NJtRoFXjW0GPEujA0j+5J/B723rTN+REHthJu48HdBKouHGhdzkDWkkh1bu52V02Wprw8w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/property-provider": "^3.1.1", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.598.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.598.0.tgz", + "integrity": "sha512-rM707XbLW8huMk722AgjVyxu2tMZee++fNA8TJVNgs1Ma02Wx6bBrfIvlyK0rCcIRb0WdQYP6fe3Xhiu4e8IBA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/property-provider": "^3.1.1", + "@smithy/shared-ini-file-loader": "^3.1.1", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.598.0.tgz", + "integrity": "sha512-5InwUmrAuqQdOOgxTccRayMMkSmekdLk6s+az9tmikq0QFAHUCtofI+/fllMXSR9iL6JbGYi1940+EUmS4pHJA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.598.0", + "@aws-sdk/token-providers": "3.598.0", + "@aws-sdk/types": "3.598.0", + "@smithy/property-provider": "^3.1.1", + "@smithy/shared-ini-file-loader": "^3.1.1", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.598.0.tgz", + "integrity": "sha512-TKY1EVdHVBnZqpyxyTHdpZpa1tUpb6nxVeRNn1zWG8QB5MvH4ALLd/jR+gtmWDNQbIG4cVuBOZFVL8hIYicKTA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/property-provider": "^3.1.1", + "@smithy/shared-ini-file-loader": "^3.1.1", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.598.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.598.0.tgz", + "integrity": "sha512-WiaG059YBQwQraNejLIi0gMNkX7dfPZ8hDIhvMr5aVPRbaHH8AYF3iNSsXYCHvA2Cfa1O9haYXsuMF9flXnCmA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/protocol-http": "^4.0.1", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/middleware-logger": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.598.0.tgz", + "integrity": "sha512-bxBjf/VYiu3zfu8SYM2S9dQQc3tz5uBAOcPz/Bt8DyyK3GgOpjhschH/2XuUErsoUO1gDJqZSdGOmuHGZQn00Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.598.0.tgz", + "integrity": "sha512-vjT9BeFY9FeN0f8hm2l6F53tI0N5bUq6RcDkQXKNabXBnQxKptJRad6oP2X5y3FoVfBLOuDkQgiC2940GIPxtQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/protocol-http": "^4.0.1", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.598.0.tgz", + "integrity": "sha512-4tjESlHG5B5MdjUaLK7tQs/miUtHbb6deauQx8ryqSBYOhfHVgb1ZnzvQR0bTrhpqUg0WlybSkDaZAICf9xctg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@aws-sdk/util-endpoints": "3.598.0", + "@smithy/protocol-http": "^4.0.1", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.598.0.tgz", + "integrity": "sha512-oYXhmTokSav4ytmWleCr3rs/1nyvZW/S0tdi6X7u+dLNL5Jee+uMxWGzgOrWK6wrQOzucLVjS4E/wA11Kv2GTw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/node-config-provider": "^3.1.1", + "@smithy/types": "^3.1.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/types": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.598.0.tgz", + "integrity": "sha512-742uRl6z7u0LFmZwDrFP6r1wlZcgVPw+/TilluDJmCAR8BgRw3IR+743kUXKBGd8QZDRW2n6v/PYsi/AWCDDMQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/util-endpoints": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.598.0.tgz", + "integrity": "sha512-Qo9UoiVVZxcOEdiOMZg3xb1mzkTxrhd4qSlg5QQrfWPJVx/QOg+Iy0NtGxPtHtVZNHZxohYwDwV/tfsnDSE2gQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/types": "^3.1.0", + "@smithy/util-endpoints": "^2.0.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.598.0.tgz", + "integrity": "sha512-36Sxo6F+ykElaL1mWzWjlg+1epMpSe8obwhCN1yGE7Js9ywy5U6k6l+A3q3YM9YRbm740sNxncbwLklMvuhTKw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/types": "^3.1.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.598.0.tgz", + "integrity": "sha512-oyWGcOlfTdzkC6SVplyr0AGh54IMrDxbhg5RxJ5P+V4BKfcDoDcZV9xenUk9NsOi9MuUjxMumb9UJGkDhM1m0A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/node-config-provider": "^3.1.1", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/abort-controller": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.9.tgz", + "integrity": "sha512-yiW0WI30zj8ZKoSYNx90no7ugVn3khlyH/z5W8qtKBtVE6awRALbhSG+2SAHA1r6bO/6M9utxYKVZ3PCJ1rWxw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/config-resolver": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.13.tgz", + "integrity": "sha512-Gr/qwzyPaTL1tZcq8WQyHhTZREER5R1Wytmz4WnVGL4onA3dNk6Btll55c8Vr58pLdvWZmtG8oZxJTw3t3q7Jg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.12", + "@smithy/types": "^3.7.2", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.11", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/core": { + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.5.7.tgz", + "integrity": "sha512-8olpW6mKCa0v+ibCjoCzgZHQx1SQmZuW/WkrdZo73wiTprTH6qhmskT60QLFdT9DRa5mXxjz89kQPZ7ZSsoqqg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-serde": "^3.0.11", + "@smithy/protocol-http": "^4.1.8", + "@smithy/types": "^3.7.2", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-middleware": "^3.0.11", + "@smithy/util-stream": "^3.3.4", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/credential-provider-imds": { + "version": "3.2.8", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.8.tgz", + "integrity": "sha512-ZCY2yD0BY+K9iMXkkbnjo+08T2h8/34oHd0Jmh6BZUSZwaaGlGCyBT/3wnS7u7Xl33/EEfN4B6nQr3Gx5bYxgw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.12", + "@smithy/property-provider": "^3.1.11", + "@smithy/types": "^3.7.2", + "@smithy/url-parser": "^3.0.11", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/fetch-http-handler": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-3.2.9.tgz", + "integrity": "sha512-hYNVQOqhFQ6vOpenifFME546f0GfJn2OiQ3M0FDmuUu8V/Uiwy2wej7ZXxFBNqdx0R5DZAqWM1l6VRhGz8oE6A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.4", + "@smithy/querystring-builder": "^3.0.7", + "@smithy/types": "^3.5.0", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/hash-node": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.11.tgz", + "integrity": "sha512-emP23rwYyZhQBvklqTtwetkQlqbNYirDiEEwXl2v0GYWMnCzxst7ZaRAnWuy28njp5kAH54lvkdG37MblZzaHA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/invalid-dependency": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.11.tgz", + "integrity": "sha512-NuQmVPEJjUX6c+UELyVz8kUx8Q539EDeNwbRyu4IIF8MeV7hUtq1FB3SHVyki2u++5XLMFqngeMKk7ccspnNyQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", + "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/middleware-content-length": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.13.tgz", + "integrity": "sha512-zfMhzojhFpIX3P5ug7jxTjfUcIPcGjcQYzB9t+rv0g1TX7B0QdwONW+ATouaLoD7h7LOw/ZlXfkq4xJ/g2TrIw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.8", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/middleware-endpoint": { + "version": "3.2.8", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.2.8.tgz", + "integrity": "sha512-OEJZKVUEhMOqMs3ktrTWp7UvvluMJEvD5XgQwRePSbDg1VvBaL8pX8mwPltFn6wk1GySbcVwwyldL8S+iqnrEQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^2.5.7", + "@smithy/middleware-serde": "^3.0.11", + "@smithy/node-config-provider": "^3.1.12", + "@smithy/shared-ini-file-loader": "^3.1.12", + "@smithy/types": "^3.7.2", + "@smithy/url-parser": "^3.0.11", + "@smithy/util-middleware": "^3.0.11", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/middleware-retry": { + "version": "3.0.34", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.34.tgz", + "integrity": "sha512-yVRr/AAtPZlUvwEkrq7S3x7Z8/xCd97m2hLDaqdz6ucP2RKHsBjEqaUA2ebNv2SsZoPEi+ZD0dZbOB1u37tGCA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.12", + "@smithy/protocol-http": "^4.1.8", + "@smithy/service-error-classification": "^3.0.11", + "@smithy/smithy-client": "^3.7.0", + "@smithy/types": "^3.7.2", + "@smithy/util-middleware": "^3.0.11", + "@smithy/util-retry": "^3.0.11", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/middleware-serde": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.11.tgz", + "integrity": "sha512-KzPAeySp/fOoQA82TpnwItvX8BBURecpx6ZMu75EZDkAcnPtO6vf7q4aH5QHs/F1s3/snQaSFbbUMcFFZ086Mw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/middleware-stack": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.11.tgz", + "integrity": "sha512-1HGo9a6/ikgOMrTrWL/WiN9N8GSVYpuRQO5kjstAq4CvV59bjqnh7TbdXGQ4vxLD3xlSjfBjq5t1SOELePsLnA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/node-config-provider": { + "version": "3.1.12", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.12.tgz", + "integrity": "sha512-O9LVEu5J/u/FuNlZs+L7Ikn3lz7VB9hb0GtPT9MQeiBmtK8RSY3ULmsZgXhe6VAlgTw0YO+paQx4p8xdbs43vQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^3.1.11", + "@smithy/shared-ini-file-loader": "^3.1.12", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/node-http-handler": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.3.3.tgz", + "integrity": "sha512-BrpZOaZ4RCbcJ2igiSNG16S+kgAc65l/2hmxWdmhyoGWHTLlzQzr06PXavJp9OBlPEG/sHlqdxjWmjzV66+BSQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^3.1.9", + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/property-provider": { + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.11.tgz", + "integrity": "sha512-I/+TMc4XTQ3QAjXfOcUWbSS073oOEAxgx4aZy8jHaf8JQnRkq2SZWw8+PfDtBvLUjcGMdxl+YwtzWe6i5uhL/A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/protocol-http": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.8.tgz", + "integrity": "sha512-hmgIAVyxw1LySOwkgMIUN0kjN8TG9Nc85LJeEmEE/cNEe2rkHDUWhnJf2gxcSRFLWsyqWsrZGw40ROjUogg+Iw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/querystring-builder": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.11.tgz", + "integrity": "sha512-u+5HV/9uJaeLj5XTb6+IEF/dokWWkEqJ0XiaRRogyREmKGUgZnNecLucADLdauWFKUNbQfulHFEZEdjwEBjXRg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "@smithy/util-uri-escape": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/querystring-parser": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.11.tgz", + "integrity": "sha512-Je3kFvCsFMnso1ilPwA7GtlbPaTixa3WwC+K21kmMZHsBEOZYQaqxcMqeFFoU7/slFjKDIpiiPydvdJm8Q/MCw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/service-error-classification": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.11.tgz", + "integrity": "sha512-QnYDPkyewrJzCyaeI2Rmp7pDwbUETe+hU8ADkXmgNusO1bgHBH7ovXJiYmba8t0fNfJx75fE8dlM6SEmZxheog==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/shared-ini-file-loader": { + "version": "3.1.12", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.12.tgz", + "integrity": "sha512-1xKSGI+U9KKdbG2qDvIR9dGrw3CNx+baqJfyr0igKEpjbHL5stsqAesYBzHChYHlelWtb87VnLWlhvfCz13H8Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/signature-v4": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-3.1.2.tgz", + "integrity": "sha512-3BcPylEsYtD0esM4Hoyml/+s7WP2LFhcM3J2AGdcL2vx9O60TtfpDOL72gjb4lU8NeRPeKAwR77YNyyGvMbuEA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "@smithy/types": "^3.3.0", + "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-uri-escape": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/smithy-client": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.7.0.tgz", + "integrity": "sha512-9wYrjAZFlqWhgVo3C4y/9kpc68jgiSsKUnsFPzr/MSiRL93+QRDafGTfhhKAb2wsr69Ru87WTiqSfQusSmWipA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^2.5.7", + "@smithy/middleware-endpoint": "^3.2.8", + "@smithy/middleware-stack": "^3.0.11", + "@smithy/protocol-http": "^4.1.8", + "@smithy/types": "^3.7.2", + "@smithy/util-stream": "^3.3.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/types": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.7.2.tgz", + "integrity": "sha512-bNwBYYmN8Eh9RyjS1p2gW6MIhSO2rl7X9QeLM8iTdcGRP+eDiIWDt66c9IysCc22gefKszZv+ubV9qZc7hdESg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/url-parser": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.11.tgz", + "integrity": "sha512-TmlqXkSk8ZPhfc+SQutjmFr5FjC0av3GZP4B/10caK1SbRwe/v+Wzu/R6xEKxoNqL+8nY18s1byiy6HqPG37Aw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/querystring-parser": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-base64": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-3.0.0.tgz", + "integrity": "sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-body-length-browser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-3.0.0.tgz", + "integrity": "sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-body-length-node": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-3.0.0.tgz", + "integrity": "sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz", + "integrity": "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-config-provider": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-3.0.0.tgz", + "integrity": "sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-defaults-mode-browser": { + "version": "3.0.34", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.34.tgz", + "integrity": "sha512-FumjjF631lR521cX+svMLBj3SwSDh9VdtyynTYDAiBDEf8YPP5xORNXKQ9j0105o5+ARAGnOOP/RqSl40uXddA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^3.1.11", + "@smithy/smithy-client": "^3.7.0", + "@smithy/types": "^3.7.2", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-defaults-mode-node": { + "version": "3.0.34", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.34.tgz", + "integrity": "sha512-vN6aHfzW9dVVzkI0wcZoUXvfjkl4CSbM9nE//08lmUMyf00S75uuCpTrqF9uD4bD9eldIXlt53colrlwKAT8Gw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/config-resolver": "^3.0.13", + "@smithy/credential-provider-imds": "^3.2.8", + "@smithy/node-config-provider": "^3.1.12", + "@smithy/property-provider": "^3.1.11", + "@smithy/smithy-client": "^3.7.0", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-endpoints": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.1.7.tgz", + "integrity": "sha512-tSfcqKcN/Oo2STEYCABVuKgJ76nyyr6skGl9t15hs+YaiU06sgMkN7QYjo0BbVw+KT26zok3IzbdSOksQ4YzVw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.12", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-hex-encoding": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-3.0.0.tgz", + "integrity": "sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-middleware": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.11.tgz", + "integrity": "sha512-dWpyc1e1R6VoXrwLoLDd57U1z6CwNSdkM69Ie4+6uYh2GC7Vg51Qtan7ITzczuVpqezdDTKJGJB95fFvvjU/ow==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-retry": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.11.tgz", + "integrity": "sha512-hJUC6W7A3DQgaee3Hp9ZFcOxVDZzmBIRBPlUAk8/fSOEl7pE/aX7Dci0JycNOnm9Mfr0KV2XjIlUOcGWXQUdVQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-stream": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.3.4.tgz", + "integrity": "sha512-SGhGBG/KupieJvJSZp/rfHHka8BFgj56eek9px4pp7lZbOF+fRiVr4U7A3y3zJD8uGhxq32C5D96HxsTC9BckQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/fetch-http-handler": "^4.1.3", + "@smithy/node-http-handler": "^3.3.3", + "@smithy/types": "^3.7.2", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-stream/node_modules/@smithy/fetch-http-handler": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-4.1.3.tgz", + "integrity": "sha512-6SxNltSncI8s689nvnzZQc/dPXcpHQ34KUj6gR/HBroytKOd/isMG3gJF/zBE1TBmTT18TXyzhg3O3SOOqGEhA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-uri-escape": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-3.0.0.tgz", + "integrity": "sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", + "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/fast-xml-parser": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz", + "integrity": "sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==", + "funding": [ + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + }, + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/@aws-sdk/client-cognito-identity/node_modules/strnum": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", + "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, "node_modules/@aws-sdk/client-s3": { "version": "3.879.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.879.0.tgz", @@ -360,6 +1373,2028 @@ "node": ">=18.0.0" } }, + "node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.600.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.600.0.tgz", + "integrity": "sha512-7+I8RWURGfzvChyNQSyj5/tKrqRbzRl7H+BnTOf/4Vsw1nFOi5ROhlhD4X/Y0QCTacxnaoNcIrqnY7uGGvVRzw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sts": "3.600.0", + "@aws-sdk/core": "3.598.0", + "@aws-sdk/credential-provider-node": "3.600.0", + "@aws-sdk/middleware-host-header": "3.598.0", + "@aws-sdk/middleware-logger": "3.598.0", + "@aws-sdk/middleware-recursion-detection": "3.598.0", + "@aws-sdk/middleware-user-agent": "3.598.0", + "@aws-sdk/region-config-resolver": "3.598.0", + "@aws-sdk/types": "3.598.0", + "@aws-sdk/util-endpoints": "3.598.0", + "@aws-sdk/util-user-agent-browser": "3.598.0", + "@aws-sdk/util-user-agent-node": "3.598.0", + "@smithy/config-resolver": "^3.0.2", + "@smithy/core": "^2.2.1", + "@smithy/fetch-http-handler": "^3.0.2", + "@smithy/hash-node": "^3.0.1", + "@smithy/invalid-dependency": "^3.0.1", + "@smithy/middleware-content-length": "^3.0.1", + "@smithy/middleware-endpoint": "^3.0.2", + "@smithy/middleware-retry": "^3.0.4", + "@smithy/middleware-serde": "^3.0.1", + "@smithy/middleware-stack": "^3.0.1", + "@smithy/node-config-provider": "^3.1.1", + "@smithy/node-http-handler": "^3.0.1", + "@smithy/protocol-http": "^4.0.1", + "@smithy/smithy-client": "^3.1.2", + "@smithy/types": "^3.1.0", + "@smithy/url-parser": "^3.0.1", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.4", + "@smithy/util-defaults-mode-node": "^3.0.4", + "@smithy/util-endpoints": "^2.0.2", + "@smithy/util-middleware": "^3.0.1", + "@smithy/util-retry": "^3.0.1", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/client-sso": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.598.0.tgz", + "integrity": "sha512-nOI5lqPYa+YZlrrzwAJywJSw3MKVjvu6Ge2fCqQUNYMfxFB0NAaDFnl0EPjXi+sEbtCuz/uWE77poHbqiZ+7Iw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.598.0", + "@aws-sdk/middleware-host-header": "3.598.0", + "@aws-sdk/middleware-logger": "3.598.0", + "@aws-sdk/middleware-recursion-detection": "3.598.0", + "@aws-sdk/middleware-user-agent": "3.598.0", + "@aws-sdk/region-config-resolver": "3.598.0", + "@aws-sdk/types": "3.598.0", + "@aws-sdk/util-endpoints": "3.598.0", + "@aws-sdk/util-user-agent-browser": "3.598.0", + "@aws-sdk/util-user-agent-node": "3.598.0", + "@smithy/config-resolver": "^3.0.2", + "@smithy/core": "^2.2.1", + "@smithy/fetch-http-handler": "^3.0.2", + "@smithy/hash-node": "^3.0.1", + "@smithy/invalid-dependency": "^3.0.1", + "@smithy/middleware-content-length": "^3.0.1", + "@smithy/middleware-endpoint": "^3.0.2", + "@smithy/middleware-retry": "^3.0.4", + "@smithy/middleware-serde": "^3.0.1", + "@smithy/middleware-stack": "^3.0.1", + "@smithy/node-config-provider": "^3.1.1", + "@smithy/node-http-handler": "^3.0.1", + "@smithy/protocol-http": "^4.0.1", + "@smithy/smithy-client": "^3.1.2", + "@smithy/types": "^3.1.0", + "@smithy/url-parser": "^3.0.1", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.4", + "@smithy/util-defaults-mode-node": "^3.0.4", + "@smithy/util-endpoints": "^2.0.2", + "@smithy/util-middleware": "^3.0.1", + "@smithy/util-retry": "^3.0.1", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/core": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.598.0.tgz", + "integrity": "sha512-HaSjt7puO5Cc7cOlrXFCW0rtA0BM9lvzjl56x0A20Pt+0wxXGeTOZZOkXQIepbrFkV2e/HYukuT9e99vXDm59g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^2.2.1", + "@smithy/protocol-http": "^4.0.1", + "@smithy/signature-v4": "^3.1.0", + "@smithy/smithy-client": "^3.1.2", + "@smithy/types": "^3.1.0", + "fast-xml-parser": "4.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.598.0.tgz", + "integrity": "sha512-vi1khgn7yXzLCcgSIzQrrtd2ilUM0dWodxj3PQ6BLfP0O+q1imO3hG1nq7DVyJtq7rFHs6+9N8G4mYvTkxby2w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/property-provider": "^3.1.1", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.598.0.tgz", + "integrity": "sha512-N7cIafi4HVlQvEgvZSo1G4T9qb/JMLGMdBsDCT5XkeJrF0aptQWzTFH0jIdZcLrMYvzPcuEyO3yCBe6cy/ba0g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/fetch-http-handler": "^3.0.2", + "@smithy/node-http-handler": "^3.0.1", + "@smithy/property-provider": "^3.1.1", + "@smithy/protocol-http": "^4.0.1", + "@smithy/smithy-client": "^3.1.2", + "@smithy/types": "^3.1.0", + "@smithy/util-stream": "^3.0.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.600.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.600.0.tgz", + "integrity": "sha512-1pC7MPMYD45J7yFjA90SxpR0yaSvy+yZiq23aXhAPZLYgJBAxHLu0s0mDCk/piWGPh8+UGur5K0bVdx4B1D5hw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.598.0", + "@aws-sdk/credential-provider-http": "3.598.0", + "@aws-sdk/credential-provider-ini": "3.598.0", + "@aws-sdk/credential-provider-process": "3.598.0", + "@aws-sdk/credential-provider-sso": "3.598.0", + "@aws-sdk/credential-provider-web-identity": "3.598.0", + "@aws-sdk/types": "3.598.0", + "@smithy/credential-provider-imds": "^3.1.1", + "@smithy/property-provider": "^3.1.1", + "@smithy/shared-ini-file-loader": "^3.1.1", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/credential-provider-node/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.598.0.tgz", + "integrity": "sha512-/ppcIVUbRwDIwJDoYfp90X3+AuJo2mvE52Y1t2VSrvUovYn6N4v95/vXj6LS8CNDhz2jvEJYmu+0cTMHdhI6eA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.598.0", + "@aws-sdk/credential-provider-http": "3.598.0", + "@aws-sdk/credential-provider-process": "3.598.0", + "@aws-sdk/credential-provider-sso": "3.598.0", + "@aws-sdk/credential-provider-web-identity": "3.598.0", + "@aws-sdk/types": "3.598.0", + "@smithy/credential-provider-imds": "^3.1.1", + "@smithy/property-provider": "^3.1.1", + "@smithy/shared-ini-file-loader": "^3.1.1", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.598.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/credential-provider-node/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.598.0.tgz", + "integrity": "sha512-GV5GdiMbz5Tz9JO4NJtRoFXjW0GPEujA0j+5J/B723rTN+REHthJu48HdBKouHGhdzkDWkkh1bu52V02Wprw8w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/property-provider": "^3.1.1", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.598.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.598.0.tgz", + "integrity": "sha512-rM707XbLW8huMk722AgjVyxu2tMZee++fNA8TJVNgs1Ma02Wx6bBrfIvlyK0rCcIRb0WdQYP6fe3Xhiu4e8IBA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/property-provider": "^3.1.1", + "@smithy/shared-ini-file-loader": "^3.1.1", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.598.0.tgz", + "integrity": "sha512-5InwUmrAuqQdOOgxTccRayMMkSmekdLk6s+az9tmikq0QFAHUCtofI+/fllMXSR9iL6JbGYi1940+EUmS4pHJA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.598.0", + "@aws-sdk/token-providers": "3.598.0", + "@aws-sdk/types": "3.598.0", + "@smithy/property-provider": "^3.1.1", + "@smithy/shared-ini-file-loader": "^3.1.1", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.598.0.tgz", + "integrity": "sha512-WiaG059YBQwQraNejLIi0gMNkX7dfPZ8hDIhvMr5aVPRbaHH8AYF3iNSsXYCHvA2Cfa1O9haYXsuMF9flXnCmA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/protocol-http": "^4.0.1", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/middleware-logger": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.598.0.tgz", + "integrity": "sha512-bxBjf/VYiu3zfu8SYM2S9dQQc3tz5uBAOcPz/Bt8DyyK3GgOpjhschH/2XuUErsoUO1gDJqZSdGOmuHGZQn00Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.598.0.tgz", + "integrity": "sha512-vjT9BeFY9FeN0f8hm2l6F53tI0N5bUq6RcDkQXKNabXBnQxKptJRad6oP2X5y3FoVfBLOuDkQgiC2940GIPxtQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/protocol-http": "^4.0.1", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.598.0.tgz", + "integrity": "sha512-4tjESlHG5B5MdjUaLK7tQs/miUtHbb6deauQx8ryqSBYOhfHVgb1ZnzvQR0bTrhpqUg0WlybSkDaZAICf9xctg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@aws-sdk/util-endpoints": "3.598.0", + "@smithy/protocol-http": "^4.0.1", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.598.0.tgz", + "integrity": "sha512-oYXhmTokSav4ytmWleCr3rs/1nyvZW/S0tdi6X7u+dLNL5Jee+uMxWGzgOrWK6wrQOzucLVjS4E/wA11Kv2GTw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/node-config-provider": "^3.1.1", + "@smithy/types": "^3.1.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/token-providers": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.598.0.tgz", + "integrity": "sha512-TKY1EVdHVBnZqpyxyTHdpZpa1tUpb6nxVeRNn1zWG8QB5MvH4ALLd/jR+gtmWDNQbIG4cVuBOZFVL8hIYicKTA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/property-provider": "^3.1.1", + "@smithy/shared-ini-file-loader": "^3.1.1", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.598.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/types": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.598.0.tgz", + "integrity": "sha512-742uRl6z7u0LFmZwDrFP6r1wlZcgVPw+/TilluDJmCAR8BgRw3IR+743kUXKBGd8QZDRW2n6v/PYsi/AWCDDMQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/util-endpoints": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.598.0.tgz", + "integrity": "sha512-Qo9UoiVVZxcOEdiOMZg3xb1mzkTxrhd4qSlg5QQrfWPJVx/QOg+Iy0NtGxPtHtVZNHZxohYwDwV/tfsnDSE2gQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/types": "^3.1.0", + "@smithy/util-endpoints": "^2.0.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.598.0.tgz", + "integrity": "sha512-36Sxo6F+ykElaL1mWzWjlg+1epMpSe8obwhCN1yGE7Js9ywy5U6k6l+A3q3YM9YRbm740sNxncbwLklMvuhTKw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/types": "^3.1.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.598.0.tgz", + "integrity": "sha512-oyWGcOlfTdzkC6SVplyr0AGh54IMrDxbhg5RxJ5P+V4BKfcDoDcZV9xenUk9NsOi9MuUjxMumb9UJGkDhM1m0A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/node-config-provider": "^3.1.1", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/abort-controller": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.9.tgz", + "integrity": "sha512-yiW0WI30zj8ZKoSYNx90no7ugVn3khlyH/z5W8qtKBtVE6awRALbhSG+2SAHA1r6bO/6M9utxYKVZ3PCJ1rWxw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/config-resolver": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.13.tgz", + "integrity": "sha512-Gr/qwzyPaTL1tZcq8WQyHhTZREER5R1Wytmz4WnVGL4onA3dNk6Btll55c8Vr58pLdvWZmtG8oZxJTw3t3q7Jg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.12", + "@smithy/types": "^3.7.2", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.11", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/core": { + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.5.7.tgz", + "integrity": "sha512-8olpW6mKCa0v+ibCjoCzgZHQx1SQmZuW/WkrdZo73wiTprTH6qhmskT60QLFdT9DRa5mXxjz89kQPZ7ZSsoqqg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-serde": "^3.0.11", + "@smithy/protocol-http": "^4.1.8", + "@smithy/types": "^3.7.2", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-middleware": "^3.0.11", + "@smithy/util-stream": "^3.3.4", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/credential-provider-imds": { + "version": "3.2.8", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.8.tgz", + "integrity": "sha512-ZCY2yD0BY+K9iMXkkbnjo+08T2h8/34oHd0Jmh6BZUSZwaaGlGCyBT/3wnS7u7Xl33/EEfN4B6nQr3Gx5bYxgw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.12", + "@smithy/property-provider": "^3.1.11", + "@smithy/types": "^3.7.2", + "@smithy/url-parser": "^3.0.11", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/fetch-http-handler": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-3.2.9.tgz", + "integrity": "sha512-hYNVQOqhFQ6vOpenifFME546f0GfJn2OiQ3M0FDmuUu8V/Uiwy2wej7ZXxFBNqdx0R5DZAqWM1l6VRhGz8oE6A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.4", + "@smithy/querystring-builder": "^3.0.7", + "@smithy/types": "^3.5.0", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/hash-node": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.11.tgz", + "integrity": "sha512-emP23rwYyZhQBvklqTtwetkQlqbNYirDiEEwXl2v0GYWMnCzxst7ZaRAnWuy28njp5kAH54lvkdG37MblZzaHA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/invalid-dependency": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.11.tgz", + "integrity": "sha512-NuQmVPEJjUX6c+UELyVz8kUx8Q539EDeNwbRyu4IIF8MeV7hUtq1FB3SHVyki2u++5XLMFqngeMKk7ccspnNyQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", + "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/middleware-content-length": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.13.tgz", + "integrity": "sha512-zfMhzojhFpIX3P5ug7jxTjfUcIPcGjcQYzB9t+rv0g1TX7B0QdwONW+ATouaLoD7h7LOw/ZlXfkq4xJ/g2TrIw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.8", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/middleware-endpoint": { + "version": "3.2.8", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.2.8.tgz", + "integrity": "sha512-OEJZKVUEhMOqMs3ktrTWp7UvvluMJEvD5XgQwRePSbDg1VvBaL8pX8mwPltFn6wk1GySbcVwwyldL8S+iqnrEQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^2.5.7", + "@smithy/middleware-serde": "^3.0.11", + "@smithy/node-config-provider": "^3.1.12", + "@smithy/shared-ini-file-loader": "^3.1.12", + "@smithy/types": "^3.7.2", + "@smithy/url-parser": "^3.0.11", + "@smithy/util-middleware": "^3.0.11", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/middleware-retry": { + "version": "3.0.34", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.34.tgz", + "integrity": "sha512-yVRr/AAtPZlUvwEkrq7S3x7Z8/xCd97m2hLDaqdz6ucP2RKHsBjEqaUA2ebNv2SsZoPEi+ZD0dZbOB1u37tGCA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.12", + "@smithy/protocol-http": "^4.1.8", + "@smithy/service-error-classification": "^3.0.11", + "@smithy/smithy-client": "^3.7.0", + "@smithy/types": "^3.7.2", + "@smithy/util-middleware": "^3.0.11", + "@smithy/util-retry": "^3.0.11", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/middleware-serde": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.11.tgz", + "integrity": "sha512-KzPAeySp/fOoQA82TpnwItvX8BBURecpx6ZMu75EZDkAcnPtO6vf7q4aH5QHs/F1s3/snQaSFbbUMcFFZ086Mw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/middleware-stack": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.11.tgz", + "integrity": "sha512-1HGo9a6/ikgOMrTrWL/WiN9N8GSVYpuRQO5kjstAq4CvV59bjqnh7TbdXGQ4vxLD3xlSjfBjq5t1SOELePsLnA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/node-config-provider": { + "version": "3.1.12", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.12.tgz", + "integrity": "sha512-O9LVEu5J/u/FuNlZs+L7Ikn3lz7VB9hb0GtPT9MQeiBmtK8RSY3ULmsZgXhe6VAlgTw0YO+paQx4p8xdbs43vQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^3.1.11", + "@smithy/shared-ini-file-loader": "^3.1.12", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/node-http-handler": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.3.3.tgz", + "integrity": "sha512-BrpZOaZ4RCbcJ2igiSNG16S+kgAc65l/2hmxWdmhyoGWHTLlzQzr06PXavJp9OBlPEG/sHlqdxjWmjzV66+BSQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^3.1.9", + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/property-provider": { + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.11.tgz", + "integrity": "sha512-I/+TMc4XTQ3QAjXfOcUWbSS073oOEAxgx4aZy8jHaf8JQnRkq2SZWw8+PfDtBvLUjcGMdxl+YwtzWe6i5uhL/A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/protocol-http": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.8.tgz", + "integrity": "sha512-hmgIAVyxw1LySOwkgMIUN0kjN8TG9Nc85LJeEmEE/cNEe2rkHDUWhnJf2gxcSRFLWsyqWsrZGw40ROjUogg+Iw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/querystring-builder": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.11.tgz", + "integrity": "sha512-u+5HV/9uJaeLj5XTb6+IEF/dokWWkEqJ0XiaRRogyREmKGUgZnNecLucADLdauWFKUNbQfulHFEZEdjwEBjXRg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "@smithy/util-uri-escape": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/querystring-parser": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.11.tgz", + "integrity": "sha512-Je3kFvCsFMnso1ilPwA7GtlbPaTixa3WwC+K21kmMZHsBEOZYQaqxcMqeFFoU7/slFjKDIpiiPydvdJm8Q/MCw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/service-error-classification": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.11.tgz", + "integrity": "sha512-QnYDPkyewrJzCyaeI2Rmp7pDwbUETe+hU8ADkXmgNusO1bgHBH7ovXJiYmba8t0fNfJx75fE8dlM6SEmZxheog==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/shared-ini-file-loader": { + "version": "3.1.12", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.12.tgz", + "integrity": "sha512-1xKSGI+U9KKdbG2qDvIR9dGrw3CNx+baqJfyr0igKEpjbHL5stsqAesYBzHChYHlelWtb87VnLWlhvfCz13H8Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/signature-v4": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-3.1.2.tgz", + "integrity": "sha512-3BcPylEsYtD0esM4Hoyml/+s7WP2LFhcM3J2AGdcL2vx9O60TtfpDOL72gjb4lU8NeRPeKAwR77YNyyGvMbuEA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "@smithy/types": "^3.3.0", + "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-uri-escape": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/smithy-client": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.7.0.tgz", + "integrity": "sha512-9wYrjAZFlqWhgVo3C4y/9kpc68jgiSsKUnsFPzr/MSiRL93+QRDafGTfhhKAb2wsr69Ru87WTiqSfQusSmWipA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^2.5.7", + "@smithy/middleware-endpoint": "^3.2.8", + "@smithy/middleware-stack": "^3.0.11", + "@smithy/protocol-http": "^4.1.8", + "@smithy/types": "^3.7.2", + "@smithy/util-stream": "^3.3.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/types": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.7.2.tgz", + "integrity": "sha512-bNwBYYmN8Eh9RyjS1p2gW6MIhSO2rl7X9QeLM8iTdcGRP+eDiIWDt66c9IysCc22gefKszZv+ubV9qZc7hdESg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/url-parser": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.11.tgz", + "integrity": "sha512-TmlqXkSk8ZPhfc+SQutjmFr5FjC0av3GZP4B/10caK1SbRwe/v+Wzu/R6xEKxoNqL+8nY18s1byiy6HqPG37Aw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/querystring-parser": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-base64": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-3.0.0.tgz", + "integrity": "sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-body-length-browser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-3.0.0.tgz", + "integrity": "sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-body-length-node": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-3.0.0.tgz", + "integrity": "sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz", + "integrity": "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-config-provider": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-3.0.0.tgz", + "integrity": "sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-defaults-mode-browser": { + "version": "3.0.34", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.34.tgz", + "integrity": "sha512-FumjjF631lR521cX+svMLBj3SwSDh9VdtyynTYDAiBDEf8YPP5xORNXKQ9j0105o5+ARAGnOOP/RqSl40uXddA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^3.1.11", + "@smithy/smithy-client": "^3.7.0", + "@smithy/types": "^3.7.2", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-defaults-mode-node": { + "version": "3.0.34", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.34.tgz", + "integrity": "sha512-vN6aHfzW9dVVzkI0wcZoUXvfjkl4CSbM9nE//08lmUMyf00S75uuCpTrqF9uD4bD9eldIXlt53colrlwKAT8Gw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/config-resolver": "^3.0.13", + "@smithy/credential-provider-imds": "^3.2.8", + "@smithy/node-config-provider": "^3.1.12", + "@smithy/property-provider": "^3.1.11", + "@smithy/smithy-client": "^3.7.0", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-endpoints": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.1.7.tgz", + "integrity": "sha512-tSfcqKcN/Oo2STEYCABVuKgJ76nyyr6skGl9t15hs+YaiU06sgMkN7QYjo0BbVw+KT26zok3IzbdSOksQ4YzVw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.12", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-hex-encoding": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-3.0.0.tgz", + "integrity": "sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-middleware": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.11.tgz", + "integrity": "sha512-dWpyc1e1R6VoXrwLoLDd57U1z6CwNSdkM69Ie4+6uYh2GC7Vg51Qtan7ITzczuVpqezdDTKJGJB95fFvvjU/ow==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-retry": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.11.tgz", + "integrity": "sha512-hJUC6W7A3DQgaee3Hp9ZFcOxVDZzmBIRBPlUAk8/fSOEl7pE/aX7Dci0JycNOnm9Mfr0KV2XjIlUOcGWXQUdVQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-stream": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.3.4.tgz", + "integrity": "sha512-SGhGBG/KupieJvJSZp/rfHHka8BFgj56eek9px4pp7lZbOF+fRiVr4U7A3y3zJD8uGhxq32C5D96HxsTC9BckQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/fetch-http-handler": "^4.1.3", + "@smithy/node-http-handler": "^3.3.3", + "@smithy/types": "^3.7.2", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-stream/node_modules/@smithy/fetch-http-handler": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-4.1.3.tgz", + "integrity": "sha512-6SxNltSncI8s689nvnzZQc/dPXcpHQ34KUj6gR/HBroytKOd/isMG3gJF/zBE1TBmTT18TXyzhg3O3SOOqGEhA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-uri-escape": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-3.0.0.tgz", + "integrity": "sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", + "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/fast-xml-parser": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz", + "integrity": "sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==", + "funding": [ + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + }, + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/strnum": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", + "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, + "node_modules/@aws-sdk/client-sts": { + "version": "3.600.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.600.0.tgz", + "integrity": "sha512-KQG97B7LvTtTiGmjlrG1LRAY8wUvCQzrmZVV5bjrJ/1oXAU7DITYwVbSJeX9NWg6hDuSk0VE3MFwIXS2SvfLIA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.600.0", + "@aws-sdk/core": "3.598.0", + "@aws-sdk/credential-provider-node": "3.600.0", + "@aws-sdk/middleware-host-header": "3.598.0", + "@aws-sdk/middleware-logger": "3.598.0", + "@aws-sdk/middleware-recursion-detection": "3.598.0", + "@aws-sdk/middleware-user-agent": "3.598.0", + "@aws-sdk/region-config-resolver": "3.598.0", + "@aws-sdk/types": "3.598.0", + "@aws-sdk/util-endpoints": "3.598.0", + "@aws-sdk/util-user-agent-browser": "3.598.0", + "@aws-sdk/util-user-agent-node": "3.598.0", + "@smithy/config-resolver": "^3.0.2", + "@smithy/core": "^2.2.1", + "@smithy/fetch-http-handler": "^3.0.2", + "@smithy/hash-node": "^3.0.1", + "@smithy/invalid-dependency": "^3.0.1", + "@smithy/middleware-content-length": "^3.0.1", + "@smithy/middleware-endpoint": "^3.0.2", + "@smithy/middleware-retry": "^3.0.4", + "@smithy/middleware-serde": "^3.0.1", + "@smithy/middleware-stack": "^3.0.1", + "@smithy/node-config-provider": "^3.1.1", + "@smithy/node-http-handler": "^3.0.1", + "@smithy/protocol-http": "^4.0.1", + "@smithy/smithy-client": "^3.1.2", + "@smithy/types": "^3.1.0", + "@smithy/url-parser": "^3.0.1", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.4", + "@smithy/util-defaults-mode-node": "^3.0.4", + "@smithy/util-endpoints": "^2.0.2", + "@smithy/util-middleware": "^3.0.1", + "@smithy/util-retry": "^3.0.1", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/client-sso": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.598.0.tgz", + "integrity": "sha512-nOI5lqPYa+YZlrrzwAJywJSw3MKVjvu6Ge2fCqQUNYMfxFB0NAaDFnl0EPjXi+sEbtCuz/uWE77poHbqiZ+7Iw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.598.0", + "@aws-sdk/middleware-host-header": "3.598.0", + "@aws-sdk/middleware-logger": "3.598.0", + "@aws-sdk/middleware-recursion-detection": "3.598.0", + "@aws-sdk/middleware-user-agent": "3.598.0", + "@aws-sdk/region-config-resolver": "3.598.0", + "@aws-sdk/types": "3.598.0", + "@aws-sdk/util-endpoints": "3.598.0", + "@aws-sdk/util-user-agent-browser": "3.598.0", + "@aws-sdk/util-user-agent-node": "3.598.0", + "@smithy/config-resolver": "^3.0.2", + "@smithy/core": "^2.2.1", + "@smithy/fetch-http-handler": "^3.0.2", + "@smithy/hash-node": "^3.0.1", + "@smithy/invalid-dependency": "^3.0.1", + "@smithy/middleware-content-length": "^3.0.1", + "@smithy/middleware-endpoint": "^3.0.2", + "@smithy/middleware-retry": "^3.0.4", + "@smithy/middleware-serde": "^3.0.1", + "@smithy/middleware-stack": "^3.0.1", + "@smithy/node-config-provider": "^3.1.1", + "@smithy/node-http-handler": "^3.0.1", + "@smithy/protocol-http": "^4.0.1", + "@smithy/smithy-client": "^3.1.2", + "@smithy/types": "^3.1.0", + "@smithy/url-parser": "^3.0.1", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.4", + "@smithy/util-defaults-mode-node": "^3.0.4", + "@smithy/util-endpoints": "^2.0.2", + "@smithy/util-middleware": "^3.0.1", + "@smithy/util-retry": "^3.0.1", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/core": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.598.0.tgz", + "integrity": "sha512-HaSjt7puO5Cc7cOlrXFCW0rtA0BM9lvzjl56x0A20Pt+0wxXGeTOZZOkXQIepbrFkV2e/HYukuT9e99vXDm59g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^2.2.1", + "@smithy/protocol-http": "^4.0.1", + "@smithy/signature-v4": "^3.1.0", + "@smithy/smithy-client": "^3.1.2", + "@smithy/types": "^3.1.0", + "fast-xml-parser": "4.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.598.0.tgz", + "integrity": "sha512-vi1khgn7yXzLCcgSIzQrrtd2ilUM0dWodxj3PQ6BLfP0O+q1imO3hG1nq7DVyJtq7rFHs6+9N8G4mYvTkxby2w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/property-provider": "^3.1.1", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.598.0.tgz", + "integrity": "sha512-N7cIafi4HVlQvEgvZSo1G4T9qb/JMLGMdBsDCT5XkeJrF0aptQWzTFH0jIdZcLrMYvzPcuEyO3yCBe6cy/ba0g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/fetch-http-handler": "^3.0.2", + "@smithy/node-http-handler": "^3.0.1", + "@smithy/property-provider": "^3.1.1", + "@smithy/protocol-http": "^4.0.1", + "@smithy/smithy-client": "^3.1.2", + "@smithy/types": "^3.1.0", + "@smithy/util-stream": "^3.0.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.598.0.tgz", + "integrity": "sha512-/ppcIVUbRwDIwJDoYfp90X3+AuJo2mvE52Y1t2VSrvUovYn6N4v95/vXj6LS8CNDhz2jvEJYmu+0cTMHdhI6eA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.598.0", + "@aws-sdk/credential-provider-http": "3.598.0", + "@aws-sdk/credential-provider-process": "3.598.0", + "@aws-sdk/credential-provider-sso": "3.598.0", + "@aws-sdk/credential-provider-web-identity": "3.598.0", + "@aws-sdk/types": "3.598.0", + "@smithy/credential-provider-imds": "^3.1.1", + "@smithy/property-provider": "^3.1.1", + "@smithy/shared-ini-file-loader": "^3.1.1", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.598.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.600.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.600.0.tgz", + "integrity": "sha512-1pC7MPMYD45J7yFjA90SxpR0yaSvy+yZiq23aXhAPZLYgJBAxHLu0s0mDCk/piWGPh8+UGur5K0bVdx4B1D5hw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.598.0", + "@aws-sdk/credential-provider-http": "3.598.0", + "@aws-sdk/credential-provider-ini": "3.598.0", + "@aws-sdk/credential-provider-process": "3.598.0", + "@aws-sdk/credential-provider-sso": "3.598.0", + "@aws-sdk/credential-provider-web-identity": "3.598.0", + "@aws-sdk/types": "3.598.0", + "@smithy/credential-provider-imds": "^3.1.1", + "@smithy/property-provider": "^3.1.1", + "@smithy/shared-ini-file-loader": "^3.1.1", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.598.0.tgz", + "integrity": "sha512-rM707XbLW8huMk722AgjVyxu2tMZee++fNA8TJVNgs1Ma02Wx6bBrfIvlyK0rCcIRb0WdQYP6fe3Xhiu4e8IBA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/property-provider": "^3.1.1", + "@smithy/shared-ini-file-loader": "^3.1.1", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.598.0.tgz", + "integrity": "sha512-5InwUmrAuqQdOOgxTccRayMMkSmekdLk6s+az9tmikq0QFAHUCtofI+/fllMXSR9iL6JbGYi1940+EUmS4pHJA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.598.0", + "@aws-sdk/token-providers": "3.598.0", + "@aws-sdk/types": "3.598.0", + "@smithy/property-provider": "^3.1.1", + "@smithy/shared-ini-file-loader": "^3.1.1", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.598.0.tgz", + "integrity": "sha512-TKY1EVdHVBnZqpyxyTHdpZpa1tUpb6nxVeRNn1zWG8QB5MvH4ALLd/jR+gtmWDNQbIG4cVuBOZFVL8hIYicKTA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/property-provider": "^3.1.1", + "@smithy/shared-ini-file-loader": "^3.1.1", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.598.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.598.0.tgz", + "integrity": "sha512-GV5GdiMbz5Tz9JO4NJtRoFXjW0GPEujA0j+5J/B723rTN+REHthJu48HdBKouHGhdzkDWkkh1bu52V02Wprw8w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/property-provider": "^3.1.1", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.598.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.598.0.tgz", + "integrity": "sha512-WiaG059YBQwQraNejLIi0gMNkX7dfPZ8hDIhvMr5aVPRbaHH8AYF3iNSsXYCHvA2Cfa1O9haYXsuMF9flXnCmA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/protocol-http": "^4.0.1", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/middleware-logger": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.598.0.tgz", + "integrity": "sha512-bxBjf/VYiu3zfu8SYM2S9dQQc3tz5uBAOcPz/Bt8DyyK3GgOpjhschH/2XuUErsoUO1gDJqZSdGOmuHGZQn00Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.598.0.tgz", + "integrity": "sha512-vjT9BeFY9FeN0f8hm2l6F53tI0N5bUq6RcDkQXKNabXBnQxKptJRad6oP2X5y3FoVfBLOuDkQgiC2940GIPxtQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/protocol-http": "^4.0.1", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.598.0.tgz", + "integrity": "sha512-4tjESlHG5B5MdjUaLK7tQs/miUtHbb6deauQx8ryqSBYOhfHVgb1ZnzvQR0bTrhpqUg0WlybSkDaZAICf9xctg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@aws-sdk/util-endpoints": "3.598.0", + "@smithy/protocol-http": "^4.0.1", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.598.0.tgz", + "integrity": "sha512-oYXhmTokSav4ytmWleCr3rs/1nyvZW/S0tdi6X7u+dLNL5Jee+uMxWGzgOrWK6wrQOzucLVjS4E/wA11Kv2GTw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/node-config-provider": "^3.1.1", + "@smithy/types": "^3.1.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/types": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.598.0.tgz", + "integrity": "sha512-742uRl6z7u0LFmZwDrFP6r1wlZcgVPw+/TilluDJmCAR8BgRw3IR+743kUXKBGd8QZDRW2n6v/PYsi/AWCDDMQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/util-endpoints": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.598.0.tgz", + "integrity": "sha512-Qo9UoiVVZxcOEdiOMZg3xb1mzkTxrhd4qSlg5QQrfWPJVx/QOg+Iy0NtGxPtHtVZNHZxohYwDwV/tfsnDSE2gQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/types": "^3.1.0", + "@smithy/util-endpoints": "^2.0.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.598.0.tgz", + "integrity": "sha512-36Sxo6F+ykElaL1mWzWjlg+1epMpSe8obwhCN1yGE7Js9ywy5U6k6l+A3q3YM9YRbm740sNxncbwLklMvuhTKw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/types": "^3.1.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.598.0.tgz", + "integrity": "sha512-oyWGcOlfTdzkC6SVplyr0AGh54IMrDxbhg5RxJ5P+V4BKfcDoDcZV9xenUk9NsOi9MuUjxMumb9UJGkDhM1m0A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/node-config-provider": "^3.1.1", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/abort-controller": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.9.tgz", + "integrity": "sha512-yiW0WI30zj8ZKoSYNx90no7ugVn3khlyH/z5W8qtKBtVE6awRALbhSG+2SAHA1r6bO/6M9utxYKVZ3PCJ1rWxw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/config-resolver": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.13.tgz", + "integrity": "sha512-Gr/qwzyPaTL1tZcq8WQyHhTZREER5R1Wytmz4WnVGL4onA3dNk6Btll55c8Vr58pLdvWZmtG8oZxJTw3t3q7Jg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.12", + "@smithy/types": "^3.7.2", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.11", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/core": { + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.5.7.tgz", + "integrity": "sha512-8olpW6mKCa0v+ibCjoCzgZHQx1SQmZuW/WkrdZo73wiTprTH6qhmskT60QLFdT9DRa5mXxjz89kQPZ7ZSsoqqg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-serde": "^3.0.11", + "@smithy/protocol-http": "^4.1.8", + "@smithy/types": "^3.7.2", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-middleware": "^3.0.11", + "@smithy/util-stream": "^3.3.4", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/credential-provider-imds": { + "version": "3.2.8", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.8.tgz", + "integrity": "sha512-ZCY2yD0BY+K9iMXkkbnjo+08T2h8/34oHd0Jmh6BZUSZwaaGlGCyBT/3wnS7u7Xl33/EEfN4B6nQr3Gx5bYxgw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.12", + "@smithy/property-provider": "^3.1.11", + "@smithy/types": "^3.7.2", + "@smithy/url-parser": "^3.0.11", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/fetch-http-handler": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-3.2.9.tgz", + "integrity": "sha512-hYNVQOqhFQ6vOpenifFME546f0GfJn2OiQ3M0FDmuUu8V/Uiwy2wej7ZXxFBNqdx0R5DZAqWM1l6VRhGz8oE6A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.4", + "@smithy/querystring-builder": "^3.0.7", + "@smithy/types": "^3.5.0", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/hash-node": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.11.tgz", + "integrity": "sha512-emP23rwYyZhQBvklqTtwetkQlqbNYirDiEEwXl2v0GYWMnCzxst7ZaRAnWuy28njp5kAH54lvkdG37MblZzaHA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/invalid-dependency": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.11.tgz", + "integrity": "sha512-NuQmVPEJjUX6c+UELyVz8kUx8Q539EDeNwbRyu4IIF8MeV7hUtq1FB3SHVyki2u++5XLMFqngeMKk7ccspnNyQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", + "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/middleware-content-length": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.13.tgz", + "integrity": "sha512-zfMhzojhFpIX3P5ug7jxTjfUcIPcGjcQYzB9t+rv0g1TX7B0QdwONW+ATouaLoD7h7LOw/ZlXfkq4xJ/g2TrIw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.8", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/middleware-endpoint": { + "version": "3.2.8", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.2.8.tgz", + "integrity": "sha512-OEJZKVUEhMOqMs3ktrTWp7UvvluMJEvD5XgQwRePSbDg1VvBaL8pX8mwPltFn6wk1GySbcVwwyldL8S+iqnrEQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^2.5.7", + "@smithy/middleware-serde": "^3.0.11", + "@smithy/node-config-provider": "^3.1.12", + "@smithy/shared-ini-file-loader": "^3.1.12", + "@smithy/types": "^3.7.2", + "@smithy/url-parser": "^3.0.11", + "@smithy/util-middleware": "^3.0.11", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/middleware-retry": { + "version": "3.0.34", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.34.tgz", + "integrity": "sha512-yVRr/AAtPZlUvwEkrq7S3x7Z8/xCd97m2hLDaqdz6ucP2RKHsBjEqaUA2ebNv2SsZoPEi+ZD0dZbOB1u37tGCA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.12", + "@smithy/protocol-http": "^4.1.8", + "@smithy/service-error-classification": "^3.0.11", + "@smithy/smithy-client": "^3.7.0", + "@smithy/types": "^3.7.2", + "@smithy/util-middleware": "^3.0.11", + "@smithy/util-retry": "^3.0.11", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/middleware-serde": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.11.tgz", + "integrity": "sha512-KzPAeySp/fOoQA82TpnwItvX8BBURecpx6ZMu75EZDkAcnPtO6vf7q4aH5QHs/F1s3/snQaSFbbUMcFFZ086Mw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/middleware-stack": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.11.tgz", + "integrity": "sha512-1HGo9a6/ikgOMrTrWL/WiN9N8GSVYpuRQO5kjstAq4CvV59bjqnh7TbdXGQ4vxLD3xlSjfBjq5t1SOELePsLnA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/node-config-provider": { + "version": "3.1.12", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.12.tgz", + "integrity": "sha512-O9LVEu5J/u/FuNlZs+L7Ikn3lz7VB9hb0GtPT9MQeiBmtK8RSY3ULmsZgXhe6VAlgTw0YO+paQx4p8xdbs43vQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^3.1.11", + "@smithy/shared-ini-file-loader": "^3.1.12", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/node-http-handler": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.3.3.tgz", + "integrity": "sha512-BrpZOaZ4RCbcJ2igiSNG16S+kgAc65l/2hmxWdmhyoGWHTLlzQzr06PXavJp9OBlPEG/sHlqdxjWmjzV66+BSQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^3.1.9", + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/property-provider": { + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.11.tgz", + "integrity": "sha512-I/+TMc4XTQ3QAjXfOcUWbSS073oOEAxgx4aZy8jHaf8JQnRkq2SZWw8+PfDtBvLUjcGMdxl+YwtzWe6i5uhL/A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/protocol-http": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.8.tgz", + "integrity": "sha512-hmgIAVyxw1LySOwkgMIUN0kjN8TG9Nc85LJeEmEE/cNEe2rkHDUWhnJf2gxcSRFLWsyqWsrZGw40ROjUogg+Iw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/querystring-builder": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.11.tgz", + "integrity": "sha512-u+5HV/9uJaeLj5XTb6+IEF/dokWWkEqJ0XiaRRogyREmKGUgZnNecLucADLdauWFKUNbQfulHFEZEdjwEBjXRg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "@smithy/util-uri-escape": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/querystring-parser": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.11.tgz", + "integrity": "sha512-Je3kFvCsFMnso1ilPwA7GtlbPaTixa3WwC+K21kmMZHsBEOZYQaqxcMqeFFoU7/slFjKDIpiiPydvdJm8Q/MCw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/service-error-classification": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.11.tgz", + "integrity": "sha512-QnYDPkyewrJzCyaeI2Rmp7pDwbUETe+hU8ADkXmgNusO1bgHBH7ovXJiYmba8t0fNfJx75fE8dlM6SEmZxheog==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/shared-ini-file-loader": { + "version": "3.1.12", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.12.tgz", + "integrity": "sha512-1xKSGI+U9KKdbG2qDvIR9dGrw3CNx+baqJfyr0igKEpjbHL5stsqAesYBzHChYHlelWtb87VnLWlhvfCz13H8Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/signature-v4": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-3.1.2.tgz", + "integrity": "sha512-3BcPylEsYtD0esM4Hoyml/+s7WP2LFhcM3J2AGdcL2vx9O60TtfpDOL72gjb4lU8NeRPeKAwR77YNyyGvMbuEA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "@smithy/types": "^3.3.0", + "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-uri-escape": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/smithy-client": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.7.0.tgz", + "integrity": "sha512-9wYrjAZFlqWhgVo3C4y/9kpc68jgiSsKUnsFPzr/MSiRL93+QRDafGTfhhKAb2wsr69Ru87WTiqSfQusSmWipA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^2.5.7", + "@smithy/middleware-endpoint": "^3.2.8", + "@smithy/middleware-stack": "^3.0.11", + "@smithy/protocol-http": "^4.1.8", + "@smithy/types": "^3.7.2", + "@smithy/util-stream": "^3.3.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/types": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.7.2.tgz", + "integrity": "sha512-bNwBYYmN8Eh9RyjS1p2gW6MIhSO2rl7X9QeLM8iTdcGRP+eDiIWDt66c9IysCc22gefKszZv+ubV9qZc7hdESg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/url-parser": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.11.tgz", + "integrity": "sha512-TmlqXkSk8ZPhfc+SQutjmFr5FjC0av3GZP4B/10caK1SbRwe/v+Wzu/R6xEKxoNqL+8nY18s1byiy6HqPG37Aw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/querystring-parser": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/util-base64": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-3.0.0.tgz", + "integrity": "sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/util-body-length-browser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-3.0.0.tgz", + "integrity": "sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/util-body-length-node": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-3.0.0.tgz", + "integrity": "sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz", + "integrity": "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/util-config-provider": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-3.0.0.tgz", + "integrity": "sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/util-defaults-mode-browser": { + "version": "3.0.34", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.34.tgz", + "integrity": "sha512-FumjjF631lR521cX+svMLBj3SwSDh9VdtyynTYDAiBDEf8YPP5xORNXKQ9j0105o5+ARAGnOOP/RqSl40uXddA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^3.1.11", + "@smithy/smithy-client": "^3.7.0", + "@smithy/types": "^3.7.2", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/util-defaults-mode-node": { + "version": "3.0.34", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.34.tgz", + "integrity": "sha512-vN6aHfzW9dVVzkI0wcZoUXvfjkl4CSbM9nE//08lmUMyf00S75uuCpTrqF9uD4bD9eldIXlt53colrlwKAT8Gw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/config-resolver": "^3.0.13", + "@smithy/credential-provider-imds": "^3.2.8", + "@smithy/node-config-provider": "^3.1.12", + "@smithy/property-provider": "^3.1.11", + "@smithy/smithy-client": "^3.7.0", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/util-endpoints": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.1.7.tgz", + "integrity": "sha512-tSfcqKcN/Oo2STEYCABVuKgJ76nyyr6skGl9t15hs+YaiU06sgMkN7QYjo0BbVw+KT26zok3IzbdSOksQ4YzVw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.12", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/util-hex-encoding": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-3.0.0.tgz", + "integrity": "sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/util-middleware": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.11.tgz", + "integrity": "sha512-dWpyc1e1R6VoXrwLoLDd57U1z6CwNSdkM69Ie4+6uYh2GC7Vg51Qtan7ITzczuVpqezdDTKJGJB95fFvvjU/ow==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/util-retry": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.11.tgz", + "integrity": "sha512-hJUC6W7A3DQgaee3Hp9ZFcOxVDZzmBIRBPlUAk8/fSOEl7pE/aX7Dci0JycNOnm9Mfr0KV2XjIlUOcGWXQUdVQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/util-stream": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.3.4.tgz", + "integrity": "sha512-SGhGBG/KupieJvJSZp/rfHHka8BFgj56eek9px4pp7lZbOF+fRiVr4U7A3y3zJD8uGhxq32C5D96HxsTC9BckQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/fetch-http-handler": "^4.1.3", + "@smithy/node-http-handler": "^3.3.3", + "@smithy/types": "^3.7.2", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/util-stream/node_modules/@smithy/fetch-http-handler": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-4.1.3.tgz", + "integrity": "sha512-6SxNltSncI8s689nvnzZQc/dPXcpHQ34KUj6gR/HBroytKOd/isMG3gJF/zBE1TBmTT18TXyzhg3O3SOOqGEhA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/util-uri-escape": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-3.0.0.tgz", + "integrity": "sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", + "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/fast-xml-parser": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz", + "integrity": "sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==", + "funding": [ + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + }, + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/strnum": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", + "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, "node_modules/@aws-sdk/core": { "version": "3.879.0", "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.879.0.tgz", @@ -386,6 +3421,60 @@ "node": ">=18.0.0" } }, + "node_modules/@aws-sdk/credential-provider-cognito-identity": { + "version": "3.600.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.600.0.tgz", + "integrity": "sha512-AIM+B06d1+71EuBrk2UR9ZZgRS3a+ARxE3oZKMZYlfqtZ3kY8w4DkhEt7OVruc6uSsMhkrcQT6nxsOxFSi4RtA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-cognito-identity": "3.600.0", + "@aws-sdk/types": "3.598.0", + "@smithy/property-provider": "^3.1.1", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-cognito-identity/node_modules/@aws-sdk/types": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.598.0.tgz", + "integrity": "sha512-742uRl6z7u0LFmZwDrFP6r1wlZcgVPw+/TilluDJmCAR8BgRw3IR+743kUXKBGd8QZDRW2n6v/PYsi/AWCDDMQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-cognito-identity/node_modules/@smithy/property-provider": { + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.11.tgz", + "integrity": "sha512-I/+TMc4XTQ3QAjXfOcUWbSS073oOEAxgx4aZy8jHaf8JQnRkq2SZWw8+PfDtBvLUjcGMdxl+YwtzWe6i5uhL/A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-cognito-identity/node_modules/@smithy/types": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.7.2.tgz", + "integrity": "sha512-bNwBYYmN8Eh9RyjS1p2gW6MIhSO2rl7X9QeLM8iTdcGRP+eDiIWDt66c9IysCc22gefKszZv+ubV9qZc7hdESg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/@aws-sdk/credential-provider-env": { "version": "3.879.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.879.0.tgz", @@ -523,6 +3612,1076 @@ "node": ">=18.0.0" } }, + "node_modules/@aws-sdk/credential-providers": { + "version": "3.600.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.600.0.tgz", + "integrity": "sha512-cC9uqmX0rgx1efiJGqeR+i0EXr8RQ5SAzH7M45WNBZpYiLEe6reWgIYJY9hmOxuaoMdWSi8kekuN3IjTIORRjw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-cognito-identity": "3.600.0", + "@aws-sdk/client-sso": "3.598.0", + "@aws-sdk/client-sts": "3.600.0", + "@aws-sdk/credential-provider-cognito-identity": "3.600.0", + "@aws-sdk/credential-provider-env": "3.598.0", + "@aws-sdk/credential-provider-http": "3.598.0", + "@aws-sdk/credential-provider-ini": "3.598.0", + "@aws-sdk/credential-provider-node": "3.600.0", + "@aws-sdk/credential-provider-process": "3.598.0", + "@aws-sdk/credential-provider-sso": "3.598.0", + "@aws-sdk/credential-provider-web-identity": "3.598.0", + "@aws-sdk/types": "3.598.0", + "@smithy/credential-provider-imds": "^3.1.1", + "@smithy/property-provider": "^3.1.1", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/client-sso": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.598.0.tgz", + "integrity": "sha512-nOI5lqPYa+YZlrrzwAJywJSw3MKVjvu6Ge2fCqQUNYMfxFB0NAaDFnl0EPjXi+sEbtCuz/uWE77poHbqiZ+7Iw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.598.0", + "@aws-sdk/middleware-host-header": "3.598.0", + "@aws-sdk/middleware-logger": "3.598.0", + "@aws-sdk/middleware-recursion-detection": "3.598.0", + "@aws-sdk/middleware-user-agent": "3.598.0", + "@aws-sdk/region-config-resolver": "3.598.0", + "@aws-sdk/types": "3.598.0", + "@aws-sdk/util-endpoints": "3.598.0", + "@aws-sdk/util-user-agent-browser": "3.598.0", + "@aws-sdk/util-user-agent-node": "3.598.0", + "@smithy/config-resolver": "^3.0.2", + "@smithy/core": "^2.2.1", + "@smithy/fetch-http-handler": "^3.0.2", + "@smithy/hash-node": "^3.0.1", + "@smithy/invalid-dependency": "^3.0.1", + "@smithy/middleware-content-length": "^3.0.1", + "@smithy/middleware-endpoint": "^3.0.2", + "@smithy/middleware-retry": "^3.0.4", + "@smithy/middleware-serde": "^3.0.1", + "@smithy/middleware-stack": "^3.0.1", + "@smithy/node-config-provider": "^3.1.1", + "@smithy/node-http-handler": "^3.0.1", + "@smithy/protocol-http": "^4.0.1", + "@smithy/smithy-client": "^3.1.2", + "@smithy/types": "^3.1.0", + "@smithy/url-parser": "^3.0.1", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.4", + "@smithy/util-defaults-mode-node": "^3.0.4", + "@smithy/util-endpoints": "^2.0.2", + "@smithy/util-middleware": "^3.0.1", + "@smithy/util-retry": "^3.0.1", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/core": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.598.0.tgz", + "integrity": "sha512-HaSjt7puO5Cc7cOlrXFCW0rtA0BM9lvzjl56x0A20Pt+0wxXGeTOZZOkXQIepbrFkV2e/HYukuT9e99vXDm59g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^2.2.1", + "@smithy/protocol-http": "^4.0.1", + "@smithy/signature-v4": "^3.1.0", + "@smithy/smithy-client": "^3.1.2", + "@smithy/types": "^3.1.0", + "fast-xml-parser": "4.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.598.0.tgz", + "integrity": "sha512-vi1khgn7yXzLCcgSIzQrrtd2ilUM0dWodxj3PQ6BLfP0O+q1imO3hG1nq7DVyJtq7rFHs6+9N8G4mYvTkxby2w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/property-provider": "^3.1.1", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.598.0.tgz", + "integrity": "sha512-N7cIafi4HVlQvEgvZSo1G4T9qb/JMLGMdBsDCT5XkeJrF0aptQWzTFH0jIdZcLrMYvzPcuEyO3yCBe6cy/ba0g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/fetch-http-handler": "^3.0.2", + "@smithy/node-http-handler": "^3.0.1", + "@smithy/property-provider": "^3.1.1", + "@smithy/protocol-http": "^4.0.1", + "@smithy/smithy-client": "^3.1.2", + "@smithy/types": "^3.1.0", + "@smithy/util-stream": "^3.0.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.598.0.tgz", + "integrity": "sha512-/ppcIVUbRwDIwJDoYfp90X3+AuJo2mvE52Y1t2VSrvUovYn6N4v95/vXj6LS8CNDhz2jvEJYmu+0cTMHdhI6eA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.598.0", + "@aws-sdk/credential-provider-http": "3.598.0", + "@aws-sdk/credential-provider-process": "3.598.0", + "@aws-sdk/credential-provider-sso": "3.598.0", + "@aws-sdk/credential-provider-web-identity": "3.598.0", + "@aws-sdk/types": "3.598.0", + "@smithy/credential-provider-imds": "^3.1.1", + "@smithy/property-provider": "^3.1.1", + "@smithy/shared-ini-file-loader": "^3.1.1", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.598.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.600.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.600.0.tgz", + "integrity": "sha512-1pC7MPMYD45J7yFjA90SxpR0yaSvy+yZiq23aXhAPZLYgJBAxHLu0s0mDCk/piWGPh8+UGur5K0bVdx4B1D5hw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.598.0", + "@aws-sdk/credential-provider-http": "3.598.0", + "@aws-sdk/credential-provider-ini": "3.598.0", + "@aws-sdk/credential-provider-process": "3.598.0", + "@aws-sdk/credential-provider-sso": "3.598.0", + "@aws-sdk/credential-provider-web-identity": "3.598.0", + "@aws-sdk/types": "3.598.0", + "@smithy/credential-provider-imds": "^3.1.1", + "@smithy/property-provider": "^3.1.1", + "@smithy/shared-ini-file-loader": "^3.1.1", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.598.0.tgz", + "integrity": "sha512-rM707XbLW8huMk722AgjVyxu2tMZee++fNA8TJVNgs1Ma02Wx6bBrfIvlyK0rCcIRb0WdQYP6fe3Xhiu4e8IBA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/property-provider": "^3.1.1", + "@smithy/shared-ini-file-loader": "^3.1.1", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.598.0.tgz", + "integrity": "sha512-5InwUmrAuqQdOOgxTccRayMMkSmekdLk6s+az9tmikq0QFAHUCtofI+/fllMXSR9iL6JbGYi1940+EUmS4pHJA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.598.0", + "@aws-sdk/token-providers": "3.598.0", + "@aws-sdk/types": "3.598.0", + "@smithy/property-provider": "^3.1.1", + "@smithy/shared-ini-file-loader": "^3.1.1", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.598.0.tgz", + "integrity": "sha512-GV5GdiMbz5Tz9JO4NJtRoFXjW0GPEujA0j+5J/B723rTN+REHthJu48HdBKouHGhdzkDWkkh1bu52V02Wprw8w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/property-provider": "^3.1.1", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.598.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.598.0.tgz", + "integrity": "sha512-WiaG059YBQwQraNejLIi0gMNkX7dfPZ8hDIhvMr5aVPRbaHH8AYF3iNSsXYCHvA2Cfa1O9haYXsuMF9flXnCmA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/protocol-http": "^4.0.1", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/middleware-logger": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.598.0.tgz", + "integrity": "sha512-bxBjf/VYiu3zfu8SYM2S9dQQc3tz5uBAOcPz/Bt8DyyK3GgOpjhschH/2XuUErsoUO1gDJqZSdGOmuHGZQn00Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.598.0.tgz", + "integrity": "sha512-vjT9BeFY9FeN0f8hm2l6F53tI0N5bUq6RcDkQXKNabXBnQxKptJRad6oP2X5y3FoVfBLOuDkQgiC2940GIPxtQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/protocol-http": "^4.0.1", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.598.0.tgz", + "integrity": "sha512-4tjESlHG5B5MdjUaLK7tQs/miUtHbb6deauQx8ryqSBYOhfHVgb1ZnzvQR0bTrhpqUg0WlybSkDaZAICf9xctg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@aws-sdk/util-endpoints": "3.598.0", + "@smithy/protocol-http": "^4.0.1", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.598.0.tgz", + "integrity": "sha512-oYXhmTokSav4ytmWleCr3rs/1nyvZW/S0tdi6X7u+dLNL5Jee+uMxWGzgOrWK6wrQOzucLVjS4E/wA11Kv2GTw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/node-config-provider": "^3.1.1", + "@smithy/types": "^3.1.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/token-providers": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.598.0.tgz", + "integrity": "sha512-TKY1EVdHVBnZqpyxyTHdpZpa1tUpb6nxVeRNn1zWG8QB5MvH4ALLd/jR+gtmWDNQbIG4cVuBOZFVL8hIYicKTA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/property-provider": "^3.1.1", + "@smithy/shared-ini-file-loader": "^3.1.1", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.598.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/types": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.598.0.tgz", + "integrity": "sha512-742uRl6z7u0LFmZwDrFP6r1wlZcgVPw+/TilluDJmCAR8BgRw3IR+743kUXKBGd8QZDRW2n6v/PYsi/AWCDDMQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/util-endpoints": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.598.0.tgz", + "integrity": "sha512-Qo9UoiVVZxcOEdiOMZg3xb1mzkTxrhd4qSlg5QQrfWPJVx/QOg+Iy0NtGxPtHtVZNHZxohYwDwV/tfsnDSE2gQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/types": "^3.1.0", + "@smithy/util-endpoints": "^2.0.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.598.0.tgz", + "integrity": "sha512-36Sxo6F+ykElaL1mWzWjlg+1epMpSe8obwhCN1yGE7Js9ywy5U6k6l+A3q3YM9YRbm740sNxncbwLklMvuhTKw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/types": "^3.1.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.598.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.598.0.tgz", + "integrity": "sha512-oyWGcOlfTdzkC6SVplyr0AGh54IMrDxbhg5RxJ5P+V4BKfcDoDcZV9xenUk9NsOi9MuUjxMumb9UJGkDhM1m0A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.598.0", + "@smithy/node-config-provider": "^3.1.1", + "@smithy/types": "^3.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/abort-controller": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.9.tgz", + "integrity": "sha512-yiW0WI30zj8ZKoSYNx90no7ugVn3khlyH/z5W8qtKBtVE6awRALbhSG+2SAHA1r6bO/6M9utxYKVZ3PCJ1rWxw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/config-resolver": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.13.tgz", + "integrity": "sha512-Gr/qwzyPaTL1tZcq8WQyHhTZREER5R1Wytmz4WnVGL4onA3dNk6Btll55c8Vr58pLdvWZmtG8oZxJTw3t3q7Jg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.12", + "@smithy/types": "^3.7.2", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.11", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/core": { + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.5.7.tgz", + "integrity": "sha512-8olpW6mKCa0v+ibCjoCzgZHQx1SQmZuW/WkrdZo73wiTprTH6qhmskT60QLFdT9DRa5mXxjz89kQPZ7ZSsoqqg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-serde": "^3.0.11", + "@smithy/protocol-http": "^4.1.8", + "@smithy/types": "^3.7.2", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-middleware": "^3.0.11", + "@smithy/util-stream": "^3.3.4", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/credential-provider-imds": { + "version": "3.2.8", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.8.tgz", + "integrity": "sha512-ZCY2yD0BY+K9iMXkkbnjo+08T2h8/34oHd0Jmh6BZUSZwaaGlGCyBT/3wnS7u7Xl33/EEfN4B6nQr3Gx5bYxgw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.12", + "@smithy/property-provider": "^3.1.11", + "@smithy/types": "^3.7.2", + "@smithy/url-parser": "^3.0.11", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/fetch-http-handler": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-3.2.9.tgz", + "integrity": "sha512-hYNVQOqhFQ6vOpenifFME546f0GfJn2OiQ3M0FDmuUu8V/Uiwy2wej7ZXxFBNqdx0R5DZAqWM1l6VRhGz8oE6A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.4", + "@smithy/querystring-builder": "^3.0.7", + "@smithy/types": "^3.5.0", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/hash-node": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.11.tgz", + "integrity": "sha512-emP23rwYyZhQBvklqTtwetkQlqbNYirDiEEwXl2v0GYWMnCzxst7ZaRAnWuy28njp5kAH54lvkdG37MblZzaHA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/invalid-dependency": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.11.tgz", + "integrity": "sha512-NuQmVPEJjUX6c+UELyVz8kUx8Q539EDeNwbRyu4IIF8MeV7hUtq1FB3SHVyki2u++5XLMFqngeMKk7ccspnNyQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", + "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/middleware-content-length": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.13.tgz", + "integrity": "sha512-zfMhzojhFpIX3P5ug7jxTjfUcIPcGjcQYzB9t+rv0g1TX7B0QdwONW+ATouaLoD7h7LOw/ZlXfkq4xJ/g2TrIw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.8", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/middleware-endpoint": { + "version": "3.2.8", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.2.8.tgz", + "integrity": "sha512-OEJZKVUEhMOqMs3ktrTWp7UvvluMJEvD5XgQwRePSbDg1VvBaL8pX8mwPltFn6wk1GySbcVwwyldL8S+iqnrEQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^2.5.7", + "@smithy/middleware-serde": "^3.0.11", + "@smithy/node-config-provider": "^3.1.12", + "@smithy/shared-ini-file-loader": "^3.1.12", + "@smithy/types": "^3.7.2", + "@smithy/url-parser": "^3.0.11", + "@smithy/util-middleware": "^3.0.11", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/middleware-retry": { + "version": "3.0.34", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.34.tgz", + "integrity": "sha512-yVRr/AAtPZlUvwEkrq7S3x7Z8/xCd97m2hLDaqdz6ucP2RKHsBjEqaUA2ebNv2SsZoPEi+ZD0dZbOB1u37tGCA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.12", + "@smithy/protocol-http": "^4.1.8", + "@smithy/service-error-classification": "^3.0.11", + "@smithy/smithy-client": "^3.7.0", + "@smithy/types": "^3.7.2", + "@smithy/util-middleware": "^3.0.11", + "@smithy/util-retry": "^3.0.11", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/middleware-serde": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.11.tgz", + "integrity": "sha512-KzPAeySp/fOoQA82TpnwItvX8BBURecpx6ZMu75EZDkAcnPtO6vf7q4aH5QHs/F1s3/snQaSFbbUMcFFZ086Mw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/middleware-stack": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.11.tgz", + "integrity": "sha512-1HGo9a6/ikgOMrTrWL/WiN9N8GSVYpuRQO5kjstAq4CvV59bjqnh7TbdXGQ4vxLD3xlSjfBjq5t1SOELePsLnA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/node-config-provider": { + "version": "3.1.12", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.12.tgz", + "integrity": "sha512-O9LVEu5J/u/FuNlZs+L7Ikn3lz7VB9hb0GtPT9MQeiBmtK8RSY3ULmsZgXhe6VAlgTw0YO+paQx4p8xdbs43vQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^3.1.11", + "@smithy/shared-ini-file-loader": "^3.1.12", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/node-http-handler": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.3.3.tgz", + "integrity": "sha512-BrpZOaZ4RCbcJ2igiSNG16S+kgAc65l/2hmxWdmhyoGWHTLlzQzr06PXavJp9OBlPEG/sHlqdxjWmjzV66+BSQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^3.1.9", + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/property-provider": { + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.11.tgz", + "integrity": "sha512-I/+TMc4XTQ3QAjXfOcUWbSS073oOEAxgx4aZy8jHaf8JQnRkq2SZWw8+PfDtBvLUjcGMdxl+YwtzWe6i5uhL/A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/protocol-http": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.8.tgz", + "integrity": "sha512-hmgIAVyxw1LySOwkgMIUN0kjN8TG9Nc85LJeEmEE/cNEe2rkHDUWhnJf2gxcSRFLWsyqWsrZGw40ROjUogg+Iw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/querystring-builder": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.11.tgz", + "integrity": "sha512-u+5HV/9uJaeLj5XTb6+IEF/dokWWkEqJ0XiaRRogyREmKGUgZnNecLucADLdauWFKUNbQfulHFEZEdjwEBjXRg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "@smithy/util-uri-escape": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/querystring-parser": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.11.tgz", + "integrity": "sha512-Je3kFvCsFMnso1ilPwA7GtlbPaTixa3WwC+K21kmMZHsBEOZYQaqxcMqeFFoU7/slFjKDIpiiPydvdJm8Q/MCw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/service-error-classification": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.11.tgz", + "integrity": "sha512-QnYDPkyewrJzCyaeI2Rmp7pDwbUETe+hU8ADkXmgNusO1bgHBH7ovXJiYmba8t0fNfJx75fE8dlM6SEmZxheog==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/shared-ini-file-loader": { + "version": "3.1.12", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.12.tgz", + "integrity": "sha512-1xKSGI+U9KKdbG2qDvIR9dGrw3CNx+baqJfyr0igKEpjbHL5stsqAesYBzHChYHlelWtb87VnLWlhvfCz13H8Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/signature-v4": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-3.1.2.tgz", + "integrity": "sha512-3BcPylEsYtD0esM4Hoyml/+s7WP2LFhcM3J2AGdcL2vx9O60TtfpDOL72gjb4lU8NeRPeKAwR77YNyyGvMbuEA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "@smithy/types": "^3.3.0", + "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-uri-escape": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/smithy-client": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.7.0.tgz", + "integrity": "sha512-9wYrjAZFlqWhgVo3C4y/9kpc68jgiSsKUnsFPzr/MSiRL93+QRDafGTfhhKAb2wsr69Ru87WTiqSfQusSmWipA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^2.5.7", + "@smithy/middleware-endpoint": "^3.2.8", + "@smithy/middleware-stack": "^3.0.11", + "@smithy/protocol-http": "^4.1.8", + "@smithy/types": "^3.7.2", + "@smithy/util-stream": "^3.3.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/types": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.7.2.tgz", + "integrity": "sha512-bNwBYYmN8Eh9RyjS1p2gW6MIhSO2rl7X9QeLM8iTdcGRP+eDiIWDt66c9IysCc22gefKszZv+ubV9qZc7hdESg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/url-parser": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.11.tgz", + "integrity": "sha512-TmlqXkSk8ZPhfc+SQutjmFr5FjC0av3GZP4B/10caK1SbRwe/v+Wzu/R6xEKxoNqL+8nY18s1byiy6HqPG37Aw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/querystring-parser": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-base64": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-3.0.0.tgz", + "integrity": "sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-body-length-browser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-3.0.0.tgz", + "integrity": "sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-body-length-node": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-3.0.0.tgz", + "integrity": "sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz", + "integrity": "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-config-provider": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-3.0.0.tgz", + "integrity": "sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-defaults-mode-browser": { + "version": "3.0.34", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.34.tgz", + "integrity": "sha512-FumjjF631lR521cX+svMLBj3SwSDh9VdtyynTYDAiBDEf8YPP5xORNXKQ9j0105o5+ARAGnOOP/RqSl40uXddA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^3.1.11", + "@smithy/smithy-client": "^3.7.0", + "@smithy/types": "^3.7.2", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-defaults-mode-node": { + "version": "3.0.34", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.34.tgz", + "integrity": "sha512-vN6aHfzW9dVVzkI0wcZoUXvfjkl4CSbM9nE//08lmUMyf00S75uuCpTrqF9uD4bD9eldIXlt53colrlwKAT8Gw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/config-resolver": "^3.0.13", + "@smithy/credential-provider-imds": "^3.2.8", + "@smithy/node-config-provider": "^3.1.12", + "@smithy/property-provider": "^3.1.11", + "@smithy/smithy-client": "^3.7.0", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-endpoints": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.1.7.tgz", + "integrity": "sha512-tSfcqKcN/Oo2STEYCABVuKgJ76nyyr6skGl9t15hs+YaiU06sgMkN7QYjo0BbVw+KT26zok3IzbdSOksQ4YzVw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.12", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-hex-encoding": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-3.0.0.tgz", + "integrity": "sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-middleware": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.11.tgz", + "integrity": "sha512-dWpyc1e1R6VoXrwLoLDd57U1z6CwNSdkM69Ie4+6uYh2GC7Vg51Qtan7ITzczuVpqezdDTKJGJB95fFvvjU/ow==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-retry": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.11.tgz", + "integrity": "sha512-hJUC6W7A3DQgaee3Hp9ZFcOxVDZzmBIRBPlUAk8/fSOEl7pE/aX7Dci0JycNOnm9Mfr0KV2XjIlUOcGWXQUdVQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-stream": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.3.4.tgz", + "integrity": "sha512-SGhGBG/KupieJvJSZp/rfHHka8BFgj56eek9px4pp7lZbOF+fRiVr4U7A3y3zJD8uGhxq32C5D96HxsTC9BckQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/fetch-http-handler": "^4.1.3", + "@smithy/node-http-handler": "^3.3.3", + "@smithy/types": "^3.7.2", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-stream/node_modules/@smithy/fetch-http-handler": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-4.1.3.tgz", + "integrity": "sha512-6SxNltSncI8s689nvnzZQc/dPXcpHQ34KUj6gR/HBroytKOd/isMG3gJF/zBE1TBmTT18TXyzhg3O3SOOqGEhA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-uri-escape": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-3.0.0.tgz", + "integrity": "sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", + "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/fast-xml-parser": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz", + "integrity": "sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==", + "funding": [ + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + }, + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/@aws-sdk/credential-providers/node_modules/strnum": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", + "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, + "node_modules/@aws-sdk/eventstream-codec": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-codec/-/eventstream-codec-3.370.0.tgz", + "integrity": "sha512-PiaDMum7TNsIE3DGECSsNYwibBIPN2/e13BJbTwi6KgVx8BV2mYA3kQkaUDiy++tEpzN81Nh5OPTFVb7bvgYYg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "3.0.0", + "@aws-sdk/types": "3.370.0", + "@aws-sdk/util-hex-encoding": "3.310.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/eventstream-codec/node_modules/@aws-crypto/crc32": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-3.0.0.tgz", + "integrity": "sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-sdk/eventstream-codec/node_modules/@aws-crypto/crc32/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "license": "0BSD" + }, + "node_modules/@aws-sdk/eventstream-codec/node_modules/@aws-crypto/util": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz", + "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-sdk/eventstream-codec/node_modules/@aws-crypto/util/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "license": "0BSD" + }, + "node_modules/@aws-sdk/eventstream-codec/node_modules/@aws-sdk/types": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.370.0.tgz", + "integrity": "sha512-8PGMKklSkRKjunFhzM2y5Jm0H2TBu7YRNISdYzXLUHKSP9zlMEYagseKVdmox0zKHf1LXVNuSlUV2b6SRrieCQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/eventstream-codec/node_modules/@smithy/types": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-1.2.0.tgz", + "integrity": "sha512-z1r00TvBqF3dh4aHhya7nz1HhvCg4TRmw51fjMrh5do3h+ngSstt/yKlNbHeb9QxJmFbmN8KEVSWgb1bRvfEoA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/is-array-buffer": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/is-array-buffer/-/is-array-buffer-3.310.0.tgz", + "integrity": "sha512-urnbcCR+h9NWUnmOtet/s4ghvzsidFmspfhYaHAmSRdy9yDjdjBJMFjjsn85A1ODUktztm+cVncXjQ38WCMjMQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@aws-sdk/middleware-bucket-endpoint": { "version": "3.873.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.873.0.tgz", @@ -744,6 +4903,44 @@ "node": ">=18.0.0" } }, + "node_modules/@aws-sdk/protocol-http": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/protocol-http/-/protocol-http-3.370.0.tgz", + "integrity": "sha512-MfZCgSsVmir+4kJps7xT0awOPNi+swBpcVp9ZtAP7POduUVV6zVLurMNLXsppKsErggssD5E9HUgQFs5w06U4Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.370.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/protocol-http/node_modules/@aws-sdk/types": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.370.0.tgz", + "integrity": "sha512-8PGMKklSkRKjunFhzM2y5Jm0H2TBu7YRNISdYzXLUHKSP9zlMEYagseKVdmox0zKHf1LXVNuSlUV2b6SRrieCQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/protocol-http/node_modules/@smithy/types": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-1.2.0.tgz", + "integrity": "sha512-z1r00TvBqF3dh4aHhya7nz1HhvCg4TRmw51fjMrh5do3h+ngSstt/yKlNbHeb9QxJmFbmN8KEVSWgb1bRvfEoA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@aws-sdk/region-config-resolver": { "version": "3.873.0", "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.873.0.tgz", @@ -780,6 +4977,25 @@ "node": ">=18.0.0" } }, + "node_modules/@aws-sdk/signature-v4": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4/-/signature-v4-3.370.0.tgz", + "integrity": "sha512-Mh++NJiXoBxMzz4d8GQPNB37nqjS1gsVwjKoSAWFE67sjgsjb8D5JWRCm9CinqPoXi2iN57+1DcQalTDKQGc0A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/eventstream-codec": "3.370.0", + "@aws-sdk/is-array-buffer": "3.310.0", + "@aws-sdk/types": "3.370.0", + "@aws-sdk/util-hex-encoding": "3.310.0", + "@aws-sdk/util-middleware": "3.370.0", + "@aws-sdk/util-uri-escape": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@aws-sdk/signature-v4-multi-region": { "version": "3.879.0", "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.879.0.tgz", @@ -797,6 +5013,31 @@ "node": ">=18.0.0" } }, + "node_modules/@aws-sdk/signature-v4/node_modules/@aws-sdk/types": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.370.0.tgz", + "integrity": "sha512-8PGMKklSkRKjunFhzM2y5Jm0H2TBu7YRNISdYzXLUHKSP9zlMEYagseKVdmox0zKHf1LXVNuSlUV2b6SRrieCQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4/node_modules/@smithy/types": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-1.2.0.tgz", + "integrity": "sha512-z1r00TvBqF3dh4aHhya7nz1HhvCg4TRmw51fjMrh5do3h+ngSstt/yKlNbHeb9QxJmFbmN8KEVSWgb1bRvfEoA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@aws-sdk/token-providers": { "version": "3.879.0", "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.879.0.tgz", @@ -840,6 +5081,19 @@ "node": ">=18.0.0" } }, + "node_modules/@aws-sdk/util-buffer-from": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-buffer-from/-/util-buffer-from-3.310.0.tgz", + "integrity": "sha512-i6LVeXFtGih5Zs8enLrt+ExXY92QV25jtEnTKHsmlFqFAuL3VBeod6boeMXkN2p9lbSVVQ1sAOOYZOHYbYkntw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/is-array-buffer": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@aws-sdk/util-endpoints": { "version": "3.879.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.879.0.tgz", @@ -871,6 +5125,18 @@ "node": ">=18.0.0" } }, + "node_modules/@aws-sdk/util-hex-encoding": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-hex-encoding/-/util-hex-encoding-3.310.0.tgz", + "integrity": "sha512-sVN7mcCCDSJ67pI1ZMtk84SKGqyix6/0A1Ab163YKn+lFBQRMKexleZzpYzNGxYzmQS6VanP/cfU7NiLQOaSfA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@aws-sdk/util-locate-window": { "version": "3.873.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.873.0.tgz", @@ -883,6 +5149,30 @@ "node": ">=18.0.0" } }, + "node_modules/@aws-sdk/util-middleware": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-middleware/-/util-middleware-3.370.0.tgz", + "integrity": "sha512-Jvs9FZHaQznWGLkRel3PFEP93I1n0Kp6356zxYHk3LIOmjpzoob3R+v96mzyN+dZrnhPdPubYS41qbU2F9lROg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-uri-escape": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-uri-escape/-/util-uri-escape-3.310.0.tgz", + "integrity": "sha512-drzt+aB2qo2LgtDoiy/3sVG8w63cgLkqFIa2NFlGpUgHFWTXkqtbgf4L5QdjRGKWhmZsnqkbtL7vkSWEcYDJ4Q==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@aws-sdk/util-user-agent-browser": { "version": "3.873.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.873.0.tgz", @@ -919,6 +5209,28 @@ } } }, + "node_modules/@aws-sdk/util-utf8": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8/-/util-utf8-3.310.0.tgz", + "integrity": "sha512-DnLfFT8uCO22uOJc0pt0DsSNau1GTisngBCDw8jQuWT5CqogMJu4b/uXmwEqfj8B3GX6Xsz8zOd6JpRlPftQoA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/util-buffer-from": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-utf8-browser": { + "version": "3.259.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", + "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.3.1" + } + }, "node_modules/@aws-sdk/xml-builder": { "version": "3.873.0", "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.873.0.tgz", @@ -1675,6 +5987,21 @@ "yaml": "^2.4.1" } }, + "node_modules/@infisical/sdk": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@infisical/sdk/-/sdk-4.0.6.tgz", + "integrity": "sha512-aK/oQj0prIx8jTybcwQfPYow3/KsBGPbHCyK8zCIWGvUjHzYU2is34AWjRvxQ6GhZFpW1LaXfgxgrmbWrsgWZA==", + "license": "ISC", + "dependencies": { + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-sdk/credential-providers": "3.600.0", + "@aws-sdk/protocol-http": "^3.370.0", + "@aws-sdk/signature-v4": "^3.370.0", + "axios": "^1.11.0", + "typescript": "^5.5.4", + "zod": "^3.23.8" + } + }, "node_modules/@isaacs/balanced-match": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", @@ -3236,6 +7563,13 @@ "node": ">= 8" } }, + "node_modules/crypto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", + "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==", + "deprecated": "This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in.", + "license": "ISC" + }, "node_modules/dayjs": { "version": "1.11.18", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.18.tgz", @@ -5076,7 +9410,6 @@ "version": "5.9.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", - "devOptional": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -5288,6 +9621,15 @@ "engines": { "node": ">= 14" } + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } } } diff --git a/package.json b/package.json index 6788b5b..5e56cc9 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "archiver": "^7.0.1", "axios": "^1.12.1", "bcrypt": "^6.0.0", + "crypto": "^1.0.1", "dayjs": "^1.11.18", "fastify": "^5.5.0", "fastify-plugin": "^5.0.1", diff --git a/src/utils/crypt.ts b/src/utils/crypt.ts new file mode 100644 index 0000000..a7e1868 --- /dev/null +++ b/src/utils/crypt.ts @@ -0,0 +1,39 @@ +import crypto from "crypto"; +import {secrets} from "./secrets" +const ALGORITHM = "aes-256-gcm"; + + + + +export function encrypt(text) { + const ENCRYPTION_KEY = Buffer.from(secrets.ENCRYPTION_KEY, "hex"); + const iv = crypto.randomBytes(16); + const cipher = crypto.createCipheriv(ALGORITHM, ENCRYPTION_KEY, iv); + + const encrypted = Buffer.concat([cipher.update(text, "utf8"), cipher.final()]); + const tag = cipher.getAuthTag(); + + return { + iv: iv.toString("hex"), + content: encrypted.toString("hex"), + tag: tag.toString("hex"), + }; +} + +export function decrypt({ iv, content, tag }) { + console.log(iv) + const ENCRYPTION_KEY = Buffer.from(secrets.ENCRYPTION_KEY, "hex"); + const decipher = crypto.createDecipheriv( + ALGORITHM, + ENCRYPTION_KEY, + Buffer.from(iv, "hex") + ); + decipher.setAuthTag(Buffer.from(tag, "hex")); + + const decrypted = Buffer.concat([ + decipher.update(Buffer.from(content, "hex")), + decipher.final(), + ]); + + return decrypted.toString("utf8"); +} From f683f149e68b3e28a3cc9b83bd37aaaa5e56befa Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sun, 28 Sep 2025 17:44:00 +0200 Subject: [PATCH 031/149] Added S3 Init Function --- src/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/index.ts b/src/index.ts index 87f6fba..0ca61c6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -23,10 +23,12 @@ import emailAsUserRoutes from "./routes/emailAsUser"; import {sendMail} from "./utils/mailer"; import {loadSecrets, secrets} from "./utils/secrets"; +import {initS3} from "./utils/s3"; async function main() { const app = Fastify({ logger: true }); await loadSecrets(); + await initS3(); /*app.addHook("onRequest", (req, reply, done) => { console.log("Incoming:", req.method, req.url, "Headers:", req.headers) From f51ced1c3195eb2e7e96aa609c887ea25cfff229 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sun, 28 Sep 2025 17:44:17 +0200 Subject: [PATCH 032/149] Added Types for Tickets --- src/routes/resources.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/routes/resources.ts b/src/routes/resources.ts index 5891747..0527b90 100644 --- a/src/routes/resources.ts +++ b/src/routes/resources.ts @@ -456,6 +456,18 @@ const dataTypes: any[] = { supabaseSelectWithInformation: "*, statementallocations(*, bs_id(*))", showTabs: [{label: 'Informationen'},{label: 'Buchungen'}] }, + tickets: { + isArchivable: true, + label: "Tickets", + labelSingle: "Ticket", + + }, + ticketmessages: { + isArchivable: true, + label: "Nachrichten", + labelSingle: "Nachricht", + + }, } export default async function resourceRoutes(server: FastifyInstance) { From 9aefa5d08fa454576297fdf6c8a5703881e6b86e Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sun, 28 Sep 2025 17:44:31 +0200 Subject: [PATCH 033/149] Added E-Mail Sending Features --- src/routes/emailAsUser.ts | 170 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 162 insertions(+), 8 deletions(-) diff --git a/src/routes/emailAsUser.ts b/src/routes/emailAsUser.ts index 9755e45..6977ede 100644 --- a/src/routes/emailAsUser.ts +++ b/src/routes/emailAsUser.ts @@ -1,11 +1,115 @@ -import { FastifyInstance } from "fastify"; -import {createInvoicePDF} from "../utils/pdf"; -import {useNextNumberRangeNumber} from "../utils/functions"; -import {sendMailAsUser} from "../utils/emailengine"; -import {subtle} from "node:crypto"; +import nodemailer from "nodemailer" +import { FastifyInstance } from "fastify"; +import {sendMailAsUser} from "../utils/emailengine"; +import {encrypt, decrypt} from "../utils/crypt" +import {secrets} from "../utils/secrets"; export default async function emailAsUserRoutes(server: FastifyInstance) { - server.post("/emailasuser/send", async (req, reply) => { + + // Create E-Mail Account + server.post("/email/accounts/", async (req, reply) => { + if (!req.user?.tenant_id) { + return reply.code(400).send({ error: "No tenant selected" }); + } + + const body = req.body as Record; + + let createData = { + user_id: req.user.user_id, + email_encrypted: encrypt(body.email), + password_encrypted: encrypt(body.password), + tenant_id: req.user.tenant_id, + smtp_host_encrypted: encrypt(body.smtp_host), + smtp_port: body.smtp_port, + smtp_ssl: body.smtp_ssl, + type: "mail", + imap_host_encrypted: encrypt(body.imap_host), + imap_port: body.imap_port, + imap_ssl: body.imap_ssl, + } + + + const { data, error } = await server.supabase + .from("user_credentials") + .insert(createData) + .select("*") + .single(); + + if (error) { + return reply.code(400).send({ error: error.message }); + } + + return data; + }); + + server.get("/email/accounts/:id?", async (req, reply) => { + if (!req.user?.tenant_id) { + return reply.code(400).send({ error: "No tenant selected" }); + } + + const { id } = req.params as { id: string }; + + if(id) { + let returnData = {} + // @ts-ignore + const { data, error } = await server.supabase + .from("user_credentials") + .select("id, email_encrypted, smtp_host_encrypted, smtp_port, smtp_ssl, imap_host_encrypted, imap_port, imap_ssl, user_id, tenant_id") + .eq("id", id) + .eq("tenant_id", req.user.tenant_id) + .eq("type", "mail") + .single(); + + if (error || !data) { + return reply.code(404).send({ error: "Not found" }); + } else { + Object.keys(data).forEach((key) => { + if(key.includes("encrypted")){ + returnData[key.substring(0,key.length-10)] = decrypt(data[key]) + } else { + returnData[key] = data[key] + } + }) + } + } else { + + const { data, error } = await server.supabase + .from("user_credentials") + .select("id, email_encrypted, user_id, tenant_id") + .eq("tenant_id", req.user.tenant_id) + .eq("type", "mail") + + let accounts = [] + data.forEach(item => { + let temp = {} + Object.keys(item).forEach((key) => { + if(key.includes("encrypted")){ + temp[key.substring(0,key.length-10)] = decrypt(item[key]) + } else { + temp[key] = item[key] + } + }) + accounts.push(temp) + }) + + + + return accounts + } + + + + + + return returnData; + }); + + + + + + + server.post("/email/send", async (req, reply) => { const body = req.body as { to: string cc?: string @@ -18,10 +122,60 @@ export default async function emailAsUserRoutes(server: FastifyInstance) { } try { - reply.send(await sendMailAsUser(body.to,body.subject,body.html,body.text,body.account,body.cc,body.bcc,body.attachments)) + + let accountData = {} + // @ts-ignore + const { data, error } = await server.supabase + .from("user_credentials") + .select("id, email_encrypted,password_encrypted, smtp_host_encrypted, smtp_port, smtp_ssl, user_id, tenant_id") + .eq("id", body.account) + .eq("tenant_id", req.user.tenant_id) + .eq("type", "mail") + .single(); + + if (error || !data) { + return reply.code(404).send({ error: "Not found" }); + } else { + Object.keys(data).forEach((key) => { + if(key.includes("encrypted")){ + accountData[key.substring(0,key.length-10)] = decrypt(data[key]) + } else { + accountData[key] = data[key] + } + }) + } + + + const transporter = nodemailer.createTransport({ + host: accountData.smtp_host, + port: accountData.smtp_port, + secure: accountData.smtp_ssl, + auth: { + user: accountData.email, + pass: accountData.password, + }, + }) + + const info = await transporter.sendMail({ + from: accountData.email, + to: body.to, + cc: body.cc ? body.cc : undefined, + bcc: body.bcc ? body.bcc : undefined, + subject: body.subject, + html: body.html ? body.html : undefined, + text: body.text, + attachments: body.attachments ? body.attachments : undefined, + }) + + + if(info.response.includes("OK")){ + reply.send({success: true}) + }{ + reply.status(500) + } + } catch (err) { - console.log(err) reply.code(500).send({ error: "Failed to send E-Mail as User" }) } }) From 3cbd6b265e558d3b9d176eb56d913f8e676368d8 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sun, 28 Sep 2025 17:44:43 +0200 Subject: [PATCH 034/149] Removed Env Vars from Compose --- docker-compose.yml | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index ab0b02c..c6a115c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,21 +4,4 @@ services: restart: always environment: - JWT_SECRET: "pd6Hp76xtN8R9t6WkagH2FnW8wDWx" - COOKIE_SECRET: "X9mieXp98PCXkFWja66xnDNaJhVKq" - MAIL_FROM: "FEDEO Notify " - SMTP_HOST: "mail.your-server.de" - SMTP_PASS: "NDor3A9QLbc87V39" - SMTP_PORT: 465 - SMTP_SSL: true - SMTP_USER: "notify@fedeo.de" - SUPABASE_SERVICE_ROLE_KEY: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InV3cHB2Y3hmbHJjc2lidXpzYmlsIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTcwMDkzODE5NCwiZXhwIjoyMDE2NTE0MTk0fQ.6hOkD1J8XBkVJUm-swv0ngLQ74xrEYr28EEbo0rUrts" - SUPABASE_URL: "https://uwppvcxflrcsibuzsbil.supabase.co" - PORT: 3100 - S3_BUCKET: "FEDEO" - S3_SECRET_KEY: "aZ33xBv47sPPsHuFKeHSDiLagjqF7nShnuGkj7B1" - S3_ACCESS_KEY: "RYOMQRW8KSTY3UQX7RPJ" - S3_REGION: "eu-central" - S3_ENDPOINT: "https://fedeo.nbg1.your-objectstorage.com" - EMAILENGINE_TOKEN: "dcd8209bc5371c728f9ec951600afcfc74e8c391a7e984b2a6df9c4665dc7ad6" - EMAILENGINE_URL: "https://ee.fedeo.io/v1" + From 1dfcc694ef966752bcf73239ab4d39ed0d102eac Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sun, 28 Sep 2025 17:57:28 +0200 Subject: [PATCH 035/149] Added Types --- src/utils/secrets.ts | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/utils/secrets.ts b/src/utils/secrets.ts index 84447ff..9575ac5 100644 --- a/src/utils/secrets.ts +++ b/src/utils/secrets.ts @@ -6,7 +6,28 @@ const client = new InfisicalSDK({ -export let secrets = {} +export let secrets = { + +} as { + COOKIE_SECRET: string + JWT_SECRET: string + PORT: number + HOST: string + SUPABASE_URL: string + SUPABASE_SERVICE_ROLE_KEY: string + S3_BUCKET: string + ENCRYPTION_KEY: string + MAILER_SMTP_HOST: string + MAILER_SMTP_PORT: number + MAILER_SMTP_SSL: string + MAILER_SMTP_USER: string + MAILER_SMTP_PASS: string + MAILER_FROM: string + S3_ENDPOINT: string + S3_REGION: string + S3_ACCESS_KEY: string + S3_SECRET_KEY: string +} export async function loadSecrets () { From 87411d9b87b2e2d984f8337a86a20ae38c947501 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Mon, 29 Sep 2025 18:40:13 +0200 Subject: [PATCH 036/149] Added Number Ranges to DataTypes with Holder in Creation --- src/routes/resources.ts | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/routes/resources.ts b/src/routes/resources.ts index 0527b90..5b5dad8 100644 --- a/src/routes/resources.ts +++ b/src/routes/resources.ts @@ -2,6 +2,7 @@ import { FastifyInstance } from "fastify"; import {insertHistoryItem } from "../utils/history" import {diffObjects} from "../utils/diff"; import {sortData} from "../utils/sort"; +import {useNextNumberRangeNumber} from "../utils/functions"; const dataTypes: any[] = { @@ -531,14 +532,24 @@ export default async function resourceRoutes(server: FastifyInstance) { const body = req.body as Record; const dataType = dataTypes[resource]; + let createData = { + ...body, + tenant: req.user.tenant_id, + archived: false, // Standardwert + } + + if(dataType.numberRangeHolder && !body[dataType.numberRangeHolder]) { + const result = await useNextNumberRangeNumber(server,req.user.tenant_id, resource) + createData[dataType.numberRangeHolder] = result.usedNumber + } + + + + const { data, error } = await server.supabase .from(resource) - .insert({ - ...body, - tenant: req.user.tenant_id, - archived: false, // Standardwert - }) + .insert(createData) .select("*") .single(); From 8750ccbb7dc7656dabfc433c9ae442d5e8edc99e Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Mon, 29 Sep 2025 18:40:22 +0200 Subject: [PATCH 037/149] Removed Critical Log Output --- src/routes/history.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/routes/history.ts b/src/routes/history.ts index 2f29ce8..dca8105 100644 --- a/src/routes/history.ts +++ b/src/routes/history.ts @@ -64,13 +64,8 @@ export default async function resourceHistoryRoutes(server: FastifyInstance) { .from("auth_users") .select("*, auth_profiles(*), tenants!auth_tenant_users(*)") - console.log(users) - console.log(usersError) - const filteredUsers = (users ||[]).filter(i => i.tenants.find((t:any) => t.id === req.user?.tenant_id)) - console.log(filteredUsers) - const dataCombined = data.map(historyitem => { return { ...historyitem, From fc6e76d75626ff8cb3c8ea8f05ce56fe983b3cc9 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Mon, 29 Sep 2025 18:47:21 +0200 Subject: [PATCH 038/149] Added Types --- src/routes/emailAsUser.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/routes/emailAsUser.ts b/src/routes/emailAsUser.ts index 6977ede..03b2bd9 100644 --- a/src/routes/emailAsUser.ts +++ b/src/routes/emailAsUser.ts @@ -12,7 +12,16 @@ export default async function emailAsUserRoutes(server: FastifyInstance) { return reply.code(400).send({ error: "No tenant selected" }); } - const body = req.body as Record; + const body = req.body as { + email: string + password: string + smtp_host: string + smtp_port: number + smtp_ssl: boolean + imap_host: string + imap_port: number + imap_ssl: boolean + }; let createData = { user_id: req.user.user_id, From f917d8e370b77e09094952baed0f586e5d3b001c Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Mon, 29 Sep 2025 18:50:35 +0200 Subject: [PATCH 039/149] Added Types Corrected Return --- src/routes/emailAsUser.ts | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/routes/emailAsUser.ts b/src/routes/emailAsUser.ts index 03b2bd9..dc7e571 100644 --- a/src/routes/emailAsUser.ts +++ b/src/routes/emailAsUser.ts @@ -80,6 +80,8 @@ export default async function emailAsUserRoutes(server: FastifyInstance) { } }) } + + return returnData; } else { const { data, error } = await server.supabase @@ -105,19 +107,8 @@ export default async function emailAsUserRoutes(server: FastifyInstance) { return accounts } - - - - - - return returnData; }); - - - - - server.post("/email/send", async (req, reply) => { const body = req.body as { to: string @@ -132,7 +123,13 @@ export default async function emailAsUserRoutes(server: FastifyInstance) { try { - let accountData = {} + let accountData = {} as { + email: string + password: string + smtp_host: string + smtp_port: number + smtp_ssl: boolean + } // @ts-ignore const { data, error } = await server.supabase .from("user_credentials") From 26cb53b23113b545705a69ea70c922f24f1ad894 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Mon, 29 Sep 2025 20:45:22 +0200 Subject: [PATCH 040/149] Secrets --- src/utils/secrets.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils/secrets.ts b/src/utils/secrets.ts index 9575ac5..0d20dda 100644 --- a/src/utils/secrets.ts +++ b/src/utils/secrets.ts @@ -46,5 +46,6 @@ export async function loadSecrets () { }) console.log("✅ Secrets aus Infisical geladen"); + console.log(Object.keys(secrets).length + " Stück") } From 22ce0d6e7af4c226143b094f0d5a19feeace1b98 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Mon, 29 Sep 2025 20:47:25 +0200 Subject: [PATCH 041/149] Fixed Mailer --- src/utils/mailer.ts | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/utils/mailer.ts b/src/utils/mailer.ts index 143b64d..1fbed85 100644 --- a/src/utils/mailer.ts +++ b/src/utils/mailer.ts @@ -1,15 +1,18 @@ import nodemailer from "nodemailer" import {secrets} from "./secrets" -const transporter = nodemailer.createTransport({ - host: secrets.MAILER_SMTP_HOST, - port: Number(secrets.MAILER_SMTP_PORT) || 587, - secure: secrets.MAILER_SMTP_SSL === "true", // true für 465, false für andere Ports - auth: { - user: secrets.MAILER_SMTP_USER, - pass: secrets.MAILER_SMTP_PASS, - }, -}) +export let transporter = null +export const loadMailer = async () => { + transporter = nodemailer.createTransport({ + host: secrets.MAILER_SMTP_HOST, + port: Number(secrets.MAILER_SMTP_PORT) || 587, + secure: secrets.MAILER_SMTP_SSL === "true", // true für 465, false für andere Ports + auth: { + user: secrets.MAILER_SMTP_USER, + pass: secrets.MAILER_SMTP_PASS, + }, + }) +} export async function sendMail( to: string, From 4588c2b9e96346161e529969cc12c1fa7b529bac Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Mon, 29 Sep 2025 20:49:29 +0200 Subject: [PATCH 042/149] Fixed Mailer --- src/index.ts | 2 ++ src/utils/mailer.ts | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 0ca61c6..0040ea7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -23,11 +23,13 @@ import emailAsUserRoutes from "./routes/emailAsUser"; import {sendMail} from "./utils/mailer"; import {loadSecrets, secrets} from "./utils/secrets"; +import {initMailer} from "./utils/mailer" import {initS3} from "./utils/s3"; async function main() { const app = Fastify({ logger: true }); await loadSecrets(); + await initMailer(); await initS3(); /*app.addHook("onRequest", (req, reply, done) => { diff --git a/src/utils/mailer.ts b/src/utils/mailer.ts index 1fbed85..ce52288 100644 --- a/src/utils/mailer.ts +++ b/src/utils/mailer.ts @@ -2,7 +2,7 @@ import nodemailer from "nodemailer" import {secrets} from "./secrets" export let transporter = null -export const loadMailer = async () => { +export const initMailer = async () => { transporter = nodemailer.createTransport({ host: secrets.MAILER_SMTP_HOST, port: Number(secrets.MAILER_SMTP_PORT) || 587, @@ -12,6 +12,7 @@ export const loadMailer = async () => { pass: secrets.MAILER_SMTP_PASS, }, }) + console.log("Mailer Initialized!") } export async function sendMail( From 1fd568c562c06bb7dffee44521883f3e1483e994 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sun, 5 Oct 2025 15:00:21 +0200 Subject: [PATCH 043/149] Tidying --- src/plugins/tenant.ts | 3 --- src/routes/auth/auth.ts | 12 ++---------- src/routes/auth/me.ts | 1 - src/routes/files.ts | 17 ----------------- src/routes/resources.ts | 4 ---- 5 files changed, 2 insertions(+), 35 deletions(-) diff --git a/src/plugins/tenant.ts b/src/plugins/tenant.ts index d77fab2..0556b36 100644 --- a/src/plugins/tenant.ts +++ b/src/plugins/tenant.ts @@ -8,9 +8,6 @@ export default fp(async (server: FastifyInstance) => { reply.code(400).send({ error: "Missing host header" }); return; } - - console.log(host) - // Tenant aus DB laden const { data: tenant } = await server.supabase .from("tenants") diff --git a/src/routes/auth/auth.ts b/src/routes/auth/auth.ts index 86cf6d6..72e3199 100644 --- a/src/routes/auth/auth.ts +++ b/src/routes/auth/auth.ts @@ -101,12 +101,8 @@ export default async function authRoutes(server: FastifyInstance) { .select("*, tenants!auth_tenant_users(*)") .eq("email", body.email) - console.log(data) - console.log(error) - // @ts-ignore user = (data || []).find(i => i.tenants.find(x => x.id === req.tenant.id)) - console.log(user) if(error) { // @ts-ignore return reply.code(500).send({ error: "Internal Server Error" }); @@ -120,7 +116,6 @@ export default async function authRoutes(server: FastifyInstance) { .single(); user = data if(error) { - console.log(error); // @ts-ignore return reply.code(500).send({ error: "Internal Server Error" }); } @@ -131,18 +126,15 @@ export default async function authRoutes(server: FastifyInstance) { return reply.code(401).send({ error: "Invalid credentials" }); } else { - console.log(user); - console.log(body) - const valid = await bcrypt.compare(body.password, user.password_hash); if (!valid) { // @ts-ignore return reply.code(401).send({ error: "Invalid credentials" }); } else { const token = jwt.sign( - { user_id: user.id, email: user.email, tenant_id: req.tenant ? req.tenant.id : null }, + { user_id: user.id, email: user.email, tenant_id: req.tenant?.id ? req.tenant.id : null }, secrets.JWT_SECRET!, - { expiresIn: "3h" } + { expiresIn: "6h" } ); reply.setCookie("token", token, { diff --git a/src/routes/auth/me.ts b/src/routes/auth/me.ts index 510ea89..ed528c3 100644 --- a/src/routes/auth/me.ts +++ b/src/routes/auth/me.ts @@ -64,7 +64,6 @@ export default async function meRoutes(server: FastifyInstance) { if(permissionsError) { console.log(permissionsError) } - console.log(permissionsData) const permissions = permissionsData.map(i => i.permission) || [] diff --git a/src/routes/files.ts b/src/routes/files.ts index c18529c..886fd2d 100644 --- a/src/routes/files.ts +++ b/src/routes/files.ts @@ -22,23 +22,6 @@ export default async function fileRoutes(server: FastifyInstance) { console.log(data) - /*const parts = req.parts() // mehrere FormData-Felder - - console.log(parts) - - let file: any - let meta: any - - for await (const part of parts) { - //console.log(part) - // @ts-ignore - if (part.file) { - file = part - - // @ts-ignore - meta = JSON.parse(part.fields?.meta?.value) - } - }*/ let meta = JSON.parse(data.fields?.meta?.value) diff --git a/src/routes/resources.ts b/src/routes/resources.ts index 5b5dad8..5bba9b3 100644 --- a/src/routes/resources.ts +++ b/src/routes/resources.ts @@ -543,10 +543,6 @@ export default async function resourceRoutes(server: FastifyInstance) { createData[dataType.numberRangeHolder] = result.usedNumber } - - - - const { data, error } = await server.supabase .from(resource) .insert(createData) From c092696a9a6f54cbf28ed52924f1bd564788e1d3 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sun, 5 Oct 2025 17:23:39 +0200 Subject: [PATCH 044/149] Added E-Mail Account Saving --- src/routes/emailAsUser.ts | 78 +++++++++++++++++++++++++++------------ 1 file changed, 55 insertions(+), 23 deletions(-) diff --git a/src/routes/emailAsUser.ts b/src/routes/emailAsUser.ts index dc7e571..10a036f 100644 --- a/src/routes/emailAsUser.ts +++ b/src/routes/emailAsUser.ts @@ -7,7 +7,7 @@ import {secrets} from "../utils/secrets"; export default async function emailAsUserRoutes(server: FastifyInstance) { // Create E-Mail Account - server.post("/email/accounts/", async (req, reply) => { + server.post("/email/accounts/:id?", async (req, reply) => { if (!req.user?.tenant_id) { return reply.code(400).send({ error: "No tenant selected" }); } @@ -23,32 +23,64 @@ export default async function emailAsUserRoutes(server: FastifyInstance) { imap_ssl: boolean }; - let createData = { - user_id: req.user.user_id, - email_encrypted: encrypt(body.email), - password_encrypted: encrypt(body.password), - tenant_id: req.user.tenant_id, - smtp_host_encrypted: encrypt(body.smtp_host), - smtp_port: body.smtp_port, - smtp_ssl: body.smtp_ssl, - type: "mail", - imap_host_encrypted: encrypt(body.imap_host), - imap_port: body.imap_port, - imap_ssl: body.imap_ssl, + if(req.params.id) { + //SAVE Existing + let saveData = { + email_encrypted: body.email ? encrypt(body.email) : undefined, + password_encrypted: body.password ? encrypt(body.password) : undefined, + smtp_host_encrypted: body.smtp_host ? encrypt(body.smtp_host) : undefined, + smtp_port: body.smtp_port, + smtp_ssl: body.smtp_ssl, + imap_host_encrypted: body.imap_host ? encrypt(body.imap_host) : undefined, + imap_port: body.imap_port, + imap_ssl: body.imap_ssl, + } + + + const { data, error } = await server.supabase + .from("user_credentials") + .update(saveData) + .eq("id", req.params.id) + .select("*") + .single(); + + if (error) { + return reply.code(400).send({ error: error.message }); + } else { + return reply.send({success: true}) + } + } else { + //Create New + let createData = { + user_id: req.user.user_id, + email_encrypted: encrypt(body.email), + password_encrypted: encrypt(body.password), + tenant_id: req.user.tenant_id, + smtp_host_encrypted: encrypt(body.smtp_host), + smtp_port: body.smtp_port, + smtp_ssl: body.smtp_ssl, + type: "mail", + imap_host_encrypted: encrypt(body.imap_host), + imap_port: body.imap_port, + imap_ssl: body.imap_ssl, + } + + + const { data, error } = await server.supabase + .from("user_credentials") + .insert(createData) + .select("*") + .single(); + + if (error) { + return reply.code(400).send({ error: error.message }); + } else { + return reply.send({success: true}) + } } - const { data, error } = await server.supabase - .from("user_credentials") - .insert(createData) - .select("*") - .single(); - if (error) { - return reply.code(400).send({ error: error.message }); - } - - return data; }); server.get("/email/accounts/:id?", async (req, reply) => { From f9b69be791b4c97bc44901123b7c6e178dbdb90d Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sun, 5 Oct 2025 17:24:01 +0200 Subject: [PATCH 045/149] Cahnge token lifespan --- src/routes/auth/auth.ts | 2 -- src/routes/tenant.ts | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/routes/auth/auth.ts b/src/routes/auth/auth.ts index 72e3199..bafb19c 100644 --- a/src/routes/auth/auth.ts +++ b/src/routes/auth/auth.ts @@ -84,8 +84,6 @@ export default async function authRoutes(server: FastifyInstance) { return reply.code(400).send({ error: "Email and password required" }); } - console.log(req.tenant) - /** * Wenn das Tenant Objekt verfügbar ist, befindet sich das Backend im Single Tenant Modus. * Es werden nur Benutzer zugelassen, welche auschließlich diesem Tenant angehören. diff --git a/src/routes/tenant.ts b/src/routes/tenant.ts index 0739e04..ef02a5d 100644 --- a/src/routes/tenant.ts +++ b/src/routes/tenant.ts @@ -47,7 +47,7 @@ export default async function routes(server: FastifyInstance) { tenant_id: body.tenant_id, }, secrets.JWT_SECRET!, - { expiresIn: "3h" } + { expiresIn: "6h" } ); reply.setCookie("token", token, { From ef0af906ff5d9627aa286860fab09650865fd8ec Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Mon, 13 Oct 2025 21:32:59 +0200 Subject: [PATCH 046/149] Fixed standardE-Mail for Invoices --- src/routes/auth/me.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/auth/me.ts b/src/routes/auth/me.ts index ed528c3..96418c0 100644 --- a/src/routes/auth/me.ts +++ b/src/routes/auth/me.ts @@ -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,short, locked, extraModules, businessInfo, numberRanges, dokuboxkey )`) + .select(`*, tenants!auth_tenant_users ( id, name,short, locked, extraModules, businessInfo, numberRanges, dokuboxkey, standardEmailForInvoices )`) .eq("id", authUser.user_id) .single(); From e0ed8f41bbe81cff536c3a3ddac7a2b610c20a78 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Mon, 13 Oct 2025 21:33:28 +0200 Subject: [PATCH 047/149] Working times and tenant profiles --- src/routes/functions.ts | 198 +++++++++++++++++++++++++++++++++++++++- src/routes/tenant.ts | 22 +++++ 2 files changed, 216 insertions(+), 4 deletions(-) diff --git a/src/routes/functions.ts b/src/routes/functions.ts index b3618c0..8e24f85 100644 --- a/src/routes/functions.ts +++ b/src/routes/functions.ts @@ -1,6 +1,22 @@ import { FastifyInstance } from "fastify"; import {createInvoicePDF} from "../utils/pdf"; import {useNextNumberRangeNumber} from "../utils/functions"; +import dayjs from "dayjs"; + +import customParseFormat from "dayjs/plugin/customParseFormat.js"; +import isoWeek from "dayjs/plugin/isoWeek.js"; +import isBetween from "dayjs/plugin/isBetween.js"; +import isSameOrAfter from "dayjs/plugin/isSameOrAfter.js" +import isSameOrBefore from "dayjs/plugin/isSameOrBefore.js" +import duration from "dayjs/plugin/duration.js"; +import timezone from "dayjs/plugin/timezone.js"; +dayjs.extend(customParseFormat) +dayjs.extend(isoWeek) +dayjs.extend(isBetween) +dayjs.extend(isSameOrAfter) +dayjs.extend(isSameOrBefore) +dayjs.extend(duration) +dayjs.extend(timezone) export default async function functionRoutes(server: FastifyInstance) { server.post("/functions/createinvoicepdf", async (req, reply) => { @@ -24,9 +40,7 @@ export default async function functionRoutes(server: FastifyInstance) { } }) - server.get( - "/functions/usenextnumber/:numberrange", - async (req, reply) => { + server.get("/functions/usenextnumber/:numberrange", async (req, reply) => { const { numberrange } = req.params as { numberrange: string }; const tenant = (req as any).user.tenant_id @@ -37,6 +51,182 @@ export default async function functionRoutes(server: FastifyInstance) { req.log.error(err) reply.code(500).send({ error: "Failed to generate next number" }) } + }) + + server.get("/functions/workingtimeevaluation/:profile_id", async (req, reply) => { + + async function generateWorkingTimesEvaluationValues(profile_id,startDateInput,endDateInput) { + + console.log(dayjs(startDateInput)) + console.log(dayjs(endDateInput)) + + let startDate = dayjs(startDateInput) + let endDate = dayjs(endDateInput) + //console.log(startDate) + //console.log(endDate) + + const {data:profile} = await server.supabase.from("auth_profiles").select().eq("old_profile_id",profile_id).single() + + console.log(profile) + + let {data:times,error:timesError} = await server.supabase.from("workingtimes").select().eq("profile", profile.old_profile_id).order("startDate",{ascending: false}) + const {data:absencerequests, error: timesAbsenceRequestsError} = await server.supabase.from("absencerequests").select().eq("profile",profile.old_profile_id).order("startDate",{ascending: false}) + + times = times.filter(i => dayjs(i.startDate).isSameOrAfter(startDate) && dayjs(i.endDate).subtract(1,"day").isSameOrBefore(endDate)) + + console.log(times) + + let weekFactor = 4.33 + //let monthlyWorkingMinutes = profile.weeklyWorkingHours * weekFactor * 60 + + //Get Every Day to Calc + let days = [] + let minuteSum = 0 + let dayCount = dayjs(endDate).add(1,"day").diff(startDate,"days") + for (let count = 0; count < dayCount; count++) { + let date = dayjs(startDate).add(count,"days") + minuteSum += (profile.weekly_regular_working_hours[date.day()] || 0)*60 + } + let timeSpanWorkingMinutes = minuteSum + + + + + let workingMinutesTarget = Math.ceil(Number((Number(dayjs(endDate).add(1,"days").diff(dayjs(startDate),'month',true).toFixed(2)) * 4.33 * profile.weeklyWorkingHours * 60).toFixed(2))) + + //Eingreicht + let sumWorkingMinutesEingereicht = 0 + times.forEach(time => { + const minutes = dayjs(time.endDate).diff(dayjs(time.startDate),'minutes') + sumWorkingMinutesEingereicht = sumWorkingMinutesEingereicht + minutes + }) + + //Bestätigt + let sumWorkingMinutesApproved = 0 + times.filter(i => i.approved).forEach(time => { + const minutes = dayjs(time.endDate).diff(dayjs(time.startDate),'minutes') + sumWorkingMinutesApproved = sumWorkingMinutesApproved + minutes + }) + + let recreationDays = ["2025-01-01","2025-04-18","2025-04-21","2025-05-01","2025-05-29","2025-06-09","2024-10-03","2024-10-31","2024-12-25","2024-12-26"] + + //Feiertagsausgleich + let sumWorkingMinutesRecreationDays = 0 + let sumRecreationDays = 0 + if(profile.recreationDaysCompensation) { + recreationDays.filter(i => dayjs(i).isSameOrAfter(startDate) && dayjs(i).isSameOrBefore(endDate)).forEach(day => { + let compensationTime = profile.weekly_regular_working_hours[dayjs(day).day()] || 0 + sumWorkingMinutesRecreationDays += compensationTime * 60 + sumRecreationDays++ + }) + } + + + let isBetween = (date,start,end) => { + return dayjs(date).isSameOrAfter(start) && dayjs(date).isSameOrBefore(end) + } + + //Urlaubsausgleich + let sumWorkingMinutesVacationDays = 0 + let sumVacationDays = 0 + + absencerequests.filter(i => (dayjs(i.startDate).isBetween(dayjs(startDate),dayjs(endDate),"day","[]") || dayjs(i.endDate).isBetween(dayjs(startDate),dayjs(endDate),"day","[]")) && (i.reason === "Urlaub" || i.reason === "Berufsschule") && i.approved === "Genehmigt").forEach(absenceRequest => { + let durationInDays = 0 + + let durationStartDate = null + + if(isBetween(absenceRequest.startDate,startDate,endDate) && isBetween(absenceRequest.endDate,startDate,endDate)) { + //Full in Range + durationInDays = dayjs(absenceRequest.endDate).diff(absenceRequest.startDate, "days") + 1 + durationStartDate = absenceRequest.startDate + } else if(isBetween(absenceRequest.startDate,startDate,endDate) && !isBetween(absenceRequest.endDate,startDate,endDate)) { + //Start in Range + durationInDays = dayjs(endDate).diff(absenceRequest.startDate, "days") + 1 + durationStartDate = absenceRequest.startDate + } else if(!isBetween(absenceRequest.startDate,startDate,endDate) && isBetween(absenceRequest.endDate,startDate,endDate)) { + //End in Range + durationInDays = dayjs(absenceRequest.endDate).diff(startDate, "days") + 1 + durationStartDate = startDate + + } + + let minuteSum = 0 + + for (let count = 0; count < durationInDays; count++) { + let date = dayjs(durationStartDate).add(count,"days") + minuteSum += (profile.weekly_regular_working_hours[date.day()] || 0)*60 + } + sumVacationDays += durationInDays + sumWorkingMinutesVacationDays += minuteSum + }) + + //Krankheitsausgleich + let sumWorkingMinutesSickDays = 0 + let sumSickDays = 0 + + absencerequests.filter(i => (dayjs(i.startDate).isBetween(dayjs(startDate),dayjs(endDate)) || dayjs(i.endDate).isBetween(dayjs(startDate),dayjs(endDate)) ) && (i.reason === "Krankheit" || i.reason === "Krankheit ab 1 Tag (mit Attest)" || i.reason === "Krankheit ab 2. Tag (mit Attest)") && i.approved === "Genehmigt").forEach(absenceRequest => { + let durationInDays = 0 + + let durationStartDate = null + + if(isBetween(absenceRequest.startDate,startDate,endDate) && isBetween(absenceRequest.endDate,startDate,endDate)) { + //Full in Range + console.log("FULL") + durationInDays = dayjs(absenceRequest.endDate).diff(absenceRequest.startDate, "days") + 1 + durationStartDate = absenceRequest.startDate + } else if(isBetween(absenceRequest.startDate,startDate,endDate) && !isBetween(absenceRequest.endDate,startDate,endDate)) { + //Start in Range + console.log("Start") + durationInDays = dayjs(endDate).diff(absenceRequest.startDate, "days") + 1 + durationStartDate = absenceRequest.startDate + } else if(!isBetween(absenceRequest.startDate,startDate,endDate) && isBetween(absenceRequest.endDate,startDate,endDate)) { + //End in Range + console.log("End") + durationInDays = dayjs(absenceRequest.endDate).diff(startDate, "days") + 1 + durationStartDate = startDate + + } + + let minuteSum = 0 + + for (let count = 0; count < durationInDays; count++) { + let date = dayjs(durationStartDate).add(count,"days") + minuteSum += (profile.weekly_regular_working_hours[date.day()] || 0)*60 + } + + sumSickDays += durationInDays + sumWorkingMinutesSickDays += minuteSum + }) + + //Saldo + let saldo = (sumWorkingMinutesApproved + sumWorkingMinutesRecreationDays + sumWorkingMinutesVacationDays + sumWorkingMinutesSickDays - timeSpanWorkingMinutes).toFixed(2) + let saldoInOfficial = (sumWorkingMinutesEingereicht + sumWorkingMinutesRecreationDays + sumWorkingMinutesVacationDays + sumWorkingMinutesSickDays - timeSpanWorkingMinutes).toFixed(2) + + return { + timeSpanWorkingMinutes, + workingMinutesTarget, + sumWorkingMinutesEingereicht, + sumWorkingMinutesApproved, + sumWorkingMinutesRecreationDays, + sumRecreationDays, + sumWorkingMinutesVacationDays, + sumVacationDays, + sumWorkingMinutesSickDays, + sumSickDays, + saldo, + saldoInOfficial, + times + } } - ) + + try { + reply.send(await generateWorkingTimesEvaluationValues(req.params.profile_id,req.query.start_date,req.query.end_date)) + } catch(error) { + console.log(error) + } + + + + }) + } \ No newline at end of file diff --git a/src/routes/tenant.ts b/src/routes/tenant.ts index ef02a5d..36634ab 100644 --- a/src/routes/tenant.ts +++ b/src/routes/tenant.ts @@ -98,6 +98,28 @@ export default async function routes(server: FastifyInstance) { return { tenant_id, users: correctedData }; }); + server.get("/tenant/profiles", 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_profiles") + .select() + .eq("tenant_id", authUser.tenant_id); + + if (error) { + console.log(error); + return reply.code(400).send({ error: error.message }); + } + + + return { data }; + }); + server.put("/tenant/numberrange/:numberrange", async (req, reply) => { if (!req.user) { return reply.code(401).send({ error: "Unauthorized" }); From f918e735a569f7822fd597b81412a778393b979d Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Mon, 13 Oct 2025 21:42:39 +0200 Subject: [PATCH 048/149] fixed ts errors --- src/routes/emailAsUser.ts | 7 +++++-- src/routes/functions.ts | 8 +++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/routes/emailAsUser.ts b/src/routes/emailAsUser.ts index 10a036f..9f8bf1d 100644 --- a/src/routes/emailAsUser.ts +++ b/src/routes/emailAsUser.ts @@ -12,6 +12,9 @@ export default async function emailAsUserRoutes(server: FastifyInstance) { return reply.code(400).send({ error: "No tenant selected" }); } + const { id } = req.params as { id: string }; + + const body = req.body as { email: string password: string @@ -23,7 +26,7 @@ export default async function emailAsUserRoutes(server: FastifyInstance) { imap_ssl: boolean }; - if(req.params.id) { + if(id) { //SAVE Existing let saveData = { email_encrypted: body.email ? encrypt(body.email) : undefined, @@ -40,7 +43,7 @@ export default async function emailAsUserRoutes(server: FastifyInstance) { const { data, error } = await server.supabase .from("user_credentials") .update(saveData) - .eq("id", req.params.id) + .eq("id", id) .select("*") .single(); diff --git a/src/routes/functions.ts b/src/routes/functions.ts index 8e24f85..512bb01 100644 --- a/src/routes/functions.ts +++ b/src/routes/functions.ts @@ -55,6 +55,10 @@ export default async function functionRoutes(server: FastifyInstance) { server.get("/functions/workingtimeevaluation/:profile_id", async (req, reply) => { + const { profile_id } = req.params as { profile_id: string }; + const { start_date, end_date } = req.query as { start_date: string, end_date: string }; + + async function generateWorkingTimesEvaluationValues(profile_id,startDateInput,endDateInput) { console.log(dayjs(startDateInput)) @@ -219,8 +223,10 @@ export default async function functionRoutes(server: FastifyInstance) { } } + + try { - reply.send(await generateWorkingTimesEvaluationValues(req.params.profile_id,req.query.start_date,req.query.end_date)) + reply.send(await generateWorkingTimesEvaluationValues(profile_id,start_date,end_date)) } catch(error) { console.log(error) } From efaebb2f4e1d216c2abbb508253d646cf9fdeb16 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Mon, 27 Oct 2025 17:37:51 +0100 Subject: [PATCH 049/149] Added Save in Sent for emailAsUser.ts --- src/routes/emailAsUser.ts | 48 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/src/routes/emailAsUser.ts b/src/routes/emailAsUser.ts index 9f8bf1d..45693b3 100644 --- a/src/routes/emailAsUser.ts +++ b/src/routes/emailAsUser.ts @@ -4,6 +4,11 @@ import { FastifyInstance } from "fastify"; import {sendMailAsUser} from "../utils/emailengine"; import {encrypt, decrypt} from "../utils/crypt" import {secrets} from "../utils/secrets"; +// @ts-ignore +import MailComposer from 'nodemailer/lib/mail-composer/index.js' + +import {ImapFlow} from "imapflow" + export default async function emailAsUserRoutes(server: FastifyInstance) { // Create E-Mail Account @@ -164,11 +169,14 @@ export default async function emailAsUserRoutes(server: FastifyInstance) { smtp_host: string smtp_port: number smtp_ssl: boolean + imap_host: string + imap_port: number + imap_ssl: boolean } // @ts-ignore const { data, error } = await server.supabase .from("user_credentials") - .select("id, email_encrypted,password_encrypted, smtp_host_encrypted, smtp_port, smtp_ssl, user_id, tenant_id") + .select("id, email_encrypted,password_encrypted, smtp_host_encrypted, smtp_port, smtp_ssl,imap_host_encrypted,imap_port, imap_ssl, user_id, tenant_id") .eq("id", body.account) .eq("tenant_id", req.user.tenant_id) .eq("type", "mail") @@ -197,7 +205,7 @@ export default async function emailAsUserRoutes(server: FastifyInstance) { }, }) - const info = await transporter.sendMail({ + const message = { from: accountData.email, to: body.to, cc: body.cc ? body.cc : undefined, @@ -206,8 +214,43 @@ export default async function emailAsUserRoutes(server: FastifyInstance) { html: body.html ? body.html : undefined, text: body.text, attachments: body.attachments ? body.attachments : undefined, + } + + + const info = await transporter.sendMail(message) + + const imapClient = new ImapFlow({ + host: accountData.imap_host, + port: accountData.imap_port, + secure: accountData.imap_ssl, + auth: { + user: accountData.email, + pass: accountData.password, + }, + logger: false }) + await imapClient.connect() + + const mail = new MailComposer(message) + + const raw = await mail.compile().build() // → Buffer mit kompletter MIME + + + for await (const mailbox of await imapClient.list()) { + // mailbox.flags enthält z. B. ['\\Sent', '\\HasChildren'] + console.log(mailbox.specialUse) + if (mailbox.specialUse == '\\Sent') { + console.log('📨 Sent folder gefunden:', mailbox.path) + await imapClient.mailboxOpen(mailbox.path) + + await imapClient.append(mailbox.path, raw, ['\\Seen']) + + await imapClient.logout() + + break + } + } if(info.response.includes("OK")){ reply.send({success: true}) @@ -217,6 +260,7 @@ export default async function emailAsUserRoutes(server: FastifyInstance) { } catch (err) { + console.log(err) reply.code(500).send({ error: "Failed to send E-Mail as User" }) } }) From 560b15ec931b005fefc3e4751090e14f6dcf6fc7 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Mon, 27 Oct 2025 17:37:57 +0100 Subject: [PATCH 050/149] Removed output --- src/utils/crypt.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/utils/crypt.ts b/src/utils/crypt.ts index a7e1868..1ed7ddd 100644 --- a/src/utils/crypt.ts +++ b/src/utils/crypt.ts @@ -21,7 +21,6 @@ export function encrypt(text) { } export function decrypt({ iv, content, tag }) { - console.log(iv) const ENCRYPTION_KEY = Buffer.from(secrets.ENCRYPTION_KEY, "hex"); const decipher = crypto.createDecipheriv( ALGORITHM, From d4fe66546234ef2645bd64906c190ecbad8715a7 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Mon, 27 Oct 2025 17:38:11 +0100 Subject: [PATCH 051/149] Added Standard E-Mail in Select --- src/routes/auth/me.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/auth/me.ts b/src/routes/auth/me.ts index 96418c0..e99de99 100644 --- a/src/routes/auth/me.ts +++ b/src/routes/auth/me.ts @@ -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,short, locked, extraModules, businessInfo, numberRanges, dokuboxkey, standardEmailForInvoices )`) + .select(`*, tenants!auth_tenant_users ( id, name,short, locked, extraModules, businessInfo, numberRanges, dokuboxkey, standardEmailForInvoices, standardPaymentDays )`) .eq("id", authUser.user_id) .single(); From 5d3cdeb960e14db3bead273cc1f5c45ec4fa84d3 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Mon, 27 Oct 2025 17:39:25 +0100 Subject: [PATCH 052/149] Added Paginated Endpoint Reformatted Code --- src/routes/resources.ts | 262 ++++++++++++++++++++++++++++++++-------- 1 file changed, 209 insertions(+), 53 deletions(-) diff --git a/src/routes/resources.ts b/src/routes/resources.ts index 5bba9b3..e60eea7 100644 --- a/src/routes/resources.ts +++ b/src/routes/resources.ts @@ -1,5 +1,5 @@ -import { FastifyInstance } from "fastify"; -import {insertHistoryItem } from "../utils/history" +import {FastifyInstance} from "fastify"; +import {insertHistoryItem} from "../utils/history" import {diffObjects} from "../utils/diff"; import {sortData} from "../utils/sort"; import {useNextNumberRangeNumber} from "../utils/functions"; @@ -26,7 +26,7 @@ const dataTypes: any[] = { label: "Kunden", labelSingle: "Kunde", isStandardEntity: true, - redirect:true, + redirect: true, numberRangeHolder: "customerNumber", historyItemHolder: "customer", supabaseSortColumn: "customerNumber", @@ -35,17 +35,17 @@ const dataTypes: any[] = { "Allgemeines", "Kontaktdaten" ], - showTabs: [{label: 'Informationen'},{label: 'Ansprechpartner'},{label: 'Dateien'},{label: 'Ausgangsbelege'},{label: 'Projekte'},{label: 'Objekte'},{label: 'Termine'},{label: 'Verträge'}] + showTabs: [{label: 'Informationen'}, {label: 'Ansprechpartner'}, {label: 'Dateien'}, {label: 'Ausgangsbelege'}, {label: 'Projekte'}, {label: 'Objekte'}, {label: 'Termine'}, {label: 'Verträge'}] }, contacts: { isArchivable: true, label: "Kontakte", labelSingle: "Kontakt", isStandardEntity: true, - redirect:true, + redirect: true, historyItemHolder: "contact", supabaseSelectWithInformation: "*, customer(*), vendor(*)", - showTabs:[ + showTabs: [ { label: 'Informationen', } @@ -57,24 +57,24 @@ const dataTypes: any[] = { labelSingle: "Vertrag", isStandardEntity: true, numberRangeHolder: "contractNumber", - redirect:true, + redirect: true, inputColumns: [ "Allgemeines", "Abrechnung" ], supabaseSelectWithInformation: "*, customer(*), files(*)", - showTabs: [{label: 'Informationen'},{label: 'Dateien'}] + showTabs: [{label: 'Informationen'}, {label: 'Dateien'}] }, absencerequests: { isArchivable: true, label: "Abwesenheiten", labelSingle: "Abwesenheit", isStandardEntity: true, - supabaseSortColumn:"startDate", + supabaseSortColumn: "startDate", supabaseSortAscending: false, supabaseSelectWithInformation: "*", historyItemHolder: "absencerequest", - redirect:true, + redirect: true, showTabs: [{label: 'Informationen'}] }, plants: { @@ -82,17 +82,17 @@ const dataTypes: any[] = { label: "Objekte", labelSingle: "Objekt", isStandardEntity: true, - redirect:true, + redirect: true, historyItemHolder: "plant", supabaseSelectWithInformation: "*, customer(id,name)", showTabs: [ { label: "Informationen" - },{ + }, { label: "Projekte" - },{ + }, { label: "Aufgaben" - },{ + }, { label: "Dateien" }] }, @@ -101,7 +101,7 @@ const dataTypes: any[] = { label: "Artikel", labelSingle: "Artikel", isStandardEntity: true, - redirect:true, + redirect: true, supabaseSelectWithInformation: "*, unit(name)", historyItemHolder: "product", showTabs: [ @@ -115,7 +115,7 @@ const dataTypes: any[] = { label: "Projekte", labelSingle: "Projekt", isStandardEntity: true, - redirect:true, + redirect: true, historyItemHolder: "project", numberRangeHolder: "projectNumber", supabaseSelectWithInformation: "*, customer(id,name), plant(id,name), projecttype(name, id), tasks(*, project(id,name), customer(id,name), plant(id,name)), files(*), createddocuments(*, statementallocations(*)), events(*), times(*, profile(id, fullName))", @@ -128,17 +128,17 @@ const dataTypes: any[] = { { key: "phases", label: "Phasen" - },{ + }, { key: "tasks", label: "Aufgaben" - },{ + }, { key: "files", label: "Dateien" - },{ + }, { label: "Zeiten" - },{ + }, { label: "Ausgangsbelege" - },{ + }, { label: "Termine" }/*,{ key: "timetracking", @@ -156,7 +156,7 @@ const dataTypes: any[] = { label: "Fahrzeuge", labelSingle: "Fahrzeug", isStandardEntity: true, - redirect:true, + redirect: true, historyItemHolder: "vehicle", supabaseSelectWithInformation: "*, checks(*), files(*)", showTabs: [ @@ -174,7 +174,7 @@ const dataTypes: any[] = { label: "Lieferanten", labelSingle: "Lieferant", isStandardEntity: true, - redirect:true, + redirect: true, numberRangeHolder: "vendorNumber", historyItemHolder: "vendor", supabaseSortColumn: "vendorNumber", @@ -182,7 +182,7 @@ const dataTypes: any[] = { showTabs: [ { label: 'Informationen', - },{ + }, { label: 'Ansprechpartner', }, { label: 'Dateien', @@ -212,7 +212,7 @@ const dataTypes: any[] = { label: 'Informationen', }, { label: 'Dateien', - },{label: 'Inventarartikel'} + }, {label: 'Inventarartikel'} ] }, users: { @@ -240,7 +240,7 @@ const dataTypes: any[] = { incominginvoices: { label: "Eingangsrechnungen", labelSingle: "Eingangsrechnung", - redirect:true + redirect: true }, inventoryitems: { isArchivable: true, @@ -335,7 +335,8 @@ const dataTypes: any[] = { redirect: true, showTabs: [ { - label: 'Informationen',} + label: 'Informationen', + } ] }, profiles: { @@ -421,7 +422,7 @@ const dataTypes: any[] = { roles: { label: "Rollen", labelSingle: "Rolle", - redirect:true, + redirect: true, historyItemHolder: "role", filters: [], templateColumns: [ @@ -439,23 +440,23 @@ const dataTypes: any[] = { label: "Kostenstellen", labelSingle: "Kostenstelle", isStandardEntity: true, - redirect:true, + redirect: true, numberRangeHolder: "number", historyItemHolder: "costcentre", supabaseSortColumn: "number", supabaseSelectWithInformation: "*, project(*), vehicle(*), inventoryitem(*)", - showTabs: [{label: 'Informationen'},{label: 'Auswertung Kostenstelle'}] + showTabs: [{label: 'Informationen'}, {label: 'Auswertung Kostenstelle'}] }, ownaccounts: { isArchivable: true, label: "zusätzliche Buchungskonten", labelSingle: "zusätzliches Buchungskonto", isStandardEntity: true, - redirect:true, + redirect: true, historyItemHolder: "ownaccount", supabaseSortColumn: "number", supabaseSelectWithInformation: "*, statementallocations(*, bs_id(*))", - showTabs: [{label: 'Informationen'},{label: 'Buchungen'}] + showTabs: [{label: 'Informationen'}, {label: 'Buchungen'}] }, tickets: { isArchivable: true, @@ -472,7 +473,8 @@ const dataTypes: any[] = { } export default async function resourceRoutes(server: FastifyInstance) { - // Liste + + //Liste server.get("/resource/:resource", async (req, reply) => { if (!req.user?.tenant_id) { return reply.code(400).send({ error: "No tenant selected" }); @@ -499,24 +501,179 @@ export default async function resourceRoutes(server: FastifyInstance) { return sorted; }); + + // Liste Paginated + server.get("/resource/:resource/paginated", async (req, reply) => { + if (!req.user?.tenant_id) { + return reply.code(400).send({error: "No tenant selected"}); + } + + + const {resource} = req.params as { resource: string } + const {queryConfig} = req + + const {pagination, sort, filters, paginationDisabled} = queryConfig + const {select, search, searchColumns,distinctColumns} = req.query as { + select?: string, search?: string, searchColumns?: string, distinctColumns?: string + } + + console.log(queryConfig) + + // --- Supabase-Basisabfrage --- + let baseQuery = server.supabase + .from(resource) + .select(select ? select : "*", {count: "exact"}) // 👈 Zählt Ergebnisse direkt mit + .eq("tenant", req.user.tenant_id) + + // --- 🔍 Serverseitige Suche über angegebene Spalten --- + if (search && search.trim().length > 0) { + const cols = searchColumns + ? searchColumns.split(',').map(c => c.trim()).filter(Boolean) + : [] + + if (cols.length > 0) { + const searchValue = `%${search.trim()}%` + + // JSONB-Unterfelder umwandeln in "->>" Syntax + const formattedCols = cols.map(c => { + if (c.includes('.')) { + const [jsonField, jsonKey] = c.split('.') + return `${jsonField}->>${jsonKey}` + } + return c + }) + + // or() Query dynamisch zusammenbauen + const orConditions = formattedCols + .map(f => `${f}.ilike.${searchValue}`) + .join(',') + + baseQuery = baseQuery.or(orConditions) + } + } + + // --- Filterung (intelligente Typ-Erkennung) --- + for (const [key, val] of Object.entries(queryConfig.filters)) { + if (Array.isArray(val)) { + baseQuery = baseQuery.in(key, val) + //@ts-ignore + } else if (val === true || val === false || val === null) { + baseQuery = baseQuery.is(key, val) + } else { + baseQuery = baseQuery.eq(key, val) + } + } + + // --- Sortierung --- + if (sort.length > 0) { + for (const s of sort) { + baseQuery = baseQuery.order(s.field, {ascending: s.direction === "asc"}) + } + } + + // --- Pagination --- + if (!paginationDisabled && pagination) { + const {offset, limit} = pagination + baseQuery = baseQuery.range(offset, offset + limit - 1) + } + + // --- Abfrage ausführen --- + const {data, error, count} = await baseQuery + + if (error) { + server.log.error(error) + return reply.code(400).send({error: error.message}) + } + + // --- Distinct-Werte ermitteln --- + const distinctValues: Record = {} + if (distinctColumns) { + const cols = distinctColumns.split(",").map(c => c.trim()).filter(Boolean) + + for (const col of cols) { + const isJson = col.includes(".") + const {data: allRows, error: distinctErr} = await server.supabase + .from(resource) + .select(isJson ? "*" : col) + .eq("tenant", req.user.tenant_id) + + if (distinctErr) continue + + const values = (allRows || []) + .map(row => { + if (isJson) { + const [jsonField, jsonKey] = col.split(".") + return row?.[jsonField]?.[jsonKey] ?? null + } + return row?.[col] ?? null + }) + .filter(v => v !== null && v !== undefined && v !== "") + distinctValues[col] = [...new Set(values)].sort() + } + } + + // --- Gesamtanzahl & Seitenberechnung --- + const total = count + const totalPages = + !paginationDisabled && pagination?.limit + ? Math.ceil(total / pagination.limit) + : 1 + + // --- queryConfig erweitern --- + const enrichedConfig = { + ...queryConfig, + total, + totalPages, + distinctValues, + search: search || null + } + + return { + data: data, + queryConfig: enrichedConfig + } + + + /*const { data, error } = await server.supabase + .from(resource) + //@ts-ignore + .select(select || dataTypes[resource].supabaseSelectWithInformation) + .eq("tenant", req.user.tenant_id) + if (error) { + console.log(error) + return reply.code(400).send({ error: error.message }); + } + + const sorted =sortData(data,sort,asc === "true" ? true : false) + + return sorted;*/ + }); + // Detail server.get("/resource/:resource/:id/:with_information?", async (req, reply) => { if (!req.user?.tenant_id) { - return reply.code(400).send({ error: "No tenant selected" }); + return reply.code(400).send({error: "No tenant selected"}); } - const { resource, id, with_information } = req.params as { resource: string; id: string, with_information: boolean }; - const {select } = req.query as { select?: string } + const {resource, id, with_information} = req.params as { + resource: string; + id: string, + with_information: boolean + }; + const {select} = req.query as { select?: string } // @ts-ignore - const { data, error } = await server.supabase.from(resource).select(with_information ? dataTypes[resource].supabaseSelectWithInformation : (select ? select : "*")) + const { + data, + error + } = await server.supabase.from(resource).select(with_information ? dataTypes[resource].supabaseSelectWithInformation : (select ? select : "*")) .eq("id", id) .eq("tenant", req.user.tenant_id) .single(); if (error || !data) { - return reply.code(404).send({ error: "Not found" }); + return reply.code(404).send({error: "Not found"}); } return data; @@ -525,10 +682,10 @@ export default async function resourceRoutes(server: FastifyInstance) { // Create server.post("/resource/:resource", async (req, reply) => { if (!req.user?.tenant_id) { - return reply.code(400).send({ error: "No tenant selected" }); + return reply.code(400).send({error: "No tenant selected"}); } - const { resource } = req.params as { resource: string }; + const {resource} = req.params as { resource: string }; const body = req.body as Record; const dataType = dataTypes[resource]; @@ -538,22 +695,22 @@ export default async function resourceRoutes(server: FastifyInstance) { archived: false, // Standardwert } - if(dataType.numberRangeHolder && !body[dataType.numberRangeHolder]) { - const result = await useNextNumberRangeNumber(server,req.user.tenant_id, resource) + if (dataType.numberRangeHolder && !body[dataType.numberRangeHolder]) { + const result = await useNextNumberRangeNumber(server, req.user.tenant_id, resource) createData[dataType.numberRangeHolder] = result.usedNumber } - const { data, error } = await server.supabase + const {data, error} = await server.supabase .from(resource) .insert(createData) .select("*") .single(); if (error) { - return reply.code(400).send({ error: error.message }); + return reply.code(400).send({error: error.message}); } - await insertHistoryItem(server,{ + await insertHistoryItem(server, { entity: resource, entityId: data.id, action: "created", @@ -570,40 +727,39 @@ export default async function resourceRoutes(server: FastifyInstance) { // UPDATE (inkl. Soft-Delete/Archive) server.put("/resource/:resource/:id", async (req, reply) => { console.log("hi") - const { resource, id } = req.params as { resource: string; id: string } + const {resource, id} = req.params as { resource: string; id: string } const body = req.body as Record const tenantId = (req.user as any)?.tenant_id const userId = (req.user as any)?.user_id if (!tenantId || !userId) { - return reply.code(401).send({ error: "Unauthorized" }) + return reply.code(401).send({error: "Unauthorized"}) } // vorherige Version für History laden - const { data: oldItem } = await server.supabase + const {data: oldItem} = await server.supabase .from(resource) .select("*") .eq("id", id) .eq("tenant", tenantId) .single() - const { data:newItem, error } = await server.supabase + const {data: newItem, error} = await server.supabase .from(resource) - .update({ ...body, updated_at: new Date().toISOString(), updated_by: userId }) + .update({...body, updated_at: new Date().toISOString(), updated_by: userId}) .eq("id", id) .eq("tenant", tenantId) .select() .single() - if (error) return reply.code(500).send({ error }) + if (error) return reply.code(500).send({error}) const diffs = diffObjects(oldItem, newItem); - for (const d of diffs) { - await insertHistoryItem(server,{ + await insertHistoryItem(server, { entity: resource, entityId: id, action: d.type, From 8af09e1841c0be95c9bd388bc7a886cb2ecfe995 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Mon, 27 Oct 2025 17:39:32 +0100 Subject: [PATCH 053/149] Added Paginated Endpoint Reformatted Code --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 5e56cc9..e316dc7 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "dayjs": "^1.11.18", "fastify": "^5.5.0", "fastify-plugin": "^5.0.1", + "imapflow": "^1.1.1", "jsonwebtoken": "^9.0.2", "nodemailer": "^7.0.6", "pdf-lib": "^1.17.1", From 8ff63fadec20a65e8aec4d590b985306ada2bf36 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Mon, 27 Oct 2025 17:40:25 +0100 Subject: [PATCH 054/149] Added Paginated Endpoint Reformatted Code --- src/index.ts | 16 ++++- src/plugins/queryconfig.ts | 125 +++++++++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 src/plugins/queryconfig.ts diff --git a/src/index.ts b/src/index.ts index 0040ea7..7a435d9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,6 +10,7 @@ import authRoutesAuthenticated from "./routes/auth/auth-authenticated"; import authPlugin from "./plugins/auth"; import adminRoutes from "./routes/admin"; import corsPlugin from "./plugins/cors"; +import queryConfigPlugin from "./plugins/queryconfig"; import resourceRoutes from "./routes/resources"; import resourceRoutesSpecial from "./routes/resourcesSpecial"; import fastifyCookie from "@fastify/cookie"; @@ -20,6 +21,7 @@ import functionRoutes from "./routes/functions"; import bankingRoutes from "./routes/banking"; import exportRoutes from "./routes/exports" import emailAsUserRoutes from "./routes/emailAsUser"; +import authProfilesRoutes from "./routes/profiles"; import {sendMail} from "./utils/mailer"; import {loadSecrets, secrets} from "./utils/secrets"; @@ -27,7 +29,7 @@ import {initMailer} from "./utils/mailer" import {initS3} from "./utils/s3"; async function main() { - const app = Fastify({ logger: true }); + const app = Fastify({ logger: false }); await loadSecrets(); await initMailer(); await initS3(); @@ -42,6 +44,17 @@ async function main() { await app.register(corsPlugin); await app.register(supabasePlugin); await app.register(tenantPlugin); + + app.addHook('preHandler', (req, reply, done) => { + console.log('Matched path:', req.routeOptions.url) + done() + }) + + //Plugin nur auf bestimmten Routes + await app.register(queryConfigPlugin, { + routes: ['/api/resource/:resource/paginated'] + }) + app.register(fastifyCookie, { secret: secrets.COOKIE_SECRET, }) @@ -68,6 +81,7 @@ async function main() { await subApp.register(bankingRoutes); await subApp.register(exportRoutes); await subApp.register(emailAsUserRoutes); + await subApp.register(authProfilesRoutes); },{prefix: "/api"}) diff --git a/src/plugins/queryconfig.ts b/src/plugins/queryconfig.ts new file mode 100644 index 0000000..3ecb8c0 --- /dev/null +++ b/src/plugins/queryconfig.ts @@ -0,0 +1,125 @@ +import fp from 'fastify-plugin' +import { FastifyPluginAsync, FastifyRequest } from 'fastify' + +export interface QueryConfigPagination { + page: number + limit: number + offset: number +} + +export interface QueryConfigSort { + field: string + direction: 'asc' | 'desc' +} + +export interface QueryConfig { + pagination: QueryConfigPagination | null + sort: QueryConfigSort[] + filters: Record + paginationDisabled: boolean +} + +declare module 'fastify' { + interface FastifyRequest { + queryConfig: QueryConfig + } +} + +interface QueryConfigPluginOptions { + routes?: string[] +} + +function matchRoutePattern(currentPath: string, patterns: string[]): boolean { + return patterns.some(pattern => { + // Beispiel: /users/:id -> /^\/users\/[^/]+$/ + const regex = new RegExp( + '^' + + pattern + .replace(/\*/g, '.*') // wildcard + .replace(/:[^/]+/g, '[^/]+') + + '$' + ) + return regex.test(currentPath) + }) +} + +const queryConfigPlugin: FastifyPluginAsync = async ( + fastify, + opts +) => { + const routePatterns = opts.routes || [] + + fastify.addHook('preHandler', async (req: FastifyRequest, reply) => { + const path = req.routeOptions.url || req.raw.url || '' + + if (!matchRoutePattern(path, routePatterns)) { + return + } + + const query = req.query as Record + + console.log(query) + + // Pagination deaktivieren? + const disablePagination = + query.noPagination === 'true' || + query.pagination === 'false' || + query.limit === '0' + + // Pagination berechnen + let pagination: QueryConfigPagination | null = null + if (!disablePagination) { + const page = Math.max(parseInt(query.page) || 1, 1) + const limit = Math.max(parseInt(query.limit) || 25, 1) + const offset = (page - 1) * limit + pagination = { page, limit, offset } + } + + // Sortierung + const sort: QueryConfigSort[] = [] + if (typeof query.sort === 'string') { + const items = query.sort.split(',') + for (const item of items) { + const [field, direction] = item.split(':') + sort.push({ + field: field.trim(), + direction: (direction || 'asc').toLowerCase() === 'desc' ? 'desc' : 'asc' + }) + } + } + + // Filterung + const filters: Record = {} + + for (const [key, value] of Object.entries(query)) { + const match = key.match(/^filter\[(.+)\]$/) + if (!match) continue + + const filterKey = match[1] + + if (typeof value === 'string') { + // Split bei Komma → mehrere Werte + const parts = value.split(',').map(v => v.trim()).filter(Boolean) + + // Automatische Typkonvertierung je Element + const parsedValues = parts.map(v => { + if (v === 'true') return true + if (v === 'false') return false + if (v === 'null') return null + return v + }) + + filters[filterKey] = parsedValues.length > 1 ? parsedValues : parsedValues[0] + } + } + + req.queryConfig = { + pagination, + sort, + filters, + paginationDisabled: disablePagination + } + }) +} + +export default fp(queryConfigPlugin, { name: 'query-config' }) From bbd5bbab9b5d88398b0f5e978cfc52458caefbc5 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Mon, 27 Oct 2025 17:40:43 +0100 Subject: [PATCH 055/149] Added Helpdesk --- src/index.ts | 5 + .../helpdesk/helpdesk.contact.service.ts | 38 +++ .../helpdesk/helpdesk.conversation.service.ts | 81 +++++ .../helpdesk/helpdesk.message.service.ts | 58 ++++ src/routes/helpdesk.inbound.ts | 136 ++++++++ src/routes/helpdesk.ts | 306 ++++++++++++++++++ 6 files changed, 624 insertions(+) create mode 100644 src/modules/helpdesk/helpdesk.contact.service.ts create mode 100644 src/modules/helpdesk/helpdesk.conversation.service.ts create mode 100644 src/modules/helpdesk/helpdesk.message.service.ts create mode 100644 src/routes/helpdesk.inbound.ts create mode 100644 src/routes/helpdesk.ts diff --git a/src/index.ts b/src/index.ts index 7a435d9..11e739b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -22,6 +22,8 @@ import bankingRoutes from "./routes/banking"; import exportRoutes from "./routes/exports" import emailAsUserRoutes from "./routes/emailAsUser"; import authProfilesRoutes from "./routes/profiles"; +import helpdeskRoutes from "./routes/helpdesk"; +import helpdeskInboundRoutes from "./routes/helpdesk.inbound"; import {sendMail} from "./utils/mailer"; import {loadSecrets, secrets} from "./utils/secrets"; @@ -62,6 +64,8 @@ async function main() { await app.register(authRoutes); await app.register(healthRoutes); + await app.register(helpdeskInboundRoutes); + //Geschützte Routes @@ -82,6 +86,7 @@ async function main() { await subApp.register(exportRoutes); await subApp.register(emailAsUserRoutes); await subApp.register(authProfilesRoutes); + await subApp.register(helpdeskRoutes); },{prefix: "/api"}) diff --git a/src/modules/helpdesk/helpdesk.contact.service.ts b/src/modules/helpdesk/helpdesk.contact.service.ts new file mode 100644 index 0000000..f8337df --- /dev/null +++ b/src/modules/helpdesk/helpdesk.contact.service.ts @@ -0,0 +1,38 @@ +// modules/helpdesk/helpdesk.contact.service.ts +import { FastifyInstance } from 'fastify' + +export async function getOrCreateContact( + server: FastifyInstance, + tenant_id: number, + { email, phone, display_name, customer_id, contact_id }: { email?: string; phone?: string; display_name?: string; customer_id?: number; contact_id?: number } +) { + if (!email && !phone) throw new Error('Contact must have at least an email or phone') + + // Bestehenden Kontakt prüfen + const { data: existing, error: findError } = await server.supabase + .from('helpdesk_contacts') + .select('*') + .eq('tenant_id', tenant_id) + .or(`email.eq.${email || ''},phone.eq.${phone || ''}`) + .maybeSingle() + + if (findError) throw findError + if (existing) return existing + + // Anlegen + const { data: created, error: insertError } = await server.supabase + .from('helpdesk_contacts') + .insert({ + tenant_id, + email, + phone, + display_name, + customer_id, + contact_id + }) + .select() + .single() + + if (insertError) throw insertError + return created +} diff --git a/src/modules/helpdesk/helpdesk.conversation.service.ts b/src/modules/helpdesk/helpdesk.conversation.service.ts new file mode 100644 index 0000000..9c5ff9f --- /dev/null +++ b/src/modules/helpdesk/helpdesk.conversation.service.ts @@ -0,0 +1,81 @@ +// modules/helpdesk/helpdesk.conversation.service.ts +import { FastifyInstance } from 'fastify' +import { getOrCreateContact } from './helpdesk.contact.service.js' + +export async function createConversation( + server: FastifyInstance, + { + tenant_id, + contact, + channel_instance_id, + subject, + customer_id = null, + contact_person_id = null + }: { + tenant_id: number + contact: { email?: string; phone?: string; display_name?: string } + channel_instance_id: string + subject?: string, + customer_id?: number, + contact_person_id?: number + } +) { + const contactRecord = await getOrCreateContact(server, tenant_id, contact) + + const { data, error } = await server.supabase + .from('helpdesk_conversations') + .insert({ + tenant_id, + contact_id: contactRecord.id, + channel_instance_id, + subject: subject || null, + status: 'open', + created_at: new Date().toISOString(), + customer_id, + contact_person_id + }) + .select() + .single() + + if (error) throw error + return data +} + +export async function getConversations( + server: FastifyInstance, + tenant_id: number, + opts?: { status?: string; limit?: number } +) { + const { status, limit = 50 } = opts || {} + + let query = server.supabase.from('helpdesk_conversations').select('*, customer_id(*)').eq('tenant_id', tenant_id) + + if (status) query = query.eq('status', status) + query = query.order('last_message_at', { ascending: false }).limit(limit) + + const { data, error } = await query + if (error) throw error + return { + ...data, + customer: data.customer_id + } +} + +export async function updateConversationStatus( + server: FastifyInstance, + conversation_id: string, + status: string +) { + const valid = ['open', 'in_progress', 'waiting_for_customer', 'answered', 'closed'] + if (!valid.includes(status)) throw new Error('Invalid status') + + const { data, error } = await server.supabase + .from('helpdesk_conversations') + .update({ status }) + .eq('id', conversation_id) + .select() + .single() + + if (error) throw error + return data +} diff --git a/src/modules/helpdesk/helpdesk.message.service.ts b/src/modules/helpdesk/helpdesk.message.service.ts new file mode 100644 index 0000000..71f0c4c --- /dev/null +++ b/src/modules/helpdesk/helpdesk.message.service.ts @@ -0,0 +1,58 @@ +// modules/helpdesk/helpdesk.message.service.ts +import { FastifyInstance } from 'fastify' + +export async function addMessage( + server: FastifyInstance, + { + tenant_id, + conversation_id, + author_user_id = null, + direction = 'incoming', + payload, + raw_meta = null, + }: { + tenant_id: number + conversation_id: string + author_user_id?: string | null + direction?: 'incoming' | 'outgoing' | 'internal' | 'system' + payload: any + raw_meta?: any + } +) { + if (!payload?.text) throw new Error('Message payload requires text content') + + const { data: message, error } = await server.supabase + .from('helpdesk_messages') + .insert({ + tenant_id, + conversation_id, + author_user_id, + direction, + payload, + raw_meta, + created_at: new Date().toISOString(), + }) + .select() + .single() + + if (error) throw error + + // Letzte Nachricht aktualisieren + await server.supabase + .from('helpdesk_conversations') + .update({ last_message_at: new Date().toISOString() }) + .eq('id', conversation_id) + + return message +} + +export async function getMessages(server: FastifyInstance, conversation_id: string) { + const { data, error } = await server.supabase + .from('helpdesk_messages') + .select('*') + .eq('conversation_id', conversation_id) + .order('created_at', { ascending: true }) + + if (error) throw error + return data +} diff --git a/src/routes/helpdesk.inbound.ts b/src/routes/helpdesk.inbound.ts new file mode 100644 index 0000000..96752f7 --- /dev/null +++ b/src/routes/helpdesk.inbound.ts @@ -0,0 +1,136 @@ +// modules/helpdesk/helpdesk.inbound.routes.ts +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 +} + +const helpdeskInboundRoutes: FastifyPluginAsync = async (server) => { + // Öffentliche POST-Route + server.post('/helpdesk/inbound/:public_token', async (req, res) => { + const { public_token } = req.params + const { email, phone, display_name, subject, message } = req.body + + if (!message) { + return res.status(400).send({ error: 'Message content required' }) + } + + // 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() + + if (channelError || !channel) { + return res.status(404).send({ error: 'Invalid channel token' }) + } + + const tenant_id = channel.tenant_id + const channel_instance_id = channel.id + + // @ts-ignore + const {customer, contact: contactPerson} = await findCustomerOrContactByEmailOrDomain(server,email, tenant_id ) + + + // 2️⃣ Kontakt finden oder anlegen + const contact = await getOrCreateContact(server, tenant_id, { + email, + phone, + display_name, + customer_id: customer, + contact_id: contactPerson, + }) + + // 3️⃣ Konversation erstellen + const conversation = await createConversation(server, { + tenant_id, + contact, + channel_instance_id, + subject: subject ?? 'Kontaktformular Anfrage', + customer_id: customer, + contact_person_id: contactPerson + }) + + // 4️⃣ Erste Nachricht hinzufügen + await addMessage(server, { + tenant_id, + conversation_id: conversation.id, + direction: 'incoming', + payload: { type: 'text', text: message }, + raw_meta: { source: 'contact_form' }, + }) + + // (optional) Auto-Antwort oder Event hier ergänzen + + return res.status(201).send({ + success: true, + conversation_id: conversation.id, + }) + }) +} + +export default helpdeskInboundRoutes diff --git a/src/routes/helpdesk.ts b/src/routes/helpdesk.ts new file mode 100644 index 0000000..0e4d302 --- /dev/null +++ b/src/routes/helpdesk.ts @@ -0,0 +1,306 @@ +// modules/helpdesk/helpdesk.routes.ts +import { FastifyPluginAsync } from 'fastify' +import { createConversation, getConversations, updateConversationStatus } from '../modules/helpdesk/helpdesk.conversation.service.js' +import { addMessage, getMessages } from '../modules/helpdesk/helpdesk.message.service.js' +import { getOrCreateContact } from '../modules/helpdesk/helpdesk.contact.service.js' +import {decrypt, encrypt} from "../utils/crypt"; +import nodemailer from "nodemailer" + +const helpdeskRoutes: FastifyPluginAsync = async (server) => { + // 📩 1. Liste aller Konversationen + server.get('/helpdesk/conversations', async (req, res) => { + const tenant_id = req.user?.tenant_id + if (!tenant_id) return res.status(401).send({ error: 'Unauthorized' }) + + const { status } = req.query + const conversations = await getConversations(server, tenant_id, { status }) + return res.send(conversations) + }) + + // 🆕 2. Neue Konversation erstellen + server.post('/helpdesk/conversations', async (req, res) => { + const tenant_id = req.user?.tenant_id + if (!tenant_id) return res.status(401).send({ error: 'Unauthorized' }) + + const { contact, channel_instance_id, subject, message } = req.body + if (!contact || !channel_instance_id) { + return res.status(400).send({ error: 'Missing contact or channel_instance_id' }) + } + + // 1. Konversation erstellen + const conversation = await createConversation(server, { + tenant_id, + contact, + channel_instance_id, + subject, + }) + + // 2. Falls erste Nachricht vorhanden → hinzufügen + if (message) { + await addMessage(server, { + tenant_id, + conversation_id: conversation.id, + direction: 'incoming', + payload: { type: 'text', text: message }, + }) + } + + return res.status(201).send(conversation) + }) + + // 🧭 3. Einzelne Konversation abrufen + server.get('/helpdesk/conversations/:id', async (req, res) => { + const tenant_id = req.user?.tenant_id + const conversation_id = req.params.id + + const { data, error } = await server.supabase + .from('helpdesk_conversations') + .select('*, helpdesk_contacts(*)') + .eq('tenant_id', tenant_id) + .eq('id', conversation_id) + .single() + + if (error) return res.status(404).send({ error: 'Conversation not found' }) + return res.send(data) + }) + + // 🔄 4. Konversation Status ändern + server.patch('/helpdesk/conversations/:id/status', async (req, res) => { + const conversation_id = req.params.id + const { status } = req.body + + const updated = await updateConversationStatus(server, conversation_id, status) + return res.send(updated) + }) + + // 💬 5. Nachrichten abrufen + server.get('/helpdesk/conversations/:id/messages', async (req, res) => { + const conversation_id = req.params.id + const messages = await getMessages(server, conversation_id) + return res.send(messages) + }) + + // 💌 6. Nachricht hinzufügen (z. B. Antwort eines Agents) + server.post('/helpdesk/conversations/:id/messages', async (req, res) => { + console.log(req.user) + const tenant_id = req.user?.tenant_id + const author_user_id = req.user?.user_id + const conversation_id = req.params.id + const { text } = req.body + + if (!text) return res.status(400).send({ error: 'Missing message text' }) + + const message = await addMessage(server, { + tenant_id, + conversation_id, + author_user_id, + direction: 'outgoing', + payload: { type: 'text', text }, + }) + + return res.status(201).send(message) + }) + + // 👤 7. Kontakt suchen oder anlegen + server.post('/helpdesk/contacts', async (req, res) => { + const tenant_id = req.user?.tenant_id + const { email, phone, display_name } = req.body + + const contact = await getOrCreateContact(server, tenant_id, { email, phone, display_name }) + return res.status(201).send(contact) + }) + + server.post("/helpdesk/channels", { + schema: { + body: { + type: "object", + required: ["type_id", "name", "config"], + properties: { + type_id: { type: "string" }, + name: { type: "string" }, + config: { type: "object" }, + is_active: { type: "boolean", default: true }, + }, + }, + }, + handler: async (req, reply) => { + const { type_id, name, config, is_active = true } = req.body + + // 🔒 Tenant aus Auth-Context + const tenant_id = req.user?.tenant_id + if (!tenant_id) { + return reply.status(401).send({ error: "Kein Tenant im Benutzerkontext gefunden." }) + } + + if (type_id !== "email") { + return reply.status(400).send({ error: "Nur Typ 'email' wird aktuell unterstützt." }) + } + + try { + const safeConfig = { ...config } + + // 🔐 IMAP-Daten verschlüsseln + if (safeConfig.imap) { + if (safeConfig.imap.host) + safeConfig.imap.host = encrypt(safeConfig.imap.host) + if (safeConfig.imap.user) + safeConfig.imap.user = encrypt(safeConfig.imap.user) + if (safeConfig.imap.pass) + safeConfig.imap.pass = encrypt(safeConfig.imap.pass) + } + + // 🔐 SMTP-Daten verschlüsseln + if (safeConfig.smtp) { + if (safeConfig.smtp.host) + safeConfig.smtp.host = encrypt(safeConfig.smtp.host) + if (safeConfig.smtp.user) + safeConfig.smtp.user = encrypt(safeConfig.smtp.user) + if (safeConfig.smtp.pass) + 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, + name, + config: safeConfig, + is_active, + }) + .select() + .single() + + if (error) throw error + + // 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 (data.config?.smtp) { + delete data.config.smtp.host + delete data.config.smtp.user + delete data.config.smtp.pass + } + + reply.send({ + message: "E-Mail-Channel erfolgreich erstellt", + channel: data, + }) + } catch (err) { + console.error("Fehler bei Channel-Erstellung:", err) + reply.status(500).send({ error: err.message }) + } + }, + }) + + server.post("/helpdesk/conversations/:id/reply", { + schema: { + body: { + type: "object", + required: ["text"], + properties: { + text: { type: "string" }, + }, + }, + }, + handler: async (req, reply) => { + const conversationId = (req.params as any).id + 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) + `) + .eq("id", conversationId) + .single() + + console.log(conv) + + if (convErr || !conv) { + reply.status(404).send({ error: "Konversation nicht gefunden" }) + return + } + + const contact = conv.helpdesk_contacts + const channel = conv.helpdesk_channel_instances + + console.log(contact) + if (!contact?.email) { + reply.status(400).send({ error: "Kein Empfänger gefunden" }) + return + } + + // 🔐 SMTP-Daten entschlüsseln + try { + const smtp = channel?.config?.smtp + const host = + typeof smtp.host === "object" ? decrypt(smtp.host) : smtp.host + const user = + typeof smtp.user === "object" ? decrypt(smtp.user) : smtp.user + const pass = + typeof smtp.pass === "object" ? decrypt(smtp.pass) : smtp.pass + + // 🔧 Transporter + const transporter = nodemailer.createTransport({ + host, + port: smtp.port || 465, + secure: smtp.secure ?? true, + auth: { user, pass }, + }) + + // 📩 Mail senden + const mailOptions = { + from: `"${channel?.name}" <${user}>`, + to: contact.email, + subject: conv.subject || "Antwort vom FEDEO Helpdesk", + text, + } + + const info = await transporter.sendMail(mailOptions) + 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, + direction: "outgoing", + payload: { type: "text", text }, + external_message_id: info.messageId, + received_at: new Date().toISOString(), + }) + + if (insertErr) throw insertErr + + // 🔁 Konversation aktualisieren + await server.supabase + .from("helpdesk_conversations") + .update({ last_message_at: new Date().toISOString() }) + .eq("id", conversationId) + + reply.send({ + message: "E-Mail erfolgreich gesendet", + messageId: info.messageId, + }) + } catch (err: any) { + console.error("Fehler beim SMTP-Versand:", err) + reply.status(500).send({ error: err.message }) + } + }, + }) + +} + +export default helpdeskRoutes From 2eb19b36a6429365cf0be8bf6e4a82608ac4f3e6 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Fri, 31 Oct 2025 16:27:56 +0100 Subject: [PATCH 056/149] Added Helpdesk Added M2M Auth --- src/index.ts | 8 ++ .../helpdesk/helpdesk.conversation.service.ts | 19 +++- src/plugins/auth.m2m.ts | 50 ++++++++++ src/routes/helpdesk.inbound.email.ts | 99 +++++++++++++++++++ src/routes/helpdesk.ts | 5 +- src/utils/helpers.ts | 56 +++++++++++ src/utils/secrets.ts | 2 + 7 files changed, 232 insertions(+), 7 deletions(-) create mode 100644 src/plugins/auth.m2m.ts create mode 100644 src/routes/helpdesk.inbound.email.ts create mode 100644 src/utils/helpers.ts diff --git a/src/index.ts b/src/index.ts index 11e739b..b567e46 100644 --- a/src/index.ts +++ b/src/index.ts @@ -24,6 +24,9 @@ import emailAsUserRoutes from "./routes/emailAsUser"; import authProfilesRoutes from "./routes/profiles"; import helpdeskRoutes from "./routes/helpdesk"; import helpdeskInboundRoutes from "./routes/helpdesk.inbound"; +//M2M +import authM2m from "./plugins/auth.m2m"; +import helpdeskInboundEmailRoutes from "./routes/helpdesk.inbound.email"; import {sendMail} from "./utils/mailer"; import {loadSecrets, secrets} from "./utils/secrets"; @@ -67,6 +70,11 @@ async function main() { await app.register(helpdeskInboundRoutes); + await app.register(async (m2mApp) => { + await m2mApp.register(authM2m) + await m2mApp.register(helpdeskInboundEmailRoutes) + },{prefix: "/internal"}) + //Geschützte Routes diff --git a/src/modules/helpdesk/helpdesk.conversation.service.ts b/src/modules/helpdesk/helpdesk.conversation.service.ts index 9c5ff9f..15efe7b 100644 --- a/src/modules/helpdesk/helpdesk.conversation.service.ts +++ b/src/modules/helpdesk/helpdesk.conversation.service.ts @@ -1,6 +1,7 @@ // modules/helpdesk/helpdesk.conversation.service.ts import { FastifyInstance } from 'fastify' import { getOrCreateContact } from './helpdesk.contact.service.js' +import {useNextNumberRangeNumber} from "../../utils/functions"; export async function createConversation( server: FastifyInstance, @@ -22,6 +23,8 @@ export async function createConversation( ) { const contactRecord = await getOrCreateContact(server, tenant_id, contact) + const {usedNumber } = await useNextNumberRangeNumber(server, tenant_id, "tickets") + const { data, error } = await server.supabase .from('helpdesk_conversations') .insert({ @@ -32,7 +35,8 @@ export async function createConversation( status: 'open', created_at: new Date().toISOString(), customer_id, - contact_person_id + contact_person_id, + ticket_number: usedNumber }) .select() .single() @@ -55,10 +59,15 @@ export async function getConversations( const { data, error } = await query if (error) throw error - return { - ...data, - customer: data.customer_id - } + + const mappedData = data.map(entry => { + return { + ...entry, + customer: entry.customer_id + } + }) + + return mappedData } export async function updateConversationStatus( diff --git a/src/plugins/auth.m2m.ts b/src/plugins/auth.m2m.ts new file mode 100644 index 0000000..2523b23 --- /dev/null +++ b/src/plugins/auth.m2m.ts @@ -0,0 +1,50 @@ +import { FastifyInstance } from "fastify"; +import fp from "fastify-plugin"; +import { secrets } from "../utils/secrets"; + +/** + * Fastify Plugin für Machine-to-Machine Authentifizierung. + * + * Dieses Plugin prüft, ob der Header `x-api-key` vorhanden ist + * und mit dem in der .env hinterlegten M2M_API_KEY übereinstimmt. + * + * Verwendung: + * server.register(m2mAuthPlugin, { allowedPrefix: '/internal' }) + */ +export default fp(async (server: FastifyInstance, opts: { allowedPrefix?: string } = {}) => { + //const allowedPrefix = opts.allowedPrefix || "/internal"; + + server.addHook("preHandler", async (req, reply) => { + try { + // Nur prüfen, wenn Route unterhalb des Prefix liegt + //if (!req.url.startsWith(allowedPrefix)) return; + + const apiKey = req.headers["x-api-key"]; + + if (!apiKey || apiKey !== secrets.M2M_API_KEY) { + server.log.warn(`[M2M Auth] Ungültiger oder fehlender API-Key bei ${req.url}`); + return reply.status(401).send({ error: "Unauthorized" }); + } + + // Zusatzinformationen im Request (z. B. interne Kennung) + (req as any).m2m = { + verified: true, + type: "internal", + key: apiKey, + }; + } catch (err) { + server.log.error("[M2M Auth] Fehler beim Prüfen des API-Keys:", err); + return reply.status(500).send({ error: "Internal Server Error" }); + } + }); +}); + +declare module "fastify" { + interface FastifyRequest { + m2m?: { + verified: boolean; + type: "internal"; + key: string; + }; + } +} diff --git a/src/routes/helpdesk.inbound.email.ts b/src/routes/helpdesk.inbound.email.ts new file mode 100644 index 0000000..7c0b9fb --- /dev/null +++ b/src/routes/helpdesk.inbound.email.ts @@ -0,0 +1,99 @@ +// modules/helpdesk/helpdesk.inbound.email.ts +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"; + +// ------------------------------------------------------------- +// 📧 Interne M2M-Route für eingehende E-Mails +// ------------------------------------------------------------- + +const helpdeskInboundEmailRoutes: FastifyPluginAsync = async (server) => { + server.post('/helpdesk/inbound-email', async (req, res) => { + + const { + tenant_id, + channel_id, + from, + subject, + text, + message_id, + in_reply_to, + } = req.body + + if (!tenant_id || !from?.address || !text) { + return res.status(400).send({ error: 'Invalid payload' }) + } + + server.log.info(`[InboundEmail] Neue Mail von ${from.address} für Tenant ${tenant_id}`) + + // 1️⃣ Kunde & Kontakt ermitteln + const { customer, contact: contactPerson } = + (await findCustomerOrContactByEmailOrDomain(server, from.address, tenant_id)) || {} + + // 2️⃣ Kontakt anlegen oder laden + const contact = await getOrCreateContact(server, tenant_id, { + email: from.address, + display_name: from.name || from.address, + customer_id: customer, + contact_id: contactPerson, + }) + + // 3️⃣ Konversation anhand In-Reply-To suchen + let conversationId: number | 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 + } + + // 4️⃣ Neue Konversation anlegen falls keine existiert + let conversation + if (!conversationId) { + const { usedNumber } = await useNextNumberRangeNumber(server, tenant_id, 'tickets') + conversation = await createConversation(server, { + tenant_id, + contact, + channel_instance_id: channel_id, + status: 'open', + subject: subject || '(kein Betreff)', + customer_id: customer, + contact_person_id: contactPerson, + ticket_number: usedNumber, + }) + conversationId = conversation.id + } else { + const { data } = await server.supabase + .from('helpdesk_conversations') + .select('*') + .eq('id', conversationId) + .single() + conversation = data + } + + // 5️⃣ Nachricht speichern + await addMessage(server, { + tenant_id, + conversation_id: conversationId, + direction: 'incoming', + payload: { type: 'text', text }, + external_message_id: message_id, + raw_meta: { source: 'email' }, + }) + + server.log.info(`[InboundEmail] Ticket ${conversationId} gespeichert`) + + return res.status(201).send({ + success: true, + conversation_id: conversationId, + ticket_number: conversation.ticket_number, + }) + }) +} + +export default helpdeskInboundEmailRoutes diff --git a/src/routes/helpdesk.ts b/src/routes/helpdesk.ts index 0e4d302..a194d25 100644 --- a/src/routes/helpdesk.ts +++ b/src/routes/helpdesk.ts @@ -220,7 +220,8 @@ const helpdeskRoutes: FastifyPluginAsync = async (server) => { subject, channel_instance_id, helpdesk_contacts(email), - helpdesk_channel_instances(config, name) + helpdesk_channel_instances(config, name), + ticket_number `) .eq("id", conversationId) .single() @@ -263,7 +264,7 @@ const helpdeskRoutes: FastifyPluginAsync = async (server) => { const mailOptions = { from: `"${channel?.name}" <${user}>`, to: contact.email, - subject: conv.subject || "Antwort vom FEDEO Helpdesk", + subject: `${conv.ticket_number} | ${conv.subject}` || `${conv.ticket_number} | Antwort vom FEDEO Helpdesk`, text, } diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts new file mode 100644 index 0000000..fdd7046 --- /dev/null +++ b/src/utils/helpers.ts @@ -0,0 +1,56 @@ +// 🔧 Hilfsfunktionen +import {FastifyInstance} from "fastify"; + +export function extractDomain(email: string) { + if (!email) return null + const parts = email.split('@') + return parts.length === 2 ? parts[1].toLowerCase() : null +} + +export async function findCustomerOrContactByEmailOrDomain(server:FastifyInstance, fromMail: string, tenantId: number) { + const sender = fromMail.toLowerCase() + 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) { + return { customer: contactMatch.customer, contact: contactMatch.id } + } + + // 2️⃣ Kunden nach Domain oder Rechnungs-E-Mail durchsuchen + const { data: customers, error } = await server.supabase + .from('customers') + .select('id, infoData') + .eq('tenant', tenantId) + + if (error) { + server.log.error(`[Helpdesk] Fehler beim Laden der Kunden: ${error.message}`) + return null + } + + 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) + + if ( + sender === email || + sender === invoiceEmail || + senderDomain === emailDomain || + senderDomain === invoiceDomain + ) { + return { customer: c.id, contact: null } + } + } + + return null +} \ No newline at end of file diff --git a/src/utils/secrets.ts b/src/utils/secrets.ts index 0d20dda..0107b28 100644 --- a/src/utils/secrets.ts +++ b/src/utils/secrets.ts @@ -27,6 +27,8 @@ export let secrets = { S3_REGION: string S3_ACCESS_KEY: string S3_SECRET_KEY: string + M2M_API_KEY: string + API_BASE_URL: string } export async function loadSecrets () { From 3bd4ac1f56b8c8411b3c7f4a5769a42348b6635f Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Fri, 31 Oct 2025 16:28:17 +0100 Subject: [PATCH 057/149] Added Notification Basics --- src/index.ts | 2 + src/modules/notification.service.ts | 148 ++++++++++++++++++++++++++++ src/routes/notifications.ts | 30 ++++++ 3 files changed, 180 insertions(+) create mode 100644 src/modules/notification.service.ts create mode 100644 src/routes/notifications.ts diff --git a/src/index.ts b/src/index.ts index b567e46..33e43d5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -24,6 +24,8 @@ import emailAsUserRoutes from "./routes/emailAsUser"; import authProfilesRoutes from "./routes/profiles"; import helpdeskRoutes from "./routes/helpdesk"; import helpdeskInboundRoutes from "./routes/helpdesk.inbound"; +import notificationsRoutes from "./routes/notifications"; + //M2M import authM2m from "./plugins/auth.m2m"; import helpdeskInboundEmailRoutes from "./routes/helpdesk.inbound.email"; diff --git a/src/modules/notification.service.ts b/src/modules/notification.service.ts new file mode 100644 index 0000000..241fea4 --- /dev/null +++ b/src/modules/notification.service.ts @@ -0,0 +1,148 @@ +// services/notification.service.ts +import type { FastifyInstance } from 'fastify'; +import {secrets} from "../utils/secrets"; + +export type NotificationStatus = 'queued' | 'sent' | 'failed'; + +export interface TriggerInput { + tenantId: number; + userId: string; // muss auf public.auth_users.id zeigen + eventType: string; // muss in notifications_event_types existieren + title: string; // Betreff/Title + message: string; // Klartext-Inhalt + payload?: Record; +} + +export interface UserDirectoryInfo { + email?: string; +} + +export type UserDirectory = (server: FastifyInstance, userId: string, tenantId: number) => Promise; + +export class NotificationService { + constructor( + private server: FastifyInstance, + private getUser: UserDirectory + ) {} + + /** + * Löst eine E-Mail-Benachrichtigung aus: + * - Validiert den Event-Typ + * - Legt einen Datensatz in notifications_items an (status: queued) + * - Versendet E-Mail (FEDEO Branding) + * - Aktualisiert status/sent_at bzw. error + */ + async trigger(input: TriggerInput) { + const { tenantId, userId, eventType, title, message, payload } = input; + const supabase = this.server.supabase; + + // 1) Event-Typ prüfen (aktiv?) + const { data: eventTypeRow, error: etErr } = await supabase + .from('notifications_event_types') + .select('event_key,is_active') + .eq('event_key', eventType) + .maybeSingle(); + + if (etErr || !eventTypeRow || eventTypeRow.is_active !== true) { + throw new Error(`Unbekannter oder inaktiver Event-Typ: ${eventType}`); + } + + // 2) Zieladresse beschaffen + const user = await this.getUser(this.server, userId, tenantId); + if (!user?.email) { + throw new Error(`Nutzer ${userId} hat keine E-Mail-Adresse`); + } + + // 3) Notification anlegen (status: queued) + const { data: inserted, error: insErr } = await supabase + .from('notifications_items') + .insert({ + tenant_id: tenantId, + user_id: userId, + event_type: eventType, + title, + message, + payload: payload ?? null, + channel: 'email', + status: 'queued' + }) + .select('id') + .single(); + + if (insErr || !inserted) { + throw new Error(`Fehler beim Einfügen der Notification: ${insErr?.message}`); + } + + // 4) E-Mail versenden + try { + await this.sendEmail(user.email, title, message); + + await supabase + .from('notifications_items') + .update({ status: 'sent', sent_at: new Date().toISOString() }) + .eq('id', inserted.id); + + return { success: true, id: inserted.id }; + } catch (err: any) { + await supabase + .from('notifications_items') + .update({ status: 'failed', error: String(err?.message || err) }) + .eq('id', inserted.id); + + this.server.log.error({ err, notificationId: inserted.id }, 'E-Mail Versand fehlgeschlagen'); + return { success: false, error: err?.message || 'E-Mail Versand fehlgeschlagen' }; + } + } + + // ---- private helpers ------------------------------------------------------ + + private async sendEmail(to: string, subject: string, message: string) { + const nodemailer = await import('nodemailer'); + + const transporter = nodemailer.createTransport({ + host: secrets.MAILER_SMTP_HOST, + port: Number(secrets.MAILER_SMTP_PORT), + secure: secrets.MAILER_SMTP_SSL === 'true', + auth: { + user: secrets.MAILER_SMTP_USER, + pass: secrets.MAILER_SMTP_PASS + } + }); + + const html = this.renderFedeoHtml(subject, message); + + await transporter.sendMail({ + from: secrets.MAILER_FROM, + to, + subject, + text: message, + html + }); + } + + private renderFedeoHtml(title: string, message: string) { + return ` + +
+

FEDEO

+

${this.escapeHtml(title)}

+

${this.nl2br(this.escapeHtml(message))}

+
+

Automatisch generiert von FEDEO

+
+ + `; + } + + // simple escaping (ausreichend für unser Template) + private escapeHtml(s: string) { + return s + .replace(/&/g, '&') + .replace(//g, '>'); + } + + private nl2br(s: string) { + return s.replace(/\n/g, '
'); + } +} diff --git a/src/routes/notifications.ts b/src/routes/notifications.ts new file mode 100644 index 0000000..395a6c5 --- /dev/null +++ b/src/routes/notifications.ts @@ -0,0 +1,30 @@ +// routes/notifications.routes.ts +import { FastifyInstance } from 'fastify'; +import { NotificationService, UserDirectory } from '../modules/notification.service'; + +// 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; + 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) => { + try { + const res = await svc.trigger(req.body as any); + reply.send(res); + } catch (err: any) { + server.log.error(err); + reply.code(500).send({ error: err.message }); + } + }); +} From 75518897f163c166a83e0c4f87eb4d91962657c0 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Fri, 31 Oct 2025 16:29:20 +0100 Subject: [PATCH 058/149] Fixed Paginated Search --- src/routes/resources.ts | 244 ++++++++++++++++++++++++---------------- src/utils/helpers.ts | 16 +++ 2 files changed, 161 insertions(+), 99 deletions(-) diff --git a/src/routes/resources.ts b/src/routes/resources.ts index e60eea7..8ae1840 100644 --- a/src/routes/resources.ts +++ b/src/routes/resources.ts @@ -3,6 +3,7 @@ import {insertHistoryItem} from "../utils/history" import {diffObjects} from "../utils/diff"; import {sortData} from "../utils/sort"; import {useNextNumberRangeNumber} from "../utils/functions"; +import {compareValues, getNestedValue} from "../utils/helpers"; const dataTypes: any[] = { @@ -502,153 +503,198 @@ export default async function resourceRoutes(server: FastifyInstance) { }); - // Liste Paginated + // Helper Funktionen + + +// Liste Paginated server.get("/resource/:resource/paginated", async (req, reply) => { if (!req.user?.tenant_id) { - return reply.code(400).send({error: "No tenant selected"}); + return reply.code(400).send({ error: "No tenant selected" }); } + const { resource } = req.params as { resource: string }; + const { queryConfig } = req; + const { pagination, sort, filters, paginationDisabled } = queryConfig; + const { select, search, searchColumns, distinctColumns } = req.query as { + select?: string; + search?: string; + searchColumns?: string; + distinctColumns?: string; + }; - const {resource} = req.params as { resource: string } - const {queryConfig} = req + console.log(req.query); + console.log(select); - const {pagination, sort, filters, paginationDisabled} = queryConfig - const {select, search, searchColumns,distinctColumns} = req.query as { - select?: string, search?: string, searchColumns?: string, distinctColumns?: string + // --- 🔍 Suche (im Backend mit Joins) --- + if (search && search.trim().length > 0) { + // 1. Alle Daten mit Joins holen (OHNE Pagination, aber mit Filtern) + let searchQuery = server.supabase + .from(resource) + .select(select || dataTypes[resource].supabaseSelectWithInformation) + .eq("tenant", req.user.tenant_id); + + // --- Filterung anwenden --- + for (const [key, val] of Object.entries(filters || {})) { + if (Array.isArray(val)) { + searchQuery = searchQuery.in(key, val); + } else if (val === true || val === false || val === null) { + searchQuery = searchQuery.is(key, val); + } else { + searchQuery = searchQuery.eq(key, val); + } + } + + const { data: allData, error: searchError } = await searchQuery; + + if (searchError) { + server.log.error(searchError); + return reply.code(400).send({ error: searchError.message }); + } + + // 2. Im Backend nach Suchbegriff filtern + const searchTerm = search.trim().toLowerCase(); + const searchCols = searchColumns + ? searchColumns.split(",").map(c => c.trim()).filter(Boolean) + : dataTypes[resource].searchableColumns || []; + + const filteredData = (allData || []).filter(row => { + if (searchCols.length === 0) { + // Fallback: Durchsuche alle String-Felder der Hauptebene + return Object.values(row).some(val => + val?.toString().toLowerCase().includes(searchTerm) + ); + } + + return searchCols.some(col => { + const value = getNestedValue(row, col); + return value?.toString().toLowerCase().includes(searchTerm); + }); + }); + + // 3. Im Backend sortieren + let sortedData = [...filteredData]; + if (sort?.length > 0) { + sortedData.sort((a, b) => { + for (const s of sort) { + const aVal = getNestedValue(a, s.field); + const bVal = getNestedValue(b, s.field); + const comparison = compareValues(aVal, bVal); + if (comparison !== 0) { + return s.direction === "asc" ? comparison : -comparison; + } + } + return 0; + }); + } + + // 4. Im Backend paginieren + const total = sortedData.length; + const paginatedData = !paginationDisabled && pagination + ? sortedData.slice(pagination.offset, pagination.offset + pagination.limit) + : sortedData; + + // 5. Distinct Values berechnen + const distinctValues: Record = {}; + if (distinctColumns) { + const cols = distinctColumns.split(",").map(c => c.trim()).filter(Boolean); + + for (const col of cols) { + // Distinct values aus den gefilterten Daten + const values = filteredData + .map(row => getNestedValue(row, col)) + .filter(v => v !== null && v !== undefined && v !== ""); + distinctValues[col] = [...new Set(values)].sort(); + } + } + + const totalPages = !paginationDisabled && pagination?.limit + ? Math.ceil(total / pagination.limit) + : 1; + + const enrichedConfig = { + ...queryConfig, + total, + totalPages, + distinctValues, + search: search || null, + }; + + return { data: paginatedData, queryConfig: enrichedConfig }; } - console.log(queryConfig) - - // --- Supabase-Basisabfrage --- + // --- Standardabfrage (ohne Suche) --- let baseQuery = server.supabase .from(resource) - .select(select ? select : "*", {count: "exact"}) // 👈 Zählt Ergebnisse direkt mit - .eq("tenant", req.user.tenant_id) + .select(select || dataTypes[resource].supabaseSelectWithInformation, { count: "exact" }) + .eq("tenant", req.user.tenant_id); - // --- 🔍 Serverseitige Suche über angegebene Spalten --- - if (search && search.trim().length > 0) { - const cols = searchColumns - ? searchColumns.split(',').map(c => c.trim()).filter(Boolean) - : [] - - if (cols.length > 0) { - const searchValue = `%${search.trim()}%` - - // JSONB-Unterfelder umwandeln in "->>" Syntax - const formattedCols = cols.map(c => { - if (c.includes('.')) { - const [jsonField, jsonKey] = c.split('.') - return `${jsonField}->>${jsonKey}` - } - return c - }) - - // or() Query dynamisch zusammenbauen - const orConditions = formattedCols - .map(f => `${f}.ilike.${searchValue}`) - .join(',') - - baseQuery = baseQuery.or(orConditions) - } - } - - // --- Filterung (intelligente Typ-Erkennung) --- - for (const [key, val] of Object.entries(queryConfig.filters)) { + // --- Filterung --- + for (const [key, val] of Object.entries(filters || {})) { if (Array.isArray(val)) { - baseQuery = baseQuery.in(key, val) - //@ts-ignore + baseQuery = baseQuery.in(key, val); } else if (val === true || val === false || val === null) { - baseQuery = baseQuery.is(key, val) + baseQuery = baseQuery.is(key, val); } else { - baseQuery = baseQuery.eq(key, val) + baseQuery = baseQuery.eq(key, val); } } // --- Sortierung --- - if (sort.length > 0) { + if (sort?.length > 0) { for (const s of sort) { - baseQuery = baseQuery.order(s.field, {ascending: s.direction === "asc"}) + baseQuery = baseQuery.order(s.field, { ascending: s.direction === "asc" }); } } // --- Pagination --- if (!paginationDisabled && pagination) { - const {offset, limit} = pagination - baseQuery = baseQuery.range(offset, offset + limit - 1) + const { offset, limit } = pagination; + baseQuery = baseQuery.range(offset, offset + limit - 1); } - // --- Abfrage ausführen --- - const {data, error, count} = await baseQuery - + const { data, error, count } = await baseQuery; if (error) { - server.log.error(error) - return reply.code(400).send({error: error.message}) + server.log.error(error); + return reply.code(400).send({ error: error.message }); } - // --- Distinct-Werte ermitteln --- - const distinctValues: Record = {} + // --- Distinct-Werte (auch ohne Suche) --- + const distinctValues: Record = {}; if (distinctColumns) { - const cols = distinctColumns.split(",").map(c => c.trim()).filter(Boolean) + const cols = distinctColumns.split(",").map(c => c.trim()).filter(Boolean); for (const col of cols) { - const isJson = col.includes(".") - const {data: allRows, error: distinctErr} = await server.supabase + const { data: allRows, error: distinctErr } = await server.supabase .from(resource) - .select(isJson ? "*" : col) - .eq("tenant", req.user.tenant_id) + .select(col) + .eq("tenant", req.user.tenant_id); - if (distinctErr) continue + if (distinctErr) continue; const values = (allRows || []) - .map(row => { - if (isJson) { - const [jsonField, jsonKey] = col.split(".") - return row?.[jsonField]?.[jsonKey] ?? null - } - return row?.[col] ?? null - }) - .filter(v => v !== null && v !== undefined && v !== "") - distinctValues[col] = [...new Set(values)].sort() + .map((row) => row?.[col] ?? null) + .filter((v) => v !== null && v !== undefined && v !== ""); + distinctValues[col] = [...new Set(values)].sort(); } } - // --- Gesamtanzahl & Seitenberechnung --- - const total = count - const totalPages = - !paginationDisabled && pagination?.limit - ? Math.ceil(total / pagination.limit) - : 1 + const total = count || 0; + const totalPages = !paginationDisabled && pagination?.limit + ? Math.ceil(total / pagination.limit) + : 1; - // --- queryConfig erweitern --- const enrichedConfig = { ...queryConfig, total, totalPages, distinctValues, - search: search || null - } + search: search || null, + }; - return { - data: data, - queryConfig: enrichedConfig - } - - - /*const { data, error } = await server.supabase - .from(resource) - //@ts-ignore - .select(select || dataTypes[resource].supabaseSelectWithInformation) - .eq("tenant", req.user.tenant_id) - if (error) { - console.log(error) - return reply.code(400).send({ error: error.message }); - } - - const sorted =sortData(data,sort,asc === "true" ? true : false) - - return sorted;*/ + return { data, queryConfig: enrichedConfig }; }); + // Detail server.get("/resource/:resource/:id/:with_information?", async (req, reply) => { if (!req.user?.tenant_id) { diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts index fdd7046..f484ae1 100644 --- a/src/utils/helpers.ts +++ b/src/utils/helpers.ts @@ -53,4 +53,20 @@ export async function findCustomerOrContactByEmailOrDomain(server:FastifyInstanc } return null +} + +export function getNestedValue(obj: any, path: string): any { + return path.split('.').reduce((acc, part) => acc?.[part], obj); +} + +export function compareValues(a: any, b: any): number { + if (a === b) return 0; + if (a == null) return 1; + if (b == null) return -1; + + if (typeof a === 'string' && typeof b === 'string') { + return a.localeCompare(b); + } + + return a < b ? -1 : 1; } \ No newline at end of file From c439eec66c97b95df199ab00b88df3bba43f591b Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Fri, 31 Oct 2025 16:29:36 +0100 Subject: [PATCH 059/149] Added Notifications Basics --- src/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/index.ts b/src/index.ts index 33e43d5..8ea9848 100644 --- a/src/index.ts +++ b/src/index.ts @@ -97,6 +97,7 @@ async function main() { await subApp.register(emailAsUserRoutes); await subApp.register(authProfilesRoutes); await subApp.register(helpdeskRoutes); + await subApp.register(notificationsRoutes); },{prefix: "/api"}) From 55c101df12363dedc83f953d7f6df02e6ef7e27d Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Fri, 31 Oct 2025 16:39:49 +0100 Subject: [PATCH 060/149] Fixed TS Errors --- src/routes/helpdesk.ts | 48 ++++++++++++++++++++++++++++++----------- src/routes/resources.ts | 20 ++++++++++------- 2 files changed, 48 insertions(+), 20 deletions(-) diff --git a/src/routes/helpdesk.ts b/src/routes/helpdesk.ts index a194d25..4f9688e 100644 --- a/src/routes/helpdesk.ts +++ b/src/routes/helpdesk.ts @@ -12,7 +12,7 @@ const helpdeskRoutes: FastifyPluginAsync = async (server) => { const tenant_id = req.user?.tenant_id if (!tenant_id) return res.status(401).send({ error: 'Unauthorized' }) - const { status } = req.query + const { status } = req.query as {status: string} const conversations = await getConversations(server, tenant_id, { status }) return res.send(conversations) }) @@ -22,7 +22,12 @@ const helpdeskRoutes: FastifyPluginAsync = async (server) => { const tenant_id = req.user?.tenant_id if (!tenant_id) return res.status(401).send({ error: 'Unauthorized' }) - const { contact, channel_instance_id, subject, message } = req.body + const { contact, channel_instance_id, subject, message } = req.body as { + contact: object + channel_instance_id: string + subject: string + message: string + } if (!contact || !channel_instance_id) { return res.status(400).send({ error: 'Missing contact or channel_instance_id' }) } @@ -51,7 +56,7 @@ const helpdeskRoutes: FastifyPluginAsync = async (server) => { // 🧭 3. Einzelne Konversation abrufen server.get('/helpdesk/conversations/:id', async (req, res) => { const tenant_id = req.user?.tenant_id - const conversation_id = req.params.id + const {id: conversation_id} = req.params as {id: string} const { data, error } = await server.supabase .from('helpdesk_conversations') @@ -66,8 +71,8 @@ const helpdeskRoutes: FastifyPluginAsync = async (server) => { // 🔄 4. Konversation Status ändern server.patch('/helpdesk/conversations/:id/status', async (req, res) => { - const conversation_id = req.params.id - const { status } = req.body + const {id: conversation_id} = req.params as { id: string } + const { status } = req.body as { status: string } const updated = await updateConversationStatus(server, conversation_id, status) return res.send(updated) @@ -75,7 +80,7 @@ const helpdeskRoutes: FastifyPluginAsync = async (server) => { // 💬 5. Nachrichten abrufen server.get('/helpdesk/conversations/:id/messages', async (req, res) => { - const conversation_id = req.params.id + const {id:conversation_id} = req.params as { id: string } const messages = await getMessages(server, conversation_id) return res.send(messages) }) @@ -85,8 +90,8 @@ const helpdeskRoutes: FastifyPluginAsync = async (server) => { console.log(req.user) const tenant_id = req.user?.tenant_id const author_user_id = req.user?.user_id - const conversation_id = req.params.id - const { text } = req.body + const {id: conversation_id} = req.params as { id: string } + const { text } = req.body as { text: string } if (!text) return res.status(400).send({ error: 'Missing message text' }) @@ -104,7 +109,7 @@ const helpdeskRoutes: FastifyPluginAsync = async (server) => { // 👤 7. Kontakt suchen oder anlegen server.post('/helpdesk/contacts', async (req, res) => { const tenant_id = req.user?.tenant_id - const { email, phone, display_name } = req.body + const { email, phone, display_name } = req.body as { email: string; phone: string, display_name: string } const contact = await getOrCreateContact(server, tenant_id, { email, phone, display_name }) return res.status(201).send(contact) @@ -124,7 +129,24 @@ const helpdeskRoutes: FastifyPluginAsync = async (server) => { }, }, handler: async (req, reply) => { - const { type_id, name, config, is_active = true } = req.body + const { type_id, name, config, is_active = true } = req.body as + { + type_id: string, + name: string, + config: { + imap:{ + host: string | object, + user: string | object, + pass: string | object, + }, + smtp:{ + host: string | object, + user: string | object, + pass: string | object, + } + }, + is_active: boolean + } // 🔒 Tenant aus Auth-Context const tenant_id = req.user?.tenant_id @@ -233,8 +255,8 @@ const helpdeskRoutes: FastifyPluginAsync = async (server) => { return } - const contact = conv.helpdesk_contacts - const channel = conv.helpdesk_channel_instances + const contact = conv.helpdesk_contacts as unknown as {email: string} + const channel = conv.helpdesk_channel_instances as unknown as {name: string} console.log(contact) if (!contact?.email) { @@ -244,6 +266,7 @@ const helpdeskRoutes: FastifyPluginAsync = async (server) => { // 🔐 SMTP-Daten entschlüsseln try { + // @ts-ignore const smtp = channel?.config?.smtp const host = typeof smtp.host === "object" ? decrypt(smtp.host) : smtp.host @@ -261,6 +284,7 @@ const helpdeskRoutes: FastifyPluginAsync = async (server) => { }) // 📩 Mail senden + const mailOptions = { from: `"${channel?.name}" <${user}>`, to: contact.email, diff --git a/src/routes/resources.ts b/src/routes/resources.ts index 8ae1840..09ee761 100644 --- a/src/routes/resources.ts +++ b/src/routes/resources.ts @@ -537,10 +537,12 @@ export default async function resourceRoutes(server: FastifyInstance) { for (const [key, val] of Object.entries(filters || {})) { if (Array.isArray(val)) { searchQuery = searchQuery.in(key, val); - } else if (val === true || val === false || val === null) { - searchQuery = searchQuery.is(key, val); - } else { - searchQuery = searchQuery.eq(key, val); + } else { // @ts-ignore + if (val === true || val === false || val === null) { + searchQuery = searchQuery.is(key, val); + } else { + searchQuery = searchQuery.eq(key, val); + } } } @@ -632,10 +634,12 @@ export default async function resourceRoutes(server: FastifyInstance) { for (const [key, val] of Object.entries(filters || {})) { if (Array.isArray(val)) { baseQuery = baseQuery.in(key, val); - } else if (val === true || val === false || val === null) { - baseQuery = baseQuery.is(key, val); - } else { - baseQuery = baseQuery.eq(key, val); + } else { // @ts-ignore + if (val == true || val == false || val === null) { + baseQuery = baseQuery.is(key, val); + } else { + baseQuery = baseQuery.eq(key, val); + } } } From 61835c3f26fa2e46dd8207293d7be923a7de93df Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Fri, 31 Oct 2025 16:45:27 +0100 Subject: [PATCH 061/149] Fixed TS Errors --- .../helpdesk/helpdesk.conversation.service.ts | 2 +- src/modules/helpdesk/helpdesk.message.service.ts | 2 ++ src/plugins/auth.m2m.ts | 1 + src/routes/helpdesk.inbound.email.ts | 15 ++++++++++----- src/routes/helpdesk.inbound.ts | 10 ++++++++-- 5 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/modules/helpdesk/helpdesk.conversation.service.ts b/src/modules/helpdesk/helpdesk.conversation.service.ts index 15efe7b..b7edc2b 100644 --- a/src/modules/helpdesk/helpdesk.conversation.service.ts +++ b/src/modules/helpdesk/helpdesk.conversation.service.ts @@ -11,7 +11,7 @@ export async function createConversation( channel_instance_id, subject, customer_id = null, - contact_person_id = null + contact_person_id = null, }: { tenant_id: number contact: { email?: string; phone?: string; display_name?: string } diff --git a/src/modules/helpdesk/helpdesk.message.service.ts b/src/modules/helpdesk/helpdesk.message.service.ts index 71f0c4c..2d942eb 100644 --- a/src/modules/helpdesk/helpdesk.message.service.ts +++ b/src/modules/helpdesk/helpdesk.message.service.ts @@ -10,6 +10,7 @@ export async function addMessage( direction = 'incoming', payload, raw_meta = null, + external_message_id = null, }: { tenant_id: number conversation_id: string @@ -17,6 +18,7 @@ export async function addMessage( direction?: 'incoming' | 'outgoing' | 'internal' | 'system' payload: any raw_meta?: any + external_message_id?: string } ) { if (!payload?.text) throw new Error('Message payload requires text content') diff --git a/src/plugins/auth.m2m.ts b/src/plugins/auth.m2m.ts index 2523b23..58e6c3c 100644 --- a/src/plugins/auth.m2m.ts +++ b/src/plugins/auth.m2m.ts @@ -33,6 +33,7 @@ export default fp(async (server: FastifyInstance, opts: { allowedPrefix?: string key: apiKey, }; } catch (err) { + // @ts-ignore server.log.error("[M2M Auth] Fehler beim Prüfen des API-Keys:", err); return reply.status(500).send({ error: "Internal Server Error" }); } diff --git a/src/routes/helpdesk.inbound.email.ts b/src/routes/helpdesk.inbound.email.ts index 7c0b9fb..5b24a4d 100644 --- a/src/routes/helpdesk.inbound.email.ts +++ b/src/routes/helpdesk.inbound.email.ts @@ -21,7 +21,15 @@ const helpdeskInboundEmailRoutes: FastifyPluginAsync = async (server) => { text, message_id, in_reply_to, - } = req.body + } = req.body as { + tenant_id: number + channel_id: string + from: {address: string, name: string} + subject: string + text: string + message_id: string + in_reply_to: string + } if (!tenant_id || !from?.address || !text) { return res.status(400).send({ error: 'Invalid payload' }) @@ -42,7 +50,7 @@ const helpdeskInboundEmailRoutes: FastifyPluginAsync = async (server) => { }) // 3️⃣ Konversation anhand In-Reply-To suchen - let conversationId: number | null = null + let conversationId: string | null = null if (in_reply_to) { const { data: msg } = await server.supabase .from('helpdesk_messages') @@ -55,16 +63,13 @@ const helpdeskInboundEmailRoutes: FastifyPluginAsync = async (server) => { // 4️⃣ Neue Konversation anlegen falls keine existiert let conversation if (!conversationId) { - const { usedNumber } = await useNextNumberRangeNumber(server, tenant_id, 'tickets') conversation = await createConversation(server, { tenant_id, contact, channel_instance_id: channel_id, - status: 'open', subject: subject || '(kein Betreff)', customer_id: customer, contact_person_id: contactPerson, - ticket_number: usedNumber, }) conversationId = conversation.id } else { diff --git a/src/routes/helpdesk.inbound.ts b/src/routes/helpdesk.inbound.ts index 96752f7..49e8ec7 100644 --- a/src/routes/helpdesk.inbound.ts +++ b/src/routes/helpdesk.inbound.ts @@ -71,8 +71,14 @@ async function findCustomerOrContactByEmailOrDomain(server,fromMail, tenantId) { const helpdeskInboundRoutes: FastifyPluginAsync = async (server) => { // Öffentliche POST-Route server.post('/helpdesk/inbound/:public_token', async (req, res) => { - const { public_token } = req.params - const { email, phone, display_name, subject, message } = req.body + const { public_token } = req.params as { public_token: string } + const { email, phone, display_name, subject, message } = req.body as { + email: string, + phone: string, + display_name: string + subject: string + message: string + } if (!message) { return res.status(400).send({ error: 'Message content required' }) From 61d22dbfa1a2ac2fac7a72a3afea8849f2e0ad11 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sat, 8 Nov 2025 18:58:54 +0100 Subject: [PATCH 062/149] Added GoCardless Generation --- src/routes/banking.ts | 147 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 145 insertions(+), 2 deletions(-) diff --git a/src/routes/banking.ts b/src/routes/banking.ts index 1e88cee..fd228b7 100644 --- a/src/routes/banking.ts +++ b/src/routes/banking.ts @@ -1,9 +1,149 @@ import { FastifyInstance } from "fastify"; -import jwt from "jsonwebtoken"; import {insertHistoryItem} from "../utils/history"; +import axios from "axios" +import dayjs from "dayjs" +import {secrets} from "../utils/secrets"; export default async function bankingRoutes(server: FastifyInstance) { + const goCardLessBaseUrl = secrets.GOCARDLESS_BASE_URL + const goCardLessSecretId = secrets.GOCARDLESS_SECRET_ID + const goCardLessSecretKey = secrets.GOCARDLESS_SECRET_KEY + + let tokenData = null + + const getToken = async () => { + const res = await axios({ + url: goCardLessBaseUrl + "/token/new/", + method: "POST", + data: { + secret_id: goCardLessSecretId, + secret_key: goCardLessSecretKey, + }, + }) + + tokenData = res.data + tokenData.created_at = new Date().toISOString() + server.log.info("Got new GoCardless token") + } + + const checkToken = async () => { + if (tokenData) { + const expired = dayjs(tokenData.created_at) + .add(tokenData.access_expires, "seconds") + .isBefore(dayjs()) + if (expired) { + server.log.info("Token expired — refreshing…") + await getToken() + } + } else { + await getToken() + } + } + + // 🔹 Generate Link + server.get("/banking/link/:institutionid", async (req, reply) => { + await checkToken() + + const {institutionid} = req.params as {institutionid: string} + + try { + const { data } = await axios({ + url: `${goCardLessBaseUrl}/requisitions/`, + method: "POST", + headers: { + Authorization: `Bearer ${tokenData.access}`, + accept: "application/json", + }, + data: { + redirect: "https://app.fedeo.de/settings/banking", + institution_id: institutionid, + user_language: "de", + }, + }) + + await server.supabase + .from("bankrequisitions") + .insert({ + tenant: req.user.tenant_id, + institutionId: institutionid, + id: data.id, + status: data.status, + }) + + return reply.send({ link: data.link }) + } catch (err) { + server.log.error(err.response?.data || err.message) + return reply.code(500).send({ error: "Failed to generate link" }) + } + }) + + // 🔹 Check Institution + server.get("/banking/institutions/:bic", async (req, reply) => { + const { bic } = req.params as {bic: string} + if (!bic) return reply.code(400).send("BIC not provided") + + await checkToken() + + try { + const { data } = await axios({ + url: `${goCardLessBaseUrl}/institutions/?country=de`, + method: "GET", + headers: { + Authorization: `Bearer ${tokenData.access}`, + }, + }) + + const bank = data.find((i) => i.bic.toLowerCase() === bic.toLowerCase()) + if (!bank) return reply.code(404).send("Bank not found") + + return reply.send(bank) + } catch (err) { + server.log.error(err.response?.data || err.message) + return reply.code(500).send("Failed to fetch institutions") + } + }) + + // 🔹 List Requisitions + server.get("/banking/requisitions/:reqId", async (req, reply) => { + const { reqId } = req.params as {reqId: string} + if (!reqId) return reply.code(400).send("Requisition ID not provided") + + await checkToken() + + try { + const { data } = await axios({ + url: `${goCardLessBaseUrl}/requisitions/${reqId}`, + method: "GET", + headers: { + Authorization: `Bearer ${tokenData.access}`, + }, + }) + + if (data.accounts) { + data.accounts = await Promise.all( + data.accounts.map(async (accId) => { + const { data: accountData } = await axios({ + url: `${goCardLessBaseUrl}/accounts/${accId}`, + method: "GET", + headers: { + Authorization: `Bearer ${tokenData.access}`, + accept: "application/json", + }, + }) + return accountData + }) + ) + } + + return reply.send(data) + } catch (err) { + server.log.error(err.response?.data || err.message) + return reply.code(500).send("Failed to fetch requisition data") + } + }) + + //Create Banking Statement server.post("/banking/statements", async (req, reply) => { if (!req.user) { @@ -71,4 +211,7 @@ export default async function bankingRoutes(server: FastifyInstance) { -} \ No newline at end of file + + +} + From 89abbde75348cae57f0d94742248938c0e85ffd2 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sat, 8 Nov 2025 18:59:10 +0100 Subject: [PATCH 063/149] Fixed Datev Export --- src/utils/export/datev.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/utils/export/datev.ts b/src/utils/export/datev.ts index 647d27c..8c9efa6 100644 --- a/src/utils/export/datev.ts +++ b/src/utils/export/datev.ts @@ -78,8 +78,8 @@ export async function buildExportZip(server: FastifyInstance, tenant: number, st //Get Bookings const {data:statementallocationsRaw,error: statementallocationsError} = await server.supabase.from("statementallocations").select('*, account(*), bs_id(*, account(*)), cd_id(*,customer(*)), ii_id(*, vendor(*)), vendor(*), customer(*), ownaccount(*)').eq("tenant", tenant); - let {data:createddocumentsRaw,error: createddocumentsError} = await server.supabase.from("createddocuments").select('*,customer(*)').eq("tenant", tenant).in("type",["invoices","advanceInvoices","cancellationInvoices"]).eq("state","Gebucht") - let {data:incominginvoicesRaw,error: incominginvoicesError} = await server.supabase.from("incominginvoices").select('*, vendor(*)').eq("tenant", tenant) + let {data:createddocumentsRaw,error: createddocumentsError} = await server.supabase.from("createddocuments").select('*,customer(*)').eq("tenant", tenant).in("type",["invoices","advanceInvoices","cancellationInvoices"]).eq("state","Gebucht").eq("archived",false) + let {data:incominginvoicesRaw,error: incominginvoicesError} = await server.supabase.from("incominginvoices").select('*, vendor(*)').eq("tenant", tenant).eq("state","Gebucht").eq("archived",false) const {data:accounts} = await server.supabase.from("accounts").select() const {data:tenantData} = await server.supabase.from("tenants").select().eq("id",tenant).single() @@ -92,6 +92,9 @@ export async function buildExportZip(server: FastifyInstance, tenant: number, st const {data:filesIncomingInvoices, error: filesErrorII} = await server.supabase.from("files").select().eq("tenant",tenant).or(`incominginvoice.in.(${incominginvoices.map(i => i.id).join(",")})`) const downloadFile = async (bucketName, filePath, downloadFilePath,fileId) => { + + console.log(filePath) + const command = new GetObjectCommand({ Bucket: secrets.S3_BUCKET, Key: filePath, From 6d0b764ee22566356dfd438cb2069534c3c738bc Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sat, 8 Nov 2025 18:59:25 +0100 Subject: [PATCH 064/149] Introduced Dayjs as Server Plugin --- src/plugins/dayjs.ts | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 src/plugins/dayjs.ts diff --git a/src/plugins/dayjs.ts b/src/plugins/dayjs.ts new file mode 100644 index 0000000..3b721fa --- /dev/null +++ b/src/plugins/dayjs.ts @@ -0,0 +1,40 @@ +import fp from "fastify-plugin" +import dayjs from "dayjs" + +// 🧩 Plugins +import customParseFormat from "dayjs/plugin/customParseFormat.js"; +import isBetween from "dayjs/plugin/isBetween.js"; +import duration from "dayjs/plugin/duration.js"; +import utc from "dayjs/plugin/utc" +import timezone from "dayjs/plugin/timezone" +import isSameOrAfter from "dayjs/plugin/isSameOrAfter" +import isSameOrBefore from "dayjs/plugin/isSameOrBefore" +import isoWeek from "dayjs/plugin/isoWeek" +import localizedFormat from "dayjs/plugin/localizedFormat" + +// 🔧 Erweiterungen aktivieren +dayjs.extend(utc) +dayjs.extend(timezone) +dayjs.extend(isSameOrAfter) +dayjs.extend(isSameOrBefore) +dayjs.extend(isoWeek) +dayjs.extend(localizedFormat) +dayjs.extend(customParseFormat) +dayjs.extend(isBetween) +dayjs.extend(duration) + +/** + * Fastify Plugin: hängt dayjs an den Server an + */ +export default fp(async (server) => { + server.decorate("dayjs", dayjs) +}) + +/** + * Typ-Erweiterung für TypeScript + */ +declare module "fastify" { + interface FastifyInstance { + dayjs: typeof dayjs + } +} From 29bebe6149e2ad078725e1243285e7098c2161ff Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sat, 8 Nov 2025 18:59:47 +0100 Subject: [PATCH 065/149] Functions Time Eval and PDF --- src/modules/time/evaluation.service.ts | 128 ++++++++++++ src/routes/functions.ts | 276 +++++++++---------------- src/utils/pdf.ts | 226 +++++++++++++++++++- 3 files changed, 444 insertions(+), 186 deletions(-) create mode 100644 src/modules/time/evaluation.service.ts diff --git a/src/modules/time/evaluation.service.ts b/src/modules/time/evaluation.service.ts new file mode 100644 index 0000000..745711d --- /dev/null +++ b/src/modules/time/evaluation.service.ts @@ -0,0 +1,128 @@ +import {FastifyInstance} from "fastify"; + + +export async function generateTimesEvaluation( + server: FastifyInstance, + user_id: string, + tenant_id: number, + startDateInput: string, + endDateInput: string +) { + const startDate = server.dayjs(startDateInput) + const endDate = server.dayjs(endDateInput) + + // 🧾 Profil laden (Arbeitszeiten + Bundesland) + const { data: profile, error: profileError } = await server.supabase + .from("auth_profiles") + .select("*") + .eq("user_id", user_id) + .eq("tenant_id", tenant_id) + .maybeSingle() + + if (profileError || !profile) throw new Error("Profil konnte nicht geladen werden.") + + // 🕒 Arbeitszeiten abrufen + const { data: times, error: timeError } = await server.supabase + .from("staff_time_entries") + .select("*") + .eq("tenant_id", tenant_id) + .eq("user_id", user_id) + .gte("started_at", startDate.toISOString()) + .lte("started_at", endDate.toISOString()) + .order("started_at", { ascending: true }) + + if (timeError) throw new Error("Fehler beim Laden der Arbeitszeiten: " + timeError.message) + + // 📅 Feiertage aus Tabelle für Bundesland + DE + const { data: holidays, error: holidaysError } = await server.supabase + .from("holidays") + .select("date") + .in("state_code", [profile.state_code, "DE"]) + .gte("date", startDate.format("YYYY-MM-DD")) + .lte("date", endDate.format("YYYY-MM-DD")) + + if (holidaysError) throw new Error("Fehler beim Laden der Feiertage: " + holidaysError.message) + + // 🗓️ Sollzeit berechnen + let timeSpanWorkingMinutes = 0 + const totalDays = endDate.add(1, "day").diff(startDate, "days") + + for (let i = 0; i < totalDays; i++) { + const date = startDate.add(i, "days") + const weekday = date.day() + timeSpanWorkingMinutes += (profile.weekly_regular_working_hours?.[weekday] || 0) * 60 + } + + // 🧮 Eingereicht & genehmigt + const calcMinutes = (start: string, end: string | null) => + server.dayjs(end || new Date()).diff(server.dayjs(start), "minutes") + + let sumWorkingMinutesEingereicht = 0 + let sumWorkingMinutesApproved = 0 + + for (const t of times) { + const minutes = calcMinutes(t.started_at, t.stopped_at) + if(["submitted","approved"].includes(t.state))sumWorkingMinutesEingereicht += minutes + if (t.state === "approved") sumWorkingMinutesApproved += minutes + } + + // 🎉 Feiertagsausgleich + let sumWorkingMinutesRecreationDays = 0 + let sumRecreationDays = 0 + + if (profile.recreationDaysCompensation && holidays?.length) { + holidays.forEach(({ date }) => { + const weekday = server.dayjs(date).day() + const hours = profile.weekly_regular_working_hours?.[weekday] || 0 + sumWorkingMinutesRecreationDays += hours * 60 + sumRecreationDays++ + }) + } + + // 🏖️ Urlaub & Krankheit (über Typ) + const sumWorkingMinutesVacationDays = times + .filter((t) => t.type === "vacation") + .reduce((sum, t) => sum + calcMinutes(t.started_at, t.stopped_at), 0) + + const sumWorkingMinutesSickDays = times + .filter((t) => t.type === "sick") + .reduce((sum, t) => sum + calcMinutes(t.started_at, t.stopped_at), 0) + + const sumVacationDays = times.filter((t) => t.type === "vacation").length + const sumSickDays = times.filter((t) => t.type === "sick").length + + // 💰 Salden + const saldo = + sumWorkingMinutesApproved + + sumWorkingMinutesRecreationDays + + sumWorkingMinutesVacationDays + + sumWorkingMinutesSickDays - + timeSpanWorkingMinutes + + const saldoInOfficial = + sumWorkingMinutesEingereicht + + sumWorkingMinutesRecreationDays + + sumWorkingMinutesVacationDays + + sumWorkingMinutesSickDays - + timeSpanWorkingMinutes + + // 📦 Rückgabe (kompatibel zur alten Struktur) + return { + user_id, + tenant_id, + from: startDate.format("YYYY-MM-DD"), + to: endDate.format("YYYY-MM-DD"), + timeSpanWorkingMinutes, + sumWorkingMinutesEingereicht, + sumWorkingMinutesApproved, + sumWorkingMinutesRecreationDays, + sumRecreationDays, + sumWorkingMinutesVacationDays, + sumVacationDays, + sumWorkingMinutesSickDays, + sumSickDays, + saldo, + saldoInOfficial, + times + } +} \ No newline at end of file diff --git a/src/routes/functions.ts b/src/routes/functions.ts index 512bb01..bcd49f9 100644 --- a/src/routes/functions.ts +++ b/src/routes/functions.ts @@ -1,5 +1,5 @@ import { FastifyInstance } from "fastify"; -import {createInvoicePDF} from "../utils/pdf"; +import {createInvoicePDF, createTimeSheetPDF} from "../utils/pdf"; import {useNextNumberRangeNumber} from "../utils/functions"; import dayjs from "dayjs"; @@ -10,6 +10,7 @@ import isSameOrAfter from "dayjs/plugin/isSameOrAfter.js" import isSameOrBefore from "dayjs/plugin/isSameOrBefore.js" import duration from "dayjs/plugin/duration.js"; import timezone from "dayjs/plugin/timezone.js"; +import {generateTimesEvaluation} from "../modules/time/evaluation.service"; dayjs.extend(customParseFormat) dayjs.extend(isoWeek) dayjs.extend(isBetween) @@ -19,19 +20,35 @@ dayjs.extend(duration) dayjs.extend(timezone) export default async function functionRoutes(server: FastifyInstance) { - server.post("/functions/createinvoicepdf", async (req, reply) => { + server.post("/functions/pdf/:type", async (req, reply) => { const body = req.body as { - invoiceData: any + data: any backgroundPath?: string } + const {type} = req.params as {type:string} + try { - const pdf = await createInvoicePDF( - server, - "base64", - body.invoiceData, - body.backgroundPath - ) + + let pdf = null + + if(type === "createdDocument") { + pdf = await createInvoicePDF( + server, + "base64", + body.data, + body.backgroundPath + ) + } else if(type === "timesheet") { + pdf = await createTimeSheetPDF( + server, + "base64", + body.data, + body.backgroundPath + ) + } + + console.log(pdf) reply.send(pdf) // Fastify wandelt automatisch in JSON } catch (err) { @@ -53,186 +70,83 @@ export default async function functionRoutes(server: FastifyInstance) { } }) - server.get("/functions/workingtimeevaluation/:profile_id", async (req, reply) => { + /** + * @route GET /functions/workingtimeevaluation/:user_id + * @query start_date=YYYY-MM-DD + * @query end_date=YYYY-MM-DD + */ + server.get("/functions/timeevaluation/:user_id", async (req, reply) => { + const { user_id } = req.params as { user_id: string } + const { start_date, end_date } = req.query as { start_date: string; end_date: string } + const { tenant_id } = req.user - const { profile_id } = req.params as { profile_id: string }; - const { start_date, end_date } = req.query as { start_date: string, end_date: string }; - - - async function generateWorkingTimesEvaluationValues(profile_id,startDateInput,endDateInput) { - - console.log(dayjs(startDateInput)) - console.log(dayjs(endDateInput)) - - let startDate = dayjs(startDateInput) - let endDate = dayjs(endDateInput) - //console.log(startDate) - //console.log(endDate) - - const {data:profile} = await server.supabase.from("auth_profiles").select().eq("old_profile_id",profile_id).single() - - console.log(profile) - - let {data:times,error:timesError} = await server.supabase.from("workingtimes").select().eq("profile", profile.old_profile_id).order("startDate",{ascending: false}) - const {data:absencerequests, error: timesAbsenceRequestsError} = await server.supabase.from("absencerequests").select().eq("profile",profile.old_profile_id).order("startDate",{ascending: false}) - - times = times.filter(i => dayjs(i.startDate).isSameOrAfter(startDate) && dayjs(i.endDate).subtract(1,"day").isSameOrBefore(endDate)) - - console.log(times) - - let weekFactor = 4.33 - //let monthlyWorkingMinutes = profile.weeklyWorkingHours * weekFactor * 60 - - //Get Every Day to Calc - let days = [] - let minuteSum = 0 - let dayCount = dayjs(endDate).add(1,"day").diff(startDate,"days") - for (let count = 0; count < dayCount; count++) { - let date = dayjs(startDate).add(count,"days") - minuteSum += (profile.weekly_regular_working_hours[date.day()] || 0)*60 - } - let timeSpanWorkingMinutes = minuteSum - - - - - let workingMinutesTarget = Math.ceil(Number((Number(dayjs(endDate).add(1,"days").diff(dayjs(startDate),'month',true).toFixed(2)) * 4.33 * profile.weeklyWorkingHours * 60).toFixed(2))) - - //Eingreicht - let sumWorkingMinutesEingereicht = 0 - times.forEach(time => { - const minutes = dayjs(time.endDate).diff(dayjs(time.startDate),'minutes') - sumWorkingMinutesEingereicht = sumWorkingMinutesEingereicht + minutes - }) - - //Bestätigt - let sumWorkingMinutesApproved = 0 - times.filter(i => i.approved).forEach(time => { - const minutes = dayjs(time.endDate).diff(dayjs(time.startDate),'minutes') - sumWorkingMinutesApproved = sumWorkingMinutesApproved + minutes - }) - - let recreationDays = ["2025-01-01","2025-04-18","2025-04-21","2025-05-01","2025-05-29","2025-06-09","2024-10-03","2024-10-31","2024-12-25","2024-12-26"] - - //Feiertagsausgleich - let sumWorkingMinutesRecreationDays = 0 - let sumRecreationDays = 0 - if(profile.recreationDaysCompensation) { - recreationDays.filter(i => dayjs(i).isSameOrAfter(startDate) && dayjs(i).isSameOrBefore(endDate)).forEach(day => { - let compensationTime = profile.weekly_regular_working_hours[dayjs(day).day()] || 0 - sumWorkingMinutesRecreationDays += compensationTime * 60 - sumRecreationDays++ - }) - } - - - let isBetween = (date,start,end) => { - return dayjs(date).isSameOrAfter(start) && dayjs(date).isSameOrBefore(end) - } - - //Urlaubsausgleich - let sumWorkingMinutesVacationDays = 0 - let sumVacationDays = 0 - - absencerequests.filter(i => (dayjs(i.startDate).isBetween(dayjs(startDate),dayjs(endDate),"day","[]") || dayjs(i.endDate).isBetween(dayjs(startDate),dayjs(endDate),"day","[]")) && (i.reason === "Urlaub" || i.reason === "Berufsschule") && i.approved === "Genehmigt").forEach(absenceRequest => { - let durationInDays = 0 - - let durationStartDate = null - - if(isBetween(absenceRequest.startDate,startDate,endDate) && isBetween(absenceRequest.endDate,startDate,endDate)) { - //Full in Range - durationInDays = dayjs(absenceRequest.endDate).diff(absenceRequest.startDate, "days") + 1 - durationStartDate = absenceRequest.startDate - } else if(isBetween(absenceRequest.startDate,startDate,endDate) && !isBetween(absenceRequest.endDate,startDate,endDate)) { - //Start in Range - durationInDays = dayjs(endDate).diff(absenceRequest.startDate, "days") + 1 - durationStartDate = absenceRequest.startDate - } else if(!isBetween(absenceRequest.startDate,startDate,endDate) && isBetween(absenceRequest.endDate,startDate,endDate)) { - //End in Range - durationInDays = dayjs(absenceRequest.endDate).diff(startDate, "days") + 1 - durationStartDate = startDate - - } - - let minuteSum = 0 - - for (let count = 0; count < durationInDays; count++) { - let date = dayjs(durationStartDate).add(count,"days") - minuteSum += (profile.weekly_regular_working_hours[date.day()] || 0)*60 - } - sumVacationDays += durationInDays - sumWorkingMinutesVacationDays += minuteSum - }) - - //Krankheitsausgleich - let sumWorkingMinutesSickDays = 0 - let sumSickDays = 0 - - absencerequests.filter(i => (dayjs(i.startDate).isBetween(dayjs(startDate),dayjs(endDate)) || dayjs(i.endDate).isBetween(dayjs(startDate),dayjs(endDate)) ) && (i.reason === "Krankheit" || i.reason === "Krankheit ab 1 Tag (mit Attest)" || i.reason === "Krankheit ab 2. Tag (mit Attest)") && i.approved === "Genehmigt").forEach(absenceRequest => { - let durationInDays = 0 - - let durationStartDate = null - - if(isBetween(absenceRequest.startDate,startDate,endDate) && isBetween(absenceRequest.endDate,startDate,endDate)) { - //Full in Range - console.log("FULL") - durationInDays = dayjs(absenceRequest.endDate).diff(absenceRequest.startDate, "days") + 1 - durationStartDate = absenceRequest.startDate - } else if(isBetween(absenceRequest.startDate,startDate,endDate) && !isBetween(absenceRequest.endDate,startDate,endDate)) { - //Start in Range - console.log("Start") - durationInDays = dayjs(endDate).diff(absenceRequest.startDate, "days") + 1 - durationStartDate = absenceRequest.startDate - } else if(!isBetween(absenceRequest.startDate,startDate,endDate) && isBetween(absenceRequest.endDate,startDate,endDate)) { - //End in Range - console.log("End") - durationInDays = dayjs(absenceRequest.endDate).diff(startDate, "days") + 1 - durationStartDate = startDate - - } - - let minuteSum = 0 - - for (let count = 0; count < durationInDays; count++) { - let date = dayjs(durationStartDate).add(count,"days") - minuteSum += (profile.weekly_regular_working_hours[date.day()] || 0)*60 - } - - sumSickDays += durationInDays - sumWorkingMinutesSickDays += minuteSum - }) - - //Saldo - let saldo = (sumWorkingMinutesApproved + sumWorkingMinutesRecreationDays + sumWorkingMinutesVacationDays + sumWorkingMinutesSickDays - timeSpanWorkingMinutes).toFixed(2) - let saldoInOfficial = (sumWorkingMinutesEingereicht + sumWorkingMinutesRecreationDays + sumWorkingMinutesVacationDays + sumWorkingMinutesSickDays - timeSpanWorkingMinutes).toFixed(2) - - return { - timeSpanWorkingMinutes, - workingMinutesTarget, - sumWorkingMinutesEingereicht, - sumWorkingMinutesApproved, - sumWorkingMinutesRecreationDays, - sumRecreationDays, - sumWorkingMinutesVacationDays, - sumVacationDays, - sumWorkingMinutesSickDays, - sumSickDays, - saldo, - saldoInOfficial, - times - } + // 🔒 Sicherheitscheck: andere User nur bei Berechtigung + if (user_id !== req.user.user_id && !req.hasPermission("staff.time.read_all")) { + return reply.code(403).send({ error: "Not allowed to view other users." }) } - - try { - reply.send(await generateWorkingTimesEvaluationValues(profile_id,start_date,end_date)) - } catch(error) { - console.log(error) + const result = await generateTimesEvaluation(server, user_id, tenant_id, start_date, end_date) + reply.send(result) + } catch (error) { + console.error(error) + reply.code(500).send({ error: error.message }) + } + }) + + server.get('/functions/check-zip/:zip', async (req, reply) => { + const { zip } = req.params as { zip: string } + + if (!zip) { + return reply.code(400).send({ error: 'ZIP is required' }) } + try { + 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' }) + } + + //districtMap + const bundeslaender = [ + { code: 'DE-BW', name: 'Baden-Württemberg' }, + { code: 'DE-BY', name: 'Bayern' }, + { code: 'DE-BE', name: 'Berlin' }, + { code: 'DE-BB', name: 'Brandenburg' }, + { code: 'DE-HB', name: 'Bremen' }, + { code: 'DE-HH', name: 'Hamburg' }, + { code: 'DE-HE', name: 'Hessen' }, + { code: 'DE-MV', name: 'Mecklenburg-Vorpommern' }, + { code: 'DE-NI', name: 'Niedersachsen' }, + { code: 'DE-NW', name: 'Nordrhein-Westfalen' }, + { code: 'DE-RP', name: 'Rheinland-Pfalz' }, + { code: 'DE-SL', name: 'Saarland' }, + { code: 'DE-SN', name: 'Sachsen' }, + { code: 'DE-ST', name: 'Sachsen-Anhalt' }, + { code: 'DE-SH', name: 'Schleswig-Holstein' }, + { code: 'DE-TH', name: 'Thüringen' } + ] + + return reply.send({ + ...data, + state_code: bundeslaender.find(i => i.name === data.countryName) + }) + } catch (err) { + console.log(err) + return reply.code(500).send({ error: 'Internal server error' }) + } }) } \ No newline at end of file diff --git a/src/utils/pdf.ts b/src/utils/pdf.ts index 904bbbe..c5eae47 100644 --- a/src/utils/pdf.ts +++ b/src/utils/pdf.ts @@ -23,13 +23,27 @@ const getCoordinatesForPDFLib = (x:number ,y:number, page:any) => { } +const getBackgroundSourceBuffer = async (server:FastifyInstance, path:string) => { + const {data:backgroundPDFData,error:backgroundPDFError} = await server.supabase.storage.from("files").download(path) + + return backgroundPDFData.arrayBuffer() +} + +const getDuration = (time) => { + const minutes = Math.floor(dayjs(time.stopped_at).diff(dayjs(time.started_at),'minutes',true)) + const hours = Math.floor(minutes/60) + return { + //dezimal: dez, + hours: hours, + minutes: minutes, + composed: `${hours}:${String(minutes % 60).padStart(2,"0")} Std` + } +} export const createInvoicePDF = async (server:FastifyInstance, returnMode, invoiceData, backgroundPath:string) => { - console.log(returnMode, invoiceData, backgroundPath) - const genPDF = async (invoiceData, backgroundSourceBuffer) => { const pdfDoc = await PDFDocument.create() @@ -841,14 +855,216 @@ export const createInvoicePDF = async (server:FastifyInstance, returnMode, invoi } - const {data:backgroundPDFData,error:backgroundPDFError} = await server.supabase.storage.from("files").download(backgroundPath) - - const pdfBytes = await genPDF(invoiceData, await backgroundPDFData.arrayBuffer()) + const pdfBytes = await genPDF(invoiceData, await getBackgroundSourceBuffer(server,backgroundPath)) if(returnMode === "base64"){ return { mimeType: 'application/pdf', base64: pdfBytes } + } else { + return null } +} + +export const createTimeSheetPDF = async (server: FastifyInstance, returnMode, data, backgroundPath: string) => { + + const genPDF = async (input, backgroundSourceBuffer) => { + const pdfDoc = await PDFDocument.create() + + const font = await pdfDoc.embedFont(StandardFonts.Helvetica) + const fontBold = await pdfDoc.embedFont(StandardFonts.HelveticaBold) + + let pages = [] + let pageCounter = 1 + + + + const backgroudPdf = await PDFDocument.load(backgroundSourceBuffer) + + const firstPageBackground = await pdfDoc.embedPage(backgroudPdf.getPages()[0]) + const secondPageBackground = await pdfDoc.embedPage(backgroudPdf.getPages()[backgroudPdf.getPages().length > 1 ? 1 : 0]) + + const page1 = pdfDoc.addPage() + + page1.drawPage(firstPageBackground, { + x: 0, + y: 0, + }) + + pages.push(page1) + + + //Falzmarke 1 + /*pages[pageCounter - 1].drawLine({ + start: getCoordinatesForPDFLib(0,105,page1), + end: getCoordinatesForPDFLib(7,105,page1), + thickness: 0.25, + color: rgb(0,0,0), + opacity: 1 + })*/ + + //Lochmarke + /*pages[pageCounter - 1].drawLine({ + start: getCoordinatesForPDFLib(0,148.5,page1), + end: getCoordinatesForPDFLib(7,148.5,page1), + thickness: 0.25, + color: rgb(0,0,0), + opacity: 1 + })*/ + + //Falzmarke 2 + /*pages[pageCounter - 1].drawLine({ + start: getCoordinatesForPDFLib(0,210,page1), + end: getCoordinatesForPDFLib(7,210,page1), + thickness: 0.25, + color: rgb(0,0,0), + opacity: 1 + })*/ + console.log(input) + pages[pageCounter - 1].drawText(`Mitarbeiter: ${input.full_name}`,{ + x: getCoordinatesForPDFLib(20,55,pages[pageCounter -1]).x, + y: getCoordinatesForPDFLib(20,55,pages[pageCounter -1]).y, + size: 10, + }) + pages[pageCounter - 1].drawText(`Eingereicht: ${Math.floor(input.sumWorkingMinutesEingereicht/60)}:${String(input.sumWorkingMinutesEingereicht % 60).padStart(2,"0")} Std`,{ + x: getCoordinatesForPDFLib(20,60,pages[pageCounter -1]).x, + y: getCoordinatesForPDFLib(20,60,pages[pageCounter -1]).y, + size: 10, + }) + pages[pageCounter - 1].drawText(`Genehmigt: ${Math.floor(input.sumWorkingMinutesApproved/60)}:${String(input.sumWorkingMinutesApproved % 60).padStart(2,"0")} Std`,{ + x: getCoordinatesForPDFLib(20,65,pages[pageCounter -1]).x, + y: getCoordinatesForPDFLib(20,65,pages[pageCounter -1]).y, + size: 10, + }) + + pages[pageCounter - 1].drawText(`Feiertagsausgleich: ${Math.floor(input.sumWorkingMinutesRecreationDays/60)}:${String(input.sumWorkingMinutesRecreationDays % 60).padStart(2,"0")} Std`,{ + x: getCoordinatesForPDFLib(20,70,pages[pageCounter -1]).x, + y: getCoordinatesForPDFLib(20,70,pages[pageCounter -1]).y, + size: 10, + }) + pages[pageCounter - 1].drawText(`Urlaubsausgleich: ${Math.floor(input.sumWorkingMinutesVacationDays/60)}:${String(input.sumWorkingMinutesVacationDays % 60).padStart(2,"0")} Std`,{ + x: getCoordinatesForPDFLib(20,75,pages[pageCounter -1]).x, + y: getCoordinatesForPDFLib(20,75,pages[pageCounter -1]).y, + size: 10, + }) + pages[pageCounter - 1].drawText(`Krankheitsausgleich: ${Math.floor(input.sumWorkingMinutesSickDays/60)}:${String(input.sumWorkingMinutesSickDays % 60).padStart(2,"0")} Std`,{ + x: getCoordinatesForPDFLib(20,80,pages[pageCounter -1]).x, + y: getCoordinatesForPDFLib(20,80,pages[pageCounter -1]).y, + size: 10, + }) + pages[pageCounter - 1].drawText(`Soll Stunden: ${Math.floor(input.timeSpanWorkingMinutes/60)}:${Math.floor(Number(String(input.timeSpanWorkingMinutes % 60).padStart(2,"0")))} Std`,{ + x: getCoordinatesForPDFLib(20,85,pages[pageCounter -1]).x, + y: getCoordinatesForPDFLib(20,85,pages[pageCounter -1]).y, + size: 10, + }) + pages[pageCounter - 1].drawText(`Inoffizielles Saldo: ${Math.sign(input.saldoInOfficial) === 1 ? "+" : "-"} ${Math.floor(Math.abs(input.saldoInOfficial/60))}:${Math.floor(Number(String(Math.abs(input.saldoInOfficial) % 60).padStart(2,"0")))} Std`,{ + x: getCoordinatesForPDFLib(20,90,pages[pageCounter -1]).x, + y: getCoordinatesForPDFLib(20,90,pages[pageCounter -1]).y, + size: 10, + }) + pages[pageCounter - 1].drawText(`Saldo: ${Math.sign(input.saldo) === 1 ? "+" : "-"} ${Math.floor(Math.abs(input.saldo/60))}:${Math.floor(Number(String(Math.abs(input.saldo) % 60).padStart(2,"0")))} Std`,{ + x: getCoordinatesForPDFLib(20,95,pages[pageCounter -1]).x, + y: getCoordinatesForPDFLib(20,95,pages[pageCounter -1]).y, + size: 10, + }) + + pages[pageCounter - 1].drawText(`Start:`,{ + x: getCoordinatesForPDFLib(20,100,pages[pageCounter -1]).x, + y: getCoordinatesForPDFLib(20,100,pages[pageCounter -1]).y, + size: 10, + }) + + pages[pageCounter - 1].drawText(`Ende:`,{ + x: getCoordinatesForPDFLib(60,100,pages[pageCounter -1]).x, + y: getCoordinatesForPDFLib(60,100,pages[pageCounter -1]).y, + size: 10, + }) + + pages[pageCounter - 1].drawText(`Dauer:`,{ + x: getCoordinatesForPDFLib(100,100,pages[pageCounter -1]).x, + y: getCoordinatesForPDFLib(100,100,pages[pageCounter -1]).y, + size: 10, + }) + + + let rowHeight = 115 + + + let splitted = [] + + let reversedInput = input.times.slice().reverse() + + const splittedLength = Math.floor((reversedInput.length - 25) / 40) + + splitted.push(reversedInput.slice(0,25)) + + let lastIndex = 25 + for (let i = 0; i < splittedLength; ++i ) { + splitted.push(reversedInput.slice(lastIndex, lastIndex + (i + 1) * 40)) + lastIndex = lastIndex + (i + 1) * 40 + 1 + } + + splitted.push(reversedInput.slice(lastIndex, reversedInput.length)) + + + splitted.forEach((chunk,index) => { + if(index > 0) { + const page = pdfDoc.addPage() + + page.drawPage(secondPageBackground, { + x: 0, + y: 0, + }) + + pages.push(page) + pageCounter++ + rowHeight = 20 + + } + + chunk.forEach(time => { + pages[pageCounter - 1].drawText(`${dayjs(time.started_at).format("HH:mm DD.MM.YY")}`,{ + x: getCoordinatesForPDFLib(20,rowHeight,pages[pageCounter -1]).x, + y: getCoordinatesForPDFLib(20,rowHeight,pages[pageCounter -1]).y, + size: 10, + }) + + pages[pageCounter - 1].drawText(`${dayjs(time.stopped_at).format("HH:mm DD.MM.YY")}`,{ + x: getCoordinatesForPDFLib(60,rowHeight,pages[pageCounter -1]).x, + y: getCoordinatesForPDFLib(60,rowHeight,pages[pageCounter -1]).y, + size: 10, + }) + + pages[pageCounter - 1].drawText(`${getDuration(time).composed}`,{ + x: getCoordinatesForPDFLib(100,rowHeight,pages[pageCounter -1]).x, + y: getCoordinatesForPDFLib(100,rowHeight,pages[pageCounter -1]).y, + size: 10, + }) + + rowHeight += 6 + + }) + }) + + return await pdfDoc.saveAsBase64() + } + + try { + const pdfBytes = await genPDF(data, await getBackgroundSourceBuffer(server,backgroundPath)) + + if(returnMode === "base64"){ + return { + mimeType: 'application/pdf', + base64: pdfBytes + } + } else { + return "test" + } + } catch(error) { + console.log(error) + } + + + } \ No newline at end of file From ee8e17d80eb2f214fbb448c216be501adfcd01c1 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sat, 8 Nov 2025 19:00:46 +0100 Subject: [PATCH 066/149] Index Registers --- src/index.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/index.ts b/src/index.ts index 8ea9848..6e8a3ea 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,7 @@ import Fastify from "fastify"; import swaggerPlugin from "./plugins/swagger" import supabasePlugin from "./plugins/supabase"; +import dayjsPlugin from "./plugins/dayjs"; import healthRoutes from "./routes/health"; import meRoutes from "./routes/auth/me"; import tenantRoutes from "./routes/tenant"; @@ -25,6 +26,8 @@ import authProfilesRoutes from "./routes/profiles"; import helpdeskRoutes from "./routes/helpdesk"; import helpdeskInboundRoutes from "./routes/helpdesk.inbound"; import notificationsRoutes from "./routes/notifications"; +import staffTimeRoutes from "./routes/staff/time"; +import staffTimeConnectRoutes from "./routes/staff/timeconnects"; //M2M import authM2m from "./plugins/auth.m2m"; @@ -51,8 +54,10 @@ async function main() { await app.register(corsPlugin); await app.register(supabasePlugin); await app.register(tenantPlugin); + await app.register(dayjsPlugin); app.addHook('preHandler', (req, reply, done) => { + console.log(req.method) console.log('Matched path:', req.routeOptions.url) done() }) @@ -98,6 +103,8 @@ async function main() { await subApp.register(authProfilesRoutes); await subApp.register(helpdeskRoutes); await subApp.register(notificationsRoutes); + await subApp.register(staffTimeRoutes); + await subApp.register(staffTimeConnectRoutes); },{prefix: "/api"}) From d7939a9ca48f42cde905a46c695748e0810b523f Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sat, 8 Nov 2025 19:01:22 +0100 Subject: [PATCH 067/149] New Staff Functions --- src/routes/profiles.ts | 71 ++++++++-------- src/routes/staff/time.ts | 135 +++++++++++++++++++++++++++++++ src/routes/staff/timeconnects.ts | 71 ++++++++++++++++ src/types/staff.ts | 27 +++++++ 4 files changed, 267 insertions(+), 37 deletions(-) create mode 100644 src/routes/staff/time.ts create mode 100644 src/routes/staff/timeconnects.ts create mode 100644 src/types/staff.ts diff --git a/src/routes/profiles.ts b/src/routes/profiles.ts index 021ef6b..bb535fb 100644 --- a/src/routes/profiles.ts +++ b/src/routes/profiles.ts @@ -2,36 +2,8 @@ import { FastifyInstance } from "fastify"; export default async function authProfilesRoutes(server: FastifyInstance) { // Ein einzelnes Profil laden (nur im aktuellen Tenant) - server.get<{ - Params: { id: string } - }>("/api/profiles/:id", { - schema: { - tags: ["Auth"], - summary: "Get a profile by user id (only from current tenant)", - params: { - type: "object", - properties: { - id: { type: "string", format: "uuid" } - }, - required: ["id"] - }, - response: { - 200: { - type: "object", - properties: { - id: { type: "string" }, - firstName: { type: "string" }, - lastName: { type: "string" }, - email: { type: "string", nullable: true }, - mobileTel: { type: "string", nullable: true }, - fixedTel: { type: "string", nullable: true }, - role: { type: "string", nullable: true } - } - } - } - } - }, async (req, reply) => { - const { id } = req.params; + server.get("/profiles/:id", async (req, reply) => { + const { id } = req.params as {id:string}; const tenantId = (req.user as any)?.tenant_id; if (!tenantId) { @@ -39,19 +11,44 @@ export default async function authProfilesRoutes(server: FastifyInstance) { } const { data, error } = await server.supabase - .from("auth_users") - .select("*, profile(*)") + .from("auth_profiles") + .select() .eq("id", id) - .eq("tenant", tenantId) + .eq("tenant_id", tenantId) .single(); if (error || !data) { + console.log(error) return reply.code(404).send({ error: "User not found or not in tenant" }); } - return { - user_id: data.id, - ...data.profile - }; + console.log(data); + + reply.send(data) }); + + server.put("/profiles/:id", async (req, reply) => { + if (!req.user.tenant_id) { + return reply.code(400).send({ error: "No tenant selected" }); + } + + const { id } = req.params as { id: string }; + const body = req.body + + delete body.full_name + + + const { data, error } = await server.supabase + .from("auth_profiles") + .update(body) + .eq("id", id) + .eq("tenant_id", req.user.tenant_id) + .select("*") + .single(); + + if (error || !data) { + console.log(error) + return reply.code(404).send({ error: "User not found or not in tenant" }); + } + }) } \ No newline at end of file diff --git a/src/routes/staff/time.ts b/src/routes/staff/time.ts new file mode 100644 index 0000000..4d437dd --- /dev/null +++ b/src/routes/staff/time.ts @@ -0,0 +1,135 @@ +import { FastifyInstance } from 'fastify' +import { StaffTimeEntry } from '../../types/staff' + +export default async function staffTimeRoutes(server: FastifyInstance) { + + // ▶ Neue Zeit starten + server.post<{ Body: Pick }>( + '/staff/time', + async (req, reply) => { + const { started_at, stopped_at, type = 'work', description } = req.body + const userId = req.user.user_id + const tenantId = req.user.tenant_id + + const { data, error } = await server.supabase + .from('staff_time_entries') + .insert([{ tenant_id: tenantId, user_id: userId, started_at, stopped_at, type, description }]) + .select() + .maybeSingle() + + if (error) return reply.code(400).send({ error: error.message }) + return reply.send(data) + } + ) + + // ▶ Zeit stoppen + server.put<{ Params: { id: string }, Body: { stopped_at: string } }>( + '/staff/time/:id/stop', + async (req, reply) => { + const { id } = req.params + const { stopped_at } = req.body + + const { data, error } = await server.supabase + .from('staff_time_entries') + .update({ stopped_at, updated_at: new Date().toISOString() }) + .eq('id', id) + .select() + .maybeSingle() + + if (error) return reply.code(400).send({ error: error.message }) + return reply.send(data) + } + ) + + // ▶ Liste aller Zeiten + server.get<{ + Querystring: { + from?: string + to?: string + type?: string + user_id?: string + } + }>('/staff/time', async (req, reply) => { + const { from, to, type, user_id } = req.query + const { user_id: currentUserId, tenant_id } = req.user + + // 🧩 Basis-Query für den Tenant + let query = server.supabase + .from('staff_time_entries') + .select('*') + .eq('tenant_id', tenant_id) + .order('started_at', { ascending: false }) + + // 🔒 Zugriffsbeschränkung: nur eigene Zeiten, außer Berechtigung erlaubt mehr + if (!req.hasPermission('staff.time.read_all')) { + query = query.eq('user_id', currentUserId) + } else if (user_id) { + // falls explizit user_id angegeben wurde + query = query.eq('user_id', user_id) + } + + // 📅 Zeitfilter + if (from) query = query.gte('started_at', from) + if (to) query = query.lte('started_at', to) + if (type) query = query.eq('type', type) + + const { data, error } = await query + if (error) return reply.code(400).send({ error: error.message }) + + return reply.send(data) + }) + + + // ▶ Einzelne Zeit abrufen (inkl. Connects) + server.get<{ Params: { id: string } }>( + '/staff/time/:id', + async (req, reply) => { + const { id } = req.params + + const { data, error } = await server.supabase + .from('staff_time_entries') + .select(` + *, + staff_time_entry_connects(*) + `) + .eq('id', id) + .maybeSingle() + + if (error) return reply.code(400).send({ error: error.message }) + return reply.send(data) + } + ) + + // ▶ Zeit bearbeiten + server.put<{ Params: { id: string }, Body: Partial }>( + '/staff/time/:id', + async (req, reply) => { + const { id } = req.params + + const { data, error } = await server.supabase + .from('staff_time_entries') + .update({ ...req.body, updated_at: new Date().toISOString() }) + .eq('id', id) + .select() + .maybeSingle() + + if (error) return reply.code(400).send({ error: error.message }) + return reply.send(data) + } + ) + + // ▶ Zeit löschen + server.delete<{ Params: { id: string } }>( + '/staff/time/:id', + async (req, reply) => { + const { id } = req.params + const { error } = await server.supabase + .from('staff_time_entries') + .delete() + .eq('id', id) + + if (error) return reply.code(400).send({ error: error.message }) + return reply.send({ success: true }) + } + ) +} diff --git a/src/routes/staff/timeconnects.ts b/src/routes/staff/timeconnects.ts new file mode 100644 index 0000000..288e341 --- /dev/null +++ b/src/routes/staff/timeconnects.ts @@ -0,0 +1,71 @@ +import { FastifyInstance } from 'fastify' +import { StaffTimeEntryConnect } from '../../types/staff' + +export default async function staffTimeConnectRoutes(server: FastifyInstance) { + + // ▶ Connect anlegen + server.post<{ Params: { id: string }, Body: Omit }>( + '/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 { 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() + + if (error) return reply.code(400).send({ error: error.message }) + return reply.send(data) + } + ) + + // ▶ Connects abrufen + server.get<{ Params: { id: string } }>( + '/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 }) + + if (error) return reply.code(400).send({ error: error.message }) + return reply.send(data) + } + ) + + // ▶ Connect aktualisieren + server.patch<{ Params: { connectId: string }, Body: Partial }>( + '/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() + + if (error) return reply.code(400).send({ error: error.message }) + return reply.send(data) + } + ) + + // ▶ Connect löschen + server.delete<{ Params: { connectId: string } }>( + '/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) + + if (error) return reply.code(400).send({ error: error.message }) + return reply.send({ success: true }) + } + ) +} diff --git a/src/types/staff.ts b/src/types/staff.ts new file mode 100644 index 0000000..b16ae42 --- /dev/null +++ b/src/types/staff.ts @@ -0,0 +1,27 @@ +export interface StaffTimeEntry { + id: string + tenant_id: string + user_id: string + started_at: string + stopped_at?: string | null + duration_minutes?: number | null + type: 'work' | 'break' | 'absence' | 'other' + description?: string | null + created_at?: string + updated_at?: string +} + +export interface StaffTimeEntryConnect { + id: string + time_entry_id: string + project_id?: string | null + customer_id?: string | null + task_id?: string | null + ticket_id?: string | null + started_at: string + stopped_at: string + duration_minutes?: number + notes?: string | null + created_at?: string + updated_at?: string +} From 8b34609e532154b1c663eac41ffed32dbabdbf0c Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sat, 8 Nov 2025 19:01:52 +0100 Subject: [PATCH 068/149] Added GoCardless Secrets --- src/utils/secrets.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/utils/secrets.ts b/src/utils/secrets.ts index 0107b28..1d384c2 100644 --- a/src/utils/secrets.ts +++ b/src/utils/secrets.ts @@ -29,6 +29,9 @@ export let secrets = { S3_SECRET_KEY: string M2M_API_KEY: string API_BASE_URL: string + GOCARDLESS_BASE_URL: string + GOCARDLESS_SECRET_ID: string + GOCARDLESS_SECRET_KEY: string } export async function loadSecrets () { @@ -46,7 +49,6 @@ export async function loadSecrets () { allSecrets.secrets.forEach(secret => { secrets[secret.secretKey] = secret.secretValue }) - console.log("✅ Secrets aus Infisical geladen"); console.log(Object.keys(secrets).length + " Stück") } From 75e2f8472626a9e3f832e57424abb0996f962c56 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sat, 8 Nov 2025 19:02:08 +0100 Subject: [PATCH 069/149] Redone Auth --- src/plugins/auth.ts | 96 +++++++++++++++++++++++++++++++-------------- 1 file changed, 66 insertions(+), 30 deletions(-) diff --git a/src/plugins/auth.ts b/src/plugins/auth.ts index fc2642c..5de9635 100644 --- a/src/plugins/auth.ts +++ b/src/plugins/auth.ts @@ -1,55 +1,91 @@ import { FastifyInstance } from "fastify"; import fp from "fastify-plugin"; import jwt from "jsonwebtoken"; -import {secrets} from "../utils/secrets"; +import { secrets } from "../utils/secrets.js"; export default fp(async (server: FastifyInstance) => { server.addHook("preHandler", async (req, reply) => { + // 1️⃣ Token holen (Header oder Cookie) + const cookieToken = req.cookies?.token; + const authHeader = req.headers.authorization; + const headerToken = authHeader?.startsWith("Bearer ") + ? authHeader.slice(7) + : null; + + const token = + headerToken && headerToken.length > 10 ? headerToken : cookieToken || null; + + if (!token) { + return reply.code(401).send({ error: "Authentication required" }); + } try { - // 1) Token aus Cookie lesen - const cookieToken = req.cookies?.token - - // 2) Token aus Header lesen (falls Cookie nicht da ist) - const authHeader = req.headers.authorization - const headerToken = authHeader?.startsWith("Bearer ") - ? authHeader.slice(7) - : null - - let token = null - - if(headerToken !== null && headerToken.length > 10){ - token = headerToken - } else if(cookieToken ){ - token = cookieToken - } - - if (!token) { - return // keine Exception → Route darf z. B. public sein - } - - + // 2️⃣ JWT verifizieren const payload = jwt.verify(token, secrets.JWT_SECRET!) as { user_id: string; email: string; - tenant_id?: string; - role?: string; + tenant_id: number; }; - (req as any).user = payload; + if (!payload?.user_id || !payload.tenant_id) { + return reply.code(401).send({ error: "Invalid token" }); + } + + req.user = payload; + + // 3️⃣ Rolle des Nutzers im Tenant laden + const { data: roleData, error: roleError } = await server.supabase + .from("auth_user_roles") + .select("role_id") + .eq("user_id", payload.user_id) + .eq("tenant_id", payload.tenant_id) + .maybeSingle(); + + if (roleError) { + server.log.error("Error fetching user role", roleError); + return reply.code(500).send({ error: "Failed to load user role" }); + } + + if (!roleData) { + return reply.code(403).send({ error: "No role assigned for this tenant" }); + } + + const roleId = roleData.role_id; + + // 4️⃣ Berechtigungen der Rolle laden + const { data: permissions, error: permsError } = await server.supabase + .from("auth_role_permissions") + .select("permission") + .eq("role_id", roleId); + + if (permsError) { + server.log.error("Failed to load permissions", permsError); + return reply.code(500).send({ error: "Permission lookup failed" }); + } + + const perms = permissions?.map((p) => p.permission) ?? []; + + // 5️⃣ An Request hängen + req.role = roleId; + req.permissions = perms; + req.hasPermission = (perm: string) => perms.includes(perm); + } catch (err) { return reply.code(401).send({ error: "Invalid or expired token" }); } }); }); +// 🧩 Fastify Type Declarations declare module "fastify" { interface FastifyRequest { - user?: { + user: { user_id: string; email: string; - tenant_id?: number; - role?: string; + tenant_id: number; }; + role: string; + permissions: string[]; + hasPermission: (permission: string) => boolean; } -} \ No newline at end of file +} From 572d56413516385ad1c74936a0ffba874c83335c Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sun, 9 Nov 2025 16:12:18 +0100 Subject: [PATCH 070/149] TS Fixes --- src/plugins/auth.ts | 4 ++-- src/routes/profiles.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/auth.ts b/src/plugins/auth.ts index 5de9635..a62e761 100644 --- a/src/plugins/auth.ts +++ b/src/plugins/auth.ts @@ -42,7 +42,7 @@ export default fp(async (server: FastifyInstance) => { .maybeSingle(); if (roleError) { - server.log.error("Error fetching user role", roleError); + console.log("Error fetching user role", roleError); return reply.code(500).send({ error: "Failed to load user role" }); } @@ -59,7 +59,7 @@ export default fp(async (server: FastifyInstance) => { .eq("role_id", roleId); if (permsError) { - server.log.error("Failed to load permissions", permsError); + console.log("Failed to load permissions", permsError); return reply.code(500).send({ error: "Permission lookup failed" }); } diff --git a/src/routes/profiles.ts b/src/routes/profiles.ts index bb535fb..17e9158 100644 --- a/src/routes/profiles.ts +++ b/src/routes/profiles.ts @@ -33,7 +33,7 @@ export default async function authProfilesRoutes(server: FastifyInstance) { } const { id } = req.params as { id: string }; - const body = req.body + const body = req.body as any delete body.full_name From 29cfaccd02b644ec4580db2890b8388b45bd5545 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sun, 9 Nov 2025 18:47:30 +0100 Subject: [PATCH 071/149] Fixes --- src/plugins/auth.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/plugins/auth.ts b/src/plugins/auth.ts index a62e761..551c3a0 100644 --- a/src/plugins/auth.ts +++ b/src/plugins/auth.ts @@ -1,7 +1,7 @@ import { FastifyInstance } from "fastify"; import fp from "fastify-plugin"; import jwt from "jsonwebtoken"; -import { secrets } from "../utils/secrets.js"; +import { secrets } from "../utils/secrets"; export default fp(async (server: FastifyInstance) => { server.addHook("preHandler", async (req, reply) => { @@ -15,6 +15,16 @@ export default fp(async (server: FastifyInstance) => { const token = headerToken && headerToken.length > 10 ? headerToken : cookieToken || null; + + + /*let token = null + + if(headerToken !== null && headerToken.length > 10){ + token = headerToken + } else if(cookieToken ){ + token = cookieToken + }*/ + if (!token) { return reply.code(401).send({ error: "Authentication required" }); } @@ -27,7 +37,10 @@ export default fp(async (server: FastifyInstance) => { tenant_id: number; }; + console.log("payload", payload); + if (!payload?.user_id || !payload.tenant_id) { + console.log(payload) return reply.code(401).send({ error: "Invalid token" }); } From fc3ed1fb11fff7915488448a1f77bef8a519d3b2 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sun, 9 Nov 2025 18:49:30 +0100 Subject: [PATCH 072/149] Fixed Tenant Error --- src/plugins/auth.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/plugins/auth.ts b/src/plugins/auth.ts index 551c3a0..cc86b1a 100644 --- a/src/plugins/auth.ts +++ b/src/plugins/auth.ts @@ -39,8 +39,7 @@ export default fp(async (server: FastifyInstance) => { console.log("payload", payload); - if (!payload?.user_id || !payload.tenant_id) { - console.log(payload) + if (!payload?.user_id) { return reply.code(401).send({ error: "Invalid token" }); } From 2382b2dfaecd3e2b9f3c3fb1539d843533099b67 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sun, 9 Nov 2025 18:52:44 +0100 Subject: [PATCH 073/149] Fixed Role Loading --- src/plugins/auth.ts | 72 ++++++++++++++++++++++----------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/src/plugins/auth.ts b/src/plugins/auth.ts index cc86b1a..bf9648c 100644 --- a/src/plugins/auth.ts +++ b/src/plugins/auth.ts @@ -37,51 +37,51 @@ export default fp(async (server: FastifyInstance) => { tenant_id: number; }; - console.log("payload", payload); - if (!payload?.user_id) { return reply.code(401).send({ error: "Invalid token" }); } req.user = payload; - // 3️⃣ Rolle des Nutzers im Tenant laden - const { data: roleData, error: roleError } = await server.supabase - .from("auth_user_roles") - .select("role_id") - .eq("user_id", payload.user_id) - .eq("tenant_id", payload.tenant_id) - .maybeSingle(); + if(req.user.tenant_id) { + // 3️⃣ Rolle des Nutzers im Tenant laden + const { data: roleData, error: roleError } = await server.supabase + .from("auth_user_roles") + .select("role_id") + .eq("user_id", payload.user_id) + .eq("tenant_id", payload.tenant_id) + .maybeSingle(); - if (roleError) { - console.log("Error fetching user role", roleError); - return reply.code(500).send({ error: "Failed to load user role" }); + if (roleError) { + console.log("Error fetching user role", roleError); + return reply.code(500).send({ error: "Failed to load user role" }); + } + + if (!roleData) { + return reply.code(403).send({ error: "No role assigned for this tenant" }); + } + + const roleId = roleData.role_id; + + // 4️⃣ Berechtigungen der Rolle laden + const { data: permissions, error: permsError } = await server.supabase + .from("auth_role_permissions") + .select("permission") + .eq("role_id", roleId); + + if (permsError) { + console.log("Failed to load permissions", permsError); + return reply.code(500).send({ error: "Permission lookup failed" }); + } + + const perms = permissions?.map((p) => p.permission) ?? []; + + // 5️⃣ An Request hängen + req.role = roleId; + req.permissions = perms; + req.hasPermission = (perm: string) => perms.includes(perm); } - if (!roleData) { - return reply.code(403).send({ error: "No role assigned for this tenant" }); - } - - const roleId = roleData.role_id; - - // 4️⃣ Berechtigungen der Rolle laden - const { data: permissions, error: permsError } = await server.supabase - .from("auth_role_permissions") - .select("permission") - .eq("role_id", roleId); - - if (permsError) { - console.log("Failed to load permissions", permsError); - return reply.code(500).send({ error: "Permission lookup failed" }); - } - - const perms = permissions?.map((p) => p.permission) ?? []; - - // 5️⃣ An Request hängen - req.role = roleId; - req.permissions = perms; - req.hasPermission = (perm: string) => perms.includes(perm); - } catch (err) { return reply.code(401).send({ error: "Invalid or expired token" }); } From c1dc88cdd9558acf5e9787221f1760560a040157 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Mon, 10 Nov 2025 12:03:43 +0100 Subject: [PATCH 074/149] Fixed Search --- src/routes/resources.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/resources.ts b/src/routes/resources.ts index 09ee761..ee9d0e0 100644 --- a/src/routes/resources.ts +++ b/src/routes/resources.ts @@ -563,13 +563,13 @@ export default async function resourceRoutes(server: FastifyInstance) { if (searchCols.length === 0) { // Fallback: Durchsuche alle String-Felder der Hauptebene return Object.values(row).some(val => - val?.toString().toLowerCase().includes(searchTerm) + JSON.stringify(val).toString().toLowerCase().includes(searchTerm) ); } return searchCols.some(col => { const value = getNestedValue(row, col); - return value?.toString().toLowerCase().includes(searchTerm); + return JSON.stringify(value).toLowerCase().includes(searchTerm); }); }); From 8a88c6878ecb7b01915a149321c039004092cdc0 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Mon, 10 Nov 2025 13:50:30 +0100 Subject: [PATCH 075/149] Fixed RecreationDays Compensation --- src/modules/time/evaluation.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/time/evaluation.service.ts b/src/modules/time/evaluation.service.ts index 745711d..d2c500f 100644 --- a/src/modules/time/evaluation.service.ts +++ b/src/modules/time/evaluation.service.ts @@ -39,7 +39,7 @@ export async function generateTimesEvaluation( .select("date") .in("state_code", [profile.state_code, "DE"]) .gte("date", startDate.format("YYYY-MM-DD")) - .lte("date", endDate.format("YYYY-MM-DD")) + .lte("date", endDate.add(1,"day").format("YYYY-MM-DD")) if (holidaysError) throw new Error("Fehler beim Laden der Feiertage: " + holidaysError.message) @@ -70,7 +70,7 @@ export async function generateTimesEvaluation( let sumWorkingMinutesRecreationDays = 0 let sumRecreationDays = 0 - if (profile.recreationDaysCompensation && holidays?.length) { + if (profile.recreation_days_compensation && holidays?.length) { holidays.forEach(({ date }) => { const weekday = server.dayjs(date).day() const hours = profile.weekly_regular_working_hours?.[weekday] || 0 From 914c7e4fc1ed67e964b15f6ff7b7a9055cb3344a Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Mon, 10 Nov 2025 14:40:13 +0100 Subject: [PATCH 076/149] Fixed Dumb Error --- src/routes/functions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/functions.ts b/src/routes/functions.ts index bcd49f9..fc59339 100644 --- a/src/routes/functions.ts +++ b/src/routes/functions.ts @@ -50,7 +50,7 @@ export default async function functionRoutes(server: FastifyInstance) { console.log(pdf) - reply.send(pdf) // Fastify wandelt automatisch in JSON + return pdf // Fastify wandelt automatisch in JSON } catch (err) { console.log(err) reply.code(500).send({ error: "Failed to create PDF" }) From 48263b28b5888fa6f8cd0c06a1e00cfb971728cd Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Tue, 11 Nov 2025 08:36:34 +0100 Subject: [PATCH 077/149] Fixed Search Speed --- src/routes/resources.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/routes/resources.ts b/src/routes/resources.ts index ee9d0e0..b1a3358 100644 --- a/src/routes/resources.ts +++ b/src/routes/resources.ts @@ -560,7 +560,7 @@ export default async function resourceRoutes(server: FastifyInstance) { : dataTypes[resource].searchableColumns || []; const filteredData = (allData || []).filter(row => { - if (searchCols.length === 0) { + /*if (searchCols.length === 0) { // Fallback: Durchsuche alle String-Felder der Hauptebene return Object.values(row).some(val => JSON.stringify(val).toString().toLowerCase().includes(searchTerm) @@ -570,7 +570,10 @@ export default async function resourceRoutes(server: FastifyInstance) { return searchCols.some(col => { const value = getNestedValue(row, col); return JSON.stringify(value).toLowerCase().includes(searchTerm); - }); + });*/ + + return JSON.stringify(row).toLowerCase().includes(searchTerm); + }); // 3. Im Backend sortieren From f61c780185f6312faa1f115022bc5469fabaf64b Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Tue, 11 Nov 2025 08:52:00 +0100 Subject: [PATCH 078/149] Fixed not included last day --- src/modules/time/evaluation.service.ts | 16 +++++++++++++--- src/plugins/dayjs.ts | 1 + 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/modules/time/evaluation.service.ts b/src/modules/time/evaluation.service.ts index d2c500f..314e263 100644 --- a/src/modules/time/evaluation.service.ts +++ b/src/modules/time/evaluation.service.ts @@ -11,6 +11,9 @@ export async function generateTimesEvaluation( const startDate = server.dayjs(startDateInput) const endDate = server.dayjs(endDateInput) + console.log(startDate.format("YYYY-MM-DD HH:mm:ss")); + console.log(endDate.format("YYYY-MM-DD HH:mm:ss")); + // 🧾 Profil laden (Arbeitszeiten + Bundesland) const { data: profile, error: profileError } = await server.supabase .from("auth_profiles") @@ -22,17 +25,24 @@ export async function generateTimesEvaluation( if (profileError || !profile) throw new Error("Profil konnte nicht geladen werden.") // 🕒 Arbeitszeiten abrufen - const { data: times, error: timeError } = await server.supabase + const { data: timesRaw, error: timeError } = await server.supabase .from("staff_time_entries") .select("*") .eq("tenant_id", tenant_id) .eq("user_id", user_id) - .gte("started_at", startDate.toISOString()) - .lte("started_at", endDate.toISOString()) .order("started_at", { ascending: true }) if (timeError) throw new Error("Fehler beim Laden der Arbeitszeiten: " + timeError.message) + const isBetween = (spanStartDate,spanEndDate,startDate,endDate) => { + return server.dayjs(startDate).isBetween(spanStartDate, spanEndDate, "day", "[]") && server.dayjs(endDate).isBetween(spanStartDate, spanEndDate, "day", "[]") + } + + + const times = timesRaw.filter(i => isBetween(startDate,endDate,i.started_at,i.stopped_at) ) + + console.log(times) + // 📅 Feiertage aus Tabelle für Bundesland + DE const { data: holidays, error: holidaysError } = await server.supabase .from("holidays") diff --git a/src/plugins/dayjs.ts b/src/plugins/dayjs.ts index 3b721fa..4187d9e 100644 --- a/src/plugins/dayjs.ts +++ b/src/plugins/dayjs.ts @@ -17,6 +17,7 @@ dayjs.extend(utc) dayjs.extend(timezone) dayjs.extend(isSameOrAfter) dayjs.extend(isSameOrBefore) +dayjs.extend(isBetween) dayjs.extend(isoWeek) dayjs.extend(localizedFormat) dayjs.extend(customParseFormat) From f8c62a90f0c87bea3ac848994427439ff767ae96 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Fri, 14 Nov 2025 20:13:54 +0100 Subject: [PATCH 079/149] PDF Time Eval Corrections --- src/utils/pdf.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/utils/pdf.ts b/src/utils/pdf.ts index c5eae47..03e8aa5 100644 --- a/src/utils/pdf.ts +++ b/src/utils/pdf.ts @@ -922,7 +922,19 @@ export const createTimeSheetPDF = async (server: FastifyInstance, returnMode, da opacity: 1 })*/ console.log(input) + pages[pageCounter - 1].drawText(`Anwesenheitsauswertung`,{ + x: getCoordinatesForPDFLib(20,40,pages[pageCounter -1]).x, + y: getCoordinatesForPDFLib(20,40,pages[pageCounter -1]).y, + size: 15, + font: fontBold + }) + pages[pageCounter - 1].drawText(`Mitarbeiter: ${input.full_name}`,{ + x: getCoordinatesForPDFLib(20,50,pages[pageCounter -1]).x, + y: getCoordinatesForPDFLib(20,50,pages[pageCounter -1]).y, + size: 10, + }) + pages[pageCounter - 1].drawText(`Nummer: ${input.employee_number}`,{ x: getCoordinatesForPDFLib(20,55,pages[pageCounter -1]).x, y: getCoordinatesForPDFLib(20,55,pages[pageCounter -1]).y, size: 10, @@ -1005,7 +1017,9 @@ export const createTimeSheetPDF = async (server: FastifyInstance, returnMode, da lastIndex = lastIndex + (i + 1) * 40 + 1 } - splitted.push(reversedInput.slice(lastIndex, reversedInput.length)) + if(reversedInput.slice(lastIndex, reversedInput.length).length > 0) splitted.push(reversedInput.slice(lastIndex, reversedInput.length)) + + console.log(splitted ) splitted.forEach((chunk,index) => { From d408dadd8875fc7e3c959300b68e170b6ad6cd83 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sun, 30 Nov 2025 15:04:47 +0100 Subject: [PATCH 080/149] Added Health Endpoint --- src/index.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/index.ts b/src/index.ts index 6e8a3ea..24644a2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -44,6 +44,10 @@ async function main() { await initMailer(); await initS3(); + app.get('/health', async (req, res) => { + return res.send({ status: 'ok' }) + }) + /*app.addHook("onRequest", (req, reply, done) => { console.log("Incoming:", req.method, req.url, "Headers:", req.headers) done() From d6badafeb9c3b82d9522bc1cbc39243f0654ac2a Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sun, 30 Nov 2025 15:08:18 +0100 Subject: [PATCH 081/149] Added Health Endpoint --- src/index.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/index.ts b/src/index.ts index 24644a2..37a4e55 100644 --- a/src/index.ts +++ b/src/index.ts @@ -44,9 +44,7 @@ async function main() { await initMailer(); await initS3(); - app.get('/health', async (req, res) => { - return res.send({ status: 'ok' }) - }) + /*app.addHook("onRequest", (req, reply, done) => { console.log("Incoming:", req.method, req.url, "Headers:", req.headers) @@ -66,6 +64,10 @@ async function main() { done() }) + app.get('/health', async (req, res) => { + return res.send({ status: 'ok' }) + }) + //Plugin nur auf bestimmten Routes await app.register(queryConfigPlugin, { routes: ['/api/resource/:resource/paginated'] From 407592680a7a3ee688903b0725e7a73b04afdb07 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Fri, 5 Dec 2025 11:49:33 +0100 Subject: [PATCH 082/149] Open Changes --- package-lock.json | 1145 +++++++++++++++++++++++- package.json | 9 +- src/modules/time/evaluation.service.ts | 41 +- src/plugins/cors.ts | 3 + src/routes/auth/auth.ts | 4 +- src/routes/exports.ts | 26 +- src/routes/functions.ts | 45 +- src/routes/staff/time.ts | 14 +- src/utils/export/sepa.ts | 114 +++ src/utils/functions.ts | 120 +++ 10 files changed, 1493 insertions(+), 28 deletions(-) create mode 100644 src/utils/export/sepa.ts diff --git a/package-lock.json b/package-lock.json index c4fffea..2abc221 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,20 +17,28 @@ "@fastify/swagger": "^9.5.1", "@fastify/swagger-ui": "^5.2.3", "@infisical/sdk": "^4.0.6", + "@mmote/niimbluelib": "^0.0.1-alpha.29", "@prisma/client": "^6.15.0", "@supabase/supabase-js": "^2.56.1", "@zip.js/zip.js": "^2.7.73", "archiver": "^7.0.1", "axios": "^1.12.1", "bcrypt": "^6.0.0", + "bwip-js": "^4.8.0", + "canvas": "^3.2.0", "crypto": "^1.0.1", "dayjs": "^1.11.18", "fastify": "^5.5.0", "fastify-plugin": "^5.0.1", + "imapflow": "^1.1.1", "jsonwebtoken": "^9.0.2", "nodemailer": "^7.0.6", "pdf-lib": "^1.17.1", - "xmlbuilder": "^15.1.1" + "pngjs": "^7.0.0", + "sharp": "^0.34.5", + "xmlbuilder": "^15.1.1", + "zpl-image": "^0.2.0", + "zpl-renderer-js": "^2.0.2" }, "devDependencies": { "@types/bcrypt": "^6.0.0", @@ -5244,6 +5252,37 @@ "node": ">=18.0.0" } }, + "node_modules/@capacitor-community/bluetooth-le": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@capacitor-community/bluetooth-le/-/bluetooth-le-7.2.0.tgz", + "integrity": "sha512-eBM0Ia6C68YKqYbGjy6xgjaBrCvfH9EICn77EqxpR3Pu9yniRGTlCuZQdcCAonLwp16uuyAh/5tESxkkVJMccA==", + "license": "MIT", + "dependencies": { + "@types/web-bluetooth": "^0.0.20" + }, + "peerDependencies": { + "@capacitor/core": ">=7.0.0" + } + }, + "node_modules/@capacitor/core": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@capacitor/core/-/core-7.4.4.tgz", + "integrity": "sha512-xzjxpr+d2zwTpCaN0k+C6wKSZzWFAb9OVEUtmO72ihjr/NEDoLvsGl4WLfjWPcCO2zOy0b2X52tfRWjECFUjtw==", + "license": "MIT", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.0.tgz", + "integrity": "sha512-oAYoQnCYaQZKVS53Fq23ceWMRxq5EhQsE0x0RdQ55jT7wagMu5k+fS39v1fiSLrtrLQlXwVINenqhLMtTrV/1Q==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.9", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", @@ -5987,6 +6026,471 @@ "yaml": "^2.4.1" } }, + "node_modules/@img/colour": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", + "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@infisical/sdk": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/@infisical/sdk/-/sdk-4.0.6.tgz", @@ -6049,6 +6553,19 @@ "node": ">=8" } }, + "node_modules/@mmote/niimbluelib": { + "version": "0.0.1-alpha.29", + "resolved": "https://registry.npmjs.org/@mmote/niimbluelib/-/niimbluelib-0.0.1-alpha.29.tgz", + "integrity": "sha512-/uE3thCSbRKiak1biuB4HiFRxmM+ROAiDAkz8+fqdOQ4mhlhLTLSAklFQKcWpfkSTEyj0tng8kssHaGum8ACyg==", + "license": "MIT", + "dependencies": { + "@capacitor-community/bluetooth-le": "^7.1.0", + "@capacitor/core": "^7.2.0", + "async-mutex": "^0.5.0", + "crc-32": "^1.2.2", + "eventemitter3": "^5.0.1" + } + }, "node_modules/@pdf-lib/standard-fonts": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@pdf-lib/standard-fonts/-/standard-fonts-1.0.0.tgz", @@ -6067,6 +6584,12 @@ "pako": "^1.0.10" } }, + "node_modules/@pinojs/redact": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz", + "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==", + "license": "MIT" + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -7014,6 +7537,12 @@ "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", "license": "MIT" }, + "node_modules/@types/web-bluetooth": { + "version": "0.0.20", + "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz", + "integrity": "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==", + "license": "MIT" + }, "node_modules/@types/ws": { "version": "8.18.1", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", @@ -7034,6 +7563,17 @@ "node": ">=16.5.0" } }, + "node_modules/@zone-eu/mailsplit": { + "version": "5.4.7", + "resolved": "https://registry.npmjs.org/@zone-eu/mailsplit/-/mailsplit-5.4.7.tgz", + "integrity": "sha512-jApX86aDgolMz08pP20/J2zcns02NSK3zSiYouf01QQg4250L+GUAWSWicmS7eRvs+Z7wP7QfXrnkaTBGrIpwQ==", + "license": "(MIT OR EUPL-1.1+)", + "dependencies": { + "libbase64": "1.3.0", + "libmime": "5.3.7", + "libqp": "2.1.1" + } + }, "node_modules/abort-controller": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", @@ -7223,6 +7763,15 @@ "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", "license": "MIT" }, + "node_modules/async-mutex": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz", + "integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -7312,6 +7861,55 @@ "node": ">= 18" } }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bl/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/bl/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/bowser": { "version": "2.12.1", "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.12.1.tgz", @@ -7366,6 +7964,15 @@ "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", "license": "BSD-3-Clause" }, + "node_modules/bwip-js": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/bwip-js/-/bwip-js-4.8.0.tgz", + "integrity": "sha512-gUDkDHSTv8/DJhomSIbO0fX/Dx0MO/sgllLxJyJfu4WixCQe9nfGJzmHm64ZCbxo+gUYQEsQcRmqcwcwPRwUkg==", + "license": "MIT", + "bin": { + "bwip-js": "bin/bwip-js.js" + } + }, "node_modules/c12": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/c12/-/c12-3.1.0.tgz", @@ -7408,6 +8015,26 @@ "node": ">= 0.4" } }, + "node_modules/canvas": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/canvas/-/canvas-3.2.0.tgz", + "integrity": "sha512-jk0GxrLtUEmW/TmFsk2WghvgHe8B0pxGilqCL21y8lHkPUGa6FTsnCNtHPOzT8O3y+N+m3espawV80bbBlgfTA==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^7.0.0", + "prebuild-install": "^7.1.3" + }, + "engines": { + "node": "^18.12.0 || >= 20.9.0" + } + }, + "node_modules/canvas/node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "license": "MIT" + }, "node_modules/chokidar": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", @@ -7424,6 +8051,12 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "license": "ISC" + }, "node_modules/citty": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", @@ -7593,6 +8226,30 @@ } } }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/deepmerge-ts": { "version": "7.1.5", "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz", @@ -7644,6 +8301,15 @@ "devOptional": true, "license": "MIT" }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/dotenv": { "version": "16.6.1", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", @@ -7713,6 +8379,24 @@ "node": ">=14" } }, + "node_modules/encoding-japanese": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/encoding-japanese/-/encoding-japanese-2.2.0.tgz", + "integrity": "sha512-EuJWwlHPZ1LbADuKTClvHtwbaFn4rOD+dRAbWysqEOXRc2Uui0hJInNJrsdH0c+OhJA4nrCBdSkW4DD5YxAo6A==", + "license": "MIT", + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -7815,6 +8499,12 @@ "node": ">=6" } }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "license": "MIT" + }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -7824,6 +8514,15 @@ "node": ">=0.8.x" } }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, "node_modules/exsolve": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.7.tgz", @@ -8062,6 +8761,12 @@ "node": ">= 6" } }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "license": "MIT" + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -8154,6 +8859,12 @@ "giget": "dist/cli.mjs" } }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "license": "MIT" + }, "node_modules/glob": { "version": "11.0.3", "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz", @@ -8250,6 +8961,22 @@ "node": ">= 0.8" } }, + "node_modules/iconv-lite": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", + "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -8270,12 +8997,66 @@ ], "license": "BSD-3-Clause" }, + "node_modules/imapflow": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/imapflow/-/imapflow-1.1.1.tgz", + "integrity": "sha512-cgK0eGagmw8C3pY6IiVLSF52N4bwvNRJ7mog4bdZOTafG90yNgUkShQBjo4/CRqvl/isPcaV1Gc0OmFBdXF0HQ==", + "license": "MIT", + "dependencies": { + "@zone-eu/mailsplit": "5.4.7", + "encoding-japanese": "2.2.0", + "iconv-lite": "0.7.0", + "libbase64": "1.3.0", + "libmime": "5.3.7", + "libqp": "2.1.1", + "nodemailer": "7.0.10", + "pino": "10.1.0", + "socks": "2.8.7" + } + }, + "node_modules/imapflow/node_modules/pino": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-10.1.0.tgz", + "integrity": "sha512-0zZC2ygfdqvqK8zJIr1e+wT1T/L+LF6qvqvbzEQ6tiMAoTqEVK9a1K3YRu8HEUvGEvNqZyPJTtb2sNIoTkB83w==", + "license": "MIT", + "dependencies": { + "@pinojs/redact": "^0.4.0", + "atomic-sleep": "^1.0.0", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^2.0.0", + "pino-std-serializers": "^7.0.0", + "process-warning": "^5.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^4.0.1", + "thread-stream": "^3.0.0" + }, + "bin": { + "pino": "bin.js" + } + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC" }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, + "node_modules/ip-address": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", + "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/ipaddr.js": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", @@ -8470,6 +9251,42 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/libbase64": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/libbase64/-/libbase64-1.3.0.tgz", + "integrity": "sha512-GgOXd0Eo6phYgh0DJtjQ2tO8dc0IVINtZJeARPeiIJqge+HdsWSuaDTe8ztQ7j/cONByDZ3zeB325AHiv5O0dg==", + "license": "MIT" + }, + "node_modules/libmime": { + "version": "5.3.7", + "resolved": "https://registry.npmjs.org/libmime/-/libmime-5.3.7.tgz", + "integrity": "sha512-FlDb3Wtha8P01kTL3P9M+ZDNDWPKPmKHWaU/cG/lg5pfuAwdflVpZE+wm9m7pKmC5ww6s+zTxBKS1p6yl3KpSw==", + "license": "MIT", + "dependencies": { + "encoding-japanese": "2.2.0", + "iconv-lite": "0.6.3", + "libbase64": "1.3.0", + "libqp": "2.1.1" + } + }, + "node_modules/libmime/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/libqp": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/libqp/-/libqp-2.1.1.tgz", + "integrity": "sha512-0Wd+GPz1O134cP62YU2GTOPNA7Qgl09XwCqM5zpBv87ERCXdfDtyKXvV7c9U22yWJh44QZqBocFnXN11K96qow==", + "license": "MIT" + }, "node_modules/light-my-request": { "version": "6.6.0", "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-6.6.0.tgz", @@ -8606,6 +9423,18 @@ "node": ">= 0.6" } }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/minimatch": { "version": "10.0.3", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", @@ -8621,6 +9450,15 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/minipass": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", @@ -8630,12 +9468,36 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "license": "MIT" + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "license": "MIT" + }, + "node_modules/node-abi": { + "version": "3.80.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.80.0.tgz", + "integrity": "sha512-LyPuZJcI9HVwzXK1GPxWNzrr+vr8Hp/3UqlmWxxh8p54U1ZbclOqbSog9lWHaCX+dBaiGi6n/hIX+mKu74GmPA==", + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/node-addon-api": { "version": "8.5.0", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.5.0.tgz", @@ -8664,9 +9526,9 @@ } }, "node_modules/nodemailer": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.6.tgz", - "integrity": "sha512-F44uVzgwo49xboqbFgBGkRaiMgtoBrBEWCVincJPK9+S9Adkzt/wXCLKbf7dxucmxfTI5gHGB+bEmdyzN6QKjw==", + "version": "7.0.10", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.10.tgz", + "integrity": "sha512-Us/Se1WtT0ylXgNFfyFSx4LElllVLJXQjWi2Xz17xWw7amDKO2MLtFnVp1WACy7GkVGs+oBlRopVNUzlrGSw1w==", "license": "MIT-0", "engines": { "node": ">=6.0.0" @@ -8717,6 +9579,15 @@ "node": ">=14.0.0" } }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, "node_modules/openapi-types": { "version": "12.1.3", "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", @@ -8841,6 +9712,41 @@ "pathe": "^2.0.3" } }, + "node_modules/pngjs": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz", + "integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==", + "license": "MIT", + "engines": { + "node": ">=14.19.0" + } + }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/prisma": { "version": "6.15.0", "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.15.0.tgz", @@ -8904,6 +9810,16 @@ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "license": "MIT" }, + "node_modules/pump": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/pure-rand": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", @@ -8927,6 +9843,21 @@ "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", "license": "MIT" }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, "node_modules/rc9": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz", @@ -9090,6 +10021,12 @@ "node": ">=10" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, "node_modules/secure-json-parse": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-4.0.0.tgz", @@ -9107,9 +10044,9 @@ "license": "BSD-3-Clause" }, "node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -9130,6 +10067,50 @@ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "license": "ISC" }, + "node_modules/sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -9163,6 +10144,75 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", + "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", + "license": "MIT", + "dependencies": { + "ip-address": "^10.0.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, "node_modules/sonic-boom": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz", @@ -9308,6 +10358,15 @@ "node": ">=8" } }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/strnum": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.1.tgz", @@ -9320,6 +10379,48 @@ ], "license": "MIT" }, + "node_modules/tar-fs": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-fs/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/tar-fs/node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/tar-stream": { "version": "3.1.7", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", @@ -9406,6 +10507,18 @@ "fsevents": "~2.3.3" } }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, "node_modules/typescript": { "version": "5.9.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", @@ -9566,6 +10679,12 @@ "node": ">=8" } }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, "node_modules/ws": { "version": "8.18.3", "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", @@ -9630,6 +10749,18 @@ "funding": { "url": "https://github.com/sponsors/colinhacks" } + }, + "node_modules/zpl-image": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/zpl-image/-/zpl-image-0.2.0.tgz", + "integrity": "sha512-6EqJEvVmfHGAgNt2dIA1t3C8uow/ceinDAzAlAOd3FDGNRN1L+ElY5ddfbwN4eyZykSeqwc0AC8fKus40mkinw==", + "license": "MIT" + }, + "node_modules/zpl-renderer-js": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/zpl-renderer-js/-/zpl-renderer-js-2.0.2.tgz", + "integrity": "sha512-zGGbLxwSFcYDsytuQlDl/tuIve80Ww2jnOkM3jAzxDqHhZ+jUCxMyNeflBHMAyCV0wRvTSIcL2TpHx1WgauWqA==", + "license": "MIT" } } } diff --git a/package.json b/package.json index e316dc7..46bea4d 100644 --- a/package.json +++ b/package.json @@ -24,12 +24,15 @@ "@fastify/swagger": "^9.5.1", "@fastify/swagger-ui": "^5.2.3", "@infisical/sdk": "^4.0.6", + "@mmote/niimbluelib": "^0.0.1-alpha.29", "@prisma/client": "^6.15.0", "@supabase/supabase-js": "^2.56.1", "@zip.js/zip.js": "^2.7.73", "archiver": "^7.0.1", "axios": "^1.12.1", "bcrypt": "^6.0.0", + "bwip-js": "^4.8.0", + "canvas": "^3.2.0", "crypto": "^1.0.1", "dayjs": "^1.11.18", "fastify": "^5.5.0", @@ -38,7 +41,11 @@ "jsonwebtoken": "^9.0.2", "nodemailer": "^7.0.6", "pdf-lib": "^1.17.1", - "xmlbuilder": "^15.1.1" + "pngjs": "^7.0.0", + "sharp": "^0.34.5", + "xmlbuilder": "^15.1.1", + "zpl-image": "^0.2.0", + "zpl-renderer-js": "^2.0.2" }, "devDependencies": { "@types/bcrypt": "^6.0.0", diff --git a/src/modules/time/evaluation.service.ts b/src/modules/time/evaluation.service.ts index 314e263..39b15c9 100644 --- a/src/modules/time/evaluation.service.ts +++ b/src/modules/time/evaluation.service.ts @@ -72,8 +72,8 @@ export async function generateTimesEvaluation( for (const t of times) { const minutes = calcMinutes(t.started_at, t.stopped_at) - if(["submitted","approved"].includes(t.state))sumWorkingMinutesEingereicht += minutes - if (t.state === "approved") sumWorkingMinutesApproved += minutes + if(["submitted","approved"].includes(t.state) && t.type === "work")sumWorkingMinutesEingereicht += minutes + if (t.state === "approved" && t.type === "work") sumWorkingMinutesApproved += minutes } // 🎉 Feiertagsausgleich @@ -90,16 +90,37 @@ export async function generateTimesEvaluation( } // 🏖️ Urlaub & Krankheit (über Typ) - const sumWorkingMinutesVacationDays = times - .filter((t) => t.type === "vacation") - .reduce((sum, t) => sum + calcMinutes(t.started_at, t.stopped_at), 0) + let sumWorkingMinutesVacationDays = 0 + let sumVacationDays = 0 + times + .filter((t) => t.type === "vacation" && t.state === "approved") + .forEach((time) => { + const days = server.dayjs(time.stopped_at).diff(server.dayjs(time.startet_at), "day") + 1; - const sumWorkingMinutesSickDays = times - .filter((t) => t.type === "sick") - .reduce((sum, t) => sum + calcMinutes(t.started_at, t.stopped_at), 0) + for(let i = 0; i < days; i++) { + const weekday = server.dayjs(time.started_at).add(i,"day").day() + const hours = profile.weekly_regular_working_hours?.[weekday] || 0 + sumWorkingMinutesVacationDays += hours * 60 + } + sumVacationDays += days + }) - const sumVacationDays = times.filter((t) => t.type === "vacation").length - const sumSickDays = times.filter((t) => t.type === "sick").length + let sumWorkingMinutesSickDays = 0 + let sumSickDays = 0 + + times + .filter((t) => t.type === "sick" && t.state === "approved") + .forEach((time) => { + const days = server.dayjs(time.stopped_at).diff(server.dayjs(time.startet_at), "day") + 1; + + for(let i = 0; i < days; i++) { + const weekday = server.dayjs(time.started_at).add(i,"day").day() + const hours = profile.weekly_regular_working_hours?.[weekday] || 0 + sumWorkingMinutesSickDays += hours * 60 + } + + sumSickDays += days + }) // 💰 Salden const saldo = diff --git a/src/plugins/cors.ts b/src/plugins/cors.ts index dd45375..a140d1e 100644 --- a/src/plugins/cors.ts +++ b/src/plugins/cors.ts @@ -6,7 +6,10 @@ export default fp(async (server: FastifyInstance) => { await server.register(cors, { origin: [ "http://localhost:3000", // dein Nuxt-Frontend + "http://localhost:3001", // dein Nuxt-Frontend "http://127.0.0.1:3000", // dein Nuxt-Frontend + "http://192.168.1.227:3001", // dein Nuxt-Frontend + "http://192.168.1.227:3000", // dein Nuxt-Frontend "https://beta.fedeo.de", // dein Nuxt-Frontend "https://app.fedeo.de", // dein Nuxt-Frontend "capacitor://localhost", // dein Nuxt-Frontend diff --git a/src/routes/auth/auth.ts b/src/routes/auth/auth.ts index bafb19c..2674a8f 100644 --- a/src/routes/auth/auth.ts +++ b/src/routes/auth/auth.ts @@ -97,7 +97,7 @@ export default async function authRoutes(server: FastifyInstance) { const { data, error } = await server.supabase .from("auth_users") .select("*, tenants!auth_tenant_users(*)") - .eq("email", body.email) + .eq("email", body.email.toLowerCase()) // @ts-ignore user = (data || []).find(i => i.tenants.find(x => x.id === req.tenant.id)) @@ -130,7 +130,7 @@ export default async function authRoutes(server: FastifyInstance) { return reply.code(401).send({ error: "Invalid credentials" }); } else { const token = jwt.sign( - { user_id: user.id, email: user.email, tenant_id: req.tenant?.id ? req.tenant.id : null }, + { user_id: user.id, email: user.email, tenant_id: null }, secrets.JWT_SECRET!, { expiresIn: "6h" } ); diff --git a/src/routes/exports.ts b/src/routes/exports.ts index 89d041b..5aff4ba 100644 --- a/src/routes/exports.ts +++ b/src/routes/exports.ts @@ -8,13 +8,15 @@ import {getSignedUrl} from "@aws-sdk/s3-request-presigner"; import dayjs from "dayjs"; import {randomUUID} from "node:crypto"; import {secrets} from "../utils/secrets"; +import {createSEPAExport} from "../utils/export/sepa"; -const createExport = async (server:FastifyInstance,req:any,startDate,endDate,beraternr,mandantennr) => { +const createDatevExport = 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") + console.log(buffer) // 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` @@ -80,7 +82,27 @@ export default async function exportRoutes(server: FastifyInstance) { setImmediate(async () => { try { - await createExport(server,req,start_date,end_date,beraternr,mandantennr) + await createDatevExport(server,req,start_date,end_date,beraternr,mandantennr) + console.log("Job done ✅") + } catch (err) { + console.error("Job failed ❌", err) + } + }) + + }) + + server.post("/exports/sepa", async (req, reply) => { + const { idsToExport } = req.body as { + idsToExport: Array + } + + + + reply.send({success:true}) + + setImmediate(async () => { + try { + await createSEPAExport(server, idsToExport, req.user.tenant_id) console.log("Job done ✅") } catch (err) { console.error("Job failed ❌", err) diff --git a/src/routes/functions.ts b/src/routes/functions.ts index fc59339..96cc111 100644 --- a/src/routes/functions.ts +++ b/src/routes/functions.ts @@ -1,7 +1,9 @@ import { FastifyInstance } from "fastify"; import {createInvoicePDF, createTimeSheetPDF} from "../utils/pdf"; -import {useNextNumberRangeNumber} from "../utils/functions"; +import {encodeBase64ToNiimbot, generateLabel, useNextNumberRangeNumber} from "../utils/functions"; import dayjs from "dayjs"; +import { ready as zplReady } from 'zpl-renderer-js' +import { renderZPL } from "zpl-image"; import customParseFormat from "dayjs/plugin/customParseFormat.js"; import isoWeek from "dayjs/plugin/isoWeek.js"; @@ -48,8 +50,6 @@ export default async function functionRoutes(server: FastifyInstance) { ) } - console.log(pdf) - return pdf // Fastify wandelt automatisch in JSON } catch (err) { console.log(err) @@ -149,4 +149,43 @@ export default async function functionRoutes(server: FastifyInstance) { } }) + 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} + + console.log(widthMm,heightMm,dpmm) + + if (!zpl) { + return reply.code(400).send({ error: 'Missing ZPL string' }) + } + + try { + // 1️⃣ Renderer initialisieren + const { api } = await zplReady + + // 2️⃣ Rendern (liefert base64-encoded PNG) + const base64Png = await api.zplToBase64Async(zpl, widthMm, heightMm, dpmm) + + return await encodeBase64ToNiimbot(base64Png, 'top') + } catch (err) { + console.error('[ZPL Preview Error]', err) + return reply.code(500).send({ error: err.message || 'Failed to render ZPL' }) + } + }) + + server.post('/print/label', async (req, reply) => { + const { context, width=584, heigth=354 } = req.body as {context:any,width:number,heigth:number} + + try { + const base64 = await generateLabel(context,width,heigth) + + return { + encoded: await encodeBase64ToNiimbot(base64, 'top'), + base64: base64 + } + } catch (err) { + console.error('[ZPL Preview Error]', err) + return reply.code(500).send({ error: err.message || 'Failed to render ZPL' }) + } + }) + } \ No newline at end of file diff --git a/src/routes/staff/time.ts b/src/routes/staff/time.ts index 4d437dd..d5d2cda 100644 --- a/src/routes/staff/time.ts +++ b/src/routes/staff/time.ts @@ -4,16 +4,24 @@ import { StaffTimeEntry } from '../../types/staff' export default async function staffTimeRoutes(server: FastifyInstance) { // ▶ Neue Zeit starten - server.post<{ Body: Pick }>( + server.post( '/staff/time', async (req, reply) => { - const { started_at, stopped_at, type = 'work', description } = req.body + const { started_at, stopped_at, type = 'work', description, user_id } = req.body const userId = req.user.user_id const tenantId = req.user.tenant_id + + let dataToInsert = { + tenant_id: tenantId, + user_id: user_id ? user_id : userId, + // @ts-ignore + ...req.body + } + const { data, error } = await server.supabase .from('staff_time_entries') - .insert([{ tenant_id: tenantId, user_id: userId, started_at, stopped_at, type, description }]) + .insert([dataToInsert]) .select() .maybeSingle() diff --git a/src/utils/export/sepa.ts b/src/utils/export/sepa.ts new file mode 100644 index 0000000..d13b127 --- /dev/null +++ b/src/utils/export/sepa.ts @@ -0,0 +1,114 @@ +import xmlbuilder from "xmlbuilder"; +import {randomUUID} from "node:crypto"; +import dayjs from "dayjs"; + +export const createSEPAExport = async (server,idsToExport, tenant_id) => { + const {data,error} = await server.supabase.from("createddocuments").select().eq("tenant", tenant_id).in("id", idsToExport) + const {data:tenantData,error:tenantError} = await server.supabase.from("tenants").select().eq("id", tenant_id).single() + console.log(tenantData) + console.log(tenantError) + + console.log(data) + + let transactions = [] + + let obj = { + Document: { + '@xmlns':"urn:iso:std:iso:20022:tech:xsd:pain.008.001.02", + 'CstmrDrctDbtInitn': { + 'GrpHdr': { + 'MsgId': randomUUID(), + 'CredDtTm': dayjs().format("YYYY-MM-DDTHH:mm:ss"), + 'NbOfTxs': transactions.length, + 'CtrlSum': 0, // TODO: Total Sum + 'InitgPty': { + 'Nm': tenantData.name + } + }, + 'PmtInf': { + 'PmtInfId': "", // TODO: Mandatsreferenz, + 'PmtMtd': "DD", + 'BtchBookg': "true", // TODO: BatchBooking, + 'NbOfTxs': transactions.length, + 'CtrlSum': 0, //TODO: Total Sum + 'PmtTpInf': { + 'SvcLvl': { + 'Cd': "SEPA" + }, + 'LclInstrm': { + 'Cd': "CORE" // Core für BASIS / B2B für Firmen + }, + 'SeqTp': "RCUR" // TODO: Unterscheidung Einmalig / Wiederkehrend + }, + 'ReqdColltnDt': dayjs().add(3, "days").format("YYYY-MM-DDTHH:mm:ss"), + 'Cdtr': { + 'Nm': tenantData.name + }, + 'CdtrAcct': { + 'Id': { + 'IBAN': "DE" // TODO: IBAN Gläubiger EINSETZEN + } + }, + 'CdtrAgt': { + 'FinInstnId': { + 'BIC': "BIC" // TODO: BIC des Gläubigers einsetzen + } + }, + 'ChrgBr': "SLEV", + 'CdtrSchmeId': { + 'Id': { + 'PrvtId': { + 'Othr': { + 'Id': tenantData.creditorId, + 'SchmeNm': { + 'Prty': "SEPA" + } + } + } + } + }, + //TODO ITERATE ALL INVOICES HERE + 'DrctDbtTxInf': { + 'PmtId': { + 'EndToEndId': "" // TODO: EINDEUTIGE ReFERENZ wahrscheinlich RE Nummer + }, + 'InstdAmt': { + '@Ccy':"EUR", + '#text':100 //TODO: Rechnungssumme zwei NK mit Punkt + }, + 'DrctDbtTx': { + 'MndtRltdInf': { + 'MndtId': "", // TODO: Mandatsref, + 'DtOfSgntr': "", //TODO: Unterschrieben am, + 'AmdmntInd': "" //TODO: Mandat geändert + } + }, + 'DbtrAgt': { + 'FinInstnId': { + 'BIC': "", //TODO: BIC Debtor + } + }, + 'Dbtr': { + 'Nm': "" // TODO NAME Debtor + }, + 'DbtrAcct': { + 'Id': { + 'IBAN': "DE" // TODO IBAN Debtor + } + }, + 'RmtInf': { + 'Ustrd': "" //TODO Verwendungszweck Rechnungsnummer + } + } + } + } + + } + } + + + let doc = xmlbuilder.create(obj, {encoding: 'UTF-8', standalone: true}) + + console.log(doc.end({pretty:true})) + +} \ No newline at end of file diff --git a/src/utils/functions.ts b/src/utils/functions.ts index e5fdda8..740db75 100644 --- a/src/utils/functions.ts +++ b/src/utils/functions.ts @@ -1,4 +1,11 @@ import {FastifyInstance} from "fastify"; +import { PNG } from 'pngjs' +import { ready as zplReady } from 'zpl-renderer-js' +import { Utils } from '@mmote/niimbluelib' +import { createCanvas } from 'canvas' +import bwipjs from 'bwip-js' +import Sharp from 'sharp' +import fs from 'fs' export const useNextNumberRangeNumber = async (server:FastifyInstance, tenantId:number,numberRange)=> { const {data:tenant} = await server.supabase.from("tenants").select().eq("id",tenantId).single() @@ -20,4 +27,117 @@ export const useNextNumberRangeNumber = async (server:FastifyInstance, tenantId: usedNumber } } +} + +export async function encodeBase64ToNiimbot(base64Png, printDirection = 'top') { + // 1️⃣ PNG dekodieren + const buffer = Buffer.from(base64Png, 'base64') + const png = PNG.sync.read(buffer) // liefert {width, height, data: Uint8Array(RGBA)} + + const { width, height, data } = png + console.log(width, height, data) + const cols = printDirection === 'left' ? height : width + const rows = printDirection === 'left' ? width : height + const rowsData = [] + + console.log(cols) + + if (cols % 8 !== 0) throw new Error('Column count must be multiple of 8') + + // 2️⃣ Zeilenweise durchgehen und Bits bilden + for (let row = 0; row < rows; row++) { + let isVoid = true + let blackPixelsCount = 0 + const rowData = new Uint8Array(cols / 8) + + for (let colOct = 0; colOct < cols / 8; colOct++) { + let pixelsOctet = 0 + for (let colBit = 0; colBit < 8; colBit++) { + const x = printDirection === 'left' ? row : colOct * 8 + colBit + const y = printDirection === 'left' ? height - 1 - (colOct * 8 + colBit) : row + const idx = (y * width + x) * 4 + const lum = 0.299 * data[idx] + 0.587 * data[idx + 1] + 0.114 * data[idx + 2] + const isBlack = lum < 128 + if (isBlack) { + pixelsOctet |= 1 << (7 - colBit) + isVoid = false + blackPixelsCount++ + } + } + rowData[colOct] = pixelsOctet + } + + const newPart = { + dataType: isVoid ? 'void' : 'pixels', + rowNumber: row, + repeat: 1, + rowData: isVoid ? undefined : rowData, + blackPixelsCount, + } + + if (rowsData.length === 0) { + rowsData.push(newPart) + } else { + const last = rowsData[rowsData.length - 1] + let same = newPart.dataType === last.dataType + if (same && newPart.dataType === 'pixels') { + same = Utils.u8ArraysEqual(newPart.rowData, last.rowData) + } + if (same) last.repeat++ + else rowsData.push(newPart) + if (row % 200 === 199) { + rowsData.push({ + dataType: 'check', + rowNumber: row, + repeat: 0, + rowData: undefined, + blackPixelsCount: 0, + }) + } + } + } + + return { cols, rows, rowsData } +} + +export async function generateLabel(context,width,height) { + // Canvas für Hintergrund & Text + const canvas = createCanvas(width, height) + const ctx = canvas.getContext('2d') + + // Hintergrund weiß + ctx.fillStyle = '#FFFFFF' + ctx.fillRect(0, 0, width, height) + + // Überschrift + ctx.fillStyle = '#000000' + ctx.font = '32px Arial' + ctx.fillText(context.text, 20, 40) + + // 3) DataMatrix + const dataMatrixPng = await bwipjs.toBuffer({ + bcid: 'datamatrix', + text: context.datamatrix, + scale: 6, + }) + + // Basisbild aus Canvas + const base = await Sharp(canvas.toBuffer()) + .png() + .toBuffer() + + // Alles zusammen compositen + const final = await Sharp(base) + .composite([ + { input: dataMatrixPng, top: 60, left: 20 }, + ]) + .png() + .toBuffer() + + fs.writeFileSync('label.png', final) + + // Optional: Base64 zurückgeben (z.B. für API) + const base64 = final.toString('base64') + + return base64 } \ No newline at end of file From 63af22b671cb6f9828d4e14a487e4470d63bc03d Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sat, 6 Dec 2025 10:34:58 +0100 Subject: [PATCH 083/149] Introduced New DB --- db/index.ts | 10 + db/schema/accounts.ts | 24 + db/schema/auth_profiles.ts | 83 ++ db/schema/auth_role_permisssions.ts | 23 + db/schema/auth_roles.ts | 19 + db/schema/auth_tenant_users.ts | 22 + db/schema/auth_user_roles.ts | 30 + db/schema/auth_users.ts | 22 + db/schema/bankaccounts.ts | 52 ++ db/schema/bankrequisitions.ts | 30 + db/schema/bankstatements.ts | 70 ++ db/schema/checkexecutions.ts | 27 + db/schema/checks.ts | 52 ++ db/schema/citys.ts | 32 + db/schema/contacts.ts | 66 ++ db/schema/contracts.ts | 76 ++ db/schema/costcentres.ts | 50 ++ db/schema/countrys.ts | 21 + db/schema/createddocuments.ts | 121 +++ db/schema/createdletters.ts | 43 + db/schema/customers.ts | 69 ++ db/schema/devices.ts | 29 + db/schema/documentboxes.ts | 28 + db/schema/enums.ts | 97 +++ db/schema/events.ts | 60 ++ db/schema/files.ts | 79 ++ db/schema/filetags.ts | 33 + db/schema/folders.ts | 51 ++ db/schema/generatedexports.ts | 35 + db/schema/globalmessages.ts | 22 + db/schema/globalmessagesseen.ts | 17 + db/schema/helpdesk_channel_instances.ts | 44 + db/schema/helpdesk_channel_types.ts | 9 + db/schema/helpdesk_contacts.ts | 45 + .../helpdesk_conversation_participants.ts | 34 + db/schema/helpdesk_conversations.ts | 59 ++ db/schema/helpdesk_messages.ts | 46 + db/schema/helpdesk_routing_rules.ts | 33 + db/schema/historyitems.ts | 140 +++ db/schema/holidays.ts | 18 + db/schema/hourrates.ts | 27 + db/schema/incominginvoices.ts | 63 ++ db/schema/index.ts | 70 ++ db/schema/inventoryitemgroups.ts | 39 + db/schema/inventoryitems.ts | 68 ++ db/schema/letterheads.ts | 39 + db/schema/movements.ts | 49 ++ db/schema/notifications_event_types.ts | 34 + db/schema/notifications_items.ts | 54 ++ db/schema/notifications_preferences.ts | 60 ++ .../notifications_preferences_defaults.ts | 52 ++ db/schema/ownaccounts.ts | 39 + db/schema/plants.ts | 56 ++ db/schema/productcategories.ts | 37 + db/schema/products.ts | 69 ++ db/schema/projects.ts | 78 ++ db/schema/projecttypes.ts | 41 + db/schema/servicecategories.ts | 39 + db/schema/services.ts | 63 ++ db/schema/spaces.ts | 49 ++ db/schema/staff_time_entries.ts | 68 ++ db/schema/staff_time_entry_connects.ts | 38 + db/schema/staff_zeitstromtimestamps.ts | 44 + db/schema/statementallocations.ts | 69 ++ db/schema/tasks.ts | 51 ++ db/schema/taxtypes.ts | 28 + db/schema/tenants.ts | 140 +++ db/schema/texttemplates.ts | 44 + db/schema/units.ts | 27 + db/schema/user_credentials.ts | 53 ++ db/schema/vehicles.ts | 57 ++ db/schema/vendors.ts | 45 + drizzle.config.ts | 11 + package-lock.json | 797 +++++++++++++++++- package.json | 6 +- scripts/generate-schema-index.ts | 16 + src/index.ts | 10 + src/plugins/db.ts | 34 + src/routes/auth/auth.ts | 277 +++--- src/utils/secrets.ts | 1 + 80 files changed, 4509 insertions(+), 154 deletions(-) create mode 100644 db/index.ts create mode 100644 db/schema/accounts.ts create mode 100644 db/schema/auth_profiles.ts create mode 100644 db/schema/auth_role_permisssions.ts create mode 100644 db/schema/auth_roles.ts create mode 100644 db/schema/auth_tenant_users.ts create mode 100644 db/schema/auth_user_roles.ts create mode 100644 db/schema/auth_users.ts create mode 100644 db/schema/bankaccounts.ts create mode 100644 db/schema/bankrequisitions.ts create mode 100644 db/schema/bankstatements.ts create mode 100644 db/schema/checkexecutions.ts create mode 100644 db/schema/checks.ts create mode 100644 db/schema/citys.ts create mode 100644 db/schema/contacts.ts create mode 100644 db/schema/contracts.ts create mode 100644 db/schema/costcentres.ts create mode 100644 db/schema/countrys.ts create mode 100644 db/schema/createddocuments.ts create mode 100644 db/schema/createdletters.ts create mode 100644 db/schema/customers.ts create mode 100644 db/schema/devices.ts create mode 100644 db/schema/documentboxes.ts create mode 100644 db/schema/enums.ts create mode 100644 db/schema/events.ts create mode 100644 db/schema/files.ts create mode 100644 db/schema/filetags.ts create mode 100644 db/schema/folders.ts create mode 100644 db/schema/generatedexports.ts create mode 100644 db/schema/globalmessages.ts create mode 100644 db/schema/globalmessagesseen.ts create mode 100644 db/schema/helpdesk_channel_instances.ts create mode 100644 db/schema/helpdesk_channel_types.ts create mode 100644 db/schema/helpdesk_contacts.ts create mode 100644 db/schema/helpdesk_conversation_participants.ts create mode 100644 db/schema/helpdesk_conversations.ts create mode 100644 db/schema/helpdesk_messages.ts create mode 100644 db/schema/helpdesk_routing_rules.ts create mode 100644 db/schema/historyitems.ts create mode 100644 db/schema/holidays.ts create mode 100644 db/schema/hourrates.ts create mode 100644 db/schema/incominginvoices.ts create mode 100644 db/schema/index.ts create mode 100644 db/schema/inventoryitemgroups.ts create mode 100644 db/schema/inventoryitems.ts create mode 100644 db/schema/letterheads.ts create mode 100644 db/schema/movements.ts create mode 100644 db/schema/notifications_event_types.ts create mode 100644 db/schema/notifications_items.ts create mode 100644 db/schema/notifications_preferences.ts create mode 100644 db/schema/notifications_preferences_defaults.ts create mode 100644 db/schema/ownaccounts.ts create mode 100644 db/schema/plants.ts create mode 100644 db/schema/productcategories.ts create mode 100644 db/schema/products.ts create mode 100644 db/schema/projects.ts create mode 100644 db/schema/projecttypes.ts create mode 100644 db/schema/servicecategories.ts create mode 100644 db/schema/services.ts create mode 100644 db/schema/spaces.ts create mode 100644 db/schema/staff_time_entries.ts create mode 100644 db/schema/staff_time_entry_connects.ts create mode 100644 db/schema/staff_zeitstromtimestamps.ts create mode 100644 db/schema/statementallocations.ts create mode 100644 db/schema/tasks.ts create mode 100644 db/schema/taxtypes.ts create mode 100644 db/schema/tenants.ts create mode 100644 db/schema/texttemplates.ts create mode 100644 db/schema/units.ts create mode 100644 db/schema/user_credentials.ts create mode 100644 db/schema/vehicles.ts create mode 100644 db/schema/vendors.ts create mode 100644 drizzle.config.ts create mode 100644 scripts/generate-schema-index.ts create mode 100644 src/plugins/db.ts diff --git a/db/index.ts b/db/index.ts new file mode 100644 index 0000000..eeb9f5b --- /dev/null +++ b/db/index.ts @@ -0,0 +1,10 @@ +import { drizzle } from "drizzle-orm/node-postgres" +import { Pool } from "pg" +import {secrets} from "../src/utils/secrets"; + +const pool = new Pool({ + connectionString: secrets.DATABASE_URL, + max: 10, // je nach Last +}) + +export const db = drizzle(pool) \ No newline at end of file diff --git a/db/schema/accounts.ts b/db/schema/accounts.ts new file mode 100644 index 0000000..cc154de --- /dev/null +++ b/db/schema/accounts.ts @@ -0,0 +1,24 @@ +import { + pgTable, + bigint, + timestamp, + text, +} from "drizzle-orm/pg-core" + +export const accounts = pgTable("accounts", { + id: bigint("id", { mode: "number" }) + .primaryKey() + .generatedByDefaultAsIdentity(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + number: text("number").notNull(), + label: text("label").notNull(), + + description: text("description"), +}) + +export type Account = typeof accounts.$inferSelect +export type NewAccount = typeof accounts.$inferInsert diff --git a/db/schema/auth_profiles.ts b/db/schema/auth_profiles.ts new file mode 100644 index 0000000..e8dc73a --- /dev/null +++ b/db/schema/auth_profiles.ts @@ -0,0 +1,83 @@ +import { + pgTable, + uuid, + text, + timestamp, + date, + boolean, + bigint, + doublePrecision, + jsonb, +} from "drizzle-orm/pg-core" +import { authUsers } from "./auth_users" + +export const authProfiles = pgTable("auth_profiles", { + id: uuid("id").primaryKey().defaultRandom(), + + userId: uuid("user_id").references(() => authUsers.id), + + tenantId: bigint("tenant_id", { mode: "number" }).notNull(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + firstName: text("first_name").notNull(), + lastName: text("last_name").notNull(), + + fullName: text("full_name").generatedAlwaysAs( + `((first_name || ' ') || last_name)` + ), + + mobileTel: text("mobile_tel"), + fixedTel: text("fixed_tel"), + salutation: text("salutation"), + employeeNumber: text("employee_number"), + + weeklyWorkingHours: doublePrecision("weekly_working_hours").default(0), + annualPaidLeaveDays: bigint("annual_paid_leave_days", { mode: "number" }), + + weeklyRegularWorkingHours: jsonb("weekly_regular_working_hours").default("{}"), + + clothingSizeTop: text("clothing_size_top"), + clothingSizeBottom: text("clothing_size_bottom"), + clothingSizeShoe: text("clothing_size_shoe"), + + emailSignature: text("email_signature").default("

Mit freundlichen Grüßen

"), + + birthday: date("birthday"), + entryDate: date("entry_date").defaultNow(), + + automaticHourCorrections: jsonb("automatic_hour_corrections").default("[]"), + + recreationDaysCompensation: boolean("recreation_days_compensation") + .notNull() + .default(true), + + customerForPortal: bigint("customer_for_portal", { mode: "number" }), + + pinnedOnNavigation: jsonb("pinned_on_navigation").notNull().default("[]"), + + email: text("email"), + tokenId: text("token_id"), + + weeklyWorkingDays: doublePrecision("weekly_working_days"), + + oldProfileId: uuid("old_profile_id"), + tempConfig: jsonb("temp_config"), + + stateCode: text("state_code").default("DE-NI"), + + contractType: text("contract_type"), + position: text("position"), + qualification: text("qualification"), + + addressStreet: text("address_street"), + addressZip: text("address_zip"), + addressCity: text("address_city"), + + active: boolean("active").notNull().default(true), +}) + +export type AuthProfile = typeof authProfiles.$inferSelect +export type NewAuthProfile = typeof authProfiles.$inferInsert diff --git a/db/schema/auth_role_permisssions.ts b/db/schema/auth_role_permisssions.ts new file mode 100644 index 0000000..75c95d9 --- /dev/null +++ b/db/schema/auth_role_permisssions.ts @@ -0,0 +1,23 @@ +import { pgTable, uuid, text, timestamp } from "drizzle-orm/pg-core" +import { authRoles } from "./auth_roles" + +export const authRolePermissions = pgTable( + "auth_role_permissions", + { + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + roleId: uuid("role_id") + .notNull() + .references(() => authRoles.id), + + permission: text("permission").notNull(), + }, + (table) => ({ + primaryKey: [table.roleId, table.permission], + }) +) + +export type AuthRolePermission = typeof authRolePermissions.$inferSelect +export type NewAuthRolePermission = typeof authRolePermissions.$inferInsert diff --git a/db/schema/auth_roles.ts b/db/schema/auth_roles.ts new file mode 100644 index 0000000..d2f8d9d --- /dev/null +++ b/db/schema/auth_roles.ts @@ -0,0 +1,19 @@ +import { pgTable, uuid, text, timestamp, bigint } from "drizzle-orm/pg-core" +import { authUsers } from "./auth_users" + +export const authRoles = pgTable("auth_roles", { + id: uuid("id").primaryKey().defaultRandom(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + name: text("name").notNull(), + description: text("description"), + + createdBy: uuid("created_by").references(() => authUsers.id), + tenantId: bigint("tenant_id", {mode: "number"}), +}) + +export type AuthRole = typeof authRoles.$inferSelect +export type NewAuthRole = typeof authRoles.$inferInsert diff --git a/db/schema/auth_tenant_users.ts b/db/schema/auth_tenant_users.ts new file mode 100644 index 0000000..756a8d5 --- /dev/null +++ b/db/schema/auth_tenant_users.ts @@ -0,0 +1,22 @@ +import { pgTable, uuid, bigint, timestamp } from "drizzle-orm/pg-core" +import { authUsers } from "./auth_users" + +export const authTenantUsers = pgTable( + "auth_tenant_users", + { + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + tenantId: bigint("tenant_id", { mode: "number" }).notNull(), + userId: uuid("user_id").notNull(), + + createdBy: uuid("created_by").references(() => authUsers.id), + }, + (table) => ({ + primaryKey: [table.tenantId, table.userId], + }) +) + +export type AuthTenantUser = typeof authTenantUsers.$inferSelect +export type NewAuthTenantUser = typeof authTenantUsers.$inferInsert diff --git a/db/schema/auth_user_roles.ts b/db/schema/auth_user_roles.ts new file mode 100644 index 0000000..8df85a5 --- /dev/null +++ b/db/schema/auth_user_roles.ts @@ -0,0 +1,30 @@ +import { pgTable, uuid, bigint, timestamp } from "drizzle-orm/pg-core" +import { authUsers } from "./auth_users" +import { authRoles } from "./auth_roles" + +export const authUserRoles = pgTable( + "auth_user_roles", + { + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + userId: uuid("user_id") + .notNull() + .references(() => authUsers.id), + + roleId: uuid("role_id") + .notNull() + .references(() => authRoles.id), + + tenantId: bigint("tenant_id", { mode: "number" }).notNull(), + + createdBy: uuid("created_by").references(() => authUsers.id), + }, + (table) => ({ + primaryKey: [table.userId, table.roleId, table.tenantId], + }) +) + +export type AuthUserRole = typeof authUserRoles.$inferSelect +export type NewAuthUserRole = typeof authUserRoles.$inferInsert diff --git a/db/schema/auth_users.ts b/db/schema/auth_users.ts new file mode 100644 index 0000000..d6c6a7f --- /dev/null +++ b/db/schema/auth_users.ts @@ -0,0 +1,22 @@ +import { pgTable, uuid, text, boolean, timestamp } from "drizzle-orm/pg-core" + +export const authUsers = pgTable("auth_users", { + id: uuid("id").primaryKey().defaultRandom(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + email: text("email").notNull(), + passwordHash: text("password_hash").notNull(), + + multiTenant: boolean("multi_tenant").notNull().default(true), + mustChangePassword: boolean("must_change_password").notNull().default(false), + + updatedAt: timestamp("updated_at", { withTimezone: true }), + + ported: boolean("ported").notNull().default(true), +}) + +export type AuthUser = typeof authUsers.$inferSelect +export type NewAuthUser = typeof authUsers.$inferInsert diff --git a/db/schema/bankaccounts.ts b/db/schema/bankaccounts.ts new file mode 100644 index 0000000..2b431d4 --- /dev/null +++ b/db/schema/bankaccounts.ts @@ -0,0 +1,52 @@ +import { + pgTable, + bigint, + timestamp, + text, + doublePrecision, + boolean, + uuid, +} from "drizzle-orm/pg-core" + +import { tenants } from "./tenants" +import { authUsers } from "./auth_users" + +export const bankaccounts = pgTable("bankaccounts", { + id: bigint("id", { mode: "number" }) + .primaryKey() + .generatedByDefaultAsIdentity(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + name: text("name"), + iban: text("iban").notNull(), + + tenant: bigint("tenant", { mode: "number" }) + .notNull() + .references(() => tenants.id), + + bankId: text("bankId").notNull(), + ownerName: text("ownerName"), + + accountId: text("accountId").notNull(), + + balance: doublePrecision("balance"), + + expired: boolean("expired").notNull().default(false), + + datevNumber: text("datevNumber"), + + syncedAt: timestamp("synced_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + updatedAt: timestamp("updated_at", { withTimezone: true }), + updatedBy: uuid("updated_by").references(() => authUsers.id), + + archived: boolean("archived").notNull().default(false), +}) + +export type BankAccount = typeof bankaccounts.$inferSelect +export type NewBankAccount = typeof bankaccounts.$inferInsert diff --git a/db/schema/bankrequisitions.ts b/db/schema/bankrequisitions.ts new file mode 100644 index 0000000..3cfd18e --- /dev/null +++ b/db/schema/bankrequisitions.ts @@ -0,0 +1,30 @@ +import { + pgTable, + uuid, + timestamp, + text, + bigint, +} from "drizzle-orm/pg-core" + +import { tenants } from "./tenants" +import { authUsers } from "./auth_users" + +export const bankrequisitions = pgTable("bankrequisitions", { + id: uuid("id").primaryKey(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + institutionId: text("institutionId"), + + tenant: bigint("tenant", { mode: "number" }).references(() => tenants.id), + + status: text("status"), + + updatedAt: timestamp("updated_at", { withTimezone: true }), + updatedBy: uuid("updated_by").references(() => authUsers.id), +}) + +export type BankRequisition = typeof bankrequisitions.$inferSelect +export type NewBankRequisition = typeof bankrequisitions.$inferInsert diff --git a/db/schema/bankstatements.ts b/db/schema/bankstatements.ts new file mode 100644 index 0000000..bce2af7 --- /dev/null +++ b/db/schema/bankstatements.ts @@ -0,0 +1,70 @@ +import { + pgTable, + bigint, + timestamp, + text, + doublePrecision, + boolean, + uuid, +} from "drizzle-orm/pg-core" + +import { bankaccounts } from "./bankaccounts" +import { createddocuments } from "./createddocuments" +import { tenants } from "./tenants" +import { incominginvoices } from "./incominginvoices" +import { contracts } from "./contracts" +import { authUsers } from "./auth_users" + +export const bankstatements = pgTable("bankstatements", { + id: bigint("id", { mode: "number" }) + .primaryKey() + .generatedByDefaultAsIdentity(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + account: bigint("account", { mode: "number" }) + .notNull() + .references(() => bankaccounts.id), + + date: text("date").notNull(), + + credIban: text("credIban"), + credName: text("credName"), + + text: text("text"), + amount: doublePrecision("amount").notNull(), + + tenant: bigint("tenant", { mode: "number" }) + .notNull() + .references(() => tenants.id), + + debIban: text("debIban"), + debName: text("debName"), + gocardlessId: text("gocardlessId"), + currency: text("currency"), + valueDate: text("valueDate"), + + incomingInvoice: bigint("incomingInvoice", { mode: "number" }).references( + () => incominginvoices.id + ), + + mandateId: text("mandateId"), + + contract: bigint("contract", { mode: "number" }).references( + () => contracts.id + ), + + createdDocument: bigint("createdDocument", { mode: "number" }).references( + () => createddocuments.id + ), + + archived: boolean("archived").notNull().default(false), + + updatedAt: timestamp("updated_at", { withTimezone: true }), + updatedBy: uuid("updated_by").references(() => authUsers.id), +}) + +export type BankStatement = typeof bankstatements.$inferSelect +export type NewBankStatement = typeof bankstatements.$inferInsert diff --git a/db/schema/checkexecutions.ts b/db/schema/checkexecutions.ts new file mode 100644 index 0000000..455877c --- /dev/null +++ b/db/schema/checkexecutions.ts @@ -0,0 +1,27 @@ +import { + pgTable, + uuid, + timestamp, + text, +} from "drizzle-orm/pg-core" + +import { checks } from "./checks" + +export const checkexecutions = pgTable("checkexecutions", { + id: uuid("id").primaryKey().defaultRandom(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + check: uuid("check").references(() => checks.id), + + executedAt: timestamp("executed_at"), + + // ❌ executed_by removed (was 0_profiles) + + description: text("description"), +}) + +export type CheckExecution = typeof checkexecutions.$inferSelect +export type NewCheckExecution = typeof checkexecutions.$inferInsert diff --git a/db/schema/checks.ts b/db/schema/checks.ts new file mode 100644 index 0000000..744b615 --- /dev/null +++ b/db/schema/checks.ts @@ -0,0 +1,52 @@ +import { + pgTable, + uuid, + timestamp, + text, + bigint, + boolean, + jsonb, +} from "drizzle-orm/pg-core" + +import { tenants } from "./tenants" +import { vehicles } from "./vehicles" +import { inventoryItems } from "./inventoryitems" +import { authUsers } from "./auth_users" + +export const checks = pgTable("checks", { + id: uuid("id").primaryKey().defaultRandom(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + vehicle: bigint("vehicle", { mode: "number" }) + .references(() => vehicles.id), + + // ❌ profile removed (old 0_profiles reference) + + inventoryItem: bigint("inventoryitem", { mode: "number" }) + .references(() => inventoryItems.id), + + tenant: bigint("tenant", { mode: "number" }) + .references(() => tenants.id), + + name: text("name"), + type: text("type"), + + distance: bigint("distance", { mode: "number" }).default(1), + + distanceUnit: text("distanceUnit").default("days"), + + description: text("description"), + + profiles: jsonb("profiles").notNull().default([]), + + archived: boolean("archived").notNull().default(false), + + updatedAt: timestamp("updated_at", { withTimezone: true }), + updatedBy: uuid("updated_by").references(() => authUsers.id), +}) + +export type Check = typeof checks.$inferSelect +export type NewCheck = typeof checks.$inferInsert diff --git a/db/schema/citys.ts b/db/schema/citys.ts new file mode 100644 index 0000000..e54e5fd --- /dev/null +++ b/db/schema/citys.ts @@ -0,0 +1,32 @@ +import { + pgTable, + bigint, + text, + jsonb, +} from "drizzle-orm/pg-core" + +export const citys = pgTable("citys", { + id: bigint("id", { mode: "number" }) + .primaryKey() + .generatedByDefaultAsIdentity(), + + name: text("name"), + short: text("short"), + long: text("long"), + + geometry: jsonb("geometry"), + + zip: bigint("zip", { mode: "number" }), + + districtCode: bigint("districtCode", { mode: "number" }), + + countryName: text("countryName"), + countryCode: bigint("countryCode", { mode: "number" }), + + districtName: text("districtName"), + + geopoint: text("geopoint"), +}) + +export type City = typeof citys.$inferSelect +export type NewCity = typeof citys.$inferInsert diff --git a/db/schema/contacts.ts b/db/schema/contacts.ts new file mode 100644 index 0000000..b3f3824 --- /dev/null +++ b/db/schema/contacts.ts @@ -0,0 +1,66 @@ +import { + pgTable, + bigint, + text, + timestamp, + boolean, + jsonb, + date, + uuid, +} from "drizzle-orm/pg-core" + +import { customers } from "./customers" +import { tenants } from "./tenants" +import { authUsers } from "./auth_users" + +export const contacts = pgTable( + "contacts", + { + id: bigint("id", { mode: "number" }) + .primaryKey() + .generatedByDefaultAsIdentity(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + firstName: text("firstName"), + lastName: text("lastName"), + email: text("email"), + + customer: bigint("customer", { mode: "number" }).references( + () => customers.id + ), + + tenant: bigint("tenant", { mode: "number" }).notNull(), + + phoneMobile: text("phoneMobile"), + phoneHome: text("phoneHome"), + + heroId: text("heroId"), + role: text("role"), + + fullName: text("fullName"), + + salutation: text("salutation"), + + vendor: bigint("vendor", { mode: "number" }), // vendors folgt separat + + active: boolean("active").notNull().default(true), + + birthday: date("birthday"), + notes: text("notes"), + + profiles: jsonb("profiles").notNull().default([]), + + archived: boolean("archived").notNull().default(false), + + title: text("title"), + + updatedAt: timestamp("updated_at", { withTimezone: true }), + updatedBy: uuid("updated_by").references(() => authUsers.id), + } +) + +export type Contact = typeof contacts.$inferSelect +export type NewContact = typeof contacts.$inferInsert diff --git a/db/schema/contracts.ts b/db/schema/contracts.ts new file mode 100644 index 0000000..3673395 --- /dev/null +++ b/db/schema/contracts.ts @@ -0,0 +1,76 @@ +import { + pgTable, + bigint, + text, + timestamp, + boolean, + jsonb, + uuid, +} from "drizzle-orm/pg-core" + +import { tenants } from "./tenants" +import { customers } from "./customers" +import { contacts } from "./contacts" +import { authUsers } from "./auth_users" + +export const contracts = pgTable( + "contracts", + { + id: bigint("id", { mode: "number" }) + .primaryKey() + .generatedByDefaultAsIdentity(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + tenant: bigint("tenant", { mode: "number" }).notNull(), + + name: text("name").notNull(), + + customer: bigint("customer", { mode: "number" }) + .notNull() + .references(() => customers.id), + + notes: text("notes"), + + active: boolean("active").notNull().default(true), + recurring: boolean("recurring").notNull().default(false), + + rhythm: jsonb("rhythm"), + + startDate: timestamp("startDate", { withTimezone: true }), + endDate: timestamp("endDate", { withTimezone: true }), + signDate: timestamp("signDate", { withTimezone: true }), + + duration: text("duration"), + + contact: bigint("contact", { mode: "number" }).references( + () => contacts.id + ), + + bankingIban: text("bankingIban"), + bankingBIC: text("bankingBIC"), + bankingName: text("bankingName"), + bankingOwner: text("bankingOwner"), + sepaRef: text("sepaRef"), + sepaDate: timestamp("sepaDate", { withTimezone: true }), + + paymentType: text("paymentType"), + invoiceDispatch: text("invoiceDispatch"), + + ownFields: jsonb("ownFields").notNull().default({}), + profiles: jsonb("profiles").notNull().default([]), + + archived: boolean("archived").notNull().default(false), + + contractNumber: text("contractNumber"), + + updatedAt: timestamp("updated_at", { withTimezone: true }), + + updatedBy: uuid("updated_by").references(() => authUsers.id), + } +) + +export type Contract = typeof contracts.$inferSelect +export type NewContract = typeof contracts.$inferInsert diff --git a/db/schema/costcentres.ts b/db/schema/costcentres.ts new file mode 100644 index 0000000..b57a21a --- /dev/null +++ b/db/schema/costcentres.ts @@ -0,0 +1,50 @@ +import { + pgTable, + uuid, + timestamp, + text, + boolean, + jsonb, + bigint, +} from "drizzle-orm/pg-core" + +import { tenants } from "./tenants" +import { inventoryItems } from "./inventoryitems" +import { projects } from "./projects" +import { vehicles } from "./vehicles" +import { authUsers } from "./auth_users" + +export const costcentres = pgTable("costcentres", { + id: uuid("id").primaryKey().defaultRandom(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + tenant: bigint("tenant", { mode: "number" }) + .notNull() + .references(() => tenants.id), + + number: text("number").notNull(), + name: text("name").notNull(), + + vehicle: bigint("vehicle", { mode: "number" }).references(() => vehicles.id), + + project: bigint("project", { mode: "number" }).references(() => projects.id), + + inventoryitem: bigint("inventoryitem", { mode: "number" }).references( + () => inventoryItems.id + ), + + description: text("description"), + + archived: boolean("archived").notNull().default(false), + + profiles: jsonb("profiles").notNull().default([]), + + updatedAt: timestamp("updated_at", { withTimezone: true }), + updatedBy: uuid("updated_by").references(() => authUsers.id), +}) + +export type CostCentre = typeof costcentres.$inferSelect +export type NewCostCentre = typeof costcentres.$inferInsert diff --git a/db/schema/countrys.ts b/db/schema/countrys.ts new file mode 100644 index 0000000..9eb3e9f --- /dev/null +++ b/db/schema/countrys.ts @@ -0,0 +1,21 @@ +import { + pgTable, + bigint, + timestamp, + text, +} from "drizzle-orm/pg-core" + +export const countrys = pgTable("countrys", { + id: bigint("id", { mode: "number" }) + .primaryKey() + .generatedByDefaultAsIdentity(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + name: text("name").notNull(), +}) + +export type Country = typeof countrys.$inferSelect +export type NewCountry = typeof countrys.$inferInsert diff --git a/db/schema/createddocuments.ts b/db/schema/createddocuments.ts new file mode 100644 index 0000000..dca90d6 --- /dev/null +++ b/db/schema/createddocuments.ts @@ -0,0 +1,121 @@ +import { + pgTable, + bigint, + timestamp, + text, + jsonb, + boolean, + smallint, + uuid, +} from "drizzle-orm/pg-core" + +import { tenants } from "./tenants" +import { customers } from "./customers" +import { contacts } from "./contacts" +import { contracts } from "./contracts" +import { letterheads } from "./letterheads" +import { projects } from "./projects" +import { plants } from "./plants" +import { authUsers } from "./auth_users" + +export const createddocuments = pgTable("createddocuments", { + id: bigint("id", { mode: "number" }) + .primaryKey() + .generatedByDefaultAsIdentity(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + tenant: bigint("tenant", { mode: "number" }) + .notNull() + .references(() => tenants.id), + + type: text("type").notNull().default("INVOICE"), + + customer: bigint("customer", { mode: "number" }).references( + () => customers.id + ), + + contact: bigint("contact", { mode: "number" }).references( + () => contacts.id + ), + + address: jsonb("address"), + project: bigint("project", { mode: "number" }).references( + () => projects.id + ), + + documentNumber: text("documentNumber"), + documentDate: text("documentDate"), + + state: text("state").notNull().default("Entwurf"), + + info: jsonb("info"), + + createdBy: uuid("createdBy").references(() => authUsers.id), + + title: text("title"), + description: text("description"), + + startText: text("startText"), + endText: text("endText"), + + rows: jsonb("rows").default([]), + + deliveryDateType: text("deliveryDateType"), + paymentDays: smallint("paymentDays"), + deliveryDate: text("deliveryDate"), + + contactPerson: uuid("contactPerson"), + + serialConfig: jsonb("serialConfig").default({}), + + linkedDocument: bigint("linkedDocument", { mode: "number" }).references( + () => createddocuments.id + ), + + agriculture: jsonb("agriculture"), + + letterhead: bigint("letterhead", { mode: "number" }).references( + () => letterheads.id + ), + + advanceInvoiceResolved: boolean("advanceInvoiceResolved") + .notNull() + .default(false), + + usedAdvanceInvoices: jsonb("usedAdvanceInvoices").notNull().default([]), + + archived: boolean("archived").notNull().default(false), + + deliveryDateEnd: text("deliveryDateEnd"), + + plant: bigint("plant", { mode: "number" }).references(() => plants.id), + + taxType: text("taxType"), + + customSurchargePercentage: smallint("customSurchargePercentage") + .notNull() + .default(0), + + report: jsonb("report").notNull().default({}), + + availableInPortal: boolean("availableInPortal") + .notNull() + .default(false), + + updatedAt: timestamp("updated_at", { withTimezone: true }), + updatedBy: uuid("updated_by").references(() => authUsers.id), + + created_by: uuid("created_by").references(() => authUsers.id), + + payment_type: text("payment_type").default("transfer"), + + contract: bigint("contract", { mode: "number" }).references( + () => contracts.id + ), +}) + +export type CreatedDocument = typeof createddocuments.$inferSelect +export type NewCreatedDocument = typeof createddocuments.$inferInsert diff --git a/db/schema/createdletters.ts b/db/schema/createdletters.ts new file mode 100644 index 0000000..a180624 --- /dev/null +++ b/db/schema/createdletters.ts @@ -0,0 +1,43 @@ +import { + pgTable, + uuid, + timestamp, + bigint, + text, + jsonb, + boolean, +} from "drizzle-orm/pg-core" + +import { tenants } from "./tenants" +import { customers } from "./customers" +import { vendors } from "./vendors" +import { authUsers } from "./auth_users" + +export const createdletters = pgTable("createdletters", { + id: uuid("id").primaryKey().defaultRandom(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + tenant: bigint("tenant", { mode: "number" }).references(() => tenants.id), + + customer: bigint("customer", { mode: "number" }).references( + () => customers.id + ), + + vendor: bigint("vendor", { mode: "number" }).references(() => vendors.id), + + contentJson: jsonb("content_json").default([]), + + contentText: text("content_text"), + + updatedAt: timestamp("updated_at", { withTimezone: true }), + + updatedBy: uuid("updated_by").references(() => authUsers.id), + + archived: boolean("archived").notNull().default(false), +}) + +export type CreatedLetter = typeof createdletters.$inferSelect +export type NewCreatedLetter = typeof createdletters.$inferInsert diff --git a/db/schema/customers.ts b/db/schema/customers.ts new file mode 100644 index 0000000..67743e8 --- /dev/null +++ b/db/schema/customers.ts @@ -0,0 +1,69 @@ +import { + pgTable, + bigint, + text, + timestamp, + boolean, + jsonb, + smallint, + uuid, +} from "drizzle-orm/pg-core" +import { tenants } from "./tenants" +import { authUsers } from "./auth_users" + +export const customers = pgTable( + "customers", + { + id: bigint("id", { mode: "number" }) + .primaryKey() + .generatedByDefaultAsIdentity(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + customerNumber: text("customerNumber").notNull(), + name: text("name").notNull(), + + tenant: bigint("tenant", { mode: "number" }).notNull(), + + infoData: jsonb("infoData").default({}), + active: boolean("active").notNull().default(true), + + notes: text("notes"), + + type: text("type").default("Privat"), + heroId: text("heroId"), + + isCompany: boolean("isCompany").notNull().default(false), + + profiles: jsonb("profiles").notNull().default([]), + + customPaymentDays: smallint("customPaymentDays"), + + firstname: text("firstname"), + lastname: text("lastname"), + + archived: boolean("archived").notNull().default(false), + + customSurchargePercentage: smallint("customSurchargePercentage") + .notNull() + .default(0), + + salutation: text("salutation"), + title: text("title"), + nameAddition: text("nameAddition"), + + availableInPortal: boolean("availableInPortal") + .notNull() + .default(false), + + updatedAt: timestamp("updated_at", { withTimezone: true }), + updatedBy: uuid("updated_by").references(() => authUsers.id), + + customPaymentType: text("custom_payment_type"), // ENUM payment_types separat? + } +) + +export type Customer = typeof customers.$inferSelect +export type NewCustomer = typeof customers.$inferInsert diff --git a/db/schema/devices.ts b/db/schema/devices.ts new file mode 100644 index 0000000..4825826 --- /dev/null +++ b/db/schema/devices.ts @@ -0,0 +1,29 @@ +import { + pgTable, + uuid, + timestamp, + text, + bigint, +} from "drizzle-orm/pg-core" + +import { tenants } from "./tenants" + +export const devices = pgTable("devices", { + id: uuid("id").primaryKey().defaultRandom(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + name: text("name").notNull(), + type: text("type").notNull(), + + tenant: bigint("tenant", { mode: "number" }).references(() => tenants.id), + + password: text("password"), + + externalId: text("externalId"), +}) + +export type Device = typeof devices.$inferSelect +export type NewDevice = typeof devices.$inferInsert diff --git a/db/schema/documentboxes.ts b/db/schema/documentboxes.ts new file mode 100644 index 0000000..1111a19 --- /dev/null +++ b/db/schema/documentboxes.ts @@ -0,0 +1,28 @@ +import { pgTable, uuid, timestamp, text, boolean, bigint } from "drizzle-orm/pg-core" + +import { spaces } from "./spaces" +import { tenants } from "./tenants" +import { authUsers } from "./auth_users" + +export const documentboxes = pgTable("documentboxes", { + id: uuid("id").primaryKey().defaultRandom(), + + createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), + + space: bigint("space", { mode: "number" }).references(() => spaces.id), + + key: text("key").notNull(), + + tenant: bigint("tenant", { mode: "number" }) + .notNull() + .references(() => tenants.id), + + archived: boolean("archived").notNull().default(false), + + updatedAt: timestamp("updated_at", { withTimezone: true }), + + updatedBy: uuid("updated_by").references(() => authUsers.id), +}) + +export type DocumentBox = typeof documentboxes.$inferSelect +export type NewDocumentBox = typeof documentboxes.$inferInsert diff --git a/db/schema/enums.ts b/db/schema/enums.ts new file mode 100644 index 0000000..3030302 --- /dev/null +++ b/db/schema/enums.ts @@ -0,0 +1,97 @@ +import { pgEnum } from "drizzle-orm/pg-core" + +// public.textTemplatePositions +export const textTemplatePositionsEnum = pgEnum("texttemplatepositions", [ + "startText", + "endText", +]) + +// public.folderFunctions +export const folderFunctionsEnum = pgEnum("folderfunctions", [ + "none", + "yearSubCategory", + "incomingInvoices", + "invoices", + "quotes", + "confirmationOrders", + "deliveryNotes", + "vehicleData", + "reminders", + "taxData", + "deposit", + "timeEvaluations", +]) + +// public.locked_tenant +export const lockedTenantEnum = pgEnum("locked_tenant", [ + "maintenance_tenant", + "maintenance", + "general", + "no_subscription", +]) + +// public.credential_types +export const credentialTypesEnum = pgEnum("credential_types", [ + "mail", + "m365", +]) + +// public.payment_types +export const paymentTypesEnum = pgEnum("payment_types", [ + "transfer", + "direct_debit", +]) + +// public.notification_status +export const notificationStatusEnum = pgEnum("notification_status", [ + "queued", + "sent", + "failed", + "read", +]) + +// public.notification_channel +export const notificationChannelEnum = pgEnum("notification_channel", [ + "email", + "inapp", + "sms", + "push", + "webhook", +]) + +// public.notification_severity +export const notificationSeverityEnum = pgEnum("notification_severity", [ + "info", + "success", + "warning", + "error", +]) + +// public.times_state +export const timesStateEnum = pgEnum("times_state", [ + "submitted", + "approved", + "draft", +]) + +export const helpdeskStatusEnum = [ + "open", + "in_progress", + "waiting_for_customer", + "answered", + "closed", +] as const + +export const helpdeskPriorityEnum = [ + "low", + "normal", + "high", +] as const + +export const helpdeskDirectionEnum = [ + "incoming", + "outgoing", + "internal", + "system", +] as const + diff --git a/db/schema/events.ts b/db/schema/events.ts new file mode 100644 index 0000000..b703c3e --- /dev/null +++ b/db/schema/events.ts @@ -0,0 +1,60 @@ +import { + pgTable, + bigint, + text, + timestamp, + boolean, + jsonb, + uuid, +} from "drizzle-orm/pg-core" + +import { tenants } from "./tenants" +import { customers } from "./customers" +import { authUsers } from "./auth_users" + +export const events = pgTable( + "events", + { + id: bigint("id", { mode: "number" }) + .primaryKey() + .generatedByDefaultAsIdentity(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + tenant: bigint("tenant", { mode: "number" }).notNull(), + + name: text("name").notNull(), + + startDate: timestamp("startDate", { withTimezone: true }).notNull(), + endDate: timestamp("endDate", { withTimezone: true }), + + eventtype: text("eventtype").default("Umsetzung"), + + project: bigint("project", { mode: "number" }), // FK follows when projects.ts exists + + resources: jsonb("resources").default([]), + notes: text("notes"), + link: text("link"), + + profiles: jsonb("profiles").notNull().default([]), + archived: boolean("archived").notNull().default(false), + + vehicles: jsonb("vehicles").notNull().default([]), + inventoryitems: jsonb("inventoryitems").notNull().default([]), + inventoryitemgroups: jsonb("inventoryitemgroups").notNull().default([]), + + customer: bigint("customer", { mode: "number" }).references( + () => customers.id + ), + + vendor: bigint("vendor", { mode: "number" }), // will link once vendors.ts is created + + updatedAt: timestamp("updated_at", { withTimezone: true }), + updatedBy: uuid("updated_by").references(() => authUsers.id), + } +) + +export type Event = typeof events.$inferSelect +export type NewEvent = typeof events.$inferInsert diff --git a/db/schema/files.ts b/db/schema/files.ts new file mode 100644 index 0000000..f118834 --- /dev/null +++ b/db/schema/files.ts @@ -0,0 +1,79 @@ +import { + pgTable, + uuid, + timestamp, + text, + boolean, + bigint, +} from "drizzle-orm/pg-core" + +import { tenants } from "./tenants" +import { projects } from "./projects" +import { customers } from "./customers" +import { contracts } from "./contracts" +import { vendors } from "./vendors" +import { incominginvoices } from "./incominginvoices" +import { plants } from "./plants" +import { createddocuments } from "./createddocuments" +import { vehicles } from "./vehicles" +import { products } from "./products" +import { inventoryItems } from "./inventoryitems" +import { folders } from "./folders" +import { filetags } from "./filetags" +import { authUsers } from "./auth_users" +import { authProfiles } from "./auth_profiles" +import { spaces } from "./spaces" +import { documentboxes } from "./documentboxes" +import { checks } from "./checks" + +export const files = pgTable("files", { + id: uuid("id").primaryKey().defaultRandom(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + path: text("path"), + + tenant: bigint("tenant", { mode: "number" }) + .notNull() + .references(() => tenants.id), + + project: bigint("project", { mode: "number" }).references(() => projects.id), + customer: bigint("customer", { mode: "number" }).references(() => customers.id), + contract: bigint("contract", { mode: "number" }).references(() => contracts.id), + vendor: bigint("vendor", { mode: "number" }).references(() => vendors.id), + incominginvoice: bigint("incominginvoice", { mode: "number" }).references(() => incominginvoices.id), + plant: bigint("plant", { mode: "number" }).references(() => plants.id), + createddocument: bigint("createddocument", { mode: "number" }).references(() => createddocuments.id), + vehicle: bigint("vehicle", { mode: "number" }).references(() => vehicles.id), + product: bigint("product", { mode: "number" }).references(() => products.id), + + check: uuid("check").references(() => checks.id), + + inventoryitem: bigint("inventoryitem", { mode: "number" }).references(() => inventoryItems.id), + + folder: uuid("folder").references(() => folders.id), + + mimeType: text("mimeType"), + + archived: boolean("archived").notNull().default(false), + + space: bigint("space", { mode: "number" }).references(() => spaces.id), + + type: uuid("type").references(() => filetags.id), + + documentbox: uuid("documentbox").references(() => documentboxes.id), + + name: text("name"), + + updatedAt: timestamp("updated_at", { withTimezone: true }), + updatedBy: uuid("updated_by").references(() => authUsers.id), + + createdBy: uuid("created_by").references(() => authUsers.id), + + authProfile: uuid("auth_profile").references(() => authProfiles.id), +}) + +export type File = typeof files.$inferSelect +export type NewFile = typeof files.$inferInsert diff --git a/db/schema/filetags.ts b/db/schema/filetags.ts new file mode 100644 index 0000000..3dc47cf --- /dev/null +++ b/db/schema/filetags.ts @@ -0,0 +1,33 @@ +import { + pgTable, + uuid, + timestamp, + text, + boolean, + bigint, +} from "drizzle-orm/pg-core" + +import { tenants } from "./tenants" + +export const filetags = pgTable("filetags", { + id: uuid("id").primaryKey().defaultRandom(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + name: text("name").notNull(), + color: text("color"), + + tenant: bigint("tenant", { mode: "number" }) + .notNull() + .references(() => tenants.id), + + createdDocumentType: text("createddocumenttype").default(""), + incomingDocumentType: text("incomingDocumentType"), + + archived: boolean("archived").notNull().default(false), +}) + +export type FileTag = typeof filetags.$inferSelect +export type NewFileTag = typeof filetags.$inferInsert diff --git a/db/schema/folders.ts b/db/schema/folders.ts new file mode 100644 index 0000000..74a9a11 --- /dev/null +++ b/db/schema/folders.ts @@ -0,0 +1,51 @@ +import { + pgTable, + uuid, + timestamp, + text, + boolean, + integer, + bigint, +} from "drizzle-orm/pg-core" + +import { tenants } from "./tenants" +import { authUsers } from "./auth_users" +import { filetags } from "./filetags" +import { folderFunctionsEnum } from "./enums" + +export const folders = pgTable("folders", { + id: uuid("id").primaryKey().defaultRandom(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + tenant: bigint("tenant", { mode: "number" }) + .notNull() + .references(() => tenants.id), + + name: text("name").notNull(), + icon: text("icon"), + + parent: uuid("parent").references(() => folders.id), + + isSystemUsed: boolean("isSystemUsed").notNull().default(false), + + function: folderFunctionsEnum("function"), + + year: integer("year"), + + standardFiletype: uuid("standardFiletype").references(() => filetags.id), + + standardFiletypeIsOptional: boolean("standardFiletypeIsOptional") + .notNull() + .default(true), + + updatedAt: timestamp("updated_at", { withTimezone: true }), + updatedBy: uuid("updated_by").references(() => authUsers.id), + + archived: boolean("archived").notNull().default(false), +}) + +export type Folder = typeof folders.$inferSelect +export type NewFolder = typeof folders.$inferInsert diff --git a/db/schema/generatedexports.ts b/db/schema/generatedexports.ts new file mode 100644 index 0000000..b2a5c84 --- /dev/null +++ b/db/schema/generatedexports.ts @@ -0,0 +1,35 @@ +import { + pgTable, + bigint, + timestamp, + text, +} from "drizzle-orm/pg-core" + +import { tenants } from "./tenants" + +export const generatedexports = pgTable("exports", { + id: bigint("id", { mode: "number" }) + .primaryKey() + .generatedByDefaultAsIdentity(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + tenantId: bigint("tenant_id", { mode: "number" }) + .notNull() + .references(() => tenants.id), + + startDate: timestamp("start_date", { withTimezone: true }).notNull(), + endDate: timestamp("end_date", { withTimezone: true }).notNull(), + + validUntil: timestamp("valid_until", { withTimezone: true }), + + type: text("type").notNull().default("datev"), + + url: text("url").notNull(), + filePath: text("file_path"), +}) + +export type Export = typeof generatedexports.$inferSelect +export type NewExport = typeof generatedexports.$inferInsert diff --git a/db/schema/globalmessages.ts b/db/schema/globalmessages.ts new file mode 100644 index 0000000..3a2ffb9 --- /dev/null +++ b/db/schema/globalmessages.ts @@ -0,0 +1,22 @@ +import { + pgTable, + bigint, + timestamp, + text, +} from "drizzle-orm/pg-core" + +export const globalmessages = pgTable("globalmessages", { + id: bigint("id", { mode: "number" }) + .primaryKey() + .generatedByDefaultAsIdentity(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + title: text("title"), + description: text("description"), +}) + +export type GlobalMessage = typeof globalmessages.$inferSelect +export type NewGlobalMessage = typeof globalmessages.$inferInsert diff --git a/db/schema/globalmessagesseen.ts b/db/schema/globalmessagesseen.ts new file mode 100644 index 0000000..7ee9c8e --- /dev/null +++ b/db/schema/globalmessagesseen.ts @@ -0,0 +1,17 @@ +import { + pgTable, + timestamp, + bigint, +} from "drizzle-orm/pg-core" + +import { globalmessages } from "./globalmessages" + +export const globalmessagesseen = pgTable("globalmessagesseen", { + message: bigint("message", { mode: "number" }) + .notNull() + .references(() => globalmessages.id), + + seenAt: timestamp("seen_at", { withTimezone: true }) + .notNull() + .defaultNow(), +}) diff --git a/db/schema/helpdesk_channel_instances.ts b/db/schema/helpdesk_channel_instances.ts new file mode 100644 index 0000000..29edfea --- /dev/null +++ b/db/schema/helpdesk_channel_instances.ts @@ -0,0 +1,44 @@ +import { + pgTable, + uuid, + timestamp, + text, + boolean, + jsonb, + bigint, +} from "drizzle-orm/pg-core" + +import { tenants } from "./tenants" +import { authUsers } from "./auth_users" +import { helpdesk_channel_types } from "./helpdesk_channel_types" + +export const helpdesk_channel_instances = pgTable("helpdesk_channel_instances", { + id: uuid("id").primaryKey().defaultRandom(), + + tenantId: bigint("tenant_id", { mode: "number" }) + .notNull() + .references(() => tenants.id, { onDelete: "cascade" }), + + typeId: text("type_id") + .notNull() + .references(() => helpdesk_channel_types.id), + + name: text("name").notNull(), + + isActive: boolean("is_active").notNull().default(true), + + config: jsonb("config").notNull(), + publicConfig: jsonb("public_config").notNull().default({}), + + publicToken: text("public_token").unique(), + secretToken: text("secret_token"), + + createdBy: uuid("created_by").references(() => authUsers.id), + + createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(), +}) + +export type HelpdeskChannelInstance = + typeof helpdesk_channel_instances.$inferSelect +export type NewHelpdeskChannelInstance = + typeof helpdesk_channel_instances.$inferInsert diff --git a/db/schema/helpdesk_channel_types.ts b/db/schema/helpdesk_channel_types.ts new file mode 100644 index 0000000..8b1a8f3 --- /dev/null +++ b/db/schema/helpdesk_channel_types.ts @@ -0,0 +1,9 @@ +import { pgTable, text } from "drizzle-orm/pg-core" + +export const helpdesk_channel_types = pgTable("helpdesk_channel_types", { + id: text("id").primaryKey(), + description: text("description").notNull(), +}) + +export type HelpdeskChannelType = typeof helpdesk_channel_types.$inferSelect +export type NewHelpdeskChannelType = typeof helpdesk_channel_types.$inferInsert diff --git a/db/schema/helpdesk_contacts.ts b/db/schema/helpdesk_contacts.ts new file mode 100644 index 0000000..baeab18 --- /dev/null +++ b/db/schema/helpdesk_contacts.ts @@ -0,0 +1,45 @@ +import { + pgTable, + uuid, + timestamp, + text, + jsonb, + bigint, +} from "drizzle-orm/pg-core" + +import { tenants } from "./tenants" +import { customers } from "./customers" +import { contacts } from "./contacts" +import { helpdesk_channel_instances } from "./helpdesk_channel_instances" // placeholder + +export const helpdesk_contacts = pgTable("helpdesk_contacts", { + id: uuid("id").primaryKey().defaultRandom(), + + tenantId: bigint("tenant_id", { mode: "number" }) + .notNull() + .references(() => tenants.id, { onDelete: "cascade" }), + + customerId: bigint("customer_id", { mode: "number" }) + .references(() => customers.id, { onDelete: "set null" }), + + email: text("email"), + phone: text("phone"), + + externalRef: jsonb("external_ref"), + displayName: text("display_name"), + + createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(), + + sourceChannelId: uuid("source_channel_id").references( + () => helpdesk_channel_instances.id, + { onDelete: "set null" } + ), + + contactId: bigint("contact_id", { mode: "number" }).references( + () => contacts.id, + { onDelete: "set null" } + ), +}) + +export type HelpdeskContact = typeof helpdesk_contacts.$inferSelect +export type NewHelpdeskContact = typeof helpdesk_contacts.$inferInsert diff --git a/db/schema/helpdesk_conversation_participants.ts b/db/schema/helpdesk_conversation_participants.ts new file mode 100644 index 0000000..c4d7c6b --- /dev/null +++ b/db/schema/helpdesk_conversation_participants.ts @@ -0,0 +1,34 @@ +import { + pgTable, + uuid, + text, +} from "drizzle-orm/pg-core" + +import { helpdesk_conversations } from "./helpdesk_conversations" +import { authUsers } from "./auth_users" + +export const helpdesk_conversation_participants = pgTable( + "helpdesk_conversation_participants", + { + conversationId: uuid("conversation_id") + .notNull() + .references(() => helpdesk_conversations.id, { onDelete: "cascade" }), + + userId: uuid("user_id") + .notNull() + .references(() => authUsers.id, { onDelete: "cascade" }), + + role: text("role"), + }, + (table) => ({ + pk: { + name: "helpdesk_conversation_participants_pkey", + columns: [table.conversationId, table.userId], + }, + }) +) + +export type HelpdeskConversationParticipant = + typeof helpdesk_conversation_participants.$inferSelect +export type NewHelpdeskConversationParticipant = + typeof helpdesk_conversation_participants.$inferInsert diff --git a/db/schema/helpdesk_conversations.ts b/db/schema/helpdesk_conversations.ts new file mode 100644 index 0000000..f2c14dd --- /dev/null +++ b/db/schema/helpdesk_conversations.ts @@ -0,0 +1,59 @@ +import { + pgTable, + uuid, + timestamp, + text, + bigint, +} from "drizzle-orm/pg-core" + +import { tenants } from "./tenants" +import { helpdesk_contacts } from "./helpdesk_contacts" +import { contacts } from "./contacts" +import { customers } from "./customers" +import { authUsers } from "./auth_users" +import { helpdesk_channel_instances } from "./helpdesk_channel_instances" + +export const helpdesk_conversations = pgTable("helpdesk_conversations", { + id: uuid("id").primaryKey().defaultRandom(), + + tenantId: bigint("tenant_id", { mode: "number" }) + .notNull() + .references(() => tenants.id, { onDelete: "cascade" }), + + channelInstanceId: uuid("channel_instance_id") + .notNull() + .references(() => helpdesk_channel_instances.id, { onDelete: "cascade" }), + + contactId: uuid("contact_id").references(() => helpdesk_contacts.id, { + onDelete: "set null", + }), + + subject: text("subject"), + + status: text("status").notNull().default("open"), + + priority: text("priority").default("normal"), + + assigneeUserId: uuid("assignee_user_id").references(() => authUsers.id), + + lastMessageAt: timestamp("last_message_at", { withTimezone: true }), + + createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(), + + customerId: bigint("customer_id", { mode: "number" }).references( + () => customers.id, + { onDelete: "set null" } + ), + + contactPersonId: bigint("contact_person_id", { mode: "number" }).references( + () => contacts.id, + { onDelete: "set null" } + ), + + ticketNumber: text("ticket_number"), +}) + +export type HelpdeskConversation = + typeof helpdesk_conversations.$inferSelect +export type NewHelpdeskConversation = + typeof helpdesk_conversations.$inferInsert diff --git a/db/schema/helpdesk_messages.ts b/db/schema/helpdesk_messages.ts new file mode 100644 index 0000000..08eec71 --- /dev/null +++ b/db/schema/helpdesk_messages.ts @@ -0,0 +1,46 @@ +import { + pgTable, + uuid, + timestamp, + text, + jsonb, + bigint, +} from "drizzle-orm/pg-core" + +import { tenants } from "./tenants" +import { helpdesk_contacts } from "./helpdesk_contacts" +import { helpdesk_conversations } from "./helpdesk_conversations" +import { authUsers } from "./auth_users" + +export const helpdesk_messages = pgTable("helpdesk_messages", { + id: uuid("id").primaryKey().defaultRandom(), + + tenantId: bigint("tenant_id", { mode: "number" }) + .notNull() + .references(() => tenants.id, { onDelete: "cascade" }), + + conversationId: uuid("conversation_id") + .notNull() + .references(() => helpdesk_conversations.id, { onDelete: "cascade" }), + + direction: text("direction").notNull(), + + authorUserId: uuid("author_user_id").references(() => authUsers.id), + + payload: jsonb("payload").notNull(), + + rawMeta: jsonb("raw_meta"), + + createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(), + + contactId: uuid("contact_id").references(() => helpdesk_contacts.id, { + onDelete: "set null", + }), + + externalMessageId: text("external_message_id").unique(), + + receivedAt: timestamp("received_at", { withTimezone: true }).defaultNow(), +}) + +export type HelpdeskMessage = typeof helpdesk_messages.$inferSelect +export type NewHelpdeskMessage = typeof helpdesk_messages.$inferInsert diff --git a/db/schema/helpdesk_routing_rules.ts b/db/schema/helpdesk_routing_rules.ts new file mode 100644 index 0000000..4c3065b --- /dev/null +++ b/db/schema/helpdesk_routing_rules.ts @@ -0,0 +1,33 @@ +import { + pgTable, + uuid, + timestamp, + text, + jsonb, + bigint, +} from "drizzle-orm/pg-core" + +import { tenants } from "./tenants" +import { authUsers } from "./auth_users" + +export const helpdesk_routing_rules = pgTable("helpdesk_routing_rules", { + id: uuid("id").primaryKey().defaultRandom(), + + tenantId: bigint("tenant_id", { mode: "number" }) + .notNull() + .references(() => tenants.id, { onDelete: "cascade" }), + + name: text("name").notNull(), + + condition: jsonb("condition").notNull(), + action: jsonb("action").notNull(), + + createdBy: uuid("created_by").references(() => authUsers.id), + + createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(), +}) + +export type HelpdeskRoutingRule = + typeof helpdesk_routing_rules.$inferSelect +export type NewHelpdeskRoutingRule = + typeof helpdesk_routing_rules.$inferInsert diff --git a/db/schema/historyitems.ts b/db/schema/historyitems.ts new file mode 100644 index 0000000..40da4c9 --- /dev/null +++ b/db/schema/historyitems.ts @@ -0,0 +1,140 @@ +import { + pgTable, + bigint, + uuid, + timestamp, + text, + jsonb, +} from "drizzle-orm/pg-core" + +import { tenants } from "./tenants" +import { customers } from "./customers" +import { vendors } from "./vendors" +import { projects } from "./projects" +import { plants } from "./plants" +import { incominginvoices } from "./incominginvoices" +import { contacts } from "./contacts" +import { inventoryItems } from "./inventoryitems" +import { products } from "./products" +import { tasks } from "./tasks" +import { vehicles } from "./vehicles" +import { bankstatements } from "./bankstatements" +import { spaces } from "./spaces" +import { costcentres } from "./costcentres" +import { ownaccounts } from "./ownaccounts" +import { createddocuments } from "./createddocuments" +import { documentboxes } from "./documentboxes" +import { hourrates } from "./hourrates" +import { projecttypes } from "./projecttypes" +import { checks } from "./checks" +import { services } from "./services" +import { events } from "./events" +import { inventoryitemgroups } from "./inventoryitemgroups" +import { authUsers } from "./auth_users" +import {files} from "./files"; + +export const historyitems = pgTable("historyitems", { + id: bigint("id", { mode: "number" }) + .primaryKey() + .generatedByDefaultAsIdentity(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + text: text("text").notNull(), + + customer: bigint("customer", { mode: "number" }).references( + () => customers.id, + { onDelete: "cascade" } + ), + + tenant: bigint("tenant", { mode: "number" }) + .notNull() + .references(() => tenants.id), + + vendor: bigint("vendor", { mode: "number" }).references(() => vendors.id), + + project: bigint("project", { mode: "number" }).references( + () => projects.id, + { onDelete: "cascade" } + ), + + plant: bigint("plant", { mode: "number" }).references( + () => plants.id, + { onDelete: "cascade" } + ), + + incomingInvoice: bigint("incomingInvoice", { mode: "number" }).references( + () => incominginvoices.id, + { onDelete: "cascade" } + ), + + contact: bigint("contact", { mode: "number" }).references(() => contacts.id, { + onDelete: "cascade", + }), + + inventoryitem: bigint("inventoryitem", { mode: "number" }).references( + () => inventoryItems.id, + { onDelete: "cascade" } + ), + + product: bigint("product", { mode: "number" }).references( + () => products.id, + { onDelete: "cascade" } + ), + + event: bigint("event", { mode: "number" }).references(() => events.id), + + newVal: text("newVal"), + oldVal: text("oldVal"), + + task: bigint("task", { mode: "number" }).references(() => tasks.id), + + vehicle: bigint("vehicle", { mode: "number" }).references(() => vehicles.id), + + bankstatement: bigint("bankstatement", { mode: "number" }).references( + () => bankstatements.id + ), + + space: bigint("space", { mode: "number" }).references(() => spaces.id), + + config: jsonb("config"), + + projecttype: bigint("projecttype", { mode: "number" }).references( + () => projecttypes.id + ), + + check: uuid("check").references(() => checks.id), + + service: bigint("service", { mode: "number" }).references( + () => services.id + ), + + createddocument: bigint("createddocument", { mode: "number" }).references( + () => createddocuments.id + ), + + file: uuid("file").references(() => files.id), + + inventoryitemgroup: uuid("inventoryitemgroup").references( + () => inventoryitemgroups.id + ), + + source: text("source").default("Software"), + + costcentre: uuid("costcentre").references(() => costcentres.id), + + ownaccount: uuid("ownaccount").references(() => ownaccounts.id), + + documentbox: uuid("documentbox").references(() => documentboxes.id), + + hourrate: uuid("hourrate").references(() => hourrates.id), + + createdBy: uuid("created_by").references(() => authUsers.id), + + action: text("action"), +}) + +export type HistoryItem = typeof historyitems.$inferSelect +export type NewHistoryItem = typeof historyitems.$inferInsert diff --git a/db/schema/holidays.ts b/db/schema/holidays.ts new file mode 100644 index 0000000..76d3e9e --- /dev/null +++ b/db/schema/holidays.ts @@ -0,0 +1,18 @@ +import { pgTable, bigint, date, text, timestamp } from "drizzle-orm/pg-core" + +export const holidays = pgTable("holidays", { + id: bigint("id", { mode: "number" }) + .primaryKey() + .generatedAlwaysAsIdentity(), + + date: date("date").notNull(), + + name: text("name").notNull(), + + stateCode: text("state_code").notNull(), + + createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(), +}) + +export type Holiday = typeof holidays.$inferSelect +export type NewHoliday = typeof holidays.$inferInsert diff --git a/db/schema/hourrates.ts b/db/schema/hourrates.ts new file mode 100644 index 0000000..299c2d5 --- /dev/null +++ b/db/schema/hourrates.ts @@ -0,0 +1,27 @@ +import { pgTable, uuid, timestamp, text, boolean, bigint, doublePrecision } from "drizzle-orm/pg-core" + +import { tenants } from "./tenants" +import { authUsers } from "./auth_users" + +export const hourrates = pgTable("hourrates", { + id: uuid("id").primaryKey().defaultRandom(), + + createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), + + tenant: bigint("tenant", { mode: "number" }) + .notNull() + .references(() => tenants.id), + + name: text("name").notNull(), + + purchasePrice: doublePrecision("purchasePrice").notNull(), + sellingPrice: doublePrecision("sellingPrice").notNull(), + + archived: boolean("archived").notNull().default(false), + + updatedAt: timestamp("updated_at", { withTimezone: true }), + updatedBy: uuid("updated_by").references(() => authUsers.id), +}) + +export type HourRate = typeof hourrates.$inferSelect +export type NewHourRate = typeof hourrates.$inferInsert diff --git a/db/schema/incominginvoices.ts b/db/schema/incominginvoices.ts new file mode 100644 index 0000000..07ac711 --- /dev/null +++ b/db/schema/incominginvoices.ts @@ -0,0 +1,63 @@ +import { + pgTable, + bigint, + timestamp, + text, + boolean, + jsonb, + uuid, +} from "drizzle-orm/pg-core" + +import { vendors } from "./vendors" +import { tenants } from "./tenants" +import { authUsers } from "./auth_users" + +export const incominginvoices = pgTable("incominginvoices", { + id: bigint("id", { mode: "number" }) + .primaryKey() + .generatedByDefaultAsIdentity(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + tenant: bigint("tenant", { mode: "number" }) + .notNull() + .references(() => tenants.id), + + state: text("state").notNull().default("Entwurf"), + + vendor: bigint("vendor", { mode: "number" }).references(() => vendors.id), + + reference: text("reference"), + date: text("date"), + + document: bigint("document", { mode: "number" }), + + dueDate: text("dueDate"), + + description: text("description"), + + paymentType: text("paymentType"), + + accounts: jsonb("accounts").notNull().default([ + { + account: null, + taxType: null, + amountNet: null, + amountTax: 19, + costCentre: null, + }, + ]), + + paid: boolean("paid").notNull().default(false), + expense: boolean("expense").notNull().default(true), + + updatedAt: timestamp("updated_at", { withTimezone: true }), + updatedBy: uuid("updated_by").references(() => authUsers.id), + + archived: boolean("archived").notNull().default(false), +}) + +export type IncomingInvoice = typeof incominginvoices.$inferSelect +export type NewIncomingInvoice = typeof incominginvoices.$inferInsert diff --git a/db/schema/index.ts b/db/schema/index.ts new file mode 100644 index 0000000..4208507 --- /dev/null +++ b/db/schema/index.ts @@ -0,0 +1,70 @@ +export * from "./accounts" +export * from "./auth_profiles" +export * from "./auth_role_permisssions" +export * from "./auth_roles" +export * from "./auth_tenant_users" +export * from "./auth_user_roles" +export * from "./auth_users" +export * from "./bankaccounts" +export * from "./bankrequisitions" +export * from "./bankstatements" +export * from "./checkexecutions" +export * from "./checks" +export * from "./citys" +export * from "./contacts" +export * from "./contracts" +export * from "./costcentres" +export * from "./countrys" +export * from "./createddocuments" +export * from "./createdletters" +export * from "./customers" +export * from "./devices" +export * from "./documentboxes" +export * from "./enums" +export * from "./events" +export * from "./files" +export * from "./filetags" +export * from "./folders" +export * from "./generatedexports" +export * from "./globalmessages" +export * from "./globalmessagesseen" +export * from "./helpdesk_channel_instances" +export * from "./helpdesk_channel_types" +export * from "./helpdesk_contacts" +export * from "./helpdesk_conversation_participants" +export * from "./helpdesk_conversations" +export * from "./helpdesk_messages" +export * from "./helpdesk_routing_rules" +export * from "./historyitems" +export * from "./holidays" +export * from "./hourrates" +export * from "./incominginvoices" +export * from "./inventoryitemgroups" +export * from "./inventoryitems" +export * from "./letterheads" +export * from "./movements" +export * from "./notifications_event_types" +export * from "./notifications_items" +export * from "./notifications_preferences" +export * from "./notifications_preferences_defaults" +export * from "./ownaccounts" +export * from "./plants" +export * from "./productcategories" +export * from "./products" +export * from "./projects" +export * from "./projecttypes" +export * from "./servicecategories" +export * from "./services" +export * from "./spaces" +export * from "./staff_time_entries" +export * from "./staff_time_entry_connects" +export * from "./staff_zeitstromtimestamps" +export * from "./statementallocations" +export * from "./tasks" +export * from "./taxtypes" +export * from "./tenants" +export * from "./texttemplates" +export * from "./units" +export * from "./user_credentials" +export * from "./vehicles" +export * from "./vendors" \ No newline at end of file diff --git a/db/schema/inventoryitemgroups.ts b/db/schema/inventoryitemgroups.ts new file mode 100644 index 0000000..3fe9dc1 --- /dev/null +++ b/db/schema/inventoryitemgroups.ts @@ -0,0 +1,39 @@ +import { + pgTable, + uuid, + timestamp, + text, + boolean, + jsonb, bigint, +} from "drizzle-orm/pg-core" + +import { tenants } from "./tenants" +import { authUsers } from "./auth_users" + +export const inventoryitemgroups = pgTable("inventoryitemgroups", { + id: uuid("id").primaryKey().defaultRandom(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + tenant: bigint("tenant", { mode: "number" }).notNull().references(() => tenants.id), + + name: text("name").notNull(), + + inventoryitems: jsonb("inventoryitems").notNull().default([]), + + description: text("description"), + + archived: boolean("archived").notNull().default(false), + + profiles: jsonb("profiles").notNull().default([]), + + usePlanning: boolean("usePlanning").notNull().default(false), + + updatedAt: timestamp("updated_at", { withTimezone: true }), + updatedBy: uuid("updated_by").references(() => authUsers.id), +}) + +export type InventoryItemGroup = typeof inventoryitemgroups.$inferSelect +export type NewInventoryItemGroup = typeof inventoryitemgroups.$inferInsert diff --git a/db/schema/inventoryitems.ts b/db/schema/inventoryitems.ts new file mode 100644 index 0000000..9a56d5d --- /dev/null +++ b/db/schema/inventoryitems.ts @@ -0,0 +1,68 @@ +import { + pgTable, + bigint, + timestamp, + text, + boolean, + doublePrecision, + uuid, + jsonb, + date, +} from "drizzle-orm/pg-core" + +import { tenants } from "./tenants" +import { vendors } from "./vendors" +import { spaces } from "./spaces" +import { authUsers } from "./auth_users" + +export const inventoryItems = pgTable("inventoryitems", { + id: bigint("id", { mode: "number" }) + .primaryKey() + .generatedByDefaultAsIdentity(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + name: text("name").notNull(), + + usePlanning: boolean("usePlanning").notNull().default(false), + + description: text("description"), + + tenant: bigint("tenant", { mode: "number" }) + .notNull() + .references(() => tenants.id), + + currentSpace: bigint("currentSpace", { mode: "number" }).references( + () => spaces.id + ), + + articleNumber: text("articleNumber"), + serialNumber: text("serialNumber"), + + purchaseDate: date("purchaseDate"), + + vendor: bigint("vendor", { mode: "number" }).references(() => vendors.id), + + quantity: bigint("quantity", { mode: "number" }).notNull().default(0), + + purchasePrice: doublePrecision("purchasePrice").default(0), + + manufacturer: text("manufacturer"), + manufacturerNumber: text("manufacturerNumber"), + + currentValue: doublePrecision("currentValue"), + + archived: boolean("archived").notNull().default(false), + + profiles: jsonb("profiles").notNull().default([]), + + updatedAt: timestamp("updated_at", { withTimezone: true }), + updatedBy: uuid("updated_by").references(() => + authUsers.id + ), +}) + +export type InventoryItem = typeof inventoryItems.$inferSelect +export type NewInventoryItem = typeof inventoryItems.$inferInsert diff --git a/db/schema/letterheads.ts b/db/schema/letterheads.ts new file mode 100644 index 0000000..2cbc534 --- /dev/null +++ b/db/schema/letterheads.ts @@ -0,0 +1,39 @@ +import { + pgTable, + bigint, + timestamp, + text, + boolean, + uuid, +} from "drizzle-orm/pg-core" + +import { tenants } from "./tenants" +import { authUsers } from "./auth_users" + +export const letterheads = pgTable("letterheads", { + id: bigint("id", { mode: "number" }) + .primaryKey() + .generatedByDefaultAsIdentity(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + tenant: bigint("tenant", { mode: "number" }) + .notNull() + .references(() => tenants.id), + + name: text("name").default("Standard"), + + path: text("path").notNull(), + + documentTypes: text("documentTypes").array().notNull().default([]), + + updatedAt: timestamp("updated_at", { withTimezone: true }), + updatedBy: uuid("updated_by").references(() => authUsers.id), + + archived: boolean("archived").notNull().default(false), +}) + +export type Letterhead = typeof letterheads.$inferSelect +export type NewLetterhead = typeof letterheads.$inferInsert diff --git a/db/schema/movements.ts b/db/schema/movements.ts new file mode 100644 index 0000000..72a2759 --- /dev/null +++ b/db/schema/movements.ts @@ -0,0 +1,49 @@ +import { + pgTable, + bigint, + timestamp, + text, + uuid, +} from "drizzle-orm/pg-core" + +import { products } from "./products" +import { spaces } from "./spaces" +import { tenants } from "./tenants" +import { projects } from "./projects" +import { authUsers } from "./auth_users" + +export const movements = pgTable("movements", { + id: bigint("id", { mode: "number" }) + .primaryKey() + .generatedByDefaultAsIdentity(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + quantity: bigint("quantity", { mode: "number" }).notNull(), + + productId: bigint("productId", { mode: "number" }) + .notNull() + .references(() => products.id), + + spaceId: bigint("spaceId", { mode: "number" }).references(() => spaces.id), + + tenant: bigint("tenant", { mode: "number" }) + .notNull() + .references(() => tenants.id), + + projectId: bigint("projectId", { mode: "number" }).references( + () => projects.id + ), + + notes: text("notes"), + + serials: text("serials").array(), + + updatedAt: timestamp("updated_at", { withTimezone: true }), + updatedBy: uuid("updated_by").references(() => authUsers.id), +}) + +export type Movement = typeof movements.$inferSelect +export type NewMovement = typeof movements.$inferInsert diff --git a/db/schema/notifications_event_types.ts b/db/schema/notifications_event_types.ts new file mode 100644 index 0000000..c16d648 --- /dev/null +++ b/db/schema/notifications_event_types.ts @@ -0,0 +1,34 @@ +import { + pgTable, + text, + jsonb, + boolean, + timestamp, +} from "drizzle-orm/pg-core" +import {notificationSeverityEnum} from "./enums"; + + +export const notificationsEventTypes = pgTable("notifications_event_types", { + eventKey: text("event_key").primaryKey(), + + displayName: text("display_name").notNull(), + description: text("description"), + category: text("category"), + + severity: notificationSeverityEnum("severity").notNull().default("info"), + + allowedChannels: jsonb("allowed_channels").notNull().default(["inapp", "email"]), + + payloadSchema: jsonb("payload_schema"), + + isActive: boolean("is_active").notNull().default(true), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), +}) + +export type NotificationsEventType = + typeof notificationsEventTypes.$inferSelect +export type NewNotificationsEventType = + typeof notificationsEventTypes.$inferInsert diff --git a/db/schema/notifications_items.ts b/db/schema/notifications_items.ts new file mode 100644 index 0000000..d6c6cb3 --- /dev/null +++ b/db/schema/notifications_items.ts @@ -0,0 +1,54 @@ +import { + pgTable, + uuid, + bigint, + text, + jsonb, + timestamp, +} from "drizzle-orm/pg-core" + +import { tenants } from "./tenants" +import { authUsers } from "./auth_users" +import { notificationsEventTypes } from "./notifications_event_types" +import {notificationChannelEnum, notificationStatusEnum} from "./enums"; + + +export const notificationsItems = pgTable("notifications_items", { + id: uuid("id").primaryKey().defaultRandom(), + + tenantId: bigint("tenant_id", { mode: "number" }) + .notNull() + .references(() => tenants.id, { onDelete: "cascade", onUpdate: "cascade" }), + + userId: uuid("user_id") + .notNull() + .references(() => authUsers.id, { onDelete: "cascade", onUpdate: "cascade" }), + + eventType: text("event_type") + .notNull() + .references(() => notificationsEventTypes.eventKey, { + onUpdate: "cascade", + onDelete: "restrict", + }), + + title: text("title").notNull(), + message: text("message").notNull(), + + payload: jsonb("payload"), + + channel: notificationChannelEnum("channel").notNull(), + + status: notificationStatusEnum("status").notNull().default("queued"), + + error: text("error"), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + sentAt: timestamp("sent_at", { withTimezone: true }), + readAt: timestamp("read_at", { withTimezone: true }), +}) + +export type NotificationItem = typeof notificationsItems.$inferSelect +export type NewNotificationItem = typeof notificationsItems.$inferInsert diff --git a/db/schema/notifications_preferences.ts b/db/schema/notifications_preferences.ts new file mode 100644 index 0000000..8397a0a --- /dev/null +++ b/db/schema/notifications_preferences.ts @@ -0,0 +1,60 @@ +import { + pgTable, + uuid, + bigint, + text, + boolean, + timestamp, + uniqueIndex, +} from "drizzle-orm/pg-core" + +import { tenants } from "./tenants" +import { authUsers } from "./auth_users" +import { notificationsEventTypes } from "./notifications_event_types" +import {notificationChannelEnum} from "./enums"; + +export const notificationsPreferences = pgTable( + "notifications_preferences", + { + id: uuid("id").primaryKey().defaultRandom(), + + tenantId: bigint("tenant_id", { mode: "number" }) + .notNull() + .references(() => tenants.id, { + onDelete: "cascade", + onUpdate: "cascade", + }), + + userId: uuid("user_id") + .notNull() + .references(() => authUsers.id, { + onDelete: "cascade", + onUpdate: "cascade", + }), + + eventType: text("event_type") + .notNull() + .references(() => notificationsEventTypes.eventKey, { + onDelete: "restrict", + onUpdate: "cascade", + }), + + channel: notificationChannelEnum("channel").notNull(), + + enabled: boolean("enabled").notNull().default(true), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + }, + (table) => ({ + uniquePrefs: uniqueIndex( + "notifications_preferences_tenant_id_user_id_event_type_chan_key", + ).on(table.tenantId, table.userId, table.eventType, table.channel), + }), +) + +export type NotificationPreference = + typeof notificationsPreferences.$inferSelect +export type NewNotificationPreference = + typeof notificationsPreferences.$inferInsert diff --git a/db/schema/notifications_preferences_defaults.ts b/db/schema/notifications_preferences_defaults.ts new file mode 100644 index 0000000..3c263c9 --- /dev/null +++ b/db/schema/notifications_preferences_defaults.ts @@ -0,0 +1,52 @@ +import { + pgTable, + uuid, + bigint, + text, + boolean, + timestamp, + uniqueIndex, +} from "drizzle-orm/pg-core" + +import { tenants } from "./tenants" +import { notificationsEventTypes } from "./notifications_event_types" +import {notificationChannelEnum} from "./enums"; + +export const notificationsPreferencesDefaults = pgTable( + "notifications_preferences_defaults", + { + id: uuid("id").primaryKey().defaultRandom(), + + tenantId: bigint("tenant_id", { mode: "number" }) + .notNull() + .references(() => tenants.id, { + onDelete: "cascade", + onUpdate: "cascade", + }), + + eventKey: text("event_key") + .notNull() + .references(() => notificationsEventTypes.eventKey, { + onDelete: "restrict", + onUpdate: "cascade", + }), + + channel: notificationChannelEnum("channel").notNull(), + + enabled: boolean("enabled").notNull().default(true), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + }, + (table) => ({ + uniqueDefaults: uniqueIndex( + "notifications_preferences_defau_tenant_id_event_key_channel_key", + ).on(table.tenantId, table.eventKey, table.channel), + }), +) + +export type NotificationPreferenceDefault = + typeof notificationsPreferencesDefaults.$inferSelect +export type NewNotificationPreferenceDefault = + typeof notificationsPreferencesDefaults.$inferInsert diff --git a/db/schema/ownaccounts.ts b/db/schema/ownaccounts.ts new file mode 100644 index 0000000..1970adc --- /dev/null +++ b/db/schema/ownaccounts.ts @@ -0,0 +1,39 @@ +import { + pgTable, + uuid, + timestamp, + text, + boolean, + jsonb, + bigint, +} from "drizzle-orm/pg-core" + +import { tenants } from "./tenants" +import { authUsers } from "./auth_users" + +export const ownaccounts = pgTable("ownaccounts", { + id: uuid("id").primaryKey().defaultRandom(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + tenant: bigint("tenant", { mode: "number" }) + .notNull() + .references(() => tenants.id), + + number: text("number").notNull(), + name: text("name").notNull(), + + description: text("description"), + + archived: boolean("archived").notNull().default(false), + + profiles: jsonb("profiles").notNull().default([]), + + updatedAt: timestamp("updated_at", { withTimezone: true }), + updatedBy: uuid("updated_by").references(() => authUsers.id), +}) + +export type OwnAccount = typeof ownaccounts.$inferSelect +export type NewOwnAccount = typeof ownaccounts.$inferInsert diff --git a/db/schema/plants.ts b/db/schema/plants.ts new file mode 100644 index 0000000..a7b35f4 --- /dev/null +++ b/db/schema/plants.ts @@ -0,0 +1,56 @@ +import { + pgTable, + bigint, + timestamp, + text, + jsonb, + boolean, + uuid, + date, +} from "drizzle-orm/pg-core" + +import { tenants } from "./tenants" +import { customers } from "./customers" +import { contracts } from "./contracts" +import { authUsers } from "./auth_users" + +export const plants = pgTable("plants", { + id: bigint("id", { mode: "number" }) + .primaryKey() + .generatedByDefaultAsIdentity(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + tenant: bigint("tenant", { mode: "number" }) + .notNull() + .references(() => tenants.id), + + name: text("name").notNull(), + + customer: bigint("customer", { mode: "number" }).references( + () => customers.id + ), + + infoData: jsonb("infoData"), + contract: bigint("contract", { mode: "number" }).references( + () => contracts.id + ), + + description: jsonb("description").default({ + html: "", + json: [], + text: "", + }), + + archived: boolean("archived").notNull().default(false), + + profiles: jsonb("profiles").notNull().default([]), + + updatedAt: timestamp("updated_at", { withTimezone: true }), + updatedBy: uuid("updated_by").references(() => authUsers.id), +}) + +export type Plant = typeof plants.$inferSelect +export type NewPlant = typeof plants.$inferInsert diff --git a/db/schema/productcategories.ts b/db/schema/productcategories.ts new file mode 100644 index 0000000..ec8753c --- /dev/null +++ b/db/schema/productcategories.ts @@ -0,0 +1,37 @@ +import { + pgTable, + bigint, + timestamp, + text, + boolean, + uuid, +} from "drizzle-orm/pg-core" + +import { tenants } from "./tenants" +import { authUsers } from "./auth_users" + +export const productCategories = pgTable("productcategories", { + id: bigint("id", { mode: "number" }) + .primaryKey() + .generatedByDefaultAsIdentity(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + tenant: bigint("tenant", { mode: "number" }) + .notNull() + .references(() => tenants.id), + + name: text("name").notNull(), + + description: text("description"), + + archived: boolean("archived").notNull().default(false), + + updatedAt: timestamp("updated_at", { withTimezone: true }), + updatedBy: uuid("updated_by").references(() => authUsers.id), +}) + +export type ProductCategory = typeof productCategories.$inferSelect +export type NewProductCategory = typeof productCategories.$inferInsert diff --git a/db/schema/products.ts b/db/schema/products.ts new file mode 100644 index 0000000..3379d21 --- /dev/null +++ b/db/schema/products.ts @@ -0,0 +1,69 @@ +import { + pgTable, + bigint, + timestamp, + text, + doublePrecision, + boolean, + smallint, + uuid, + jsonb, + json, +} from "drizzle-orm/pg-core" + +import { tenants } from "./tenants" +import { units } from "./units" +import { authUsers } from "./auth_users" + +export const products = pgTable("products", { + id: bigint("id", { mode: "number" }) + .primaryKey() + .generatedByDefaultAsIdentity(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + name: text("name").notNull(), + manufacturer: text("manufacturer"), + + unit: bigint("unit", { mode: "number" }) + .notNull() + .references(() => units.id), + + tags: json("tags").notNull().default([]), + + tenant: bigint("tenant", { mode: "number" }) + .notNull() + .references(() => tenants.id), + + ean: text("ean"), + barcode: text("barcode"), + + purchasePrice: doublePrecision("purchasePrice"), + sellingPrice: doublePrecision("sellingPrice"), + + description: text("description"), + + manufacturerNumber: text("manufacturerNumber"), + + vendorAllocation: jsonb("vendorAllocation").default([]), + + articleNumber: text("articleNumber"), + + barcodes: text("barcodes").array().notNull().default([]), + + productcategories: jsonb("productcategories").default([]), + + archived: boolean("archived").notNull().default(false), + + taxPercentage: smallint("taxPercentage").notNull().default(19), + + markupPercentage: doublePrecision("markupPercentage"), + + updatedAt: timestamp("updated_at", { withTimezone: true }), + updatedBy: uuid("updated_by").references(() => authUsers.id), +}) + +export type Product = typeof products.$inferSelect +export type NewProduct = typeof products.$inferInsert diff --git a/db/schema/projects.ts b/db/schema/projects.ts new file mode 100644 index 0000000..fc12993 --- /dev/null +++ b/db/schema/projects.ts @@ -0,0 +1,78 @@ +import { + pgTable, + bigint, + timestamp, + text, + jsonb, + json, + boolean, + uuid, +} from "drizzle-orm/pg-core" + +import { tenants } from "./tenants" +import { customers } from "./customers" +import { contracts } from "./contracts" +import { projecttypes } from "./projecttypes" +import { authUsers } from "./auth_users" + +export const projects = pgTable("projects", { + id: bigint("id", { mode: "number" }) + .primaryKey() + .generatedByDefaultAsIdentity(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + tenant: bigint("tenant", { mode: "number" }) + .notNull() + .references(() => tenants.id), + + name: text("name").notNull(), + + notes: text("notes"), + + customer: bigint("customer", { mode: "number" }).references( + () => customers.id + ), + + phases: jsonb("phases").default([]), + + description: json("description"), + + forms: jsonb("forms").default([]), + + heroId: text("heroId"), + + measure: text("measure"), + + material: jsonb("material"), + + plant: bigint("plant", { mode: "number" }), + + profiles: uuid("profiles").array().notNull().default([]), + + projectNumber: text("projectNumber"), + + contract: bigint("contract", { mode: "number" }).references( + () => contracts.id + ), + + projectType: text("projectType").default("Projekt"), + + projecttype: bigint("projecttype", { mode: "number" }).references( + () => projecttypes.id + ), + + archived: boolean("archived").notNull().default(false), + + customerRef: text("customerRef"), + + updatedAt: timestamp("updated_at", { withTimezone: true }), + updatedBy: uuid("updated_by").references(() => authUsers.id), + + activePhase: text("active_phase"), +}) + +export type Project = typeof projects.$inferSelect +export type NewProject = typeof projects.$inferInsert diff --git a/db/schema/projecttypes.ts b/db/schema/projecttypes.ts new file mode 100644 index 0000000..fc7b90d --- /dev/null +++ b/db/schema/projecttypes.ts @@ -0,0 +1,41 @@ +import { + pgTable, + bigint, + timestamp, + text, + jsonb, + boolean, + uuid, +} from "drizzle-orm/pg-core" + +import { tenants } from "./tenants" +import { authUsers } from "./auth_users" + +export const projecttypes = pgTable("projecttypes", { + id: bigint("id", { mode: "number" }) + .primaryKey() + .generatedByDefaultAsIdentity(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + name: text("name").notNull(), + + initialPhases: jsonb("initialPhases"), + addablePhases: jsonb("addablePhases"), + + icon: text("icon"), + + tenant: bigint("tenant", { mode: "number" }) + .notNull() + .references(() => tenants.id), + + archived: boolean("archived").notNull().default(false), + + updatedAt: timestamp("updated_at", { withTimezone: true }), + updatedBy: uuid("updated_by").references(() => authUsers.id), +}) + +export type ProjectType = typeof projecttypes.$inferSelect +export type NewProjectType = typeof projecttypes.$inferInsert diff --git a/db/schema/servicecategories.ts b/db/schema/servicecategories.ts new file mode 100644 index 0000000..25ba702 --- /dev/null +++ b/db/schema/servicecategories.ts @@ -0,0 +1,39 @@ +import { + pgTable, + bigint, + timestamp, + text, + doublePrecision, + boolean, + uuid, +} from "drizzle-orm/pg-core" + +import { tenants } from "./tenants" +import { authUsers } from "./auth_users" + +export const serviceCategories = pgTable("servicecategories", { + id: bigint("id", { mode: "number" }) + .primaryKey() + .generatedByDefaultAsIdentity(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + tenant: bigint("tenant", { mode: "number" }) + .notNull() + .references(() => tenants.id), + + name: text("name").notNull(), + description: text("description"), + + discount: doublePrecision("discount").default(0), + + archived: boolean("archived").notNull().default(false), + + updatedAt: timestamp("updated_at", { withTimezone: true }), + updatedBy: uuid("updated_by").references(() => authUsers.id), +}) + +export type ServiceCategory = typeof serviceCategories.$inferSelect +export type NewServiceCategory = typeof serviceCategories.$inferInsert diff --git a/db/schema/services.ts b/db/schema/services.ts new file mode 100644 index 0000000..0dcdd4b --- /dev/null +++ b/db/schema/services.ts @@ -0,0 +1,63 @@ +import { + pgTable, + bigint, + timestamp, + text, + doublePrecision, + jsonb, + boolean, + smallint, + uuid, +} from "drizzle-orm/pg-core" + +import { tenants } from "./tenants" +import { units } from "./units" +import { authUsers } from "./auth_users" + +export const services = pgTable("services", { + id: bigint("id", { mode: "number" }) + .primaryKey() + .generatedByDefaultAsIdentity(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + name: text("name").notNull(), + + sellingPrice: doublePrecision("sellingPrice"), + + description: text("description"), + + tenant: bigint("tenant", { mode: "number" }) + .notNull() + .references(() => tenants.id), + + unit: bigint("unit", { mode: "number" }).references(() => units.id), + + serviceNumber: bigint("serviceNumber", { mode: "number" }), + + tags: jsonb("tags").default([]), + servicecategories: jsonb("servicecategories").notNull().default([]), + + archived: boolean("archived").notNull().default(false), + + purchasePriceComposed: jsonb("purchasePriceComposed") + .notNull() + .default({ total: 0 }), + + sellingPriceComposed: jsonb("sellingPriceComposed") + .notNull() + .default({ total: 0 }), + + taxPercentage: smallint("taxPercentage").notNull().default(19), + + materialComposition: jsonb("materialComposition").notNull().default([]), + personalComposition: jsonb("personalComposition").notNull().default([]), + + updatedAt: timestamp("updated_at", { withTimezone: true }), + updatedBy: uuid("updated_by").references(() => authUsers.id), +}) + +export type Service = typeof services.$inferSelect +export type NewService = typeof services.$inferInsert diff --git a/db/schema/spaces.ts b/db/schema/spaces.ts new file mode 100644 index 0000000..b0d8116 --- /dev/null +++ b/db/schema/spaces.ts @@ -0,0 +1,49 @@ +import { + pgTable, + bigint, + timestamp, + text, + boolean, + jsonb, + uuid, +} from "drizzle-orm/pg-core" + +import { tenants } from "./tenants" +import { authUsers } from "./auth_users" + +export const spaces = pgTable("spaces", { + id: bigint("id", { mode: "number" }) + .primaryKey() + .generatedByDefaultAsIdentity(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + name: text("name"), + type: text("type").notNull(), + + tenant: bigint("tenant", { mode: "number" }) + .notNull() + .references(() => tenants.id), + + spaceNumber: text("spaceNumber").notNull(), + + parentSpace: bigint("parentSpace", { mode: "number" }).references( + () => spaces.id + ), + + infoData: jsonb("infoData") + .notNull() + .default({ zip: "", city: "", streetNumber: "" }), + + description: text("description"), + + archived: boolean("archived").notNull().default(false), + + updatedAt: timestamp("updated_at", { withTimezone: true }), + updatedBy: uuid("updated_by").references(() => authUsers.id), +}) + +export type Space = typeof spaces.$inferSelect +export type NewSpace = typeof spaces.$inferInsert diff --git a/db/schema/staff_time_entries.ts b/db/schema/staff_time_entries.ts new file mode 100644 index 0000000..06534c5 --- /dev/null +++ b/db/schema/staff_time_entries.ts @@ -0,0 +1,68 @@ +import { + pgTable, + uuid, + bigint, + timestamp, + integer, + text, + boolean, + numeric, +} from "drizzle-orm/pg-core" + +import { tenants } from "./tenants" +import { authUsers } from "./auth_users" +import { timesStateEnum } from "./enums" +import {sql} from "drizzle-orm"; + +export const staffTimeEntries = pgTable("staff_time_entries", { + id: uuid("id").primaryKey().defaultRandom(), + + tenantId: bigint("tenant_id", { mode: "number" }) + .notNull() + .references(() => tenants.id), + + userId: uuid("user_id") + .notNull() + .references(() => authUsers.id, { onDelete: "cascade" }), + + startedAt: timestamp("started_at", { withTimezone: true }).notNull(), + stoppedAt: timestamp("stopped_at", { withTimezone: true }), + + durationMinutes: integer("duration_minutes").generatedAlwaysAs( + sql`CASE + WHEN stopped_at IS NOT NULL + THEN (EXTRACT(epoch FROM (stopped_at - started_at)) / 60) + ELSE NULL + END` + ), + + type: text("type").default("work"), + + description: text("description"), + + createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(), + updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow(), + + archived: boolean("archived").notNull().default(false), + + updatedBy: uuid("updated_by").references(() => authUsers.id), + + source: text("source"), + + state: timesStateEnum("state").notNull().default("draft"), + + device: uuid("device"), + + internalNote: text("internal_note"), + + vacationReason: text("vacation_reason"), + vacationDays: numeric("vacation_days", { precision: 5, scale: 2 }), + + approvedBy: uuid("approved_by").references(() => authUsers.id), + approvedAt: timestamp("approved_at", { withTimezone: true }), + + sickReason: text("sick_reason"), +}) + +export type StaffTimeEntry = typeof staffTimeEntries.$inferSelect +export type NewStaffTimeEntry = typeof staffTimeEntries.$inferInsert diff --git a/db/schema/staff_time_entry_connects.ts b/db/schema/staff_time_entry_connects.ts new file mode 100644 index 0000000..5569605 --- /dev/null +++ b/db/schema/staff_time_entry_connects.ts @@ -0,0 +1,38 @@ +import { + pgTable, + uuid, + bigint, + timestamp, + integer, + text, +} from "drizzle-orm/pg-core" + +import { staffTimeEntries } from "./staff_time_entries" +import {sql} from "drizzle-orm"; + +export const staffTimeEntryConnects = pgTable("staff_time_entry_connects", { + id: uuid("id").primaryKey().defaultRandom(), + + timeEntryId: uuid("time_entry_id") + .notNull() + .references(() => staffTimeEntries.id, { onDelete: "cascade" }), + + projectId: bigint("project_id", { mode: "number" }), // referenziert später projects.id + + startedAt: timestamp("started_at", { withTimezone: true }).notNull(), + stoppedAt: timestamp("stopped_at", { withTimezone: true }).notNull(), + + durationMinutes: integer("duration_minutes").generatedAlwaysAs( + sql`(EXTRACT(epoch FROM (stopped_at - started_at)) / 60)` + ), + + notes: text("notes"), + + createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(), + updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow(), +}) + +export type StaffTimeEntryConnect = + typeof staffTimeEntryConnects.$inferSelect +export type NewStaffTimeEntryConnect = + typeof staffTimeEntryConnects.$inferInsert diff --git a/db/schema/staff_zeitstromtimestamps.ts b/db/schema/staff_zeitstromtimestamps.ts new file mode 100644 index 0000000..2e70480 --- /dev/null +++ b/db/schema/staff_zeitstromtimestamps.ts @@ -0,0 +1,44 @@ +import { + pgTable, + uuid, + timestamp, + bigint, + text, +} from "drizzle-orm/pg-core" + +import { tenants } from "./tenants" +import { authProfiles } from "./auth_profiles" +import { staffTimeEntries } from "./staff_time_entries" + +export const staffZeitstromTimestamps = pgTable("staff_zeitstromtimestamps", { + id: uuid("id").primaryKey().defaultRandom(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + tenant: bigint("tenant", { mode: "number" }) + .notNull() + .references(() => tenants.id), + + profile: uuid("profile") + .notNull() + .references(() => authProfiles.id), + + key: text("key").notNull(), + + intent: text("intent").notNull(), + + time: timestamp("time", { withTimezone: true }).notNull(), + + staffTimeEntry: uuid("staff_time_entry").references( + () => staffTimeEntries.id + ), + + internalNote: text("internal_note"), +}) + +export type StaffZeitstromTimestamp = + typeof staffZeitstromTimestamps.$inferSelect +export type NewStaffZeitstromTimestamp = + typeof staffZeitstromTimestamps.$inferInsert diff --git a/db/schema/statementallocations.ts b/db/schema/statementallocations.ts new file mode 100644 index 0000000..52fa531 --- /dev/null +++ b/db/schema/statementallocations.ts @@ -0,0 +1,69 @@ +import { + pgTable, + uuid, + bigint, + integer, + text, + timestamp, + boolean, + doublePrecision, +} from "drizzle-orm/pg-core" + +import { tenants } from "./tenants" +import { authUsers } from "./auth_users" +import { customers } from "./customers" +import { vendors } from "./vendors" +import { ownaccounts } from "./ownaccounts" +import { incominginvoices } from "./incominginvoices" +import { createddocuments } from "./createddocuments" +import { bankstatements } from "./bankstatements" +import { accounts } from "./accounts" // Falls noch nicht erstellt → bitte melden! + +export const statementAllocations = pgTable("statementallocations", { + id: uuid("id").primaryKey().defaultRandom(), + + // foreign keys + bsId: integer("bs_id") + .notNull() + .references(() => bankstatements.id), + + cdId: integer("cd_id").references(() => createddocuments.id), + + amount: doublePrecision("amount").notNull().default(0), + + iiId: bigint("ii_id", { mode: "number" }).references( + () => incominginvoices.id + ), + + tenant: bigint("tenant", { mode: "number" }) + .notNull() + .references(() => tenants.id), + + account: bigint("account", { mode: "number" }).references( + () => accounts.id + ), + + createdAt: timestamp("created_at", { + withTimezone: false, + }).defaultNow(), + + ownaccount: uuid("ownaccount").references(() => ownaccounts.id), + + description: text("description"), + + customer: bigint("customer", { mode: "number" }).references( + () => customers.id + ), + + vendor: bigint("vendor", { mode: "number" }).references(() => vendors.id), + + updatedAt: timestamp("updated_at", { withTimezone: true }), + + updatedBy: uuid("updated_by").references(() => authUsers.id), + + archived: boolean("archived").notNull().default(false), +}) + +export type StatementAllocation = typeof statementAllocations.$inferSelect +export type NewStatementAllocation = + typeof statementAllocations.$inferInsert diff --git a/db/schema/tasks.ts b/db/schema/tasks.ts new file mode 100644 index 0000000..aa40617 --- /dev/null +++ b/db/schema/tasks.ts @@ -0,0 +1,51 @@ +import { + pgTable, + bigint, + text, + timestamp, + boolean, + jsonb, + uuid, +} from "drizzle-orm/pg-core" + +import { tenants } from "./tenants" +import { authUsers } from "./auth_users" +import { customers } from "./customers" + +export const tasks = pgTable("tasks", { + id: bigint("id", { mode: "number" }) + .primaryKey() + .generatedByDefaultAsIdentity(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + name: text("name").notNull(), + description: text("description"), + categorie: text("categorie"), + + tenant: bigint("tenant", { mode: "number" }) + .notNull() + .references(() => tenants.id), + + // FIXED: user_id statt profile, verweist auf auth_users.id + userId: uuid("user_id").references(() => authUsers.id), + + project: bigint("project", { mode: "number" }), + plant: bigint("plant", { mode: "number" }), + + customer: bigint("customer", { mode: "number" }).references( + () => customers.id + ), + + profiles: jsonb("profiles").notNull().default([]), + + archived: boolean("archived").notNull().default(false), + + updatedAt: timestamp("updated_at", { withTimezone: true }), + updatedBy: uuid("updated_by").references(() => authUsers.id), +}) + +export type Task = typeof tasks.$inferSelect +export type NewTask = typeof tasks.$inferInsert diff --git a/db/schema/taxtypes.ts b/db/schema/taxtypes.ts new file mode 100644 index 0000000..5d3c45d --- /dev/null +++ b/db/schema/taxtypes.ts @@ -0,0 +1,28 @@ +import { + pgTable, + bigint, + timestamp, + text, + uuid, +} from "drizzle-orm/pg-core" + +import { authUsers } from "./auth_users" + +export const taxTypes = pgTable("taxtypes", { + id: bigint("id", { mode: "number" }) + .primaryKey() + .generatedByDefaultAsIdentity(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + label: text("label").notNull(), + percentage: bigint("percentage", { mode: "number" }).notNull(), + + updatedAt: timestamp("updated_at", { withTimezone: true }), + updatedBy: uuid("updated_by").references(() => authUsers.id), +}) + +export type TaxType = typeof taxTypes.$inferSelect +export type NewTaxType = typeof taxTypes.$inferInsert diff --git a/db/schema/tenants.ts b/db/schema/tenants.ts new file mode 100644 index 0000000..b798aab --- /dev/null +++ b/db/schema/tenants.ts @@ -0,0 +1,140 @@ +import { + pgTable, + bigint, + text, + timestamp, + boolean, + jsonb, + integer, + smallint, + date, + uuid, + pgEnum, +} from "drizzle-orm/pg-core" +import { authUsers } from "./auth_users" +import {lockedTenantEnum} from "./enums"; + +export const tenants = pgTable( + "tenants", + { + id: bigint("id", { mode: "number" }) + .primaryKey() + .generatedByDefaultAsIdentity(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + name: text("name").notNull(), + short: text("short").notNull(), + + calendarConfig: jsonb("calendarConfig").default({ + eventTypes: [ + { color: "blue", label: "Büro" }, + { color: "yellow", label: "Besprechung" }, + { color: "green", label: "Umsetzung" }, + { color: "red", label: "Vor Ort Termin" }, + ], + }), + + timeConfig: jsonb("timeConfig").notNull().default({}), + + tags: jsonb("tags").notNull().default({ + products: [], + documents: [], + }), + + measures: jsonb("measures") + .notNull() + .default([ + { name: "Netzwerktechnik", short: "NWT" }, + { name: "Elektrotechnik", short: "ELT" }, + { name: "Photovoltaik", short: "PV" }, + { name: "Videüberwachung", short: "VÜA" }, + { name: "Projekt", short: "PRJ" }, + { name: "Smart Home", short: "SHO" }, + ]), + + businessInfo: jsonb("businessInfo").default({ + zip: "", + city: "", + name: "", + street: "", + }), + + features: jsonb("features").default({ + objects: true, + calendar: true, + contacts: true, + projects: true, + vehicles: true, + contracts: true, + inventory: true, + accounting: true, + timeTracking: true, + planningBoard: true, + workingTimeTracking: true, + }), + + ownFields: jsonb("ownFields"), + + numberRanges: jsonb("numberRanges") + .notNull() + .default({ + vendors: { prefix: "", suffix: "", nextNumber: 10000 }, + customers: { prefix: "", suffix: "", nextNumber: 10000 }, + products: { prefix: "AT-", suffix: "", nextNumber: 1000 }, + quotes: { prefix: "AN-", suffix: "", nextNumber: 1000 }, + confirmationOrders: { prefix: "AB-", suffix: "", nextNumber: 1000 }, + invoices: { prefix: "RE-", suffix: "", nextNumber: 1000 }, + spaces: { prefix: "LP-", suffix: "", nextNumber: 1000 }, + inventoryitems: { prefix: "IA-", suffix: "", nextNumber: 1000 }, + projects: { prefix: "PRJ-", suffix: "", nextNumber: 1000 }, + costcentres: { prefix: "KST-", suffix: "", nextNumber: 1000 }, + }), + + standardEmailForInvoices: text("standardEmailForInvoices"), + + extraModules: jsonb("extraModules").notNull().default([]), + + isInTrial: boolean("isInTrial").default(false), + trialEndDate: date("trialEndDate"), + + stripeCustomerId: text("stripeCustomerId"), + + hasActiveLicense: boolean("hasActiveLicense").notNull().default(false), + + userLicenseCount: integer("userLicenseCount") + .notNull() + .default(0), + + workstationLicenseCount: integer("workstationLicenseCount") + .notNull() + .default(0), + + standardPaymentDays: smallint("standardPaymentDays") + .notNull() + .default(14), + + dokuboxEmailAddresses: jsonb("dokuboxEmailAddresses").default([]), + + dokuboxkey: uuid("dokuboxkey").notNull().defaultRandom(), + + autoPrepareIncomingInvoices: boolean("autoPrepareIncomingInvoices") + .default(true), + + portalDomain: text("portalDomain"), + + portalConfig: jsonb("portalConfig") + .notNull() + .default({ primayColor: "#69c350" }), + + updatedAt: timestamp("updated_at", { withTimezone: true }), + updatedBy: uuid("updated_by").references(() => authUsers.id), + + locked: lockedTenantEnum("locked"), + } +) + +export type Tenant = typeof tenants.$inferSelect +export type NewTenant = typeof tenants.$inferInsert diff --git a/db/schema/texttemplates.ts b/db/schema/texttemplates.ts new file mode 100644 index 0000000..ce1ac94 --- /dev/null +++ b/db/schema/texttemplates.ts @@ -0,0 +1,44 @@ +import { + pgTable, + bigint, + text, + timestamp, + boolean, + jsonb, + uuid, +} from "drizzle-orm/pg-core" + +import { tenants } from "./tenants" +import { authUsers } from "./auth_users" +import { textTemplatePositionsEnum } from "./enums" + +export const textTemplates = pgTable("texttemplates", { + id: bigint("id", { mode: "number" }) + .primaryKey() + .generatedByDefaultAsIdentity(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + tenant: bigint("tenant", { mode: "number" }) + .notNull() + .references(() => tenants.id), + + name: text("name").notNull(), + text: text("text").notNull(), + + documentType: text("documentType").default(""), + + default: boolean("default").notNull().default(false), + + pos: textTemplatePositionsEnum("pos").notNull(), + + archived: boolean("archived").notNull().default(false), + + updatedAt: timestamp("updated_at", { withTimezone: true }), + updatedBy: uuid("updated_by").references(() => authUsers.id), +}) + +export type TextTemplate = typeof textTemplates.$inferSelect +export type NewTextTemplate = typeof textTemplates.$inferInsert diff --git a/db/schema/units.ts b/db/schema/units.ts new file mode 100644 index 0000000..61e5e11 --- /dev/null +++ b/db/schema/units.ts @@ -0,0 +1,27 @@ +import { + pgTable, + bigint, + timestamp, + text, +} from "drizzle-orm/pg-core" + +export const units = pgTable("units", { + id: bigint("id", { mode: "number" }) + .primaryKey() + .generatedByDefaultAsIdentity(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + name: text("name").notNull(), + single: text("single").notNull(), + + multiple: text("multiple"), + short: text("short"), + + step: text("step").notNull().default("1"), +}) + +export type Unit = typeof units.$inferSelect +export type NewUnit = typeof units.$inferInsert diff --git a/db/schema/user_credentials.ts b/db/schema/user_credentials.ts new file mode 100644 index 0000000..f1ae045 --- /dev/null +++ b/db/schema/user_credentials.ts @@ -0,0 +1,53 @@ +import { + pgTable, + uuid, + timestamp, + bigint, + boolean, + jsonb, + numeric, pgEnum, +} from "drizzle-orm/pg-core" + +import { tenants } from "./tenants" +import { authUsers } from "./auth_users" +import {credentialTypesEnum} from "./enums"; + + + +export const userCredentials = pgTable("user_credentials", { + id: uuid("id").primaryKey().defaultRandom(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + userId: uuid("user_id") + .notNull() + .references(() => authUsers.id), + + updatedAt: timestamp("updated_at", { withTimezone: true }), + + tenantId: bigint("tenant_id", { mode: "number" }) + .notNull() + .references(() => tenants.id), + + smtpPort: numeric("smtp_port"), + smtpSsl: boolean("smtp_ssl"), + + type: credentialTypesEnum("type").notNull(), + + imapPort: numeric("imap_port"), + imapSsl: boolean("imap_ssl"), + + emailEncrypted: jsonb("email_encrypted"), + passwordEncrypted: jsonb("password_encrypted"), + + smtpHostEncrypted: jsonb("smtp_host_encrypted"), + imapHostEncrypted: jsonb("imap_host_encrypted"), + + accessTokenEncrypted: jsonb("access_token_encrypted"), + refreshTokenEncrypted: jsonb("refresh_token_encrypted"), +}) + +export type UserCredential = typeof userCredentials.$inferSelect +export type NewUserCredential = typeof userCredentials.$inferInsert diff --git a/db/schema/vehicles.ts b/db/schema/vehicles.ts new file mode 100644 index 0000000..7915657 --- /dev/null +++ b/db/schema/vehicles.ts @@ -0,0 +1,57 @@ +import { + pgTable, + bigint, + text, + timestamp, + boolean, + jsonb, + uuid, + doublePrecision, +} from "drizzle-orm/pg-core" + +import { tenants } from "./tenants" +import { authUsers } from "./auth_users" + +export const vehicles = pgTable("vehicles", { + id: bigint("id", { mode: "number" }) + .primaryKey() + .generatedByDefaultAsIdentity(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + tenant: bigint("tenant", { mode: "number" }) + .notNull() + .references(() => tenants.id), + + licensePlate: text("licensePlate"), + name: text("name"), + type: text("type"), + + active: boolean("active").default(true), + + // FIXED: driver references auth_users.id + driver: uuid("driver").references(() => authUsers.id), + + vin: text("vin"), + + tankSize: doublePrecision("tankSize").notNull().default(0), + + archived: boolean("archived").notNull().default(false), + + buildYear: text("buildYear"), + + towingCapacity: bigint("towingCapacity", { mode: "number" }), + powerInKW: bigint("powerInKW", { mode: "number" }), + + color: text("color"), + + profiles: jsonb("profiles").notNull().default([]), + + updatedAt: timestamp("updated_at", { withTimezone: true }), + updatedBy: uuid("updated_by").references(() => authUsers.id), +}) + +export type Vehicle = typeof vehicles.$inferSelect +export type NewVehicle = typeof vehicles.$inferInsert diff --git a/db/schema/vendors.ts b/db/schema/vendors.ts new file mode 100644 index 0000000..32ae94f --- /dev/null +++ b/db/schema/vendors.ts @@ -0,0 +1,45 @@ +import { + pgTable, + bigint, + text, + timestamp, + boolean, + jsonb, + uuid, +} from "drizzle-orm/pg-core" + +import { tenants } from "./tenants" +import { authUsers } from "./auth_users" + +export const vendors = pgTable("vendors", { + id: bigint("id", { mode: "number" }) + .primaryKey() + .generatedByDefaultAsIdentity(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + name: text("name").notNull(), + vendorNumber: text("vendorNumber").notNull(), + + tenant: bigint("tenant", { mode: "number" }) + .notNull() + .references(() => tenants.id), + + infoData: jsonb("infoData").notNull().default({}), + notes: text("notes"), + + hasSEPA: boolean("hasSEPA").notNull().default(false), + + profiles: jsonb("profiles").notNull().default([]), + archived: boolean("archived").notNull().default(false), + + defaultPaymentMethod: text("defaultPaymentMethod"), + + updatedAt: timestamp("updated_at", { withTimezone: true }), + updatedBy: uuid("updated_by").references(() => authUsers.id), +}) + +export type Vendor = typeof vendors.$inferSelect +export type NewVendor = typeof vendors.$inferInsert diff --git a/drizzle.config.ts b/drizzle.config.ts new file mode 100644 index 0000000..d2b0225 --- /dev/null +++ b/drizzle.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from "drizzle-kit" +import {secrets} from "./src/utils/secrets"; + +export default defineConfig({ + dialect: "postgresql", + schema: "./db/schema", + out: "./db/migrations", + dbCredentials: { + url: secrets.DATABASE_URL || process.env.DATABASE_URL, + }, +}) \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 2abc221..c6aa764 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,12 +28,14 @@ "canvas": "^3.2.0", "crypto": "^1.0.1", "dayjs": "^1.11.18", + "drizzle-orm": "^0.45.0", "fastify": "^5.5.0", "fastify-plugin": "^5.0.1", "imapflow": "^1.1.1", "jsonwebtoken": "^9.0.2", "nodemailer": "^7.0.6", "pdf-lib": "^1.17.1", + "pg": "^8.16.3", "pngjs": "^7.0.0", "sharp": "^0.34.5", "xmlbuilder": "^15.1.1", @@ -44,6 +46,7 @@ "@types/bcrypt": "^6.0.0", "@types/jsonwebtoken": "^9.0.10", "@types/node": "^24.3.0", + "drizzle-kit": "^0.31.8", "prisma": "^6.15.0", "tsx": "^4.20.5", "typescript": "^5.9.2" @@ -5273,6 +5276,13 @@ "tslib": "^2.1.0" } }, + "node_modules/@drizzle-team/brocli": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@drizzle-team/brocli/-/brocli-0.10.2.tgz", + "integrity": "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/@emnapi/runtime": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.0.tgz", @@ -5283,6 +5293,442 @@ "tslib": "^2.4.0" } }, + "node_modules/@esbuild-kit/core-utils": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@esbuild-kit/core-utils/-/core-utils-3.3.2.tgz", + "integrity": "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==", + "deprecated": "Merged into tsx: https://tsx.is", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.18.20", + "source-map-support": "^0.5.21" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/android-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", + "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/android-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", + "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/android-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", + "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/darwin-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", + "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/darwin-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", + "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/freebsd-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", + "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/freebsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", + "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", + "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", + "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", + "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-loong64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", + "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-mips64el": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", + "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-ppc64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", + "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-riscv64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", + "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-s390x": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", + "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", + "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/netbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", + "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/openbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", + "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/sunos-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", + "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/win32-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", + "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/win32-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", + "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/win32-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", + "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/esbuild": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", + "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.18.20", + "@esbuild/android-arm64": "0.18.20", + "@esbuild/android-x64": "0.18.20", + "@esbuild/darwin-arm64": "0.18.20", + "@esbuild/darwin-x64": "0.18.20", + "@esbuild/freebsd-arm64": "0.18.20", + "@esbuild/freebsd-x64": "0.18.20", + "@esbuild/linux-arm": "0.18.20", + "@esbuild/linux-arm64": "0.18.20", + "@esbuild/linux-ia32": "0.18.20", + "@esbuild/linux-loong64": "0.18.20", + "@esbuild/linux-mips64el": "0.18.20", + "@esbuild/linux-ppc64": "0.18.20", + "@esbuild/linux-riscv64": "0.18.20", + "@esbuild/linux-s390x": "0.18.20", + "@esbuild/linux-x64": "0.18.20", + "@esbuild/netbsd-x64": "0.18.20", + "@esbuild/openbsd-x64": "0.18.20", + "@esbuild/sunos-x64": "0.18.20", + "@esbuild/win32-arm64": "0.18.20", + "@esbuild/win32-ia32": "0.18.20", + "@esbuild/win32-x64": "0.18.20" + } + }, + "node_modules/@esbuild-kit/esm-loader": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/@esbuild-kit/esm-loader/-/esm-loader-2.6.5.tgz", + "integrity": "sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==", + "deprecated": "Merged into tsx: https://tsx.is", + "dev": true, + "license": "MIT", + "dependencies": { + "@esbuild-kit/core-utils": "^3.3.2", + "get-tsconfig": "^4.7.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.9", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", @@ -7686,9 +8132,9 @@ } }, "node_modules/archiver-utils/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", @@ -7964,6 +8410,13 @@ "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", "license": "BSD-3-Clause" }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, "node_modules/bwip-js": { "version": "4.8.0", "resolved": "https://registry.npmjs.org/bwip-js/-/bwip-js-4.8.0.tgz", @@ -8323,6 +8776,147 @@ "url": "https://dotenvx.com" } }, + "node_modules/drizzle-kit": { + "version": "0.31.8", + "resolved": "https://registry.npmjs.org/drizzle-kit/-/drizzle-kit-0.31.8.tgz", + "integrity": "sha512-O9EC/miwdnRDY10qRxM8P3Pg8hXe3LyU4ZipReKOgTwn4OqANmftj8XJz1UPUAS6NMHf0E2htjsbQujUTkncCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@drizzle-team/brocli": "^0.10.2", + "@esbuild-kit/esm-loader": "^2.5.5", + "esbuild": "^0.25.4", + "esbuild-register": "^3.5.0" + }, + "bin": { + "drizzle-kit": "bin.cjs" + } + }, + "node_modules/drizzle-orm": { + "version": "0.45.0", + "resolved": "https://registry.npmjs.org/drizzle-orm/-/drizzle-orm-0.45.0.tgz", + "integrity": "sha512-lyd9VRk3SXKRjV/gQckQzmJgkoYMvVG3A2JAV0vh3L+Lwk+v9+rK5Gj0H22y+ZBmxsrRBgJ5/RbQCN7DWd1dtQ==", + "license": "Apache-2.0", + "peerDependencies": { + "@aws-sdk/client-rds-data": ">=3", + "@cloudflare/workers-types": ">=4", + "@electric-sql/pglite": ">=0.2.0", + "@libsql/client": ">=0.10.0", + "@libsql/client-wasm": ">=0.10.0", + "@neondatabase/serverless": ">=0.10.0", + "@op-engineering/op-sqlite": ">=2", + "@opentelemetry/api": "^1.4.1", + "@planetscale/database": ">=1.13", + "@prisma/client": "*", + "@tidbcloud/serverless": "*", + "@types/better-sqlite3": "*", + "@types/pg": "*", + "@types/sql.js": "*", + "@upstash/redis": ">=1.34.7", + "@vercel/postgres": ">=0.8.0", + "@xata.io/client": "*", + "better-sqlite3": ">=7", + "bun-types": "*", + "expo-sqlite": ">=14.0.0", + "gel": ">=2", + "knex": "*", + "kysely": "*", + "mysql2": ">=2", + "pg": ">=8", + "postgres": ">=3", + "sql.js": ">=1", + "sqlite3": ">=5" + }, + "peerDependenciesMeta": { + "@aws-sdk/client-rds-data": { + "optional": true + }, + "@cloudflare/workers-types": { + "optional": true + }, + "@electric-sql/pglite": { + "optional": true + }, + "@libsql/client": { + "optional": true + }, + "@libsql/client-wasm": { + "optional": true + }, + "@neondatabase/serverless": { + "optional": true + }, + "@op-engineering/op-sqlite": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@planetscale/database": { + "optional": true + }, + "@prisma/client": { + "optional": true + }, + "@tidbcloud/serverless": { + "optional": true + }, + "@types/better-sqlite3": { + "optional": true + }, + "@types/pg": { + "optional": true + }, + "@types/sql.js": { + "optional": true + }, + "@upstash/redis": { + "optional": true + }, + "@vercel/postgres": { + "optional": true + }, + "@xata.io/client": { + "optional": true + }, + "better-sqlite3": { + "optional": true + }, + "bun-types": { + "optional": true + }, + "expo-sqlite": { + "optional": true + }, + "gel": { + "optional": true + }, + "knex": { + "optional": true + }, + "kysely": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "pg": { + "optional": true + }, + "postgres": { + "optional": true + }, + "prisma": { + "optional": true + }, + "sql.js": { + "optional": true + }, + "sqlite3": { + "optional": true + } + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -8484,6 +9078,19 @@ "@esbuild/win32-x64": "0.25.9" } }, + "node_modules/esbuild-register": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/esbuild-register/-/esbuild-register-3.6.0.tgz", + "integrity": "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "peerDependencies": { + "esbuild": ">=0.12 <1" + } + }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -8866,14 +9473,14 @@ "license": "MIT" }, "node_modules/glob": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz", - "integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==", - "license": "ISC", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", + "license": "BlueOak-1.0.0", "dependencies": { "foreground-child": "^3.3.1", "jackspeak": "^4.1.1", - "minimatch": "^10.0.3", + "minimatch": "^10.1.1", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" @@ -9200,12 +9807,12 @@ } }, "node_modules/jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.3.tgz", + "integrity": "sha512-byiJ0FLRdLdSVSReO/U4E7RoEyOCKnEnEPMjq3HxWtvzLsV08/i5RQKsFVNkCldrCaPr2vDNAOMsfs8T/Hze7g==", "license": "MIT", "dependencies": { - "jwa": "^1.4.1", + "jwa": "^1.4.2", "safe-buffer": "^5.0.1" } }, @@ -9436,10 +10043,10 @@ } }, "node_modules/minimatch": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", - "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", - "license": "ISC", + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", + "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/brace-expansion": "^5.0.0" }, @@ -9663,6 +10270,95 @@ "devOptional": true, "license": "MIT" }, + "node_modules/pg": { + "version": "8.16.3", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz", + "integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==", + "license": "MIT", + "dependencies": { + "pg-connection-string": "^2.9.1", + "pg-pool": "^3.10.1", + "pg-protocol": "^1.10.3", + "pg-types": "2.2.0", + "pgpass": "1.0.5" + }, + "engines": { + "node": ">= 16.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.2.7" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.7.tgz", + "integrity": "sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==", + "license": "MIT", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz", + "integrity": "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==", + "license": "MIT" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "license": "ISC", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.1.tgz", + "integrity": "sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==", + "license": "MIT", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.3.tgz", + "integrity": "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==", + "license": "MIT" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "license": "MIT", + "dependencies": { + "split2": "^4.1.0" + } + }, "node_modules/pino": { "version": "9.9.0", "resolved": "https://registry.npmjs.org/pino/-/pino-9.9.0.tgz", @@ -9721,6 +10417,45 @@ "node": ">=14.19.0" } }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "license": "MIT", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/prebuild-install": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", @@ -10222,6 +10957,27 @@ "atomic-sleep": "^1.0.0" } }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, "node_modules/split2": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", @@ -10715,6 +11471,15 @@ "node": ">=8.0" } }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, "node_modules/yaml": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", diff --git a/package.json b/package.json index 46bea4d..e70ea9c 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,8 @@ "scripts": { "dev": "tsx watch src/index.ts", "build": "tsc", - "start": "node dist/index.js" + "start": "node dist/index.js", + "schema:index": "ts-node scripts/generate-schema-index.ts" }, "repository": { "type": "git", @@ -35,12 +36,14 @@ "canvas": "^3.2.0", "crypto": "^1.0.1", "dayjs": "^1.11.18", + "drizzle-orm": "^0.45.0", "fastify": "^5.5.0", "fastify-plugin": "^5.0.1", "imapflow": "^1.1.1", "jsonwebtoken": "^9.0.2", "nodemailer": "^7.0.6", "pdf-lib": "^1.17.1", + "pg": "^8.16.3", "pngjs": "^7.0.0", "sharp": "^0.34.5", "xmlbuilder": "^15.1.1", @@ -51,6 +54,7 @@ "@types/bcrypt": "^6.0.0", "@types/jsonwebtoken": "^9.0.10", "@types/node": "^24.3.0", + "drizzle-kit": "^0.31.8", "prisma": "^6.15.0", "tsx": "^4.20.5", "typescript": "^5.9.2" diff --git a/scripts/generate-schema-index.ts b/scripts/generate-schema-index.ts new file mode 100644 index 0000000..a7e6c29 --- /dev/null +++ b/scripts/generate-schema-index.ts @@ -0,0 +1,16 @@ +import fs from "node:fs" +import path from "node:path" + +const schemaDir = path.resolve("db/schema") +const indexFile = path.join(schemaDir, "index.ts") + +const files = fs + .readdirSync(schemaDir) + .filter((f) => f.endsWith(".ts") && f !== "index.ts") + +const exportsToWrite = files + .map((f) => `export * from "./${f.replace(".ts", "")}"`) + .join("\n") + +fs.writeFileSync(indexFile, exportsToWrite) +console.log("✓ schema/index.ts generated") \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 37a4e55..73cf634 100644 --- a/src/index.ts +++ b/src/index.ts @@ -12,6 +12,7 @@ import authPlugin from "./plugins/auth"; import adminRoutes from "./routes/admin"; import corsPlugin from "./plugins/cors"; import queryConfigPlugin from "./plugins/queryconfig"; +import dbPlugin from "./plugins/db"; import resourceRoutes from "./routes/resources"; import resourceRoutesSpecial from "./routes/resourcesSpecial"; import fastifyCookie from "@fastify/cookie"; @@ -57,6 +58,7 @@ async function main() { await app.register(supabasePlugin); await app.register(tenantPlugin); await app.register(dayjsPlugin); + await app.register(dbPlugin); app.addHook('preHandler', (req, reply, done) => { console.log(req.method) @@ -114,6 +116,14 @@ async function main() { },{prefix: "/api"}) + app.ready(async () => { + try { + const result = await app.db.execute("SELECT NOW()"); + console.log("✓ DB connection OK: " + JSON.stringify(result.rows[0])); + } catch (err) { + console.log("❌ DB connection failed:", err); + } + }); // Start try { diff --git a/src/plugins/db.ts b/src/plugins/db.ts new file mode 100644 index 0000000..092393a --- /dev/null +++ b/src/plugins/db.ts @@ -0,0 +1,34 @@ +import fp from "fastify-plugin" +import {drizzle, NodePgDatabase} from "drizzle-orm/node-postgres" +import { Pool } from "pg" +import * as schema from "../../db/schema" + +export default fp(async (server, opts) => { + const pool = new Pool({ + host: "db-001.netbird.cloud", + port: Number(process.env.DB_PORT || 5432), + user: "postgres", + password: "wJw7aNpEBJdcxgoct6GXNpvY4Cn6ECqu", + database: "fedeo", + ssl: process.env.DB_DISABLE_SSL === "true" ? false : undefined, + }) + + // Drizzle instance + const db = drizzle(pool, { schema }) + + // Dekorieren -> überall server.db + server.decorate("db", db) + + // Graceful Shutdown + server.addHook("onClose", async () => { + await pool.end() + }) + + server.log.info("Drizzle database connected") +}) + +declare module "fastify" { + interface FastifyInstance { + db:NodePgDatabase + } +} diff --git a/src/routes/auth/auth.ts b/src/routes/auth/auth.ts index 2674a8f..5b1d1d9 100644 --- a/src/routes/auth/auth.ts +++ b/src/routes/auth/auth.ts @@ -1,13 +1,21 @@ import { FastifyInstance } from "fastify"; import bcrypt from "bcrypt"; import jwt from "jsonwebtoken"; -import { generateRandomPassword, hashPassword } from "../../utils/password" -import { sendMail } from "../../utils/mailer" -import {secrets} from "../../utils/secrets"; +import { generateRandomPassword, hashPassword } from "../../utils/password"; +import { sendMail } from "../../utils/mailer"; +import { secrets } from "../../utils/secrets"; + +import { authUsers } from "../../../db/schema"; +import { authTenantUsers } from "../../../db/schema"; +import { tenants } from "../../../db/schema"; +import { eq, and } from "drizzle-orm"; export default async function authRoutes(server: FastifyInstance) { - // Registrierung - server.post("/auth/register",{ + + // ----------------------------------------------------- + // REGISTER + // ----------------------------------------------------- + server.post("/auth/register", { schema: { tags: ["Auth"], summary: "Register User", @@ -19,43 +27,31 @@ export default async function authRoutes(server: FastifyInstance) { password: { type: "string" }, }, }, - response: { - 200: { - type: "object", - properties: { - user: { type: "object" }, - }, - }, - }, }, }, async (req, reply) => { const body = req.body as { email: string; password: string }; - if (!body.email || !body.password) { - // @ts-ignore - return reply.code(400).send({ error: "Email and password required" }); - } - - // Passwort hashen const passwordHash = await bcrypt.hash(body.password, 10); - // User speichern - const { data, error } = await server.supabase - .from("auth_users") - .insert({ email: body.email, password_hash: passwordHash }) - .select("id, email") - .single(); + const [user] = await server.db + .insert(authUsers) + .values({ + email: body.email.toLowerCase(), + passwordHash, + }) + .returning({ + id: authUsers.id, + email: authUsers.email, + }); - if (error) { - // @ts-ignore - return reply.code(400).send({ error: error.message }); - } - - return { user: data }; + return { user }; }); - // Login - server.post("/auth/login",{ + + // ----------------------------------------------------- + // LOGIN + // ----------------------------------------------------- + server.post("/auth/login", { schema: { tags: ["Auth"], summary: "Login User", @@ -67,103 +63,110 @@ export default async function authRoutes(server: FastifyInstance) { password: { type: "string" }, }, }, - response: { - 200: { - type: "object", - properties: { - token: { type: "string" }, - }, - }, - }, }, }, async (req, reply) => { const body = req.body as { email: string; password: string }; - if (!body.email || !body.password) { - // @ts-ignore - return reply.code(400).send({ error: "Email and password required" }); - } + let user: any = null; - /** - * Wenn das Tenant Objekt verfügbar ist, befindet sich das Backend im Single Tenant Modus. - * Es werden nur Benutzer zugelassen, welche auschließlich diesem Tenant angehören. - * Das zeigt sich über das im User gesetzte Tenant Feld - * - * */ - let user = null - let error = null - if(req.tenant) { - // User finden - const { data, error } = await server.supabase - .from("auth_users") - .select("*, tenants!auth_tenant_users(*)") - .eq("email", body.email.toLowerCase()) + // ------------------------------- + // SINGLE TENANT MODE + // ------------------------------- + /* if (req.tenant) { + const tenantId = req.tenant.id; - // @ts-ignore - user = (data || []).find(i => i.tenants.find(x => x.id === req.tenant.id)) - if(error) { - // @ts-ignore - return reply.code(500).send({ error: "Internal Server Error" }); - } - } else { - // User finden - const { data, error } = await server.supabase - .from("auth_users") - .select("*") - .eq("email", body.email) - .single(); - user = data - if(error) { - // @ts-ignore - return reply.code(500).send({ error: "Internal Server Error" }); - } - } - - if(!user) { - // @ts-ignore - return reply.code(401).send({ error: "Invalid credentials" }); - } else { - - const valid = await bcrypt.compare(body.password, user.password_hash); - if (!valid) { - // @ts-ignore - return reply.code(401).send({ error: "Invalid credentials" }); - } else { - const token = jwt.sign( - { user_id: user.id, email: user.email, tenant_id: null }, - secrets.JWT_SECRET!, - { expiresIn: "6h" } - ); - - reply.setCookie("token", token, { - path: "/", - httpOnly: true, - sameSite: process.env.NODE_ENV === "production" ? "none" : "lax", - secure: process.env.NODE_ENV === "production", // lokal: false, prod: true - maxAge: 60 * 60 * 3, // 3 Stunden + const result = await server.db + .select({ + user: authUsers, }) + .from(authUsers) + .innerJoin( + authTenantUsers, + eq(authTenantUsers.userId, authUsers.id) + ) + .innerJoin( + tenants, + eq(authTenantUsers.tenantId, tenants.id) + ) + .where(and( + eq(authUsers.email, body.email.toLowerCase()), + eq(authTenantUsers.tenantId, tenantId) + )); - return { token }; + if (result.length === 0) { + return reply.code(401).send({ error: "Invalid credentials" }); } + + user = result[0].user; + + // ------------------------------- + // MULTI TENANT MODE + // ------------------------------- + } else {*/ + const [found] = await server.db + .select() + .from(authUsers) + .where(eq(authUsers.email, body.email.toLowerCase())) + .limit(1); + + if (!found) { + return reply.code(401).send({ error: "Invalid credentials" }); + } + + user = found; + /*}*/ + + // Passwort prüfen + const valid = await bcrypt.compare(body.password, user.passwordHash); + if (!valid) { + return reply.code(401).send({ error: "Invalid credentials" }); } + + const token = jwt.sign( + { + user_id: user.id, + email: user.email, + tenant_id: req.tenant?.id ?? null, + }, + secrets.JWT_SECRET!, + { expiresIn: "6h" } + ); + + reply.setCookie("token", token, { + path: "/", + httpOnly: true, + sameSite: process.env.NODE_ENV === "production" ? "none" : "lax", + secure: process.env.NODE_ENV === "production", + maxAge: 60 * 60 * 3, + }); + + return { token }; }); + + // ----------------------------------------------------- + // LOGOUT + // ----------------------------------------------------- server.post("/auth/logout", { schema: { tags: ["Auth"], - summary: "Logout User (löscht Cookie)" - }, + summary: "Logout User" + } }, async (req, reply) => { reply.clearCookie("token", { path: "/", httpOnly: true, secure: process.env.NODE_ENV === "production", sameSite: "lax", - }) + }); - return { success: true } - }) + return { success: true }; + }); + + // ----------------------------------------------------- + // PASSWORD RESET + // ----------------------------------------------------- server.post("/auth/password/reset", { schema: { tags: ["Auth"], @@ -177,43 +180,43 @@ export default async function authRoutes(server: FastifyInstance) { } } }, async (req, reply) => { - const { email } = req.body as { email: string } + const { email } = req.body as { email: string }; - // User finden - const { data: user, error } = await server.supabase - .from("auth_users") - .select("id, email") - .eq("email", email) - .single() + const [user] = await server.db + .select({ + id: authUsers.id, + email: authUsers.email, + }) + .from(authUsers) + .where(eq(authUsers.email, email.toLowerCase())) + .limit(1); - if (error || !user) { - return reply.code(404).send({ error: "User not found" }) + if (!user) { + return reply.code(404).send({ error: "User not found" }); } - // Neues Passwort generieren - const plainPassword = generateRandomPassword() - const passwordHash = await hashPassword(plainPassword) + const plainPassword = generateRandomPassword(); + const passwordHash = await hashPassword(plainPassword); - // In DB updaten - const { error: updateError } = await server.supabase - .from("auth_users") - .update({ password_hash: passwordHash, must_change_password: true }) - .eq("id", user.id) + await server.db + .update(authUsers) + .set({ + passwordHash, + mustChangePassword: true, + }) + .where(eq(authUsers.id, user.id)); - if (updateError) { - return reply.code(500).send({ error: "Could not update password" }) - } - - // Mail verschicken await sendMail( user.email, "FEDEO | Dein neues Passwort", - `

Hallo,

-

dein Passwort wurde zurückgesetzt.

-

Neues Passwort: ${plainPassword}

-

Bitte ändere es nach dem Login umgehend.

` - ) + ` +

Hallo,

+

Dein Passwort wurde zurückgesetzt.

+

Neues Passwort: ${plainPassword}

+

Bitte ändere es nach dem Login umgehend.

+ ` + ); - return { success: true } - }) -} \ No newline at end of file + return { success: true }; + }); +} diff --git a/src/utils/secrets.ts b/src/utils/secrets.ts index 1d384c2..b67a1e7 100644 --- a/src/utils/secrets.ts +++ b/src/utils/secrets.ts @@ -13,6 +13,7 @@ export let secrets = { JWT_SECRET: string PORT: number HOST: string + DATABASE_URL: string SUPABASE_URL: string SUPABASE_SERVICE_ROLE_KEY: string S3_BUCKET: string From 7450f90a0fbc585c25cec8bf895069fe11b65cfc Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sat, 6 Dec 2025 19:29:28 +0100 Subject: [PATCH 084/149] Schema Changes --- db/schema/auth_profiles.ts | 60 ++++++++++++++--------------- db/schema/auth_role_permisssions.ts | 6 +-- db/schema/auth_roles.ts | 6 +-- db/schema/auth_tenant_users.ts | 10 ++--- db/schema/auth_user_roles.ts | 12 +++--- db/schema/auth_users.ts | 4 +- db/schema/statementallocations.ts | 12 +++--- 7 files changed, 55 insertions(+), 55 deletions(-) diff --git a/db/schema/auth_profiles.ts b/db/schema/auth_profiles.ts index e8dc73a..85085e0 100644 --- a/db/schema/auth_profiles.ts +++ b/db/schema/auth_profiles.ts @@ -14,67 +14,67 @@ import { authUsers } from "./auth_users" export const authProfiles = pgTable("auth_profiles", { id: uuid("id").primaryKey().defaultRandom(), - userId: uuid("user_id").references(() => authUsers.id), + user_id: uuid("user_id").references(() => authUsers.id), - tenantId: bigint("tenant_id", { mode: "number" }).notNull(), + tenant_id: bigint("tenant_id", { mode: "number" }).notNull(), - createdAt: timestamp("created_at", { withTimezone: true }) + created_at: timestamp("created_at", { withTimezone: true }) .notNull() .defaultNow(), - firstName: text("first_name").notNull(), - lastName: text("last_name").notNull(), + first_name: text("first_name").notNull(), + last_name: text("last_name").notNull(), - fullName: text("full_name").generatedAlwaysAs( + full_name: text("full_name").generatedAlwaysAs( `((first_name || ' ') || last_name)` ), - mobileTel: text("mobile_tel"), - fixedTel: text("fixed_tel"), + mobile_tel: text("mobile_tel"), + fixed_tel: text("fixed_tel"), salutation: text("salutation"), - employeeNumber: text("employee_number"), + employee_number: text("employee_number"), - weeklyWorkingHours: doublePrecision("weekly_working_hours").default(0), - annualPaidLeaveDays: bigint("annual_paid_leave_days", { mode: "number" }), + weekly_working_hours: doublePrecision("weekly_working_hours").default(0), + annual_paid_leave_days: bigint("annual_paid_leave_days", { mode: "number" }), - weeklyRegularWorkingHours: jsonb("weekly_regular_working_hours").default("{}"), + weekly_regular_working_hours: jsonb("weekly_regular_working_hours").default("{}"), - clothingSizeTop: text("clothing_size_top"), - clothingSizeBottom: text("clothing_size_bottom"), - clothingSizeShoe: text("clothing_size_shoe"), + clothing_size_top: text("clothing_size_top"), + clothing_size_bottom: text("clothing_size_bottom"), + clothing_size_shoe: text("clothing_size_shoe"), - emailSignature: text("email_signature").default("

Mit freundlichen Grüßen

"), + email_signature: text("email_signature").default("

Mit freundlichen Grüßen

"), birthday: date("birthday"), - entryDate: date("entry_date").defaultNow(), + entry_date: date("entry_date").defaultNow(), - automaticHourCorrections: jsonb("automatic_hour_corrections").default("[]"), + automatic_hour_corrections: jsonb("automatic_hour_corrections").default("[]"), - recreationDaysCompensation: boolean("recreation_days_compensation") + recreation_days_compensation: boolean("recreation_days_compensation") .notNull() .default(true), - customerForPortal: bigint("customer_for_portal", { mode: "number" }), + customer_for_portal: bigint("customer_for_portal", { mode: "number" }), - pinnedOnNavigation: jsonb("pinned_on_navigation").notNull().default("[]"), + pinned_on_navigation: jsonb("pinned_on_navigation").notNull().default("[]"), email: text("email"), - tokenId: text("token_id"), + token_id: text("token_id"), - weeklyWorkingDays: doublePrecision("weekly_working_days"), + weekly_working_days: doublePrecision("weekly_working_days"), - oldProfileId: uuid("old_profile_id"), - tempConfig: jsonb("temp_config"), + old_profile_id: uuid("old_profile_id"), + temp_config: jsonb("temp_config"), - stateCode: text("state_code").default("DE-NI"), + state_code: text("state_code").default("DE-NI"), - contractType: text("contract_type"), + contract_type: text("contract_type"), position: text("position"), qualification: text("qualification"), - addressStreet: text("address_street"), - addressZip: text("address_zip"), - addressCity: text("address_city"), + address_street: text("address_street"), + address_zip: text("address_zip"), + address_city: text("address_city"), active: boolean("active").notNull().default(true), }) diff --git a/db/schema/auth_role_permisssions.ts b/db/schema/auth_role_permisssions.ts index 75c95d9..fafba53 100644 --- a/db/schema/auth_role_permisssions.ts +++ b/db/schema/auth_role_permisssions.ts @@ -4,18 +4,18 @@ import { authRoles } from "./auth_roles" export const authRolePermissions = pgTable( "auth_role_permissions", { - createdAt: timestamp("created_at", { withTimezone: true }) + created_at: timestamp("created_at", { withTimezone: true }) .notNull() .defaultNow(), - roleId: uuid("role_id") + role_id: uuid("role_id") .notNull() .references(() => authRoles.id), permission: text("permission").notNull(), }, (table) => ({ - primaryKey: [table.roleId, table.permission], + primaryKey: [table.role_id, table.permission], }) ) diff --git a/db/schema/auth_roles.ts b/db/schema/auth_roles.ts index d2f8d9d..2f8f657 100644 --- a/db/schema/auth_roles.ts +++ b/db/schema/auth_roles.ts @@ -4,15 +4,15 @@ import { authUsers } from "./auth_users" export const authRoles = pgTable("auth_roles", { id: uuid("id").primaryKey().defaultRandom(), - createdAt: timestamp("created_at", { withTimezone: true }) + created_at: timestamp("created_at", { withTimezone: true }) .notNull() .defaultNow(), name: text("name").notNull(), description: text("description"), - createdBy: uuid("created_by").references(() => authUsers.id), - tenantId: bigint("tenant_id", {mode: "number"}), + created_by: uuid("created_by").references(() => authUsers.id), + tenant_id: bigint("tenant_id", {mode: "number"}), }) export type AuthRole = typeof authRoles.$inferSelect diff --git a/db/schema/auth_tenant_users.ts b/db/schema/auth_tenant_users.ts index 756a8d5..4bf5709 100644 --- a/db/schema/auth_tenant_users.ts +++ b/db/schema/auth_tenant_users.ts @@ -4,17 +4,17 @@ import { authUsers } from "./auth_users" export const authTenantUsers = pgTable( "auth_tenant_users", { - createdAt: timestamp("created_at", { withTimezone: true }) + created_at: timestamp("created_at", { withTimezone: true }) .notNull() .defaultNow(), - tenantId: bigint("tenant_id", { mode: "number" }).notNull(), - userId: uuid("user_id").notNull(), + tenant_id: bigint("tenant_id", { mode: "number" }).notNull(), + user_id: uuid("user_id").notNull(), - createdBy: uuid("created_by").references(() => authUsers.id), + created_by: uuid("created_by").references(() => authUsers.id), }, (table) => ({ - primaryKey: [table.tenantId, table.userId], + primaryKey: [table.tenant_id, table.user_id], }) ) diff --git a/db/schema/auth_user_roles.ts b/db/schema/auth_user_roles.ts index 8df85a5..4bec35e 100644 --- a/db/schema/auth_user_roles.ts +++ b/db/schema/auth_user_roles.ts @@ -5,24 +5,24 @@ import { authRoles } from "./auth_roles" export const authUserRoles = pgTable( "auth_user_roles", { - createdAt: timestamp("created_at", { withTimezone: true }) + created_at: timestamp("created_at", { withTimezone: true }) .notNull() .defaultNow(), - userId: uuid("user_id") + user_id: uuid("user_id") .notNull() .references(() => authUsers.id), - roleId: uuid("role_id") + role_id: uuid("role_id") .notNull() .references(() => authRoles.id), - tenantId: bigint("tenant_id", { mode: "number" }).notNull(), + tenant_id: bigint("tenant_id", { mode: "number" }).notNull(), - createdBy: uuid("created_by").references(() => authUsers.id), + created_by: uuid("created_by").references(() => authUsers.id), }, (table) => ({ - primaryKey: [table.userId, table.roleId, table.tenantId], + primaryKey: [table.user_id, table.role_id, table.tenant_id], }) ) diff --git a/db/schema/auth_users.ts b/db/schema/auth_users.ts index d6c6a7f..224bd74 100644 --- a/db/schema/auth_users.ts +++ b/db/schema/auth_users.ts @@ -3,7 +3,7 @@ import { pgTable, uuid, text, boolean, timestamp } from "drizzle-orm/pg-core" export const authUsers = pgTable("auth_users", { id: uuid("id").primaryKey().defaultRandom(), - createdAt: timestamp("created_at", { withTimezone: true }) + created_at: timestamp("created_at", { withTimezone: true }) .notNull() .defaultNow(), @@ -11,7 +11,7 @@ export const authUsers = pgTable("auth_users", { passwordHash: text("password_hash").notNull(), multiTenant: boolean("multi_tenant").notNull().default(true), - mustChangePassword: boolean("must_change_password").notNull().default(false), + must_change_password: boolean("must_change_password").notNull().default(false), updatedAt: timestamp("updated_at", { withTimezone: true }), diff --git a/db/schema/statementallocations.ts b/db/schema/statementallocations.ts index 52fa531..9a5d27f 100644 --- a/db/schema/statementallocations.ts +++ b/db/schema/statementallocations.ts @@ -19,19 +19,19 @@ import { createddocuments } from "./createddocuments" import { bankstatements } from "./bankstatements" import { accounts } from "./accounts" // Falls noch nicht erstellt → bitte melden! -export const statementAllocations = pgTable("statementallocations", { +export const statementallocations = pgTable("statementallocations", { id: uuid("id").primaryKey().defaultRandom(), // foreign keys - bsId: integer("bs_id") + bs_id: integer("bs_id") .notNull() .references(() => bankstatements.id), - cdId: integer("cd_id").references(() => createddocuments.id), + cd_id: integer("cd_id").references(() => createddocuments.id), amount: doublePrecision("amount").notNull().default(0), - iiId: bigint("ii_id", { mode: "number" }).references( + ii_id: bigint("ii_id", { mode: "number" }).references( () => incominginvoices.id ), @@ -64,6 +64,6 @@ export const statementAllocations = pgTable("statementallocations", { archived: boolean("archived").notNull().default(false), }) -export type StatementAllocation = typeof statementAllocations.$inferSelect +export type StatementAllocation = typeof statementallocations.$inferSelect export type NewStatementAllocation = - typeof statementAllocations.$inferInsert + typeof statementallocations.$inferInsert From d895583ea24b225239320830641cd9df079eb6e5 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sat, 6 Dec 2025 19:29:47 +0100 Subject: [PATCH 085/149] Converted Routes --- src/index.ts | 9 + src/routes/auth/me.ts | 183 +++++++++++------ src/routes/resources/_template.ts | 155 ++++++++++++++ src/routes/resources/customers.ts | 328 ++++++++++++++++++++++++++++++ src/routes/resources/vendors.ts | 284 ++++++++++++++++++++++++++ 5 files changed, 898 insertions(+), 61 deletions(-) create mode 100644 src/routes/resources/_template.ts create mode 100644 src/routes/resources/customers.ts create mode 100644 src/routes/resources/vendors.ts diff --git a/src/index.ts b/src/index.ts index 73cf634..265e5ee 100644 --- a/src/index.ts +++ b/src/index.ts @@ -30,6 +30,11 @@ import notificationsRoutes from "./routes/notifications"; import staffTimeRoutes from "./routes/staff/time"; import staffTimeConnectRoutes from "./routes/staff/timeconnects"; +//Resources +import customerRoutes from "./routes/resources/customers"; +import vendorRoutes from "./routes/resources/vendors"; + + //M2M import authM2m from "./plugins/auth.m2m"; import helpdeskInboundEmailRoutes from "./routes/helpdesk.inbound.email"; @@ -114,6 +119,10 @@ async function main() { await subApp.register(staffTimeRoutes); await subApp.register(staffTimeConnectRoutes); + + await subApp.register(customerRoutes); + await subApp.register(vendorRoutes); + },{prefix: "/api"}) app.ready(async () => { diff --git a/src/routes/auth/me.ts b/src/routes/auth/me.ts index e99de99..fffb793 100644 --- a/src/routes/auth/me.ts +++ b/src/routes/auth/me.ts @@ -1,79 +1,140 @@ -import { FastifyInstance } from "fastify"; +import { FastifyInstance } from "fastify" +import { + authUsers, + authTenantUsers, + tenants, + authProfiles, + authUserRoles, + authRoles, + authRolePermissions, +} from "../../../db/schema" +import { eq, and, or, isNull } from "drizzle-orm" export default async function meRoutes(server: FastifyInstance) { server.get("/me", async (req, reply) => { - const authUser = req.user // kommt aus JWT (user_id + tenant_id) + try { + const authUser = req.user - if (!authUser) { - return reply.code(401).send({ error: "Unauthorized" }) - } + if (!authUser) { + return reply.code(401).send({ error: "Unauthorized" }) + } - const user_id = req.user.user_id - const tenant_id = req.user.tenant_id + const userId = authUser.user_id + const activeTenantId = authUser.tenant_id - // 1. User laden - const { data: user, error: userError } = await server.supabase - .from("auth_users") - .select("id, email, created_at, must_change_password") - .eq("id", authUser.user_id) - .single() + // ---------------------------------------------------- + // 1) USER LADEN + // ---------------------------------------------------- + const userResult = await server.db + .select({ + id: authUsers.id, + email: authUsers.email, + created_at: authUsers.created_at, + must_change_password: authUsers.must_change_password, + }) + .from(authUsers) + .where(eq(authUsers.id, userId)) + .limit(1) - if (userError || !user) { - return reply.code(401).send({ error: "User not found" }) - } + const user = userResult[0] - // 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,short, locked, extraModules, businessInfo, numberRanges, dokuboxkey, standardEmailForInvoices, standardPaymentDays )`) - .eq("id", authUser.user_id) - .single(); + if (!user) { + return reply.code(401).send({ error: "User not found" }) + } - if (tenantLinksError) { + // ---------------------------------------------------- + // 2) TENANTS LADEN + // ---------------------------------------------------- + const tenantRows = await server.db + .select({ + id: tenants.id, + name: tenants.name, + short: tenants.short, + locked: tenants.locked, + extraModules: tenants.extraModules, + businessInfo: tenants.businessInfo, + numberRanges: tenants.numberRanges, + dokuboxkey: tenants.dokuboxkey, + standardEmailForInvoices: tenants.standardEmailForInvoices, + standardPaymentDays: tenants.standardPaymentDays, + }) + .from(authTenantUsers) + .innerJoin(tenants, eq(authTenantUsers.tenant_id, tenants.id)) + .where(eq(authTenantUsers.user_id, userId)) - console.log(tenantLinksError) + const tenantList = tenantRows ?? [] - return reply.code(401).send({ error: "Tenant Error" }) - } + // ---------------------------------------------------- + // 3) ACTIVE TENANT + // ---------------------------------------------------- + const activeTenant = activeTenantId - const tenants = tenantLinks?.tenants + // ---------------------------------------------------- + // 4) PROFIL LADEN + // ---------------------------------------------------- + let profile = null + if (activeTenantId) { + const profileResult = await server.db + .select() + .from(authProfiles) + .where( + and( + eq(authProfiles.user_id, userId), + eq(authProfiles.tenant_id, activeTenantId) + ) + ) + .limit(1) - // 3. Aktiven Tenant bestimmen - const activeTenant = authUser.tenant_id /*|| tenants[0].id*/ + profile = profileResult?.[0] ?? null + } - // 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", user.id) - .eq("tenant_id", activeTenant) - .single() + // ---------------------------------------------------- + // 5) PERMISSIONS — RPC ERSETZT + // ---------------------------------------------------- + const permissionRows = + (await server.db + .select({ + permission: authRolePermissions.permission, + }) + .from(authUserRoles) + .innerJoin( + authRoles, + and( + eq(authRoles.id, authUserRoles.role_id), + or( + isNull(authRoles.tenant_id), // globale Rolle + eq(authRoles.tenant_id, activeTenantId) // tenant-spezifische Rolle + ) + ) + ) + .innerJoin( + authRolePermissions, + eq(authRolePermissions.role_id, authRoles.id) + ) + .where( + and( + eq(authUserRoles.user_id, userId), + eq(authUserRoles.tenant_id, activeTenantId) + ) + )) ?? [] - profile = profileData - } + const permissions = Array.from( + new Set(permissionRows.map((p) => p.permission)) + ) - // 5. Permissions laden (über Funktion) - const { data: permissionsData, error: permissionsError } = await server.supabase - .rpc("auth_get_user_permissions", { - uid: user.id, - tid: activeTenant || null - }) - - if(permissionsError) { - console.log(permissionsError) - } - - const permissions = permissionsData.map(i => i.permission) || [] - - // 6. Response zurückgeben - return { - user, - tenants, - activeTenant, - profile, - permissions + // ---------------------------------------------------- + // RESPONSE + // ---------------------------------------------------- + return { + user, + tenants: tenantList, + activeTenant, + profile, + permissions, + } + } catch (err: any) { + console.error("ERROR in /me route:", err) + return reply.code(500).send({ error: "Internal server error" }) } }) -} \ No newline at end of file +} diff --git a/src/routes/resources/_template.ts b/src/routes/resources/_template.ts new file mode 100644 index 0000000..03386e5 --- /dev/null +++ b/src/routes/resources/_template.ts @@ -0,0 +1,155 @@ +import { FastifyInstance } from "fastify" +import { asc, desc, eq, ilike, and } from "drizzle-orm" + +// Beispiel-Import — wird in der echten Datei ersetzt +import { exampleTable } from "../../../db/schema/exampleTable" + +export default async function exampleRoutes(server: FastifyInstance) { + + // ------------------------------------------- + // LIST (ohne Pagination) + // ------------------------------------------- + server.get("/resource/example", async (req, reply) => { + const tenantId = req.user?.tenant_id + if (!tenantId) return reply.code(400).send({ error: "No tenant selected" }) + + const { search, sort, asc: ascQuery } = req.query as { + search?: string + sort?: string + asc?: string + } + + let query = server.db + .select() + .from(exampleTable) + .where(eq(exampleTable.tenant, tenantId)) + + // 🔍 OPTIONAL: einfache Suche (LIKE) + if (search) { + query = server.db + .select() + .from(exampleTable) + .where( + and( + eq(exampleTable.tenant, tenantId), + ilike(exampleTable.name as any, `%${search}%`) + ) + ) + } + + // 🔄 Sortierung + if (sort) { + query = query.orderBy( + ascQuery === "true" + ? asc((exampleTable as any)[sort]) + : desc((exampleTable as any)[sort]) + ) + } + + const results = await query + return results + }) + + // ------------------------------------------- + // PAGINATED LIST + // ------------------------------------------- + server.get("/resource/example/paginated", async (req, reply) => { + const tenantId = req.user?.tenant_id + if (!tenantId) return reply.code(400).send({ error: "No tenant selected" }) + + const { + offset = "0", + limit = "25", + search, + sort, + asc: ascQuery, + } = req.query as { + offset?: string + limit?: string + search?: string + sort?: string + asc?: string + } + + const offsetNum = parseInt(offset) + const limitNum = parseInt(limit) + + // --- Basis WHERE + let whereClause: any = eq(exampleTable.tenant, tenantId) + + // --- Suche + if (search) { + whereClause = and( + eq(exampleTable.tenant, tenantId), + ilike(exampleTable.name as any, `%${search}%`) + ) + } + + // ------------------------- + // 1. COUNT Query (total rows) + // ------------------------- + const totalRowsResult = await server.db + .select({ count: server.db.fn.count(exampleTable.id) }) + .from(exampleTable) + .where(whereClause) + + const total = Number(totalRowsResult[0].count) + + // ------------------------- + // 2. DATA Query + // ------------------------- + let dataQuery = server.db + .select() + .from(exampleTable) + .where(whereClause) + .offset(offsetNum) + .limit(limitNum) + + if (sort) { + dataQuery = dataQuery.orderBy( + ascQuery === "true" + ? asc((exampleTable as any)[sort]) + : desc((exampleTable as any)[sort]) + ) + } + + const rows = await dataQuery + + return { + data: rows, + pagination: { + total, + offset: offsetNum, + limit: limitNum, + totalPages: Math.ceil(total / limitNum), + }, + } + }) + + // ------------------------------------------- + // DETAIL ROUTE + // ------------------------------------------- + server.get("/resource/example/:id", async (req, reply) => { + const { id } = req.params as { id: string } + const tenantId = req.user?.tenant_id + + if (!tenantId) return reply.code(400).send({ error: "No tenant selected" }) + + const result = await server.db + .select() + .from(exampleTable) + .where( + and( + eq(exampleTable.id, id), + eq(exampleTable.tenant, tenantId) + ) + ) + .limit(1) + + if (!result.length) { + return reply.code(404).send({ error: "Not found" }) + } + + return result[0] + }) +} diff --git a/src/routes/resources/customers.ts b/src/routes/resources/customers.ts new file mode 100644 index 0000000..5888b55 --- /dev/null +++ b/src/routes/resources/customers.ts @@ -0,0 +1,328 @@ +import { FastifyInstance } from "fastify" +import { + eq, + ilike, + asc, + desc, + and, count, inArray, +} from "drizzle-orm" + +import { + customers, + projects, + plants, + contracts, + contacts, + createddocuments, + statementallocations, + files, + events, +} from "../../../db/schema" // dein zentraler index.ts Export + +export default async function customerRoutes(server: FastifyInstance) { + + + // ------------------------------------------------------------- + // LIST + // ------------------------------------------------------------- + server.get("/resource/customers", async (req, reply) => { + const tenantId = req.user?.tenant_id + if (!tenantId) return reply.code(400).send({ error: "No tenant selected" }) + + const { search, sort, asc: ascQuery } = req.query as { + search?: string + sort?: string + asc?: string + } + + // Basisquery + let baseQuery = server.db + .select() + .from(customers) + .where(eq(customers.tenant, tenantId)) + + // Suche + if (search) { + baseQuery = server.db + .select() + .from(customers) + .where( + and( + eq(customers.tenant, tenantId), + ilike(customers.name, `%${search}%`) + ) + ) + } + + // Sortierung + if (sort) { + const field = (customers as any)[sort] + if (field) { + // @ts-ignore + baseQuery = baseQuery.orderBy( + ascQuery === "true" ? asc(field) : desc(field) + ) + } + } + + const customerList = await baseQuery + + return customerList + }) + + + + // ------------------------------------------------------------- + // PAGINATED + // ------------------------------------------------------------- + server.get("/resource/customers/paginated", async (req, reply) => { + try { + const tenantId = req.user?.tenant_id + if (!tenantId) { + return reply.code(400).send({ error: "No tenant selected" }) + } + + const queryConfig = req.queryConfig + const { + pagination, + sort, + filters, + paginationDisabled + } = queryConfig + + const { + select, + search, + searchColumns, + distinctColumns + } = req.query as { + select?: string + search?: string + searchColumns?: string + distinctColumns?: string + } + + // ---------------------------- + // WHERE CONDITIONS + // ---------------------------- + let whereCond: any = eq(customers.tenant, tenantId) + + if (filters) { + for (const [key, val] of Object.entries(filters)) { + const col = (customers as any)[key] + if (!col) continue + + if (Array.isArray(val)) { + whereCond = and(whereCond, inArray(col, val)) + } else if (val === true || val === false || val === null) { + whereCond = and(whereCond, eq(col, val)) + } else { + whereCond = and(whereCond, eq(col, val)) + } + } + } + + if (search && search.trim().length > 0) { + const searchTerm = `%${search.trim().toLowerCase()}%` + whereCond = and( + whereCond, + ilike(customers.name, searchTerm) + ) + } + + // ---------------------------- + // COUNT FIX (Drizzle-safe) + // ---------------------------- + const totalRes = await server.db + .select({ value: count(customers.id) }) + .from(customers) + .where(whereCond) + + const total = Number(totalRes[0]?.value ?? 0) + + // ---------------------------- + // DISTINCT VALUES (optional) + // ---------------------------- + const distinctValues: Record = {} + + if (distinctColumns) { + for (const colName of distinctColumns.split(",").map(v => v.trim())) { + const col = (customers as any)[colName] + if (!col) continue + + const rows = await server.db + .select({ v: col }) + .from(customers) + .where(eq(customers.tenant, tenantId)) + + const values = rows + .map(r => r.v) + .filter(v => v != null && v !== "") + + distinctValues[colName] = [...new Set(values)].sort() + } + } + + // ---------------------------- + // PAGINATION + // ---------------------------- + let offset = 0 + let limit = 999999 + + if (!paginationDisabled && pagination) { + offset = pagination.offset + limit = pagination.limit + } + + // ---------------------------- + // ORDER BY + // ---------------------------- + let orderField = null + let orderDirection: "asc" | "desc" = "asc" + + if (sort?.length > 0) { + const s = sort[0] + const col = (customers as any)[s.field] + if (col) { + orderField = col + orderDirection = s.direction === "asc" ? "asc" : "desc" + } + } + + // ---------------------------- + // QUERY DATA + // ---------------------------- + let dataQuery = server.db + .select() + .from(customers) + .where(whereCond) + .offset(offset) + .limit(limit) + + if (orderField) { + dataQuery = + orderDirection === "asc" + ? dataQuery.orderBy(asc(orderField)) + : dataQuery.orderBy(desc(orderField)) + } + + const data = await dataQuery + + // ---------------------------- + // BUILD RETURN CONFIG + // ---------------------------- + const totalPages = pagination?.limit + ? Math.ceil(total / pagination.limit) + : 1 + + const enrichedConfig = { + ...queryConfig, + total, + totalPages, + distinctValues, + search: search || null, + } + + return { + data, + queryConfig: enrichedConfig, + } + } + catch (e) { + console.log(e) + } + + }) + + + + // ------------------------------------------------------------- + // DETAIL (mit ALLEN JOINS) + // ------------------------------------------------------------- + server.get("/resource/customers/:id", async (req, reply) => { + const { id } = req.params as { id: string } + const tenantId = req.user?.tenant_id + + if (!tenantId) return reply.code(400).send({ error: "No tenant selected" }) + + // --- 1) Customer selbst laden + const customerRecord = await server.db + .select() + .from(customers) + .where(and(eq(customers.id, Number(id)), eq(customers.tenant, tenantId))) + .limit(1) + + if (!customerRecord.length) { + return reply.code(404).send({ error: "Customer not found" }) + } + + const customer = customerRecord[0] + + + // --- 2) Relations: + const [ + customerProjects, + customerPlants, + customerContracts, + customerContacts, + customerDocuments, + customerFiles, + customerEvents, + ] = await Promise.all([ + // Projekte, die dem Kunden zugeordnet sind + server.db + .select() + .from(projects) + .where(eq(projects.customer, Number(id))), + + server.db + .select() + .from(plants) + .where(eq(plants.customer, Number(id))), + + server.db + .select() + .from(contracts) + .where(eq(contracts.customer, Number(id))), + + server.db + .select() + .from(contacts) + .where(eq(contacts.customer, Number(id))), + + // createddocuments + inner join statementallocations + server.db + .select({ + ...createddocuments, + allocations: statementallocations, + }) + .from(createddocuments) + .leftJoin( + statementallocations, + eq(statementallocations.cd_id, createddocuments.id) + ) + .where(eq(createddocuments.customer, Number(id))), + + server.db + .select() + .from(files) + .where(eq(files.customer, Number(id))), + + server.db + .select() + .from(events) + .where(eq(events.customer, Number(id))), + ]) + + return { + ...customer, + projects: customerProjects, + plants: customerPlants, + contracts: customerContracts, + contacts: customerContacts, + createddocuments: customerDocuments, + files: customerFiles, + events: customerEvents, + } + }) +} diff --git a/src/routes/resources/vendors.ts b/src/routes/resources/vendors.ts new file mode 100644 index 0000000..cbcac84 --- /dev/null +++ b/src/routes/resources/vendors.ts @@ -0,0 +1,284 @@ +import { FastifyInstance } from "fastify" +import { + eq, + ilike, + asc, + desc, + and, + count, + inArray +} from "drizzle-orm" + +import { + vendors, + contacts, + files, +} from "../../../db/schema" + +export default async function vendorRoutes(server: FastifyInstance) { + + // ------------------------------------------------------------- + // LIST + // ------------------------------------------------------------- + server.get("/resource/vendors", async (req, reply) => { + try { + const tenantId = req.user?.tenant_id + if (!tenantId) return reply.code(400).send({ error: "No tenant selected" }) + + const { search, sort, asc: ascQuery } = req.query as { + search?: string + sort?: string + asc?: string + } + + let baseQuery = server.db + .select() + .from(vendors) + .where(eq(vendors.tenant, tenantId)) + + // 🔎 Suche + if (search) { + baseQuery = server.db + .select() + .from(vendors) + .where( + and( + eq(vendors.tenant, tenantId), + ilike(vendors.name, `%${search}%`) + ) + ) + } + + // 🔽 Sortierung + if (sort) { + const field = (vendors as any)[sort] + if (field) { + // @ts-ignore + baseQuery = baseQuery.orderBy( + ascQuery === "true" ? asc(field) : desc(field) + ) + } + } + + return await baseQuery + } catch (err) { + console.error("ERROR /resource/vendors:", err) + return reply.code(500).send({ error: "Internal Server Error" }) + } + }) + + + // ------------------------------------------------------------- + // PAGINATED + // ------------------------------------------------------------- + server.get("/resource/vendors/paginated", async (req, reply) => { + try { + const tenantId = req.user?.tenant_id + if (!tenantId) { + return reply.code(400).send({ error: "No tenant selected" }) + } + + const queryConfig = req.queryConfig + const { + pagination, + sort, + filters, + paginationDisabled + } = queryConfig + + const { + select, + search, + searchColumns, + distinctColumns + } = req.query as { + select?: string + search?: string + searchColumns?: string + distinctColumns?: string + } + + // ---------------------------- + // WHERE CONDITIONS + // ---------------------------- + let whereCond: any = eq(vendors.tenant, tenantId) + + if (filters) { + for (const [key, val] of Object.entries(filters)) { + const col = (vendors as any)[key] + if (!col) continue + + if (Array.isArray(val)) { + whereCond = and(whereCond, inArray(col, val)) + } else if (val === true || val === false || val === null) { + whereCond = and(whereCond, eq(col, val)) + } else { + whereCond = and(whereCond, eq(col, val)) + } + } + } + + if (search && search.trim().length > 0) { + whereCond = and( + whereCond, + ilike(vendors.name, `%${search.trim().toLowerCase()}%`) + ) + } + + // ---------------------------- + // COUNT + // ---------------------------- + const totalRes = await server.db + .select({ value: count(vendors.id) }) + .from(vendors) + .where(whereCond) + + const total = Number(totalRes[0]?.value ?? 0) + + // ---------------------------- + // DISTINCT FILTER VALUES + // ---------------------------- + const distinctValues: Record = {} + + if (distinctColumns) { + for (const colName of distinctColumns.split(",").map(v => v.trim())) { + const col = (vendors as any)[colName] + if (!col) continue + + const rows = await server.db + .select({ v: col }) + .from(vendors) + .where(eq(vendors.tenant, tenantId)) + + const values = rows + .map(r => r.v) + .filter(v => v != null && v !== "") + + distinctValues[colName] = [...new Set(values)].sort() + } + } + + // ---------------------------- + // PAGINATION + // ---------------------------- + let offset = 0 + let limit = 999999 + + if (!paginationDisabled && pagination) { + offset = pagination.offset + limit = pagination.limit + } + + // ---------------------------- + // ORDER BY + // ---------------------------- + let orderField = null + let orderDirection: "asc" | "desc" = "asc" + + if (sort?.length > 0) { + const s = sort[0] + const col = (vendors as any)[s.field] + if (col) { + orderField = col + orderDirection = s.direction === "asc" ? "asc" : "desc" + } + } + + // ---------------------------- + // QUERY DATA + // ---------------------------- + let dataQuery = server.db + .select() + .from(vendors) + .where(whereCond) + .offset(offset) + .limit(limit) + + if (orderField) { + dataQuery = + orderDirection === "asc" + ? dataQuery.orderBy(asc(orderField)) + : dataQuery.orderBy(desc(orderField)) + } + + const data = await dataQuery + + const totalPages = pagination?.limit + ? Math.ceil(total / pagination.limit) + : 1 + + const enrichedConfig = { + ...queryConfig, + total, + totalPages, + distinctValues, + search: search || null, + } + + return { data, queryConfig: enrichedConfig } + } catch (err) { + console.error("ERROR /resource/vendors/paginated:", err) + return reply.code(500).send({ error: "Internal Server Error" }) + } + }) + + + + // ------------------------------------------------------------- + // DETAIL (mit JOINS) + // ------------------------------------------------------------- + server.get("/resource/vendors/:id", async (req, reply) => { + try { + const { id } = req.params as { id: string } + const tenantId = req.user?.tenant_id + + if (!tenantId) return reply.code(400).send({ error: "No tenant selected" }) + + const vendorId = Number(id) + + const vendorRecord = await server.db + .select() + .from(vendors) + .where( + and( + eq(vendors.id, vendorId), + eq(vendors.tenant, tenantId) + ) + ) + .limit(1) + + if (!vendorRecord.length) { + return reply.code(404).send({ error: "Vendor not found" }) + } + + const vendor = vendorRecord[0] + + // ---------------------------- + // RELATIONS + // ---------------------------- + const [ + vendorContacts, + vendorFiles, + ] = await Promise.all([ + server.db + .select() + .from(contacts) + .where(eq(contacts.vendor, vendorId)), + + server.db + .select() + .from(files) + .where(eq(files.vendor, vendorId)), + ]) + + return { + ...vendor, + contacts: vendorContacts, + files: vendorFiles, + } + } catch (err) { + console.error("ERROR /resource/vendors/:id:", err) + return reply.code(500).send({ error: "Internal Server Error" }) + } + }) +} From 1e71e543142d7670fabf6ba43c5f64eeadbf1e3c Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sat, 6 Dec 2025 20:10:57 +0100 Subject: [PATCH 086/149] Schema --- db/schema/productcategories.ts | 6 +++--- db/schema/servicecategories.ts | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/db/schema/productcategories.ts b/db/schema/productcategories.ts index ec8753c..980a7fc 100644 --- a/db/schema/productcategories.ts +++ b/db/schema/productcategories.ts @@ -10,7 +10,7 @@ import { import { tenants } from "./tenants" import { authUsers } from "./auth_users" -export const productCategories = pgTable("productcategories", { +export const productcategories = pgTable("productcategories", { id: bigint("id", { mode: "number" }) .primaryKey() .generatedByDefaultAsIdentity(), @@ -33,5 +33,5 @@ export const productCategories = pgTable("productcategories", { updatedBy: uuid("updated_by").references(() => authUsers.id), }) -export type ProductCategory = typeof productCategories.$inferSelect -export type NewProductCategory = typeof productCategories.$inferInsert +export type ProductCategory = typeof productcategories.$inferSelect +export type NewProductCategory = typeof productcategories.$inferInsert diff --git a/db/schema/servicecategories.ts b/db/schema/servicecategories.ts index 25ba702..e51b03a 100644 --- a/db/schema/servicecategories.ts +++ b/db/schema/servicecategories.ts @@ -11,7 +11,7 @@ import { import { tenants } from "./tenants" import { authUsers } from "./auth_users" -export const serviceCategories = pgTable("servicecategories", { +export const servicecategories = pgTable("servicecategories", { id: bigint("id", { mode: "number" }) .primaryKey() .generatedByDefaultAsIdentity(), @@ -35,5 +35,5 @@ export const serviceCategories = pgTable("servicecategories", { updatedBy: uuid("updated_by").references(() => authUsers.id), }) -export type ServiceCategory = typeof serviceCategories.$inferSelect -export type NewServiceCategory = typeof serviceCategories.$inferInsert +export type ServiceCategory = typeof servicecategories.$inferSelect +export type NewServiceCategory = typeof servicecategories.$inferInsert From 765f3c42c17c58a8b4a4196b456a1e1482d34440 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sat, 6 Dec 2025 20:31:59 +0100 Subject: [PATCH 087/149] Added dbSearch util --- src/utils/dbSearch.ts | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 src/utils/dbSearch.ts diff --git a/src/utils/dbSearch.ts b/src/utils/dbSearch.ts new file mode 100644 index 0000000..e306167 --- /dev/null +++ b/src/utils/dbSearch.ts @@ -0,0 +1,27 @@ +import { ilike, or } from "drizzle-orm" + +/** + * Erzeugt eine OR-Suchbedingung über mehrere Spalten + * + * @param table - Drizzle Table Schema + * @param columns - Array der Spaltennamen (property names im schema) + * @param search - Suchbegriff + */ +export function buildSearchWhere(table: any, columns: string[], search: string) { + if (!search || !columns.length) return undefined + + const term = `%${search.toLowerCase()}%` + + const parts = columns + .map((colName) => { + const col = table[colName] + if (!col) return null + return ilike(col, term) + }) + .filter(Boolean) + + if (parts.length === 0) return undefined + + // @ts-ignore + return or(...parts) +} From dff2b0540199e50b764b756e2589d81580e1404a Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sat, 6 Dec 2025 20:33:27 +0100 Subject: [PATCH 088/149] Redone Routes customers,contracts,contacts,vendors --- src/routes/resources/contacts.ts | 276 ++++++++++++++++++++++++++++++ src/routes/resources/contracts.ts | 244 ++++++++++++++++++++++++++ src/routes/resources/customers.ts | 143 ++++++++-------- src/routes/resources/vendors.ts | 223 ++++++++++-------------- 4 files changed, 687 insertions(+), 199 deletions(-) create mode 100644 src/routes/resources/contacts.ts create mode 100644 src/routes/resources/contracts.ts diff --git a/src/routes/resources/contacts.ts b/src/routes/resources/contacts.ts new file mode 100644 index 0000000..ae5a675 --- /dev/null +++ b/src/routes/resources/contacts.ts @@ -0,0 +1,276 @@ +import { FastifyInstance } from "fastify" +import { + eq, + ilike, + asc, + desc, + and, + count, + inArray, + or +} from "drizzle-orm" + +import { + contacts, + customers, + vendors +} from "../../../db/schema" + + +// ------------------------------------------------------------- +// 🔍 Helper für SQL-Suche über mehrere Spalten +// ------------------------------------------------------------- +function buildSearchCondition(table: any, columns: string[], search: string) { + if (!search || !columns.length) return null + + const term = `%${search.toLowerCase()}%` + + const conditions = columns + .map(c => table[c]) + .filter(Boolean) + .map(col => ilike(col, term)) + + if (conditions.length === 0) return null + + // @ts-ignore + return or(...conditions) +} + + + +export default async function contactsRoutes(server: FastifyInstance) { + + + // ------------------------------------------------------------- + // LIST + // ------------------------------------------------------------- + server.get("/resource/contacts", async (req, reply) => { + const tenantId = req.user?.tenant_id + if (!tenantId) + return reply.code(400).send({ error: "No tenant selected" }) + + const { search, sort, asc: ascQuery } = req.query as { + search?: string + sort?: string + asc?: string + } + + // Grundfilter + let whereCond: any = eq(contacts.tenant, tenantId) + + // 🔍 Suche + if (search) { + const searchCond = buildSearchCondition( + contacts, + ["firstName", "lastName", "email", "phone", "notes"], + search + ) + if (searchCond) whereCond = and(whereCond, searchCond) + } + + // Query + let q = server.db.select().from(contacts).where(whereCond) + + // Sortierung + if (sort) { + const field = (contacts as any)[sort] + if (field) { + //@ts-ignore + q = q.orderBy( + ascQuery === "true" ? asc(field) : desc(field) + ) + } + } + + return await q + }) + + + + // ------------------------------------------------------------- + // PAGINATED + // ------------------------------------------------------------- + server.get("/resource/contacts/paginated", async (req, reply) => { + try { + const tenantId = req.user?.tenant_id + if (!tenantId) + return reply.code(400).send({ error: "No tenant selected" }) + + const queryConfig = req.queryConfig + const { + pagination, + sort, + filters, + paginationDisabled + } = queryConfig + + const { search, distinctColumns } = req.query as { + search?: string + distinctColumns?: string + } + + // ----------------------------------- + // WHERE CONDITIONS + // ----------------------------------- + let whereCond: any = eq(contacts.tenant, tenantId) + + // Filter + if (filters) { + for (const [key, val] of Object.entries(filters)) { + const col = (contacts as any)[key] + if (!col) continue + + if (Array.isArray(val)) { + whereCond = and(whereCond, inArray(col, val)) + } else { + whereCond = and(whereCond, eq(col, val as any)) + } + } + } + + // 🔍 Suche + if (search && search.trim().length > 0) { + const searchCond = buildSearchCondition( + contacts, + ["firstName", "lastName", "email", "phone", "notes"], + search + ) + if (searchCond) whereCond = and(whereCond, searchCond) + } + + // ----------------------------------- + // COUNT + // ----------------------------------- + const totalRes = await server.db + .select({ value: count(contacts.id) }) + .from(contacts) + .where(whereCond) + + const total = Number(totalRes[0]?.value ?? 0) + + // ----------------------------------- + // DISTINCT VALUES + // ----------------------------------- + const distinctValues: Record = {} + + if (distinctColumns) { + for (const colName of distinctColumns.split(",").map(v => v.trim())) { + const col = (contacts as any)[colName] + if (!col) continue + + const rows = await server.db + .select({ v: col }) + .from(contacts) + .where(eq(contacts.tenant, tenantId)) + + distinctValues[colName] = + [...new Set(rows.map(r => r.v).filter(v => v !== null && v !== ""))] + .sort() + } + } + + // ----------------------------------- + // PAGINATION + // ----------------------------------- + let offset = pagination?.offset ?? 0 + let limit = pagination?.limit ?? 999999 + + // ----------------------------------- + // ORDER + // ----------------------------------- + let orderField = null + let orderDirection: "asc" | "desc" = "asc" + + if (sort?.length > 0) { + const s = sort[0] + const col = (contacts as any)[s.field] + if (col) { + orderField = col + orderDirection = s.direction === "asc" ? "asc" : "desc" + } + } + + // ----------------------------------- + // DATA QUERY + // ----------------------------------- + let dataQuery = server.db + .select() + .from(contacts) + .where(whereCond) + .offset(offset) + .limit(limit) + + if (orderField) { + //@ts-ignore + dataQuery = + orderDirection === "asc" + ? dataQuery.orderBy(asc(orderField)) + : dataQuery.orderBy(desc(orderField)) + } + + const data = await dataQuery + + return { + data, + queryConfig: { + ...queryConfig, + total, + totalPages: pagination?.limit + ? Math.ceil(total / pagination.limit) + : 1, + distinctValues, + search: search || null + } + } + + } catch (err) { + console.error(err) + return reply.code(500).send({ error: "Internal Server Error" }) + } + }) + + + + // ------------------------------------------------------------- + // DETAIL (customer + vendor) + // ------------------------------------------------------------- + server.get("/resource/contacts/:id", async (req, reply) => { + const { id } = req.params as { id: string } + const tenantId = req.user?.tenant_id + + if (!tenantId) + return reply.code(400).send({ error: "No tenant selected" }) + + const rows = await server.db + .select() + .from(contacts) + .where( + and( + eq(contacts.id, Number(id)), + eq(contacts.tenant, tenantId) + ) + ) + .limit(1) + + if (!rows.length) + return reply.code(404).send({ error: "Not found" }) + + const contact = rows[0] + + const [customerRecord, vendorRecord] = await Promise.all([ + contact.customer + ? server.db.select().from(customers).where(eq(customers.id, contact.customer)) + : [], + contact.vendor + ? server.db.select().from(vendors).where(eq(vendors.id, contact.vendor)) + : [], + ]) + + return { + ...contact, + customer: customerRecord[0] ?? null, + vendor: vendorRecord[0] ?? null, + } + }) + +} diff --git a/src/routes/resources/contracts.ts b/src/routes/resources/contracts.ts new file mode 100644 index 0000000..c0e8d6c --- /dev/null +++ b/src/routes/resources/contracts.ts @@ -0,0 +1,244 @@ +import { FastifyInstance } from "fastify" +import { + eq, + ilike, + asc, + desc, + and, + count, + inArray, + or +} from "drizzle-orm" + +import { + contracts, + customers, + files +} from "../../../db/schema" + +// ------------------------------------------------------------- +// Helper: SQL‐LIKE Suche über mehrere Felder +// ------------------------------------------------------------- +function buildSearchCondition(table: any, columns: string[], search?: string) { + if (!search) return null + + const term = `%${search.toLowerCase()}%` + + const conditions = columns + .map(col => table[col]) + .filter(Boolean) + .map(col => ilike(col, term)) + + if (conditions.length === 0) return null + + // @ts-ignore + return or(...conditions) +} + +export default async function contractsRoutes(server: FastifyInstance) { + + // ------------------------------------------------------------- + // LIST + // ------------------------------------------------------------- + server.get("/resource/contracts", async (req, reply) => { + const tenantId = req.user?.tenant_id + if (!tenantId) return reply.code(400).send({ error: "No tenant selected" }) + + const { search, sort, asc: ascQuery } = req.query as { + search?: string + sort?: string + asc?: string + } + + let whereCond: any = eq(contracts.tenant, tenantId) + + // SQL SEARCH + const searchCond = buildSearchCondition( + contracts, + ["name", "notes", "contractNumber", "paymentType", "sepaRef", "bankingName"], + search + ) + if (searchCond) whereCond = and(whereCond, searchCond) + + // Query + let q = server.db + .select() + .from(contracts) + .where(whereCond) + + // SORT + if (sort) { + const field = (contracts as any)[sort] + if (field) { + //@ts-ignore + q = q.orderBy( + ascQuery === "true" ? asc(field) : desc(field) + ) + } + } + + return await q + }) + + + // ------------------------------------------------------------- + // PAGINATED + // ------------------------------------------------------------- + server.get("/resource/contracts/paginated", async (req, reply) => { + try { + const tenantId = req.user?.tenant_id + if (!tenantId) return reply.code(400).send({ error: "No tenant selected" }) + + const queryConfig = req.queryConfig + const { pagination, sort, filters, paginationDisabled } = queryConfig + + const { search, distinctColumns } = req.query as { + search?: string + distinctColumns?: string + } + + // ----------------------------------- + // WHERE + // ----------------------------------- + let whereCond: any = eq(contracts.tenant, tenantId) + + if (filters) { + for (const [key, val] of Object.entries(filters)) { + const col = (contracts as any)[key] + if (!col) continue + + if (Array.isArray(val)) { + whereCond = and(whereCond, inArray(col, val)) + } else { + whereCond = and(whereCond, eq(col, val as any)) + } + } + } + + // SQL SEARCH + const searchCond = buildSearchCondition( + contracts, + ["name", "notes", "contractNumber", "paymentType", "sepaRef", "bankingName"], + search + ) + if (searchCond) whereCond = and(whereCond, searchCond) + + // ----------------------------------- + // COUNT + // ----------------------------------- + const totalRes = await server.db + .select({ value: count(contracts.id) }) + .from(contracts) + .where(whereCond) + + const total = Number(totalRes[0]?.value ?? 0) + + // ----------------------------------- + // DISTINCT + // ----------------------------------- + const distinctValues: Record = {} + + if (distinctColumns) { + for (const colName of distinctColumns.split(",")) { + const col = (contracts as any)[colName.trim()] + if (!col) continue + + const rows = await server.db + .select({ v: col }) + .from(contracts) + .where(eq(contracts.tenant, tenantId)) + + distinctValues[colName] = + [...new Set(rows.map(r => r.v).filter(v => v != null && v !== ""))] + .sort() + } + } + + // ----------------------------------- + // PAGINATION + // ----------------------------------- + let offset = pagination?.offset ?? 0 + let limit = pagination?.limit ?? 999999 + + // ----------------------------------- + // SORT + // ----------------------------------- + let orderField = null + let orderDir: "asc" | "desc" = "asc" + + if (sort?.length > 0) { + const s = sort[0] + const col = (contracts as any)[s.field] + if (col) { + orderField = col + orderDir = s.direction === "asc" ? "asc" : "desc" + } + } + + // ----------------------------------- + // QUERY DATA + // ----------------------------------- + let q = server.db + .select() + .from(contracts) + .where(whereCond) + .offset(offset) + .limit(limit) + + if (orderField) { + //@ts-ignore + q = orderDir === "asc" ? q.orderBy(asc(orderField)) : q.orderBy(desc(orderField)) + } + + const data = await q + + return { + data, + queryConfig: { + ...queryConfig, + total, + totalPages: pagination?.limit ? Math.ceil(total / pagination.limit) : 1, + distinctValues, + search: search || null, + } + } + } catch (e) { + console.error(e) + return reply.code(500).send({ error: "Internal Server Error" }) + } + }) + + + // ------------------------------------------------------------- + // DETAIL (+ JOINS) + // ------------------------------------------------------------- + server.get("/resource/contracts/:id", async (req, reply) => { + const { id } = req.params as { id: string } + const tenantId = req.user?.tenant_id + + if (!tenantId) return reply.code(400).send({ error: "No tenant selected" }) + + const rows = await server.db + .select() + .from(contracts) + .where(and(eq(contracts.id, Number(id)), eq(contracts.tenant, tenantId))) + .limit(1) + + if (!rows.length) return reply.code(404).send({ error: "Not found" }) + + const contract = rows[0] + + const [customerRecord, fileList] = await Promise.all([ + contract.customer + ? server.db.select().from(customers).where(eq(customers.id, contract.customer)) + : [], + server.db.select().from(files).where(eq(files.contract, Number(id))), + ]) + + return { + ...contract, + customer: customerRecord[0] ?? null, + files: fileList, + } + }) +} diff --git a/src/routes/resources/customers.ts b/src/routes/resources/customers.ts index 5888b55..773b94f 100644 --- a/src/routes/resources/customers.ts +++ b/src/routes/resources/customers.ts @@ -4,7 +4,10 @@ import { ilike, asc, desc, - and, count, inArray, + and, + count, + inArray, + or, } from "drizzle-orm" import { @@ -17,7 +20,29 @@ import { statementallocations, files, events, -} from "../../../db/schema" // dein zentraler index.ts Export +} from "../../../db/schema" + + +// ------------------------------------------------------------- +// 🔍 Helper für SQL-Suche über mehrere Spalten +// ------------------------------------------------------------- +function buildSearchCondition(table: any, columns: string[], search: string) { + if (!search || !columns.length) return null + + const term = `%${search.toLowerCase()}%` + + const conditions = columns + .map((colName) => table[colName]) + .filter(Boolean) + .map((col) => ilike(col, term)) + + if (conditions.length === 0) return null + + // @ts-ignore + return or(...conditions) +} + + export default async function customerRoutes(server: FastifyInstance) { @@ -36,38 +61,38 @@ export default async function customerRoutes(server: FastifyInstance) { } // Basisquery + let whereCond: any = eq(customers.tenant, tenantId) + + // 🔍 SQL-Suche + if (search) { + const searchCond = buildSearchCondition( + customers, + ["name", "customerNumber", "firstname", "lastname", "notes"], + search + ) + + if (searchCond) { + whereCond = and(whereCond, searchCond) + } + } + let baseQuery = server.db .select() .from(customers) - .where(eq(customers.tenant, tenantId)) - - // Suche - if (search) { - baseQuery = server.db - .select() - .from(customers) - .where( - and( - eq(customers.tenant, tenantId), - ilike(customers.name, `%${search}%`) - ) - ) - } + .where(whereCond) // Sortierung if (sort) { const field = (customers as any)[sort] if (field) { - // @ts-ignore + //@ts-ignore baseQuery = baseQuery.orderBy( ascQuery === "true" ? asc(field) : desc(field) ) } } - const customerList = await baseQuery - - return customerList + return await baseQuery }) @@ -91,22 +116,19 @@ export default async function customerRoutes(server: FastifyInstance) { } = queryConfig const { - select, search, - searchColumns, distinctColumns } = req.query as { - select?: string search?: string - searchColumns?: string distinctColumns?: string } // ---------------------------- - // WHERE CONDITIONS + // WHERE CONDITIONS (Basis) // ---------------------------- let whereCond: any = eq(customers.tenant, tenantId) + // Filters if (filters) { for (const [key, val] of Object.entries(filters)) { const col = (customers as any)[key] @@ -114,24 +136,29 @@ export default async function customerRoutes(server: FastifyInstance) { if (Array.isArray(val)) { whereCond = and(whereCond, inArray(col, val)) - } else if (val === true || val === false || val === null) { - whereCond = and(whereCond, eq(col, val)) } else { - whereCond = and(whereCond, eq(col, val)) + whereCond = and(whereCond, eq(col, val as any)) } } } + // ---------------------------- + // 🔍 SEARCH + // ---------------------------- if (search && search.trim().length > 0) { - const searchTerm = `%${search.trim().toLowerCase()}%` - whereCond = and( - whereCond, - ilike(customers.name, searchTerm) + const searchCond = buildSearchCondition( + customers, + ["name", "customerNumber", "firstname", "lastname", "notes"], + search.trim() ) + + if (searchCond) { + whereCond = and(whereCond, searchCond) + } } // ---------------------------- - // COUNT FIX (Drizzle-safe) + // COUNT // ---------------------------- const totalRes = await server.db .select({ value: count(customers.id) }) @@ -141,7 +168,7 @@ export default async function customerRoutes(server: FastifyInstance) { const total = Number(totalRes[0]?.value ?? 0) // ---------------------------- - // DISTINCT VALUES (optional) + // DISTINCT VALUES // ---------------------------- const distinctValues: Record = {} @@ -155,11 +182,9 @@ export default async function customerRoutes(server: FastifyInstance) { .from(customers) .where(eq(customers.tenant, tenantId)) - const values = rows - .map(r => r.v) - .filter(v => v != null && v !== "") - - distinctValues[colName] = [...new Set(values)].sort() + distinctValues[colName] = + [...new Set(rows.map(r => r.v).filter(v => v != null && v !== ""))] + .sort() } } @@ -200,6 +225,7 @@ export default async function customerRoutes(server: FastifyInstance) { .limit(limit) if (orderField) { + //@ts-ignore dataQuery = orderDirection === "asc" ? dataQuery.orderBy(asc(orderField)) @@ -209,7 +235,7 @@ export default async function customerRoutes(server: FastifyInstance) { const data = await dataQuery // ---------------------------- - // BUILD RETURN CONFIG + // CONFIG RESPONSE // ---------------------------- const totalPages = pagination?.limit ? Math.ceil(total / pagination.limit) @@ -231,7 +257,6 @@ export default async function customerRoutes(server: FastifyInstance) { catch (e) { console.log(e) } - }) @@ -269,28 +294,11 @@ export default async function customerRoutes(server: FastifyInstance) { customerFiles, customerEvents, ] = await Promise.all([ - // Projekte, die dem Kunden zugeordnet sind - server.db - .select() - .from(projects) - .where(eq(projects.customer, Number(id))), + server.db.select().from(projects).where(eq(projects.customer, Number(id))), + server.db.select().from(plants).where(eq(plants.customer, Number(id))), + server.db.select().from(contracts).where(eq(contracts.customer, Number(id))), + server.db.select().from(contacts).where(eq(contacts.customer, Number(id))), - server.db - .select() - .from(plants) - .where(eq(plants.customer, Number(id))), - - server.db - .select() - .from(contracts) - .where(eq(contracts.customer, Number(id))), - - server.db - .select() - .from(contacts) - .where(eq(contacts.customer, Number(id))), - - // createddocuments + inner join statementallocations server.db .select({ ...createddocuments, @@ -303,15 +311,8 @@ export default async function customerRoutes(server: FastifyInstance) { ) .where(eq(createddocuments.customer, Number(id))), - server.db - .select() - .from(files) - .where(eq(files.customer, Number(id))), - - server.db - .select() - .from(events) - .where(eq(events.customer, Number(id))), + server.db.select().from(files).where(eq(files.customer, Number(id))), + server.db.select().from(events).where(eq(events.customer, Number(id))), ]) return { diff --git a/src/routes/resources/vendors.ts b/src/routes/resources/vendors.ts index cbcac84..4408f53 100644 --- a/src/routes/resources/vendors.ts +++ b/src/routes/resources/vendors.ts @@ -6,7 +6,8 @@ import { desc, and, count, - inArray + inArray, + or } from "drizzle-orm" import { @@ -15,6 +16,22 @@ import { files, } from "../../../db/schema" +// ------------------------------------------------------------- +// SQL Volltext-Suche (über mehrere relevante Felder) +// ------------------------------------------------------------- +function buildVendorSearchTerm(search?: string) { + if (!search) return null + + const term = `%${search.toLowerCase()}%` + + return or( + ilike(vendors.name, term), + ilike(vendors.vendorNumber, term), + ilike(vendors.notes, term), + ilike(vendors.defaultPaymentMethod, term) + ) +} + export default async function vendorRoutes(server: FastifyInstance) { // ------------------------------------------------------------- @@ -31,36 +48,30 @@ export default async function vendorRoutes(server: FastifyInstance) { asc?: string } - let baseQuery = server.db + // WHERE + let whereCond: any = eq(vendors.tenant, tenantId) + + const searchCond = buildVendorSearchTerm(search) + if (searchCond) whereCond = and(whereCond, searchCond) + + // QUERY + let q = server.db .select() .from(vendors) - .where(eq(vendors.tenant, tenantId)) + .where(whereCond) - // 🔎 Suche - if (search) { - baseQuery = server.db - .select() - .from(vendors) - .where( - and( - eq(vendors.tenant, tenantId), - ilike(vendors.name, `%${search}%`) - ) - ) - } - - // 🔽 Sortierung + // SORT if (sort) { - const field = (vendors as any)[sort] - if (field) { - // @ts-ignore - baseQuery = baseQuery.orderBy( - ascQuery === "true" ? asc(field) : desc(field) - ) + const col = (vendors as any)[sort] + if (col) { + //@ts-ignore + q = ascQuery === "true" + ? q.orderBy(asc(col)) + : q.orderBy(desc(col)) } } - return await baseQuery + return await q } catch (err) { console.error("ERROR /resource/vendors:", err) return reply.code(500).send({ error: "Internal Server Error" }) @@ -74,35 +85,22 @@ export default async function vendorRoutes(server: FastifyInstance) { server.get("/resource/vendors/paginated", async (req, reply) => { try { const tenantId = req.user?.tenant_id - if (!tenantId) { - return reply.code(400).send({ error: "No tenant selected" }) - } + if (!tenantId) return reply.code(400).send({ error: "No tenant selected" }) const queryConfig = req.queryConfig - const { - pagination, - sort, - filters, - paginationDisabled - } = queryConfig + const { pagination, sort, filters, paginationDisabled } = queryConfig - const { - select, - search, - searchColumns, - distinctColumns - } = req.query as { - select?: string + const { search, distinctColumns } = req.query as { search?: string - searchColumns?: string distinctColumns?: string } - // ---------------------------- - // WHERE CONDITIONS - // ---------------------------- + // ------------------------------------ + // WHERE + // ------------------------------------ let whereCond: any = eq(vendors.tenant, tenantId) + // Filters if (filters) { for (const [key, val] of Object.entries(filters)) { const col = (vendors as any)[key] @@ -110,24 +108,19 @@ export default async function vendorRoutes(server: FastifyInstance) { if (Array.isArray(val)) { whereCond = and(whereCond, inArray(col, val)) - } else if (val === true || val === false || val === null) { - whereCond = and(whereCond, eq(col, val)) } else { - whereCond = and(whereCond, eq(col, val)) + whereCond = and(whereCond, eq(col, val as any)) } } } - if (search && search.trim().length > 0) { - whereCond = and( - whereCond, - ilike(vendors.name, `%${search.trim().toLowerCase()}%`) - ) - } + // SEARCH + const searchCond = buildVendorSearchTerm(search) + if (searchCond) whereCond = and(whereCond, searchCond) - // ---------------------------- + // ------------------------------------ // COUNT - // ---------------------------- + // ------------------------------------ const totalRes = await server.db .select({ value: count(vendors.id) }) .from(vendors) @@ -135,14 +128,14 @@ export default async function vendorRoutes(server: FastifyInstance) { const total = Number(totalRes[0]?.value ?? 0) - // ---------------------------- - // DISTINCT FILTER VALUES - // ---------------------------- + // ------------------------------------ + // DISTINCT VALUES + // ------------------------------------ const distinctValues: Record = {} if (distinctColumns) { - for (const colName of distinctColumns.split(",").map(v => v.trim())) { - const col = (vendors as any)[colName] + for (const field of distinctColumns.split(",")) { + const col = (vendors as any)[field.trim()] if (!col) continue const rows = await server.db @@ -152,42 +145,37 @@ export default async function vendorRoutes(server: FastifyInstance) { const values = rows .map(r => r.v) - .filter(v => v != null && v !== "") + .filter(v => v !== null && v !== "") - distinctValues[colName] = [...new Set(values)].sort() + distinctValues[field] = [...new Set(values)].sort() } } - // ---------------------------- + // ------------------------------------ // PAGINATION - // ---------------------------- - let offset = 0 - let limit = 999999 + // ------------------------------------ + const offset = pagination?.offset ?? 0 + const limit = pagination?.limit ?? 5000 - if (!paginationDisabled && pagination) { - offset = pagination.offset - limit = pagination.limit - } - - // ---------------------------- - // ORDER BY - // ---------------------------- - let orderField = null - let orderDirection: "asc" | "desc" = "asc" + // ------------------------------------ + // SORT + // ------------------------------------ + let orderField: any = null + let orderDir: "asc" | "desc" = "asc" if (sort?.length > 0) { const s = sort[0] const col = (vendors as any)[s.field] if (col) { orderField = col - orderDirection = s.direction === "asc" ? "asc" : "desc" + orderDir = s.direction === "asc" ? "asc" : "desc" } } - // ---------------------------- - // QUERY DATA - // ---------------------------- - let dataQuery = server.db + // ------------------------------------ + // DATA QUERY + // ------------------------------------ + let q = server.db .select() .from(vendors) .where(whereCond) @@ -195,27 +183,27 @@ export default async function vendorRoutes(server: FastifyInstance) { .limit(limit) if (orderField) { - dataQuery = - orderDirection === "asc" - ? dataQuery.orderBy(asc(orderField)) - : dataQuery.orderBy(desc(orderField)) + //@ts-ignore + q = orderDir === "asc" + ? q.orderBy(asc(orderField)) + : q.orderBy(desc(orderField)) } - const data = await dataQuery + const data = await q - const totalPages = pagination?.limit - ? Math.ceil(total / pagination.limit) - : 1 - - const enrichedConfig = { - ...queryConfig, - total, - totalPages, - distinctValues, - search: search || null, + return { + data, + queryConfig: { + ...queryConfig, + total, + totalPages: pagination?.limit + ? Math.ceil(total / pagination.limit) + : 1, + distinctValues, + search: search || null + } } - return { data, queryConfig: enrichedConfig } } catch (err) { console.error("ERROR /resource/vendors/paginated:", err) return reply.code(500).send({ error: "Internal Server Error" }) @@ -223,59 +211,38 @@ export default async function vendorRoutes(server: FastifyInstance) { }) - // ------------------------------------------------------------- - // DETAIL (mit JOINS) + // DETAIL (mit JOINs: contacts, files) // ------------------------------------------------------------- server.get("/resource/vendors/:id", async (req, reply) => { try { const { id } = req.params as { id: string } const tenantId = req.user?.tenant_id - if (!tenantId) return reply.code(400).send({ error: "No tenant selected" }) const vendorId = Number(id) - const vendorRecord = await server.db + const vendorRows = await server.db .select() .from(vendors) - .where( - and( - eq(vendors.id, vendorId), - eq(vendors.tenant, tenantId) - ) - ) + .where(and(eq(vendors.id, vendorId), eq(vendors.tenant, tenantId))) .limit(1) - if (!vendorRecord.length) { - return reply.code(404).send({ error: "Vendor not found" }) - } + if (!vendorRows.length) return reply.code(404).send({ error: "Vendor not found" }) - const vendor = vendorRecord[0] + const vendor = vendorRows[0] - // ---------------------------- - // RELATIONS - // ---------------------------- - const [ - vendorContacts, - vendorFiles, - ] = await Promise.all([ - server.db - .select() - .from(contacts) - .where(eq(contacts.vendor, vendorId)), - - server.db - .select() - .from(files) - .where(eq(files.vendor, vendorId)), + const [vendorContacts, vendorFiles] = await Promise.all([ + server.db.select().from(contacts).where(eq(contacts.vendor, vendorId)), + server.db.select().from(files).where(eq(files.vendor, vendorId)) ]) return { ...vendor, contacts: vendorContacts, - files: vendorFiles, + files: vendorFiles } + } catch (err) { console.error("ERROR /resource/vendors/:id:", err) return reply.code(500).send({ error: "Internal Server Error" }) From 0f3c8c862ff1539c2d9d3e74101a893346756d53 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sat, 6 Dec 2025 22:50:15 +0100 Subject: [PATCH 089/149] removed Routes, Introduced Single Route with Cecking --- db/schema/projects.ts | 2 +- db/schema/staff_time_entries.ts | 6 +- src/index.ts | 10 +- src/routes/resources.ts | 829 ---------------------- src/routes/resources/_template.ts | 155 ---- src/routes/resources/contacts.ts | 276 -------- src/routes/resources/contracts.ts | 244 ------- src/routes/resources/customers.ts | 329 --------- src/routes/resources/main.ts | 413 +++++++++++ src/routes/resources/productsServices.ts | 858 +++++++++++++++++++++++ src/routes/resources/vendors.ts | 251 ------- 11 files changed, 1279 insertions(+), 2094 deletions(-) delete mode 100644 src/routes/resources.ts delete mode 100644 src/routes/resources/_template.ts delete mode 100644 src/routes/resources/contacts.ts delete mode 100644 src/routes/resources/contracts.ts delete mode 100644 src/routes/resources/customers.ts create mode 100644 src/routes/resources/main.ts create mode 100644 src/routes/resources/productsServices.ts delete mode 100644 src/routes/resources/vendors.ts diff --git a/db/schema/projects.ts b/db/schema/projects.ts index fc12993..badf583 100644 --- a/db/schema/projects.ts +++ b/db/schema/projects.ts @@ -71,7 +71,7 @@ export const projects = pgTable("projects", { updatedAt: timestamp("updated_at", { withTimezone: true }), updatedBy: uuid("updated_by").references(() => authUsers.id), - activePhase: text("active_phase"), + active_phase: text("active_phase"), }) export type Project = typeof projects.$inferSelect diff --git a/db/schema/staff_time_entries.ts b/db/schema/staff_time_entries.ts index 06534c5..5932fa9 100644 --- a/db/schema/staff_time_entries.ts +++ b/db/schema/staff_time_entries.ts @@ -14,7 +14,7 @@ import { authUsers } from "./auth_users" import { timesStateEnum } from "./enums" import {sql} from "drizzle-orm"; -export const staffTimeEntries = pgTable("staff_time_entries", { +export const stafftimeentries = pgTable("staff_time_entries", { id: uuid("id").primaryKey().defaultRandom(), tenantId: bigint("tenant_id", { mode: "number" }) @@ -64,5 +64,5 @@ export const staffTimeEntries = pgTable("staff_time_entries", { sickReason: text("sick_reason"), }) -export type StaffTimeEntry = typeof staffTimeEntries.$inferSelect -export type NewStaffTimeEntry = typeof staffTimeEntries.$inferInsert +export type StaffTimeEntry = typeof stafftimeentries.$inferSelect +export type NewStaffTimeEntry = typeof stafftimeentries.$inferInsert diff --git a/src/index.ts b/src/index.ts index 265e5ee..5a54508 100644 --- a/src/index.ts +++ b/src/index.ts @@ -13,7 +13,6 @@ import adminRoutes from "./routes/admin"; import corsPlugin from "./plugins/cors"; import queryConfigPlugin from "./plugins/queryconfig"; import dbPlugin from "./plugins/db"; -import resourceRoutes from "./routes/resources"; import resourceRoutesSpecial from "./routes/resourcesSpecial"; import fastifyCookie from "@fastify/cookie"; import historyRoutes from "./routes/history"; @@ -31,9 +30,9 @@ import staffTimeRoutes from "./routes/staff/time"; import staffTimeConnectRoutes from "./routes/staff/timeconnects"; //Resources -import customerRoutes from "./routes/resources/customers"; -import vendorRoutes from "./routes/resources/vendors"; +import productsAndServicesRoutes from "./routes/resources/productsServices"; +import resourceRoutes from "./routes/resources/main"; //M2M import authM2m from "./plugins/auth.m2m"; @@ -104,7 +103,6 @@ async function main() { await subApp.register(meRoutes); await subApp.register(tenantRoutes); await subApp.register(adminRoutes); - await subApp.register(resourceRoutes); await subApp.register(resourceRoutesSpecial); await subApp.register(historyRoutes); await subApp.register(fileRoutes); @@ -120,8 +118,8 @@ async function main() { await subApp.register(staffTimeConnectRoutes); - await subApp.register(customerRoutes); - await subApp.register(vendorRoutes); + await subApp.register(productsAndServicesRoutes); + await subApp.register(resourceRoutes); },{prefix: "/api"}) diff --git a/src/routes/resources.ts b/src/routes/resources.ts deleted file mode 100644 index b1a3358..0000000 --- a/src/routes/resources.ts +++ /dev/null @@ -1,829 +0,0 @@ -import {FastifyInstance} from "fastify"; -import {insertHistoryItem} from "../utils/history" -import {diffObjects} from "../utils/diff"; -import {sortData} from "../utils/sort"; -import {useNextNumberRangeNumber} from "../utils/functions"; -import {compareValues, getNestedValue} from "../utils/helpers"; - - -const dataTypes: any[] = { - // @ts-ignore - tasks: { - isArchivable: true, - label: "Aufgaben", - labelSingle: "Aufgabe", - isStandardEntity: true, - redirect: true, - historyItemHolder: "task", - supabaseSelectWithInformation: "*, plant(*), project(*), customer(*)", - inputColumns: [ - "Allgemeines", - "Zuweisungen" - ], - showTabs: [{label: 'Informationen'}] - }, - customers: { - isArchivable: true, - label: "Kunden", - labelSingle: "Kunde", - isStandardEntity: true, - redirect: true, - numberRangeHolder: "customerNumber", - historyItemHolder: "customer", - supabaseSortColumn: "customerNumber", - supabaseSelectWithInformation: "*, projects(*), plants(*), contracts(*), contacts(*), createddocuments(*, statementallocations(*)), files(*), events(*)", - inputColumns: [ - "Allgemeines", - "Kontaktdaten" - ], - showTabs: [{label: 'Informationen'}, {label: 'Ansprechpartner'}, {label: 'Dateien'}, {label: 'Ausgangsbelege'}, {label: 'Projekte'}, {label: 'Objekte'}, {label: 'Termine'}, {label: 'Verträge'}] - }, - contacts: { - isArchivable: true, - label: "Kontakte", - labelSingle: "Kontakt", - isStandardEntity: true, - redirect: true, - historyItemHolder: "contact", - supabaseSelectWithInformation: "*, customer(*), vendor(*)", - showTabs: [ - { - label: 'Informationen', - } - ] - }, - contracts: { - isArchivable: true, - label: "Verträge", - labelSingle: "Vertrag", - isStandardEntity: true, - numberRangeHolder: "contractNumber", - redirect: true, - inputColumns: [ - "Allgemeines", - "Abrechnung" - ], - supabaseSelectWithInformation: "*, customer(*), files(*)", - showTabs: [{label: 'Informationen'}, {label: 'Dateien'}] - }, - absencerequests: { - isArchivable: true, - label: "Abwesenheiten", - labelSingle: "Abwesenheit", - isStandardEntity: true, - supabaseSortColumn: "startDate", - supabaseSortAscending: false, - supabaseSelectWithInformation: "*", - historyItemHolder: "absencerequest", - redirect: true, - showTabs: [{label: 'Informationen'}] - }, - plants: { - isArchivable: true, - label: "Objekte", - labelSingle: "Objekt", - isStandardEntity: true, - redirect: true, - historyItemHolder: "plant", - supabaseSelectWithInformation: "*, customer(id,name)", - showTabs: [ - { - label: "Informationen" - }, { - label: "Projekte" - }, { - label: "Aufgaben" - }, { - label: "Dateien" - }] - }, - products: { - isArchivable: true, - label: "Artikel", - labelSingle: "Artikel", - isStandardEntity: true, - redirect: true, - supabaseSelectWithInformation: "*, unit(name)", - historyItemHolder: "product", - showTabs: [ - { - label: "Informationen" - } - ] - }, - projects: { - isArchivable: true, - label: "Projekte", - labelSingle: "Projekt", - isStandardEntity: true, - redirect: true, - historyItemHolder: "project", - numberRangeHolder: "projectNumber", - supabaseSelectWithInformation: "*, customer(id,name), plant(id,name), projecttype(name, id), tasks(*, project(id,name), customer(id,name), plant(id,name)), files(*), createddocuments(*, statementallocations(*)), events(*), times(*, profile(id, fullName))", - supabaseSortColumn: "projectNumber", - showTabs: [ - { - key: "information", - label: "Informationen" - }, - { - key: "phases", - label: "Phasen" - }, { - key: "tasks", - label: "Aufgaben" - }, { - key: "files", - label: "Dateien" - }, { - label: "Zeiten" - }, { - label: "Ausgangsbelege" - }, { - label: "Termine" - }/*,{ - key: "timetracking", - label: "Zeiterfassung" - },{ - key: "events", - label: "Termine" - },{ - key: "material", - label: "Material" - }*/] - }, - vehicles: { - isArchivable: true, - label: "Fahrzeuge", - labelSingle: "Fahrzeug", - isStandardEntity: true, - redirect: true, - historyItemHolder: "vehicle", - supabaseSelectWithInformation: "*, checks(*), files(*)", - showTabs: [ - { - label: 'Informationen', - }, { - label: 'Dateien', - }, { - label: 'Überprüfungen', - } - ] - }, - vendors: { - isArchivable: true, - label: "Lieferanten", - labelSingle: "Lieferant", - isStandardEntity: true, - redirect: true, - numberRangeHolder: "vendorNumber", - historyItemHolder: "vendor", - supabaseSortColumn: "vendorNumber", - supabaseSelectWithInformation: "*, contacts(*)", - showTabs: [ - { - label: 'Informationen', - }, { - label: 'Ansprechpartner', - }, { - label: 'Dateien', - } - ] - }, - messages: { - label: "Nachrichten", - labelSingle: "Nachricht" - }, - spaces: { - isArchivable: true, - label: "Lagerplätze", - labelSingle: "Lagerplatz", - isStandardEntity: true, - supabaseSelectWithInformation: "*, files(*)", - supabaseSortColumn: "spaceNumber", - redirect: true, - numberRangeHolder: "spaceNumber", - historyItemHolder: "space", - inputColumns: [ - "Allgemeines", - "Ort" - ], - showTabs: [ - { - label: 'Informationen', - }, { - label: 'Dateien', - }, {label: 'Inventarartikel'} - ] - }, - users: { - label: "Benutzer", - labelSingle: "Benutzer" - }, - createddocuments: { - isArchivable: true, - label: "Dokumente", - labelSingle: "Dokument", - supabaseSelectWithInformation: "*, files(*), statementallocations(*)", - }, - files: { - isArchivable: true, - label: "Dateien", - labelSingle: "Datei", - supabaseSelectWithInformation: "*", - }, - folders: { - isArchivable: true, - label: "Ordner", - labelSingle: "Ordner", - supabaseSelectWithInformation: "*", - }, - incominginvoices: { - label: "Eingangsrechnungen", - labelSingle: "Eingangsrechnung", - redirect: true - }, - inventoryitems: { - isArchivable: true, - label: "Inventarartikel", - labelSingle: "Inventarartikel", - isStandardEntity: true, - supabaseSelectWithInformation: "*, files(*), vendor(id,name), currentSpace(id,name)", - redirect: true, - numberRangeHolder: "articleNumber", - historyItemHolder: "inventoryitem", - inputColumns: [ - "Allgemeines", - "Anschaffung" - ], - showTabs: [ - { - label: 'Informationen', - }, { - label: 'Dateien', - } - ] - }, - inventoryitemgroups: { - isArchivable: true, - label: "Inventarartikelgruppen", - labelSingle: "Inventarartikelgruppe", - isStandardEntity: true, - historyItemHolder: "inventoryitemgroup", - supabaseSelectWithInformation: "*", - redirect: true, - showTabs: [ - { - label: 'Informationen', - } - ] - }, - documentboxes: { - isArchivable: true, - label: "Dokumentenboxen", - labelSingle: "Dokumentenbox", - isStandardEntity: true, - supabaseSelectWithInformation: "*, space(*), files(*)", - redirect: true, - numberRangeHolder: "key", - historyItemHolder: "documentbox", - inputColumns: [ - "Allgemeines", - ], - showTabs: [ - { - label: 'Informationen', - }, { - label: 'Dateien', - } - ] - }, - services: { - isArchivable: true, - label: "Leistungen", - labelSingle: "Leistung", - isStandardEntity: true, - redirect: true, - supabaseSelectWithInformation: "*, unit(*)", - historyItemHolder: "service", - showTabs: [ - { - label: 'Informationen', - } - ] - }, - hourrates: { - isArchivable: true, - label: "Stundensätze", - labelSingle: "Stundensatz", - isStandardEntity: true, - redirect: true, - supabaseSelectWithInformation: "*", - historyItemHolder: "hourrate", - showTabs: [ - { - label: 'Informationen', - } - ] - }, - events: { - isArchivable: true, - label: "Termine", - labelSingle: "Termin", - isStandardEntity: true, - historyItemHolder: "event", - supabaseSelectWithInformation: "*, project(id,name), customer(*)", - redirect: true, - showTabs: [ - { - label: 'Informationen', - } - ] - }, - profiles: { - label: "Mitarbeiter", - labelSingle: "Mitarbeiter", - redirect: true, - historyItemHolder: "profile" - }, - workingtimes: { - isArchivable: true, - label: "Anwesenheiten", - labelSingle: "Anwesenheit", - redirect: true, - redirectToList: true - }, - texttemplates: { - isArchivable: true, - label: "Textvorlagen", - labelSingle: "Textvorlage" - }, - bankstatements: { - isArchivable: true, - label: "Kontobewegungen", - labelSingle: "Kontobewegung", - historyItemHolder: "bankStatement", - }, - statementallocations: { - label: "Bankzuweisungen", - labelSingle: "Bankzuweisung" - }, - productcategories: { - isArchivable: true, - label: "Artikelkategorien", - labelSingle: "Artikelkategorie", - isStandardEntity: true, - redirect: true, - supabaseSelectWithInformation: "*", - showTabs: [ - { - label: 'Informationen', - } - ] - }, - servicecategories: { - isArchivable: true, - label: "Leistungskategorien", - labelSingle: "Leistungskategorie", - isStandardEntity: true, - redirect: true, - supabaseSelectWithInformation: "*", - showTabs: [ - { - label: 'Informationen', - } - ] - }, - trackingtrips: { - label: "Fahrten", - labelSingle: "Fahrt", - redirect: true, - historyItemHolder: "trackingtrip", - }, - projecttypes: { - isArchivable: true, - label: "Projekttypen", - labelSingle: "Projekttyp", - redirect: true, - historyItemHolder: "projecttype" - }, - checks: { - isArchivable: true, - label: "Überprüfungen", - labelSingle: "Überprüfung", - isStandardEntity: true, - supabaseSelectWithInformation: "*, vehicle(id,licensePlate), profile(id, fullName), inventoryitem(name), files(*)", - redirect: true, - historyItemHolder: "check", - showTabs: [ - { - label: 'Informationen', - }, {label: 'Dateien'}, {label: 'Ausführungen'}] - }, - roles: { - label: "Rollen", - labelSingle: "Rolle", - redirect: true, - historyItemHolder: "role", - filters: [], - templateColumns: [ - { - key: "name", - label: "Name" - }, { - key: "description", - label: "Beschreibung" - } - ] - }, - costcentres: { - isArchivable: true, - label: "Kostenstellen", - labelSingle: "Kostenstelle", - isStandardEntity: true, - redirect: true, - numberRangeHolder: "number", - historyItemHolder: "costcentre", - supabaseSortColumn: "number", - supabaseSelectWithInformation: "*, project(*), vehicle(*), inventoryitem(*)", - showTabs: [{label: 'Informationen'}, {label: 'Auswertung Kostenstelle'}] - }, - ownaccounts: { - isArchivable: true, - label: "zusätzliche Buchungskonten", - labelSingle: "zusätzliches Buchungskonto", - isStandardEntity: true, - redirect: true, - historyItemHolder: "ownaccount", - supabaseSortColumn: "number", - supabaseSelectWithInformation: "*, statementallocations(*, bs_id(*))", - showTabs: [{label: 'Informationen'}, {label: 'Buchungen'}] - }, - tickets: { - isArchivable: true, - label: "Tickets", - labelSingle: "Ticket", - - }, - ticketmessages: { - isArchivable: true, - label: "Nachrichten", - labelSingle: "Nachricht", - - }, -} - -export default async function resourceRoutes(server: FastifyInstance) { - - //Liste - server.get("/resource/:resource", async (req, reply) => { - if (!req.user?.tenant_id) { - return reply.code(400).send({ error: "No tenant selected" }); - } - - const { resource } = req.params as { resource: string }; - - const {select, sort, asc } = req.query as { select?: string, sort?: string, asc?: string } - console.log(select, sort, asc) - - - const { data, error } = await server.supabase - .from(resource) - //@ts-ignore - .select(select || dataTypes[resource].supabaseSelectWithInformation) - .eq("tenant", req.user.tenant_id) - if (error) { - console.log(error) - return reply.code(400).send({ error: error.message }); - } - - const sorted =sortData(data,sort,asc === "true" ? true : false) - - return sorted; - }); - - - // Helper Funktionen - - -// Liste Paginated - server.get("/resource/:resource/paginated", async (req, reply) => { - if (!req.user?.tenant_id) { - return reply.code(400).send({ error: "No tenant selected" }); - } - - const { resource } = req.params as { resource: string }; - const { queryConfig } = req; - const { pagination, sort, filters, paginationDisabled } = queryConfig; - const { select, search, searchColumns, distinctColumns } = req.query as { - select?: string; - search?: string; - searchColumns?: string; - distinctColumns?: string; - }; - - console.log(req.query); - console.log(select); - - // --- 🔍 Suche (im Backend mit Joins) --- - if (search && search.trim().length > 0) { - // 1. Alle Daten mit Joins holen (OHNE Pagination, aber mit Filtern) - let searchQuery = server.supabase - .from(resource) - .select(select || dataTypes[resource].supabaseSelectWithInformation) - .eq("tenant", req.user.tenant_id); - - // --- Filterung anwenden --- - for (const [key, val] of Object.entries(filters || {})) { - if (Array.isArray(val)) { - searchQuery = searchQuery.in(key, val); - } else { // @ts-ignore - if (val === true || val === false || val === null) { - searchQuery = searchQuery.is(key, val); - } else { - searchQuery = searchQuery.eq(key, val); - } - } - } - - const { data: allData, error: searchError } = await searchQuery; - - if (searchError) { - server.log.error(searchError); - return reply.code(400).send({ error: searchError.message }); - } - - // 2. Im Backend nach Suchbegriff filtern - const searchTerm = search.trim().toLowerCase(); - const searchCols = searchColumns - ? searchColumns.split(",").map(c => c.trim()).filter(Boolean) - : dataTypes[resource].searchableColumns || []; - - const filteredData = (allData || []).filter(row => { - /*if (searchCols.length === 0) { - // Fallback: Durchsuche alle String-Felder der Hauptebene - return Object.values(row).some(val => - JSON.stringify(val).toString().toLowerCase().includes(searchTerm) - ); - } - - return searchCols.some(col => { - const value = getNestedValue(row, col); - return JSON.stringify(value).toLowerCase().includes(searchTerm); - });*/ - - return JSON.stringify(row).toLowerCase().includes(searchTerm); - - }); - - // 3. Im Backend sortieren - let sortedData = [...filteredData]; - if (sort?.length > 0) { - sortedData.sort((a, b) => { - for (const s of sort) { - const aVal = getNestedValue(a, s.field); - const bVal = getNestedValue(b, s.field); - const comparison = compareValues(aVal, bVal); - if (comparison !== 0) { - return s.direction === "asc" ? comparison : -comparison; - } - } - return 0; - }); - } - - // 4. Im Backend paginieren - const total = sortedData.length; - const paginatedData = !paginationDisabled && pagination - ? sortedData.slice(pagination.offset, pagination.offset + pagination.limit) - : sortedData; - - // 5. Distinct Values berechnen - const distinctValues: Record = {}; - if (distinctColumns) { - const cols = distinctColumns.split(",").map(c => c.trim()).filter(Boolean); - - for (const col of cols) { - // Distinct values aus den gefilterten Daten - const values = filteredData - .map(row => getNestedValue(row, col)) - .filter(v => v !== null && v !== undefined && v !== ""); - distinctValues[col] = [...new Set(values)].sort(); - } - } - - const totalPages = !paginationDisabled && pagination?.limit - ? Math.ceil(total / pagination.limit) - : 1; - - const enrichedConfig = { - ...queryConfig, - total, - totalPages, - distinctValues, - search: search || null, - }; - - return { data: paginatedData, queryConfig: enrichedConfig }; - } - - // --- Standardabfrage (ohne Suche) --- - let baseQuery = server.supabase - .from(resource) - .select(select || dataTypes[resource].supabaseSelectWithInformation, { count: "exact" }) - .eq("tenant", req.user.tenant_id); - - // --- Filterung --- - for (const [key, val] of Object.entries(filters || {})) { - if (Array.isArray(val)) { - baseQuery = baseQuery.in(key, val); - } else { // @ts-ignore - if (val == true || val == false || val === null) { - baseQuery = baseQuery.is(key, val); - } else { - baseQuery = baseQuery.eq(key, val); - } - } - } - - // --- Sortierung --- - if (sort?.length > 0) { - for (const s of sort) { - baseQuery = baseQuery.order(s.field, { ascending: s.direction === "asc" }); - } - } - - // --- Pagination --- - if (!paginationDisabled && pagination) { - const { offset, limit } = pagination; - baseQuery = baseQuery.range(offset, offset + limit - 1); - } - - const { data, error, count } = await baseQuery; - if (error) { - server.log.error(error); - return reply.code(400).send({ error: error.message }); - } - - // --- Distinct-Werte (auch ohne Suche) --- - const distinctValues: Record = {}; - if (distinctColumns) { - const cols = distinctColumns.split(",").map(c => c.trim()).filter(Boolean); - - for (const col of cols) { - const { data: allRows, error: distinctErr } = await server.supabase - .from(resource) - .select(col) - .eq("tenant", req.user.tenant_id); - - if (distinctErr) continue; - - const values = (allRows || []) - .map((row) => row?.[col] ?? null) - .filter((v) => v !== null && v !== undefined && v !== ""); - distinctValues[col] = [...new Set(values)].sort(); - } - } - - const total = count || 0; - const totalPages = !paginationDisabled && pagination?.limit - ? Math.ceil(total / pagination.limit) - : 1; - - const enrichedConfig = { - ...queryConfig, - total, - totalPages, - distinctValues, - search: search || null, - }; - - return { data, queryConfig: enrichedConfig }; - }); - - - // Detail - server.get("/resource/:resource/:id/:with_information?", async (req, reply) => { - if (!req.user?.tenant_id) { - return reply.code(400).send({error: "No tenant selected"}); - } - - const {resource, id, with_information} = req.params as { - resource: string; - id: string, - with_information: boolean - }; - const {select} = req.query as { select?: string } - - - // @ts-ignore - const { - data, - error - } = await server.supabase.from(resource).select(with_information ? dataTypes[resource].supabaseSelectWithInformation : (select ? select : "*")) - .eq("id", id) - .eq("tenant", req.user.tenant_id) - .single(); - - if (error || !data) { - return reply.code(404).send({error: "Not found"}); - } - - return data; - }); - - // Create - server.post("/resource/:resource", async (req, reply) => { - if (!req.user?.tenant_id) { - return reply.code(400).send({error: "No tenant selected"}); - } - - const {resource} = req.params as { resource: string }; - const body = req.body as Record; - - const dataType = dataTypes[resource]; - let createData = { - ...body, - tenant: req.user.tenant_id, - archived: false, // Standardwert - } - - if (dataType.numberRangeHolder && !body[dataType.numberRangeHolder]) { - const result = await useNextNumberRangeNumber(server, req.user.tenant_id, resource) - createData[dataType.numberRangeHolder] = result.usedNumber - } - - const {data, error} = await server.supabase - .from(resource) - .insert(createData) - .select("*") - .single(); - - if (error) { - return reply.code(400).send({error: error.message}); - } - - await insertHistoryItem(server, { - entity: resource, - entityId: data.id, - action: "created", - created_by: req.user.user_id, - tenant_id: req.user.tenant_id, - oldVal: null, - newVal: data, - text: `${dataType.labelSingle} erstellt`, - }); - - return data; - }); - - // UPDATE (inkl. Soft-Delete/Archive) - server.put("/resource/:resource/:id", async (req, reply) => { - console.log("hi") - const {resource, id} = req.params as { resource: string; id: string } - const body = req.body as Record - - const tenantId = (req.user as any)?.tenant_id - const userId = (req.user as any)?.user_id - - if (!tenantId || !userId) { - return reply.code(401).send({error: "Unauthorized"}) - } - - // vorherige Version für History laden - const {data: oldItem} = await server.supabase - .from(resource) - .select("*") - .eq("id", id) - .eq("tenant", tenantId) - .single() - - const {data: newItem, error} = await server.supabase - .from(resource) - .update({...body, updated_at: new Date().toISOString(), updated_by: userId}) - .eq("id", id) - .eq("tenant", tenantId) - .select() - .single() - - if (error) return reply.code(500).send({error}) - - const diffs = diffObjects(oldItem, newItem); - - - for (const d of diffs) { - await insertHistoryItem(server, { - entity: resource, - entityId: id, - action: d.type, - created_by: userId, - tenant_id: tenantId, - oldVal: d.oldValue ? String(d.oldValue) : null, - newVal: d.newValue ? String(d.newValue) : null, - text: `Feld "${d.label}" ${d.typeLabel}: ${d.oldValue ?? ""} → ${d.newValue ?? ""}`, - }); - } - - return newItem - }) -} \ No newline at end of file diff --git a/src/routes/resources/_template.ts b/src/routes/resources/_template.ts deleted file mode 100644 index 03386e5..0000000 --- a/src/routes/resources/_template.ts +++ /dev/null @@ -1,155 +0,0 @@ -import { FastifyInstance } from "fastify" -import { asc, desc, eq, ilike, and } from "drizzle-orm" - -// Beispiel-Import — wird in der echten Datei ersetzt -import { exampleTable } from "../../../db/schema/exampleTable" - -export default async function exampleRoutes(server: FastifyInstance) { - - // ------------------------------------------- - // LIST (ohne Pagination) - // ------------------------------------------- - server.get("/resource/example", async (req, reply) => { - const tenantId = req.user?.tenant_id - if (!tenantId) return reply.code(400).send({ error: "No tenant selected" }) - - const { search, sort, asc: ascQuery } = req.query as { - search?: string - sort?: string - asc?: string - } - - let query = server.db - .select() - .from(exampleTable) - .where(eq(exampleTable.tenant, tenantId)) - - // 🔍 OPTIONAL: einfache Suche (LIKE) - if (search) { - query = server.db - .select() - .from(exampleTable) - .where( - and( - eq(exampleTable.tenant, tenantId), - ilike(exampleTable.name as any, `%${search}%`) - ) - ) - } - - // 🔄 Sortierung - if (sort) { - query = query.orderBy( - ascQuery === "true" - ? asc((exampleTable as any)[sort]) - : desc((exampleTable as any)[sort]) - ) - } - - const results = await query - return results - }) - - // ------------------------------------------- - // PAGINATED LIST - // ------------------------------------------- - server.get("/resource/example/paginated", async (req, reply) => { - const tenantId = req.user?.tenant_id - if (!tenantId) return reply.code(400).send({ error: "No tenant selected" }) - - const { - offset = "0", - limit = "25", - search, - sort, - asc: ascQuery, - } = req.query as { - offset?: string - limit?: string - search?: string - sort?: string - asc?: string - } - - const offsetNum = parseInt(offset) - const limitNum = parseInt(limit) - - // --- Basis WHERE - let whereClause: any = eq(exampleTable.tenant, tenantId) - - // --- Suche - if (search) { - whereClause = and( - eq(exampleTable.tenant, tenantId), - ilike(exampleTable.name as any, `%${search}%`) - ) - } - - // ------------------------- - // 1. COUNT Query (total rows) - // ------------------------- - const totalRowsResult = await server.db - .select({ count: server.db.fn.count(exampleTable.id) }) - .from(exampleTable) - .where(whereClause) - - const total = Number(totalRowsResult[0].count) - - // ------------------------- - // 2. DATA Query - // ------------------------- - let dataQuery = server.db - .select() - .from(exampleTable) - .where(whereClause) - .offset(offsetNum) - .limit(limitNum) - - if (sort) { - dataQuery = dataQuery.orderBy( - ascQuery === "true" - ? asc((exampleTable as any)[sort]) - : desc((exampleTable as any)[sort]) - ) - } - - const rows = await dataQuery - - return { - data: rows, - pagination: { - total, - offset: offsetNum, - limit: limitNum, - totalPages: Math.ceil(total / limitNum), - }, - } - }) - - // ------------------------------------------- - // DETAIL ROUTE - // ------------------------------------------- - server.get("/resource/example/:id", async (req, reply) => { - const { id } = req.params as { id: string } - const tenantId = req.user?.tenant_id - - if (!tenantId) return reply.code(400).send({ error: "No tenant selected" }) - - const result = await server.db - .select() - .from(exampleTable) - .where( - and( - eq(exampleTable.id, id), - eq(exampleTable.tenant, tenantId) - ) - ) - .limit(1) - - if (!result.length) { - return reply.code(404).send({ error: "Not found" }) - } - - return result[0] - }) -} diff --git a/src/routes/resources/contacts.ts b/src/routes/resources/contacts.ts deleted file mode 100644 index ae5a675..0000000 --- a/src/routes/resources/contacts.ts +++ /dev/null @@ -1,276 +0,0 @@ -import { FastifyInstance } from "fastify" -import { - eq, - ilike, - asc, - desc, - and, - count, - inArray, - or -} from "drizzle-orm" - -import { - contacts, - customers, - vendors -} from "../../../db/schema" - - -// ------------------------------------------------------------- -// 🔍 Helper für SQL-Suche über mehrere Spalten -// ------------------------------------------------------------- -function buildSearchCondition(table: any, columns: string[], search: string) { - if (!search || !columns.length) return null - - const term = `%${search.toLowerCase()}%` - - const conditions = columns - .map(c => table[c]) - .filter(Boolean) - .map(col => ilike(col, term)) - - if (conditions.length === 0) return null - - // @ts-ignore - return or(...conditions) -} - - - -export default async function contactsRoutes(server: FastifyInstance) { - - - // ------------------------------------------------------------- - // LIST - // ------------------------------------------------------------- - server.get("/resource/contacts", async (req, reply) => { - const tenantId = req.user?.tenant_id - if (!tenantId) - return reply.code(400).send({ error: "No tenant selected" }) - - const { search, sort, asc: ascQuery } = req.query as { - search?: string - sort?: string - asc?: string - } - - // Grundfilter - let whereCond: any = eq(contacts.tenant, tenantId) - - // 🔍 Suche - if (search) { - const searchCond = buildSearchCondition( - contacts, - ["firstName", "lastName", "email", "phone", "notes"], - search - ) - if (searchCond) whereCond = and(whereCond, searchCond) - } - - // Query - let q = server.db.select().from(contacts).where(whereCond) - - // Sortierung - if (sort) { - const field = (contacts as any)[sort] - if (field) { - //@ts-ignore - q = q.orderBy( - ascQuery === "true" ? asc(field) : desc(field) - ) - } - } - - return await q - }) - - - - // ------------------------------------------------------------- - // PAGINATED - // ------------------------------------------------------------- - server.get("/resource/contacts/paginated", async (req, reply) => { - try { - const tenantId = req.user?.tenant_id - if (!tenantId) - return reply.code(400).send({ error: "No tenant selected" }) - - const queryConfig = req.queryConfig - const { - pagination, - sort, - filters, - paginationDisabled - } = queryConfig - - const { search, distinctColumns } = req.query as { - search?: string - distinctColumns?: string - } - - // ----------------------------------- - // WHERE CONDITIONS - // ----------------------------------- - let whereCond: any = eq(contacts.tenant, tenantId) - - // Filter - if (filters) { - for (const [key, val] of Object.entries(filters)) { - const col = (contacts as any)[key] - if (!col) continue - - if (Array.isArray(val)) { - whereCond = and(whereCond, inArray(col, val)) - } else { - whereCond = and(whereCond, eq(col, val as any)) - } - } - } - - // 🔍 Suche - if (search && search.trim().length > 0) { - const searchCond = buildSearchCondition( - contacts, - ["firstName", "lastName", "email", "phone", "notes"], - search - ) - if (searchCond) whereCond = and(whereCond, searchCond) - } - - // ----------------------------------- - // COUNT - // ----------------------------------- - const totalRes = await server.db - .select({ value: count(contacts.id) }) - .from(contacts) - .where(whereCond) - - const total = Number(totalRes[0]?.value ?? 0) - - // ----------------------------------- - // DISTINCT VALUES - // ----------------------------------- - const distinctValues: Record = {} - - if (distinctColumns) { - for (const colName of distinctColumns.split(",").map(v => v.trim())) { - const col = (contacts as any)[colName] - if (!col) continue - - const rows = await server.db - .select({ v: col }) - .from(contacts) - .where(eq(contacts.tenant, tenantId)) - - distinctValues[colName] = - [...new Set(rows.map(r => r.v).filter(v => v !== null && v !== ""))] - .sort() - } - } - - // ----------------------------------- - // PAGINATION - // ----------------------------------- - let offset = pagination?.offset ?? 0 - let limit = pagination?.limit ?? 999999 - - // ----------------------------------- - // ORDER - // ----------------------------------- - let orderField = null - let orderDirection: "asc" | "desc" = "asc" - - if (sort?.length > 0) { - const s = sort[0] - const col = (contacts as any)[s.field] - if (col) { - orderField = col - orderDirection = s.direction === "asc" ? "asc" : "desc" - } - } - - // ----------------------------------- - // DATA QUERY - // ----------------------------------- - let dataQuery = server.db - .select() - .from(contacts) - .where(whereCond) - .offset(offset) - .limit(limit) - - if (orderField) { - //@ts-ignore - dataQuery = - orderDirection === "asc" - ? dataQuery.orderBy(asc(orderField)) - : dataQuery.orderBy(desc(orderField)) - } - - const data = await dataQuery - - return { - data, - queryConfig: { - ...queryConfig, - total, - totalPages: pagination?.limit - ? Math.ceil(total / pagination.limit) - : 1, - distinctValues, - search: search || null - } - } - - } catch (err) { - console.error(err) - return reply.code(500).send({ error: "Internal Server Error" }) - } - }) - - - - // ------------------------------------------------------------- - // DETAIL (customer + vendor) - // ------------------------------------------------------------- - server.get("/resource/contacts/:id", async (req, reply) => { - const { id } = req.params as { id: string } - const tenantId = req.user?.tenant_id - - if (!tenantId) - return reply.code(400).send({ error: "No tenant selected" }) - - const rows = await server.db - .select() - .from(contacts) - .where( - and( - eq(contacts.id, Number(id)), - eq(contacts.tenant, tenantId) - ) - ) - .limit(1) - - if (!rows.length) - return reply.code(404).send({ error: "Not found" }) - - const contact = rows[0] - - const [customerRecord, vendorRecord] = await Promise.all([ - contact.customer - ? server.db.select().from(customers).where(eq(customers.id, contact.customer)) - : [], - contact.vendor - ? server.db.select().from(vendors).where(eq(vendors.id, contact.vendor)) - : [], - ]) - - return { - ...contact, - customer: customerRecord[0] ?? null, - vendor: vendorRecord[0] ?? null, - } - }) - -} diff --git a/src/routes/resources/contracts.ts b/src/routes/resources/contracts.ts deleted file mode 100644 index c0e8d6c..0000000 --- a/src/routes/resources/contracts.ts +++ /dev/null @@ -1,244 +0,0 @@ -import { FastifyInstance } from "fastify" -import { - eq, - ilike, - asc, - desc, - and, - count, - inArray, - or -} from "drizzle-orm" - -import { - contracts, - customers, - files -} from "../../../db/schema" - -// ------------------------------------------------------------- -// Helper: SQL‐LIKE Suche über mehrere Felder -// ------------------------------------------------------------- -function buildSearchCondition(table: any, columns: string[], search?: string) { - if (!search) return null - - const term = `%${search.toLowerCase()}%` - - const conditions = columns - .map(col => table[col]) - .filter(Boolean) - .map(col => ilike(col, term)) - - if (conditions.length === 0) return null - - // @ts-ignore - return or(...conditions) -} - -export default async function contractsRoutes(server: FastifyInstance) { - - // ------------------------------------------------------------- - // LIST - // ------------------------------------------------------------- - server.get("/resource/contracts", async (req, reply) => { - const tenantId = req.user?.tenant_id - if (!tenantId) return reply.code(400).send({ error: "No tenant selected" }) - - const { search, sort, asc: ascQuery } = req.query as { - search?: string - sort?: string - asc?: string - } - - let whereCond: any = eq(contracts.tenant, tenantId) - - // SQL SEARCH - const searchCond = buildSearchCondition( - contracts, - ["name", "notes", "contractNumber", "paymentType", "sepaRef", "bankingName"], - search - ) - if (searchCond) whereCond = and(whereCond, searchCond) - - // Query - let q = server.db - .select() - .from(contracts) - .where(whereCond) - - // SORT - if (sort) { - const field = (contracts as any)[sort] - if (field) { - //@ts-ignore - q = q.orderBy( - ascQuery === "true" ? asc(field) : desc(field) - ) - } - } - - return await q - }) - - - // ------------------------------------------------------------- - // PAGINATED - // ------------------------------------------------------------- - server.get("/resource/contracts/paginated", async (req, reply) => { - try { - const tenantId = req.user?.tenant_id - if (!tenantId) return reply.code(400).send({ error: "No tenant selected" }) - - const queryConfig = req.queryConfig - const { pagination, sort, filters, paginationDisabled } = queryConfig - - const { search, distinctColumns } = req.query as { - search?: string - distinctColumns?: string - } - - // ----------------------------------- - // WHERE - // ----------------------------------- - let whereCond: any = eq(contracts.tenant, tenantId) - - if (filters) { - for (const [key, val] of Object.entries(filters)) { - const col = (contracts as any)[key] - if (!col) continue - - if (Array.isArray(val)) { - whereCond = and(whereCond, inArray(col, val)) - } else { - whereCond = and(whereCond, eq(col, val as any)) - } - } - } - - // SQL SEARCH - const searchCond = buildSearchCondition( - contracts, - ["name", "notes", "contractNumber", "paymentType", "sepaRef", "bankingName"], - search - ) - if (searchCond) whereCond = and(whereCond, searchCond) - - // ----------------------------------- - // COUNT - // ----------------------------------- - const totalRes = await server.db - .select({ value: count(contracts.id) }) - .from(contracts) - .where(whereCond) - - const total = Number(totalRes[0]?.value ?? 0) - - // ----------------------------------- - // DISTINCT - // ----------------------------------- - const distinctValues: Record = {} - - if (distinctColumns) { - for (const colName of distinctColumns.split(",")) { - const col = (contracts as any)[colName.trim()] - if (!col) continue - - const rows = await server.db - .select({ v: col }) - .from(contracts) - .where(eq(contracts.tenant, tenantId)) - - distinctValues[colName] = - [...new Set(rows.map(r => r.v).filter(v => v != null && v !== ""))] - .sort() - } - } - - // ----------------------------------- - // PAGINATION - // ----------------------------------- - let offset = pagination?.offset ?? 0 - let limit = pagination?.limit ?? 999999 - - // ----------------------------------- - // SORT - // ----------------------------------- - let orderField = null - let orderDir: "asc" | "desc" = "asc" - - if (sort?.length > 0) { - const s = sort[0] - const col = (contracts as any)[s.field] - if (col) { - orderField = col - orderDir = s.direction === "asc" ? "asc" : "desc" - } - } - - // ----------------------------------- - // QUERY DATA - // ----------------------------------- - let q = server.db - .select() - .from(contracts) - .where(whereCond) - .offset(offset) - .limit(limit) - - if (orderField) { - //@ts-ignore - q = orderDir === "asc" ? q.orderBy(asc(orderField)) : q.orderBy(desc(orderField)) - } - - const data = await q - - return { - data, - queryConfig: { - ...queryConfig, - total, - totalPages: pagination?.limit ? Math.ceil(total / pagination.limit) : 1, - distinctValues, - search: search || null, - } - } - } catch (e) { - console.error(e) - return reply.code(500).send({ error: "Internal Server Error" }) - } - }) - - - // ------------------------------------------------------------- - // DETAIL (+ JOINS) - // ------------------------------------------------------------- - server.get("/resource/contracts/:id", async (req, reply) => { - const { id } = req.params as { id: string } - const tenantId = req.user?.tenant_id - - if (!tenantId) return reply.code(400).send({ error: "No tenant selected" }) - - const rows = await server.db - .select() - .from(contracts) - .where(and(eq(contracts.id, Number(id)), eq(contracts.tenant, tenantId))) - .limit(1) - - if (!rows.length) return reply.code(404).send({ error: "Not found" }) - - const contract = rows[0] - - const [customerRecord, fileList] = await Promise.all([ - contract.customer - ? server.db.select().from(customers).where(eq(customers.id, contract.customer)) - : [], - server.db.select().from(files).where(eq(files.contract, Number(id))), - ]) - - return { - ...contract, - customer: customerRecord[0] ?? null, - files: fileList, - } - }) -} diff --git a/src/routes/resources/customers.ts b/src/routes/resources/customers.ts deleted file mode 100644 index 773b94f..0000000 --- a/src/routes/resources/customers.ts +++ /dev/null @@ -1,329 +0,0 @@ -import { FastifyInstance } from "fastify" -import { - eq, - ilike, - asc, - desc, - and, - count, - inArray, - or, -} from "drizzle-orm" - -import { - customers, - projects, - plants, - contracts, - contacts, - createddocuments, - statementallocations, - files, - events, -} from "../../../db/schema" - - -// ------------------------------------------------------------- -// 🔍 Helper für SQL-Suche über mehrere Spalten -// ------------------------------------------------------------- -function buildSearchCondition(table: any, columns: string[], search: string) { - if (!search || !columns.length) return null - - const term = `%${search.toLowerCase()}%` - - const conditions = columns - .map((colName) => table[colName]) - .filter(Boolean) - .map((col) => ilike(col, term)) - - if (conditions.length === 0) return null - - // @ts-ignore - return or(...conditions) -} - - - -export default async function customerRoutes(server: FastifyInstance) { - - - // ------------------------------------------------------------- - // LIST - // ------------------------------------------------------------- - server.get("/resource/customers", async (req, reply) => { - const tenantId = req.user?.tenant_id - if (!tenantId) return reply.code(400).send({ error: "No tenant selected" }) - - const { search, sort, asc: ascQuery } = req.query as { - search?: string - sort?: string - asc?: string - } - - // Basisquery - let whereCond: any = eq(customers.tenant, tenantId) - - // 🔍 SQL-Suche - if (search) { - const searchCond = buildSearchCondition( - customers, - ["name", "customerNumber", "firstname", "lastname", "notes"], - search - ) - - if (searchCond) { - whereCond = and(whereCond, searchCond) - } - } - - let baseQuery = server.db - .select() - .from(customers) - .where(whereCond) - - // Sortierung - if (sort) { - const field = (customers as any)[sort] - if (field) { - //@ts-ignore - baseQuery = baseQuery.orderBy( - ascQuery === "true" ? asc(field) : desc(field) - ) - } - } - - return await baseQuery - }) - - - - // ------------------------------------------------------------- - // PAGINATED - // ------------------------------------------------------------- - server.get("/resource/customers/paginated", async (req, reply) => { - try { - const tenantId = req.user?.tenant_id - if (!tenantId) { - return reply.code(400).send({ error: "No tenant selected" }) - } - - const queryConfig = req.queryConfig - const { - pagination, - sort, - filters, - paginationDisabled - } = queryConfig - - const { - search, - distinctColumns - } = req.query as { - search?: string - distinctColumns?: string - } - - // ---------------------------- - // WHERE CONDITIONS (Basis) - // ---------------------------- - let whereCond: any = eq(customers.tenant, tenantId) - - // Filters - if (filters) { - for (const [key, val] of Object.entries(filters)) { - const col = (customers as any)[key] - if (!col) continue - - if (Array.isArray(val)) { - whereCond = and(whereCond, inArray(col, val)) - } else { - whereCond = and(whereCond, eq(col, val as any)) - } - } - } - - // ---------------------------- - // 🔍 SEARCH - // ---------------------------- - if (search && search.trim().length > 0) { - const searchCond = buildSearchCondition( - customers, - ["name", "customerNumber", "firstname", "lastname", "notes"], - search.trim() - ) - - if (searchCond) { - whereCond = and(whereCond, searchCond) - } - } - - // ---------------------------- - // COUNT - // ---------------------------- - const totalRes = await server.db - .select({ value: count(customers.id) }) - .from(customers) - .where(whereCond) - - const total = Number(totalRes[0]?.value ?? 0) - - // ---------------------------- - // DISTINCT VALUES - // ---------------------------- - const distinctValues: Record = {} - - if (distinctColumns) { - for (const colName of distinctColumns.split(",").map(v => v.trim())) { - const col = (customers as any)[colName] - if (!col) continue - - const rows = await server.db - .select({ v: col }) - .from(customers) - .where(eq(customers.tenant, tenantId)) - - distinctValues[colName] = - [...new Set(rows.map(r => r.v).filter(v => v != null && v !== ""))] - .sort() - } - } - - // ---------------------------- - // PAGINATION - // ---------------------------- - let offset = 0 - let limit = 999999 - - if (!paginationDisabled && pagination) { - offset = pagination.offset - limit = pagination.limit - } - - // ---------------------------- - // ORDER BY - // ---------------------------- - let orderField = null - let orderDirection: "asc" | "desc" = "asc" - - if (sort?.length > 0) { - const s = sort[0] - const col = (customers as any)[s.field] - if (col) { - orderField = col - orderDirection = s.direction === "asc" ? "asc" : "desc" - } - } - - // ---------------------------- - // QUERY DATA - // ---------------------------- - let dataQuery = server.db - .select() - .from(customers) - .where(whereCond) - .offset(offset) - .limit(limit) - - if (orderField) { - //@ts-ignore - dataQuery = - orderDirection === "asc" - ? dataQuery.orderBy(asc(orderField)) - : dataQuery.orderBy(desc(orderField)) - } - - const data = await dataQuery - - // ---------------------------- - // CONFIG RESPONSE - // ---------------------------- - const totalPages = pagination?.limit - ? Math.ceil(total / pagination.limit) - : 1 - - const enrichedConfig = { - ...queryConfig, - total, - totalPages, - distinctValues, - search: search || null, - } - - return { - data, - queryConfig: enrichedConfig, - } - } - catch (e) { - console.log(e) - } - }) - - - - // ------------------------------------------------------------- - // DETAIL (mit ALLEN JOINS) - // ------------------------------------------------------------- - server.get("/resource/customers/:id", async (req, reply) => { - const { id } = req.params as { id: string } - const tenantId = req.user?.tenant_id - - if (!tenantId) return reply.code(400).send({ error: "No tenant selected" }) - - // --- 1) Customer selbst laden - const customerRecord = await server.db - .select() - .from(customers) - .where(and(eq(customers.id, Number(id)), eq(customers.tenant, tenantId))) - .limit(1) - - if (!customerRecord.length) { - return reply.code(404).send({ error: "Customer not found" }) - } - - const customer = customerRecord[0] - - - // --- 2) Relations: - const [ - customerProjects, - customerPlants, - customerContracts, - customerContacts, - customerDocuments, - customerFiles, - customerEvents, - ] = await Promise.all([ - server.db.select().from(projects).where(eq(projects.customer, Number(id))), - server.db.select().from(plants).where(eq(plants.customer, Number(id))), - server.db.select().from(contracts).where(eq(contracts.customer, Number(id))), - server.db.select().from(contacts).where(eq(contacts.customer, Number(id))), - - server.db - .select({ - ...createddocuments, - allocations: statementallocations, - }) - .from(createddocuments) - .leftJoin( - statementallocations, - eq(statementallocations.cd_id, createddocuments.id) - ) - .where(eq(createddocuments.customer, Number(id))), - - server.db.select().from(files).where(eq(files.customer, Number(id))), - server.db.select().from(events).where(eq(events.customer, Number(id))), - ]) - - return { - ...customer, - projects: customerProjects, - plants: customerPlants, - contracts: customerContracts, - contacts: customerContacts, - createddocuments: customerDocuments, - files: customerFiles, - events: customerEvents, - } - }) -} diff --git a/src/routes/resources/main.ts b/src/routes/resources/main.ts new file mode 100644 index 0000000..0121e42 --- /dev/null +++ b/src/routes/resources/main.ts @@ -0,0 +1,413 @@ +import { FastifyInstance } from "fastify" +import { + eq, + ilike, + asc, + desc, + and, + count, + inArray, + or +} from "drizzle-orm" + + +import { + projects, + customers, + plants, + contracts, + projecttypes, + createddocuments, + files, + events, + tasks, contacts, vendors +} from "../../../db/schema" +import * as sea from "node:sea"; + +// ------------------------------------------------------------- +// SQL Volltextsuche auf mehreren Feldern +// ------------------------------------------------------------- + + +function buildSearchCondition(table: any, columns: string[], search: string) { + if (!search || !columns.length) return null + + const term = `%${search.toLowerCase()}%` + + const conditions = columns + .map((colName) => table[colName]) + .filter(Boolean) + .map((col) => ilike(col, term)) + + if (conditions.length === 0) return null + + // @ts-ignore + return or(...conditions) +} + +export default async function resourceRoutes(server: FastifyInstance) { + + // ------------------------------------------------------------- + // LIST + // ------------------------------------------------------------- + /*server.get("/resource/:resource", async (req, reply) => { + try { + const tenantId = req.user?.tenant_id + if (!tenantId) + return reply.code(400).send({ error: "No tenant selected" }) + + const { search, sort, asc: ascQuery } = req.query as { + search?: string + sort?: string + asc?: string + } + + // WHERE-Basis + let whereCond: any = eq(projects.tenant, tenantId) + + // 🔍 SQL Search + const searchCond = buildProjectSearch(search) + if (searchCond) whereCond = and(whereCond, searchCond) + + // Base Query + let q = server.db.select().from(projects).where(whereCond) + + // Sortierung + if (sort) { + const col = (projects as any)[sort] + if (col) { + q = ascQuery === "true" + ? q.orderBy(asc(col)) + : q.orderBy(desc(col)) + } + } + + const data = await q + return data + + } catch (err) { + console.error("ERROR /resource/projects", err) + return reply.code(500).send({ error: "Internal Server Error" }) + } + })*/ + + + // ------------------------------------------------------------- + // PAGINATED LIST + // ------------------------------------------------------------- + server.get("/resource/:resource/paginated", async (req, reply) => { + try { + const tenantId = req.user?.tenant_id; + if (!tenantId) { + return reply.code(400).send({ error: "No tenant selected" }); + } + + const {resource} = req.params as {resource: string}; + + const {queryConfig} = req; + const { + pagination, + sort, + filters, + paginationDisabled + } = queryConfig; + + const { search, distinctColumns } = req.query as { + search?: string; + distinctColumns?: string; + }; + + + const config = { + projects: { + searchColumns: ["name"], + mtoLoad: ["customer","plant","contract","projecttype"], + table: projects + }, + customers: { + searchColumns: ["name", "customerNumber", "firstname", "lastname", "notes"], + table: customers, + }, + contacts: { + searchColumns: ["firstName", "lastName", "email", "phone", "notes"], + table: contacts, + mtoLoad: ["customer","vendor"] + }, + contracts: { + table: contracts, + searchColumns: ["name", "notes", "contractNumber", "paymentType", "sepaRef", "bankingName"] + }, + plants: { + table: plants + }, + projecttypes: { + table: projecttypes + }, + vendors: { + table: vendors, + searchColumns: ["name","vendorNumber","notes","defaultPaymentType"], + }, + files: { + table: files + } + } + + let table = config[resource].table + + let whereCond: any = eq(table.tenant, tenantId); + + + if(search) { + const searchCond = buildSearchCondition( + table, + config[resource].searchColumns, + search.trim() + ) + + if (searchCond) { + whereCond = and(whereCond, searchCond) + } + } + + if (filters) { + for (const [key, val] of Object.entries(filters)) { + const col = (table as any)[key]; + if (!col) continue; + + if (Array.isArray(val)) { + whereCond = and(whereCond, inArray(col, val)); + } else { + whereCond = and(whereCond, eq(col, val as any)); + } + } + } + + // ----------------------------------------------- + // COUNT (for pagination) + // ----------------------------------------------- + const totalRes = await server.db + .select({ value: count(table.id) }) + .from(table) + .where(whereCond); + + const total = Number(totalRes[0]?.value ?? 0); + + // ----------------------------------------------- + // DISTINCT VALUES (regardless of pagination) + // ----------------------------------------------- + const distinctValues: Record = {}; + + if (distinctColumns) { + for (const colName of distinctColumns.split(",").map(c => c.trim())) { + const col = (table as any)[colName]; + if (!col) continue; + + const rows = await server.db + .select({ v: col }) + .from(table) + .where(eq(table.tenant, tenantId)); + + const values = rows + .map(r => r.v) + .filter(v => v != null && v !== ""); + + distinctValues[colName] = [...new Set(values)].sort(); + } + } + + // PAGINATION + const offset = pagination?.offset ?? 0; + const limit = pagination?.limit ?? 100; + + // SORTING + let orderField: any = null; + let direction: "asc" | "desc" = "asc"; + + if (sort?.length > 0) { + const s = sort[0]; + const col = (projects as any)[s.field]; + if (col) { + orderField = col; + direction = s.direction === "asc" ? "asc" : "desc"; + } + } + + // MAIN QUERY (Paginated) + let q = server.db + .select() + .from(table) + .where(whereCond) + .offset(offset) + .limit(limit); + + if (orderField) { + //@ts-ignore + q = direction === "asc" + ? q.orderBy(asc(orderField)) + : q.orderBy(desc(orderField)); + } + + const rows = await q; + + if (!rows.length) { + return { + data: [], + queryConfig: { + ...queryConfig, + total, + totalPages: 0, + distinctValues + } + }; + } + + // RELATION LOADING (MANY-TO-ONE) + + let ids = {} + let lists = {} + let maps = {} + let data = [] + + if(config[resource].mtoLoad) { + config[resource].mtoLoad.forEach(relation => { + ids[relation] = [...new Set(rows.map(r => r[relation]).filter(Boolean))]; + }) + + for await (const relation of config[resource].mtoLoad ) { + lists[relation] = ids[relation].length ? await server.db.select().from(config[relation + "s"].table).where(inArray(config[relation + "s"].table.id, ids[relation])) : [] + + } + + config[resource].mtoLoad.forEach(relation => { + maps[relation] = Object.fromEntries(lists[relation].map(i => [i.id, i])); + }) + + data = rows.map(row => { + let toReturn = { + ...row + } + + config[resource].mtoLoad.forEach(relation => { + toReturn[relation] = row[relation] ? maps[relation][row[relation]] : null + }) + + return toReturn + }); + } + + // ----------------------------------------------- + // RETURN DATA + // ----------------------------------------------- + return { + data, + queryConfig: { + ...queryConfig, + total, + totalPages: Math.ceil(total / limit), + distinctValues + } + }; + + } catch (err) { + console.error(`ERROR /resource/:resource/paginated:`, err); + return reply.code(500).send({ error: "Internal Server Error" }); + } + }); + + + // ------------------------------------------------------------- + // DETAIL (mit JOINS) + // ------------------------------------------------------------- + /*server.get("/resource/projects/:id", async (req, reply) => { + try { + const { id } = req.params as { id: string } + const tenantId = req.user?.tenant_id + if (!tenantId) return reply.code(400).send({ error: "No tenant selected" }) + + const pid = Number(id) + + const projRows = await server.db + .select() + .from(projects) + .where(and(eq(projects.id, pid), eq(projects.tenant, tenantId))) + .limit(1) + + if (!projRows.length) + return reply.code(404).send({ error: "Project not found" }) + + const project = projRows[0] + + // ------------------------------------ + // LOAD RELATIONS + // ------------------------------------ + const [ + customerRecord, + plantRecord, + contractRecord, + projectTypeRecord, + projectTasks, + projectFiles, + projectDocuments, + projectEvents, + ] = await Promise.all([ + + project.customer + ? server.db.select().from(customers).where(eq(customers.id, project.customer)) + : [], + + project.plant + ? server.db.select().from(plants).where(eq(plants.id, project.plant)) + : [], + + project.contract + ? server.db.select().from(contracts).where(eq(contracts.id, project.contract)) + : [], + + project.projecttype + ? server.db.select().from(projecttypes).where(eq(projecttypes.id, project.projecttype)) + : [], + + // Tasks + server.db + .select() + .from(tasks) + .where(eq(tasks.project, pid)), + + // Files + server.db + .select() + .from(files) + .where(eq(files.project, pid)), + + // Documents + server.db + .select() + .from(createddocuments) + .where(eq(createddocuments.project, pid)), + + // Events + server.db + .select() + .from(events) + .where(eq(events.project, pid)), + + ]) + + return { + ...project, + customer: customerRecord[0] ?? null, + plant: plantRecord[0] ?? null, + contract: contractRecord[0] ?? null, + projecttype: projectTypeRecord[0] ?? null, + tasks: projectTasks, + files: projectFiles, + createddocuments: projectDocuments, + events: projectEvents, + } + + } catch (err) { + console.error("ERROR /resource/projects/:id", err) + return reply.code(500).send({ error: "Internal Server Error" }) + } + })*/ +} diff --git a/src/routes/resources/productsServices.ts b/src/routes/resources/productsServices.ts new file mode 100644 index 0000000..dd25bb5 --- /dev/null +++ b/src/routes/resources/productsServices.ts @@ -0,0 +1,858 @@ +import { FastifyInstance } from "fastify" +import { + eq, + ilike, + asc, + desc, + and, + count, + inArray, +} from "drizzle-orm" + +import { + products, + productcategories, + services, + servicecategories, +} from "../../../db/schema" + +// ----------------------------------------------------------------------------- +// PRODUCTS +// ----------------------------------------------------------------------------- +export default async function productsAndServicesRoutes(server: FastifyInstance) { + // ------------------------------------------------------------- + // LIST: /resource/products + // ------------------------------------------------------------- + server.get("/resource/products", async (req, reply) => { + const tenantId = req.user?.tenant_id + if (!tenantId) return reply.code(400).send({ error: "No tenant selected" }) + + const { search, sort, asc: ascQuery } = req.query as { + search?: string + sort?: string + asc?: string + } + + let baseQuery = server.db + .select() + .from(products) + .where(eq(products.tenant, tenantId)) + + if (search) { + baseQuery = server.db + .select() + .from(products) + .where( + and( + eq(products.tenant, tenantId), + ilike(products.name, `%${search}%`) + ) + ) + } + + if (sort) { + const field = (products as any)[sort] + if (field) { + // @ts-ignore + baseQuery = baseQuery.orderBy( + ascQuery === "true" ? asc(field) : desc(field) + ) + } + } + + const list = await baseQuery + return list + }) + + // ------------------------------------------------------------- + // PAGINATED: /resource/products/paginated + // ------------------------------------------------------------- + server.get("/resource/products/paginated", async (req, reply) => { + try { + const tenantId = req.user?.tenant_id + if (!tenantId) { + return reply.code(400).send({ error: "No tenant selected" }) + } + + const queryConfig = req.queryConfig + const { + pagination, + sort, + filters, + paginationDisabled, + } = queryConfig + + const { + select, // aktuell ignoriert, wie bei customers + search, + searchColumns, + distinctColumns, + } = req.query as { + select?: string + search?: string + searchColumns?: string + distinctColumns?: string + } + + let whereCond: any = eq(products.tenant, tenantId) + + if (filters) { + for (const [key, val] of Object.entries(filters)) { + const col = (products as any)[key] + if (!col) continue + + if (Array.isArray(val)) { + whereCond = and(whereCond, inArray(col, val)) + } else if (val === true || val === false || val === null) { + whereCond = and(whereCond, eq(col, val as any)) + } else { + whereCond = and(whereCond, eq(col, val as any)) + } + } + } + + if (search && search.trim().length > 0) { + const searchTerm = `%${search.trim().toLowerCase()}%` + whereCond = and( + whereCond, + ilike(products.name, searchTerm) + ) + } + + const totalRes = await server.db + .select({ value: count(products.id) }) + .from(products) + .where(whereCond) + + const total = Number(totalRes[0]?.value ?? 0) + + const distinctValues: Record = {} + + if (distinctColumns) { + for (const colName of distinctColumns.split(",").map(v => v.trim())) { + const col = (products as any)[colName] + if (!col) continue + + const rows = await server.db + .select({ v: col }) + .from(products) + .where(eq(products.tenant, tenantId)) + + const values = rows + .map(r => r.v) + .filter(v => v != null && v !== "") + + distinctValues[colName] = [...new Set(values)].sort() + } + } + + let offset = 0 + let limit = 999999 + + if (!paginationDisabled && pagination) { + offset = pagination.offset + limit = pagination.limit + } + + let orderField: any = null + let orderDirection: "asc" | "desc" = "asc" + + if (sort?.length > 0) { + const s = sort[0] + const col = (products as any)[s.field] + if (col) { + orderField = col + orderDirection = s.direction === "asc" ? "asc" : "desc" + } + } + + let dataQuery = server.db + .select() + .from(products) + .where(whereCond) + .offset(offset) + .limit(limit) + + if (orderField) { + dataQuery = + orderDirection === "asc" + ? dataQuery.orderBy(asc(orderField)) + : dataQuery.orderBy(desc(orderField)) + } + + const data = await dataQuery + + const totalPages = pagination?.limit + ? Math.ceil(total / pagination.limit) + : 1 + + const enrichedConfig = { + ...queryConfig, + total, + totalPages, + distinctValues, + search: search || null, + } + + return { + data, + queryConfig: enrichedConfig, + } + } catch (e) { + server.log.error(e) + return reply.code(500).send({ error: "Internal Server Error" }) + } + }) + + // ------------------------------------------------------------- + // DETAIL: /resource/products/:id + // (aktuell ohne weitere Joins) + // ------------------------------------------------------------- + server.get("/resource/products/:id", async (req, reply) => { + const { id } = req.params as { id: string } + const tenantId = req.user?.tenant_id + + if (!tenantId) return reply.code(400).send({ error: "No tenant selected" }) + + const rows = await server.db + .select() + .from(products) + .where( + and( + eq(products.id, Number(id)), + eq(products.tenant, tenantId) + ) + ) + .limit(1) + + if (!rows.length) { + return reply.code(404).send({ error: "Product not found" }) + } + + return rows[0] + }) + + + // --------------------------------------------------------------------------- + // PRODUCTCATEGORIES + // --------------------------------------------------------------------------- + server.get("/resource/productcategories", async (req, reply) => { + const tenantId = req.user?.tenant_id + if (!tenantId) return reply.code(400).send({ error: "No tenant selected" }) + + const { search, sort, asc: ascQuery } = req.query as { + search?: string + sort?: string + asc?: string + } + + let baseQuery = server.db + .select() + .from(productcategories) + .where(eq(productcategories.tenant, tenantId)) + + if (search) { + const searchTerm = `%${search}%` + baseQuery = server.db + .select() + .from(productcategories) + .where( + and( + eq(productcategories.tenant, tenantId), + ilike(productcategories.name, searchTerm) + ) + ) + } + + if (sort) { + const field = (productcategories as any)[sort] + if (field) { + // @ts-ignore + baseQuery = baseQuery.orderBy( + ascQuery === "true" ? asc(field) : desc(field) + ) + } + } + + const list = await baseQuery + return list + }) + + server.get("/resource/productcategories/paginated", async (req, reply) => { + try { + const tenantId = req.user?.tenant_id + if (!tenantId) { + return reply.code(400).send({ error: "No tenant selected" }) + } + + const queryConfig = req.queryConfig + const { + pagination, + sort, + filters, + paginationDisabled, + } = queryConfig + + const { + select, + search, + searchColumns, + distinctColumns, + } = req.query as { + select?: string + search?: string + searchColumns?: string + distinctColumns?: string + } + + let whereCond: any = eq(productcategories.tenant, tenantId) + + if (filters) { + for (const [key, val] of Object.entries(filters)) { + const col = (productcategories as any)[key] + if (!col) continue + + if (Array.isArray(val)) { + whereCond = and(whereCond, inArray(col, val)) + } else if (val === true || val === false || val === null) { + whereCond = and(whereCond, eq(col, val as any)) + } else { + whereCond = and(whereCond, eq(col, val as any)) + } + } + } + + if (search && search.trim().length > 0) { + const searchTerm = `%${search.trim().toLowerCase()}%` + whereCond = and( + whereCond, + ilike(productcategories.name, searchTerm) + ) + } + + const totalRes = await server.db + .select({ value: count(productcategories.id) }) + .from(productcategories) + .where(whereCond) + + const total = Number(totalRes[0]?.value ?? 0) + + const distinctValues: Record = {} + + if (distinctColumns) { + for (const colName of distinctColumns.split(",").map(v => v.trim())) { + const col = (productcategories as any)[colName] + if (!col) continue + + const rows = await server.db + .select({ v: col }) + .from(productcategories) + .where(eq(productcategories.tenant, tenantId)) + + const values = rows + .map(r => r.v) + .filter(v => v != null && v !== "") + + distinctValues[colName] = [...new Set(values)].sort() + } + } + + let offset = 0 + let limit = 999999 + + if (!paginationDisabled && pagination) { + offset = pagination.offset + limit = pagination.limit + } + + let orderField: any = null + let orderDirection: "asc" | "desc" = "asc" + + if (sort?.length > 0) { + const s = sort[0] + const col = (productcategories as any)[s.field] + if (col) { + orderField = col + orderDirection = s.direction === "asc" ? "asc" : "desc" + } + } + + let dataQuery = server.db + .select() + .from(productcategories) + .where(whereCond) + .offset(offset) + .limit(limit) + + if (orderField) { + dataQuery = + orderDirection === "asc" + ? dataQuery.orderBy(asc(orderField)) + : dataQuery.orderBy(desc(orderField)) + } + + const data = await dataQuery + + const totalPages = pagination?.limit + ? Math.ceil(total / pagination.limit) + : 1 + + const enrichedConfig = { + ...queryConfig, + total, + totalPages, + distinctValues, + search: search || null, + } + + return { + data, + queryConfig: enrichedConfig, + } + } catch (e) { + server.log.error(e) + return reply.code(500).send({ error: "Internal Server Error" }) + } + }) + + server.get("/resource/productcategories/:id", async (req, reply) => { + const { id } = req.params as { id: string } + const tenantId = req.user?.tenant_id + + if (!tenantId) return reply.code(400).send({ error: "No tenant selected" }) + + const rows = await server.db + .select() + .from(productcategories) + .where( + and( + eq(productcategories.id, Number(id)), + eq(productcategories.tenant, tenantId) + ) + ) + .limit(1) + + if (!rows.length) { + return reply.code(404).send({ error: "Product category not found" }) + } + + // Später hier: products mit Join-Tabelle + return rows[0] + }) + + + // --------------------------------------------------------------------------- + // SERVICES + // --------------------------------------------------------------------------- + server.get("/resource/services", async (req, reply) => { + const tenantId = req.user?.tenant_id + if (!tenantId) return reply.code(400).send({ error: "No tenant selected" }) + + const { search, sort, asc: ascQuery } = req.query as { + search?: string + sort?: string + asc?: string + } + + let baseQuery = server.db + .select() + .from(services) + .where(eq(services.tenant, tenantId)) + + if (search) { + const searchTerm = `%${search}%` + baseQuery = server.db + .select() + .from(services) + .where( + and( + eq(services.tenant, tenantId), + ilike(services.name, searchTerm) + ) + ) + } + + if (sort) { + const field = (services as any)[sort] + if (field) { + // @ts-ignore + baseQuery = baseQuery.orderBy( + ascQuery === "true" ? asc(field) : desc(field) + ) + } + } + + const list = await baseQuery + return list + }) + + server.get("/resource/services/paginated", async (req, reply) => { + try { + const tenantId = req.user?.tenant_id + if (!tenantId) { + return reply.code(400).send({ error: "No tenant selected" }) + } + + const queryConfig = req.queryConfig + const { + pagination, + sort, + filters, + paginationDisabled, + } = queryConfig + + const { + select, + search, + searchColumns, + distinctColumns, + } = req.query as { + select?: string + search?: string + searchColumns?: string + distinctColumns?: string + } + + let whereCond: any = eq(services.tenant, tenantId) + + if (filters) { + for (const [key, val] of Object.entries(filters)) { + const col = (services as any)[key] + if (!col) continue + + if (Array.isArray(val)) { + whereCond = and(whereCond, inArray(col, val)) + } else if (val === true || val === false || val === null) { + whereCond = and(whereCond, eq(col, val as any)) + } else { + whereCond = and(whereCond, eq(col, val as any)) + } + } + } + + if (search && search.trim().length > 0) { + const searchTerm = `%${search.trim().toLowerCase()}%` + whereCond = and( + whereCond, + ilike(services.name, searchTerm) + ) + } + + const totalRes = await server.db + .select({ value: count(services.id) }) + .from(services) + .where(whereCond) + + const total = Number(totalRes[0]?.value ?? 0) + + const distinctValues: Record = {} + + if (distinctColumns) { + for (const colName of distinctColumns.split(",").map(v => v.trim())) { + const col = (services as any)[colName] + if (!col) continue + + const rows = await server.db + .select({ v: col }) + .from(services) + .where(eq(services.tenant, tenantId)) + + const values = rows + .map(r => r.v) + .filter(v => v != null && v !== "") + + distinctValues[colName] = [...new Set(values)].sort() + } + } + + let offset = 0 + let limit = 999999 + + if (!paginationDisabled && pagination) { + offset = pagination.offset + limit = pagination.limit + } + + let orderField: any = null + let orderDirection: "asc" | "desc" = "asc" + + if (sort?.length > 0) { + const s = sort[0] + const col = (services as any)[s.field] + if (col) { + orderField = col + orderDirection = s.direction === "asc" ? "asc" : "desc" + } + } + + let dataQuery = server.db + .select() + .from(services) + .where(whereCond) + .offset(offset) + .limit(limit) + + if (orderField) { + dataQuery = + orderDirection === "asc" + ? dataQuery.orderBy(asc(orderField)) + : dataQuery.orderBy(desc(orderField)) + } + + const data = await dataQuery + + const totalPages = pagination?.limit + ? Math.ceil(total / pagination.limit) + : 1 + + const enrichedConfig = { + ...queryConfig, + total, + totalPages, + distinctValues, + search: search || null, + } + + return { + data, + queryConfig: enrichedConfig, + } + } catch (e) { + server.log.error(e) + return reply.code(500).send({ error: "Internal Server Error" }) + } + }) + + server.get("/resource/services/:id", async (req, reply) => { + const { id } = req.params as { id: string } + const tenantId = req.user?.tenant_id + + if (!tenantId) return reply.code(400).send({ error: "No tenant selected" }) + + const rows = await server.db + .select() + .from(services) + .where( + and( + eq(services.id, Number(id)), + eq(services.tenant, tenantId) + ) + ) + .limit(1) + + if (!rows.length) { + return reply.code(404).send({ error: "Service not found" }) + } + + // Später: Unit, Kategorien, etc. als Joins + return rows[0] + }) + + + // --------------------------------------------------------------------------- + // SERVICECATEGORIES + // --------------------------------------------------------------------------- + server.get("/resource/servicecategories", async (req, reply) => { + const tenantId = req.user?.tenant_id + if (!tenantId) return reply.code(400).send({ error: "No tenant selected" }) + + const { search, sort, asc: ascQuery } = req.query as { + search?: string + sort?: string + asc?: string + } + + let baseQuery = server.db + .select() + .from(servicecategories) + .where(eq(servicecategories.tenant, tenantId)) + + if (search) { + const searchTerm = `%${search}%` + baseQuery = server.db + .select() + .from(servicecategories) + .where( + and( + eq(servicecategories.tenant, tenantId), + ilike(servicecategories.name, searchTerm) + ) + ) + } + + if (sort) { + const field = (servicecategories as any)[sort] + if (field) { + // @ts-ignore + baseQuery = baseQuery.orderBy( + ascQuery === "true" ? asc(field) : desc(field) + ) + } + } + + const list = await baseQuery + return list + }) + + server.get("/resource/servicecategories/paginated", async (req, reply) => { + try { + const tenantId = req.user?.tenant_id + if (!tenantId) { + return reply.code(400).send({ error: "No tenant selected" }) + } + + const queryConfig = req.queryConfig + const { + pagination, + sort, + filters, + paginationDisabled, + } = queryConfig + + const { + select, + search, + searchColumns, + distinctColumns, + } = req.query as { + select?: string + search?: string + searchColumns?: string + distinctColumns?: string + } + + let whereCond: any = eq(servicecategories.tenant, tenantId) + + if (filters) { + for (const [key, val] of Object.entries(filters)) { + const col = (servicecategories as any)[key] + if (!col) continue + + if (Array.isArray(val)) { + whereCond = and(whereCond, inArray(col, val)) + } else if (val === true || val === false || val === null) { + whereCond = and(whereCond, eq(col, val as any)) + } else { + whereCond = and(whereCond, eq(col, val as any)) + } + } + } + + if (search && search.trim().length > 0) { + const searchTerm = `%${search.trim().toLowerCase()}%` + whereCond = and( + whereCond, + ilike(servicecategories.name, searchTerm) + ) + } + + const totalRes = await server.db + .select({ value: count(servicecategories.id) }) + .from(servicecategories) + .where(whereCond) + + const total = Number(totalRes[0]?.value ?? 0) + + const distinctValues: Record = {} + + if (distinctColumns) { + for (const colName of distinctColumns.split(",").map(v => v.trim())) { + const col = (servicecategories as any)[colName] + if (!col) continue + + const rows = await server.db + .select({ v: col }) + .from(servicecategories) + .where(eq(servicecategories.tenant, tenantId)) + + const values = rows + .map(r => r.v) + .filter(v => v != null && v !== "") + + distinctValues[colName] = [...new Set(values)].sort() + } + } + + let offset = 0 + let limit = 999999 + + if (!paginationDisabled && pagination) { + offset = pagination.offset + limit = pagination.limit + } + + let orderField: any = null + let orderDirection: "asc" | "desc" = "asc" + + if (sort?.length > 0) { + const s = sort[0] + const col = (servicecategories as any)[s.field] + if (col) { + orderField = col + orderDirection = s.direction === "asc" ? "asc" : "desc" + } + } + + let dataQuery = server.db + .select() + .from(servicecategories) + .where(whereCond) + .offset(offset) + .limit(limit) + + if (orderField) { + dataQuery = + orderDirection === "asc" + ? dataQuery.orderBy(asc(orderField)) + : dataQuery.orderBy(desc(orderField)) + } + + const data = await dataQuery + + const totalPages = pagination?.limit + ? Math.ceil(total / pagination.limit) + : 1 + + const enrichedConfig = { + ...queryConfig, + total, + totalPages, + distinctValues, + search: search || null, + } + + return { + data, + queryConfig: enrichedConfig, + } + } catch (e) { + server.log.error(e) + return reply.code(500).send({ error: "Internal Server Error" }) + } + }) + + server.get("/resource/servicecategories/:id", async (req, reply) => { + const { id } = req.params as { id: string } + const tenantId = req.user?.tenant_id + + if (!tenantId) return reply.code(400).send({ error: "No tenant selected" }) + + const rows = await server.db + .select() + .from(servicecategories) + .where( + and( + eq(servicecategories.id, Number(id)), + eq(servicecategories.tenant, tenantId) + ) + ) + .limit(1) + + if (!rows.length) { + return reply.code(404).send({ error: "Service category not found" }) + } + + // Später: zugehörige Services über Join-Tabelle + return rows[0] + }) +} diff --git a/src/routes/resources/vendors.ts b/src/routes/resources/vendors.ts deleted file mode 100644 index 4408f53..0000000 --- a/src/routes/resources/vendors.ts +++ /dev/null @@ -1,251 +0,0 @@ -import { FastifyInstance } from "fastify" -import { - eq, - ilike, - asc, - desc, - and, - count, - inArray, - or -} from "drizzle-orm" - -import { - vendors, - contacts, - files, -} from "../../../db/schema" - -// ------------------------------------------------------------- -// SQL Volltext-Suche (über mehrere relevante Felder) -// ------------------------------------------------------------- -function buildVendorSearchTerm(search?: string) { - if (!search) return null - - const term = `%${search.toLowerCase()}%` - - return or( - ilike(vendors.name, term), - ilike(vendors.vendorNumber, term), - ilike(vendors.notes, term), - ilike(vendors.defaultPaymentMethod, term) - ) -} - -export default async function vendorRoutes(server: FastifyInstance) { - - // ------------------------------------------------------------- - // LIST - // ------------------------------------------------------------- - server.get("/resource/vendors", async (req, reply) => { - try { - const tenantId = req.user?.tenant_id - if (!tenantId) return reply.code(400).send({ error: "No tenant selected" }) - - const { search, sort, asc: ascQuery } = req.query as { - search?: string - sort?: string - asc?: string - } - - // WHERE - let whereCond: any = eq(vendors.tenant, tenantId) - - const searchCond = buildVendorSearchTerm(search) - if (searchCond) whereCond = and(whereCond, searchCond) - - // QUERY - let q = server.db - .select() - .from(vendors) - .where(whereCond) - - // SORT - if (sort) { - const col = (vendors as any)[sort] - if (col) { - //@ts-ignore - q = ascQuery === "true" - ? q.orderBy(asc(col)) - : q.orderBy(desc(col)) - } - } - - return await q - } catch (err) { - console.error("ERROR /resource/vendors:", err) - return reply.code(500).send({ error: "Internal Server Error" }) - } - }) - - - // ------------------------------------------------------------- - // PAGINATED - // ------------------------------------------------------------- - server.get("/resource/vendors/paginated", async (req, reply) => { - try { - const tenantId = req.user?.tenant_id - if (!tenantId) return reply.code(400).send({ error: "No tenant selected" }) - - const queryConfig = req.queryConfig - const { pagination, sort, filters, paginationDisabled } = queryConfig - - const { search, distinctColumns } = req.query as { - search?: string - distinctColumns?: string - } - - // ------------------------------------ - // WHERE - // ------------------------------------ - let whereCond: any = eq(vendors.tenant, tenantId) - - // Filters - if (filters) { - for (const [key, val] of Object.entries(filters)) { - const col = (vendors as any)[key] - if (!col) continue - - if (Array.isArray(val)) { - whereCond = and(whereCond, inArray(col, val)) - } else { - whereCond = and(whereCond, eq(col, val as any)) - } - } - } - - // SEARCH - const searchCond = buildVendorSearchTerm(search) - if (searchCond) whereCond = and(whereCond, searchCond) - - // ------------------------------------ - // COUNT - // ------------------------------------ - const totalRes = await server.db - .select({ value: count(vendors.id) }) - .from(vendors) - .where(whereCond) - - const total = Number(totalRes[0]?.value ?? 0) - - // ------------------------------------ - // DISTINCT VALUES - // ------------------------------------ - const distinctValues: Record = {} - - if (distinctColumns) { - for (const field of distinctColumns.split(",")) { - const col = (vendors as any)[field.trim()] - if (!col) continue - - const rows = await server.db - .select({ v: col }) - .from(vendors) - .where(eq(vendors.tenant, tenantId)) - - const values = rows - .map(r => r.v) - .filter(v => v !== null && v !== "") - - distinctValues[field] = [...new Set(values)].sort() - } - } - - // ------------------------------------ - // PAGINATION - // ------------------------------------ - const offset = pagination?.offset ?? 0 - const limit = pagination?.limit ?? 5000 - - // ------------------------------------ - // SORT - // ------------------------------------ - let orderField: any = null - let orderDir: "asc" | "desc" = "asc" - - if (sort?.length > 0) { - const s = sort[0] - const col = (vendors as any)[s.field] - if (col) { - orderField = col - orderDir = s.direction === "asc" ? "asc" : "desc" - } - } - - // ------------------------------------ - // DATA QUERY - // ------------------------------------ - let q = server.db - .select() - .from(vendors) - .where(whereCond) - .offset(offset) - .limit(limit) - - if (orderField) { - //@ts-ignore - q = orderDir === "asc" - ? q.orderBy(asc(orderField)) - : q.orderBy(desc(orderField)) - } - - const data = await q - - return { - data, - queryConfig: { - ...queryConfig, - total, - totalPages: pagination?.limit - ? Math.ceil(total / pagination.limit) - : 1, - distinctValues, - search: search || null - } - } - - } catch (err) { - console.error("ERROR /resource/vendors/paginated:", err) - return reply.code(500).send({ error: "Internal Server Error" }) - } - }) - - - // ------------------------------------------------------------- - // DETAIL (mit JOINs: contacts, files) - // ------------------------------------------------------------- - server.get("/resource/vendors/:id", async (req, reply) => { - try { - const { id } = req.params as { id: string } - const tenantId = req.user?.tenant_id - if (!tenantId) return reply.code(400).send({ error: "No tenant selected" }) - - const vendorId = Number(id) - - const vendorRows = await server.db - .select() - .from(vendors) - .where(and(eq(vendors.id, vendorId), eq(vendors.tenant, tenantId))) - .limit(1) - - if (!vendorRows.length) return reply.code(404).send({ error: "Vendor not found" }) - - const vendor = vendorRows[0] - - const [vendorContacts, vendorFiles] = await Promise.all([ - server.db.select().from(contacts).where(eq(contacts.vendor, vendorId)), - server.db.select().from(files).where(eq(files.vendor, vendorId)) - ]) - - return { - ...vendor, - contacts: vendorContacts, - files: vendorFiles - } - - } catch (err) { - console.error("ERROR /resource/vendors/:id:", err) - return reply.code(500).send({ error: "Internal Server Error" }) - } - }) -} From 76e40cd9e18c7ff55cf71a1d6f40c51c26896bdc Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sun, 7 Dec 2025 17:11:26 +0100 Subject: [PATCH 090/149] db changes --- db/schema/costcentres.ts | 4 ++-- db/schema/inventoryitems.ts | 6 +++--- db/schema/products.ts | 18 +++++++++--------- db/schema/servicecategories.ts | 6 +++--- db/schema/spaces.ts | 4 ++-- db/schema/vehicles.ts | 16 ++++++++-------- 6 files changed, 27 insertions(+), 27 deletions(-) diff --git a/db/schema/costcentres.ts b/db/schema/costcentres.ts index b57a21a..7dccb10 100644 --- a/db/schema/costcentres.ts +++ b/db/schema/costcentres.ts @@ -9,7 +9,7 @@ import { } from "drizzle-orm/pg-core" import { tenants } from "./tenants" -import { inventoryItems } from "./inventoryitems" +import { inventoryitems } from "./inventoryitems" import { projects } from "./projects" import { vehicles } from "./vehicles" import { authUsers } from "./auth_users" @@ -33,7 +33,7 @@ export const costcentres = pgTable("costcentres", { project: bigint("project", { mode: "number" }).references(() => projects.id), inventoryitem: bigint("inventoryitem", { mode: "number" }).references( - () => inventoryItems.id + () => inventoryitems.id ), description: text("description"), diff --git a/db/schema/inventoryitems.ts b/db/schema/inventoryitems.ts index 9a56d5d..f3437d8 100644 --- a/db/schema/inventoryitems.ts +++ b/db/schema/inventoryitems.ts @@ -15,7 +15,7 @@ import { vendors } from "./vendors" import { spaces } from "./spaces" import { authUsers } from "./auth_users" -export const inventoryItems = pgTable("inventoryitems", { +export const inventoryitems = pgTable("inventoryitems", { id: bigint("id", { mode: "number" }) .primaryKey() .generatedByDefaultAsIdentity(), @@ -64,5 +64,5 @@ export const inventoryItems = pgTable("inventoryitems", { ), }) -export type InventoryItem = typeof inventoryItems.$inferSelect -export type NewInventoryItem = typeof inventoryItems.$inferInsert +export type InventoryItem = typeof inventoryitems.$inferSelect +export type NewInventoryItem = typeof inventoryitems.$inferInsert diff --git a/db/schema/products.ts b/db/schema/products.ts index 3379d21..d070254 100644 --- a/db/schema/products.ts +++ b/db/schema/products.ts @@ -40,16 +40,16 @@ export const products = pgTable("products", { ean: text("ean"), barcode: text("barcode"), - purchasePrice: doublePrecision("purchasePrice"), - sellingPrice: doublePrecision("sellingPrice"), + purchase_price: doublePrecision("purchasePrice"), + selling_price: doublePrecision("sellingPrice"), description: text("description"), - manufacturerNumber: text("manufacturerNumber"), + manufacturer_number: text("manufacturerNumber"), - vendorAllocation: jsonb("vendorAllocation").default([]), + vendor_allocation: jsonb("vendorAllocation").default([]), - articleNumber: text("articleNumber"), + article_number: text("articleNumber"), barcodes: text("barcodes").array().notNull().default([]), @@ -57,12 +57,12 @@ export const products = pgTable("products", { archived: boolean("archived").notNull().default(false), - taxPercentage: smallint("taxPercentage").notNull().default(19), + tax_percentage: smallint("taxPercentage").notNull().default(19), - markupPercentage: doublePrecision("markupPercentage"), + markup_percentage: doublePrecision("markupPercentage"), - updatedAt: timestamp("updated_at", { withTimezone: true }), - updatedBy: uuid("updated_by").references(() => authUsers.id), + updated_at: timestamp("updated_at", { withTimezone: true }), + updated_by: uuid("updated_by").references(() => authUsers.id), }) export type Product = typeof products.$inferSelect diff --git a/db/schema/servicecategories.ts b/db/schema/servicecategories.ts index e51b03a..f0364b5 100644 --- a/db/schema/servicecategories.ts +++ b/db/schema/servicecategories.ts @@ -16,7 +16,7 @@ export const servicecategories = pgTable("servicecategories", { .primaryKey() .generatedByDefaultAsIdentity(), - createdAt: timestamp("created_at", { withTimezone: true }) + created_at: timestamp("created_at", { withTimezone: true }) .notNull() .defaultNow(), @@ -31,8 +31,8 @@ export const servicecategories = pgTable("servicecategories", { archived: boolean("archived").notNull().default(false), - updatedAt: timestamp("updated_at", { withTimezone: true }), - updatedBy: uuid("updated_by").references(() => authUsers.id), + updated_at: timestamp("updated_at", { withTimezone: true }), + updated_by: uuid("updated_by").references(() => authUsers.id), }) export type ServiceCategory = typeof servicecategories.$inferSelect diff --git a/db/schema/spaces.ts b/db/schema/spaces.ts index b0d8116..7b8ce0d 100644 --- a/db/schema/spaces.ts +++ b/db/schema/spaces.ts @@ -27,13 +27,13 @@ export const spaces = pgTable("spaces", { .notNull() .references(() => tenants.id), - spaceNumber: text("spaceNumber").notNull(), + space_number: text("spaceNumber").notNull(), parentSpace: bigint("parentSpace", { mode: "number" }).references( () => spaces.id ), - infoData: jsonb("infoData") + info_data: jsonb("infoData") .notNull() .default({ zip: "", city: "", streetNumber: "" }), diff --git a/db/schema/vehicles.ts b/db/schema/vehicles.ts index 7915657..30a6553 100644 --- a/db/schema/vehicles.ts +++ b/db/schema/vehicles.ts @@ -17,7 +17,7 @@ export const vehicles = pgTable("vehicles", { .primaryKey() .generatedByDefaultAsIdentity(), - createdAt: timestamp("created_at", { withTimezone: true }) + created_at: timestamp("created_at", { withTimezone: true }) .notNull() .defaultNow(), @@ -25,7 +25,7 @@ export const vehicles = pgTable("vehicles", { .notNull() .references(() => tenants.id), - licensePlate: text("licensePlate"), + license_plate: text("licensePlate"), name: text("name"), type: text("type"), @@ -36,21 +36,21 @@ export const vehicles = pgTable("vehicles", { vin: text("vin"), - tankSize: doublePrecision("tankSize").notNull().default(0), + tank_size: doublePrecision("tankSize").notNull().default(0), archived: boolean("archived").notNull().default(false), - buildYear: text("buildYear"), + build_year: text("buildYear"), - towingCapacity: bigint("towingCapacity", { mode: "number" }), - powerInKW: bigint("powerInKW", { mode: "number" }), + towing_capacity: bigint("towingCapacity", { mode: "number" }), + power_in_kw: bigint("powerInKW", { mode: "number" }), color: text("color"), profiles: jsonb("profiles").notNull().default([]), - updatedAt: timestamp("updated_at", { withTimezone: true }), - updatedBy: uuid("updated_by").references(() => authUsers.id), + updated_at: timestamp("updated_at", { withTimezone: true }), + updated_by: uuid("updated_by").references(() => authUsers.id), }) export type Vehicle = typeof vehicles.$inferSelect From dc0b49355d2797f0cdaa864d4cc9a157dbbd19f8 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sun, 7 Dec 2025 22:06:26 +0100 Subject: [PATCH 091/149] db changes --- db/schema/texttemplates.ts | 6 +++--- src/routes/auth/{ => dep}/user.ts | 0 2 files changed, 3 insertions(+), 3 deletions(-) rename src/routes/auth/{ => dep}/user.ts (100%) diff --git a/db/schema/texttemplates.ts b/db/schema/texttemplates.ts index ce1ac94..baddc0d 100644 --- a/db/schema/texttemplates.ts +++ b/db/schema/texttemplates.ts @@ -12,7 +12,7 @@ import { tenants } from "./tenants" import { authUsers } from "./auth_users" import { textTemplatePositionsEnum } from "./enums" -export const textTemplates = pgTable("texttemplates", { +export const texttemplates = pgTable("texttemplates", { id: bigint("id", { mode: "number" }) .primaryKey() .generatedByDefaultAsIdentity(), @@ -40,5 +40,5 @@ export const textTemplates = pgTable("texttemplates", { updatedBy: uuid("updated_by").references(() => authUsers.id), }) -export type TextTemplate = typeof textTemplates.$inferSelect -export type NewTextTemplate = typeof textTemplates.$inferInsert +export type TextTemplate = typeof texttemplates.$inferSelect +export type NewTextTemplate = typeof texttemplates.$inferInsert diff --git a/src/routes/auth/user.ts b/src/routes/auth/dep/user.ts similarity index 100% rename from src/routes/auth/user.ts rename to src/routes/auth/dep/user.ts From b90e056e7c74a4347d686e7b88fff3a88a4da388 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sun, 7 Dec 2025 22:06:37 +0100 Subject: [PATCH 092/149] redone routes --- src/index.ts | 5 - src/resource.config.ts | 114 +++ src/routes/admin.ts | 185 ++--- src/routes/auth/auth-authenticated.ts | 120 ++-- src/routes/emailAsUser.ts | 417 ++++++----- src/routes/files.ts | 362 +++++----- src/routes/functions.ts | 10 +- src/routes/profiles.ts | 142 +++- src/routes/resources/main.ts | 237 +++---- src/routes/resources/productsServices.ts | 858 ----------------------- 10 files changed, 895 insertions(+), 1555 deletions(-) create mode 100644 src/resource.config.ts delete mode 100644 src/routes/resources/productsServices.ts diff --git a/src/index.ts b/src/index.ts index 5a54508..fa4a204 100644 --- a/src/index.ts +++ b/src/index.ts @@ -17,7 +17,6 @@ import resourceRoutesSpecial from "./routes/resourcesSpecial"; import fastifyCookie from "@fastify/cookie"; import historyRoutes from "./routes/history"; import fileRoutes from "./routes/files"; -import userRoutes from "./routes/auth/user" import functionRoutes from "./routes/functions"; import bankingRoutes from "./routes/banking"; import exportRoutes from "./routes/exports" @@ -30,8 +29,6 @@ import staffTimeRoutes from "./routes/staff/time"; import staffTimeConnectRoutes from "./routes/staff/timeconnects"; //Resources -import productsAndServicesRoutes from "./routes/resources/productsServices"; - import resourceRoutes from "./routes/resources/main"; //M2M @@ -106,7 +103,6 @@ async function main() { await subApp.register(resourceRoutesSpecial); await subApp.register(historyRoutes); await subApp.register(fileRoutes); - await subApp.register(userRoutes); await subApp.register(functionRoutes); await subApp.register(bankingRoutes); await subApp.register(exportRoutes); @@ -118,7 +114,6 @@ async function main() { await subApp.register(staffTimeConnectRoutes); - await subApp.register(productsAndServicesRoutes); await subApp.register(resourceRoutes); },{prefix: "/api"}) diff --git a/src/resource.config.ts b/src/resource.config.ts new file mode 100644 index 0000000..47be3c3 --- /dev/null +++ b/src/resource.config.ts @@ -0,0 +1,114 @@ +import { + contacts, + contracts, costcentres, createddocuments, + customers, + files, filetags, folders, hourrates, inventoryitemgroups, + inventoryitems, letterheads, ownaccounts, + plants, productcategories, products, + projects, + projecttypes, servicecategories, services, spaces, tasks, texttemplates, units, vehicles, + vendors +} from "../db/schema"; + +export const resourceConfig = { + projects: { + searchColumns: ["name"], + mtoLoad: ["customer","plant","contract","projecttype"], + mtmLoad: ["tasks", "files"], + table: projects + }, + customers: { + searchColumns: ["name", "customerNumber", "firstname", "lastname", "notes"], + mtmLoad: ["contacts","projects"], + table: customers, + }, + contacts: { + searchColumns: ["firstName", "lastName", "email", "phone", "notes"], + table: contacts, + mtoLoad: ["customer","vendor"] + }, + contracts: { + table: contracts, + searchColumns: ["name", "notes", "contractNumber", "paymentType", "sepaRef", "bankingName"] + }, + plants: { + table: plants, + mtoLoad: ["customer"], + mtmLoad: ["projects","tasks","files"], + }, + projecttypes: { + table: projecttypes + }, + vendors: { + table: vendors, + searchColumns: ["name","vendorNumber","notes","defaultPaymentType"], + }, + files: { + table: files + }, + folders: { + table: folders + }, + filetags: { + table: filetags + }, + inventoryitems: { + table: inventoryitems + }, + inventoryitemgroups: { + table: inventoryitemgroups + }, + products: { + table: products, + searchColumns: ["name","manufacturer","ean","barcode","description","manfacturer_number","article_number"], + }, + productcategories: { + table: productcategories + }, + services: { + table: services, + mtoLoad: ["unit"], + searchColumns: ["name","description"], + }, + servicecategories: { + table: servicecategories + }, + units: { + table: units, + }, + vehicles: { + table: vehicles, + searchColumns: ["name","license_plate","vin","color"], + }, + hourrates: { + table: hourrates, + searchColumns: ["name"], + }, + spaces: { + table: spaces, + searchColumns: ["name","space_number","type","info_data"], + }, + ownaccounts: { + table: ownaccounts, + searchColumns: ["name","description","number"], + }, + costcentres: { + table: costcentres, + searchColumns: ["name","number","description"], + mtoLoad: ["vehicle","project","inventoryitem"] + }, + tasks: { + table: tasks, + }, + letterheads: { + table: letterheads, + + }, + createddocuments: { + table: createddocuments, + mtoLoad: ["customer", "project", "contact", "contract", "plant","letterhead",] + }, + texttemplates: { + table: texttemplates + } +} \ No newline at end of file diff --git a/src/routes/admin.ts b/src/routes/admin.ts index ebf3e49..9120d18 100644 --- a/src/routes/admin.ts +++ b/src/routes/admin.ts @@ -1,94 +1,117 @@ import { FastifyInstance } from "fastify"; +import { eq } from "drizzle-orm"; + +import { + authTenantUsers, + authUsers, + tenants, +} from "../../db/schema"; export default async function adminRoutes(server: FastifyInstance) { + + // ------------------------------------------------------------- + // POST /admin/add-user-to-tenant + // ------------------------------------------------------------- server.post("/admin/add-user-to-tenant", async (req, reply) => { - const body = req.body as { - user_id: string; - tenant_id: string; - role?: string; - mode?: "single" | "multi"; - }; + try { + const body = req.body as { + user_id: string; + tenant_id: number; + role?: string; + mode?: "single" | "multi"; + }; - if (!body.user_id || !body.tenant_id) { - return reply.code(400).send({ error: "user_id and tenant_id required" }); + if (!body.user_id || !body.tenant_id) { + return reply.code(400).send({ + error: "user_id and tenant_id required" + }); + } + + const mode = body.mode ?? "multi"; + + // ---------------------------- + // SINGLE MODE → alte Verknüpfungen löschen + // ---------------------------- + if (mode === "single") { + await server.db + .delete(authTenantUsers) + .where(eq(authTenantUsers.user_id, body.user_id)); + } + + // ---------------------------- + // Neue Verknüpfung hinzufügen + // ---------------------------- + + await server.db + .insert(authTenantUsers) + // @ts-ignore + .values({ + user_id: body.user_id, + tenantId: body.tenant_id, + role: body.role ?? "member", + }); + + return { success: true, mode }; + + } catch (err) { + console.error("ERROR /admin/add-user-to-tenant:", err); + return reply.code(500).send({ error: "Internal Server Error" }); } - - // Default: "multi" - const mode = body.mode ?? "multi"; - - if (mode === "single") { - // Erst alle alten Verknüpfungen löschen - await server.supabase - .from("auth_tenant_users") - .delete() - .eq("user_id", body.user_id); - } - - const { error } = await server.supabase - .from("auth_tenant_users") - .insert({ - tenant_id: body.tenant_id, - user_id: body.user_id, - role: body.role ?? "member", - }); - - if (error) { - return reply.code(400).send({ error: error.message }); - } - - // Neuen Eintrag setzen - - - return { success: true, mode }; }); - /** - * Alle Tenants eines Users abfragen - */ + + // ------------------------------------------------------------- + // GET /admin/user-tenants/:user_id + // ------------------------------------------------------------- server.get("/admin/user-tenants/:user_id", async (req, reply) => { - const { user_id } = req.params as { user_id: string }; + try { + const { user_id } = req.params as { user_id: string }; - if (!user_id) { - return reply.code(400).send({ error: "user_id required" }); + if (!user_id) { + return reply.code(400).send({ error: "user_id required" }); + } + + // ---------------------------- + // 1) User existiert? + // ---------------------------- + const [user] = await server.db + .select() + .from(authUsers) + .where(eq(authUsers.id, user_id)) + .limit(1); + + if (!user) { + return reply.code(400).send({ error: "faulty user_id presented" }); + } + + // ---------------------------- + // 2) Tenants Join über auth_tenant_users + // ---------------------------- + const tenantRecords = await server.db + .select({ + id: tenants.id, + name: tenants.name, + short: tenants.short, + locked: tenants.locked, + numberRanges: tenants.numberRanges, + extraModules: tenants.extraModules, + }) + .from(authTenantUsers) + .innerJoin( + tenants, + eq(authTenantUsers.tenant_id, tenants.id) + ) + .where(eq(authTenantUsers.user_id, user_id)); + + return { + user_id, + tenants: tenantRecords, + }; + + } catch (err) { + console.error("ERROR /admin/user-tenants:", err); + return reply.code(500).send({ error: "Internal Server Error" }); } - - const {data:user, error: userError} = await server.supabase.from("auth_users").select("*,tenants(*)").eq("id", user_id).single(); - - console.log(userError) - console.log(user) - - if(!user) { - return reply.code(400).send({ error: "faulty user_id presented" }); - } else { - return { user_id, tenants: user.tenants }; - } - }); - /** - * Alle User eines Tenants abfragen - * TODO: Aktuell nur Multi Tenant - */ - /*server.get("/admin/tenant-users/:tenant_id", async (req, reply) => { - const { tenant_id } = req.params as { tenant_id: string }; - - if (!tenant_id) { - return reply.code(400).send({ error: "tenant_id required" }); - } - - const { data, error } = await server.supabase - .from("auth_tenant_users") - .select(` - user_id, - role, - users ( id, email, created_at ) - `) - .eq("tenant_id", tenant_id); - - if (error) { - return reply.code(400).send({ error: error.message }); - } - - return { tenant_id, users: data }; - });*/ -} \ No newline at end of file +} diff --git a/src/routes/auth/auth-authenticated.ts b/src/routes/auth/auth-authenticated.ts index 93e254c..b7626c0 100644 --- a/src/routes/auth/auth-authenticated.ts +++ b/src/routes/auth/auth-authenticated.ts @@ -1,12 +1,15 @@ -import { FastifyInstance } from "fastify"; -import bcrypt from "bcrypt"; +import { FastifyInstance } from "fastify" +import bcrypt from "bcrypt" +import { eq } from "drizzle-orm" +import { authUsers } from "../../../db/schema" // wichtig: Drizzle Schema importieren! export default async function authRoutesAuthenticated(server: FastifyInstance) { + server.post("/auth/password/change", { schema: { tags: ["Auth"], - summary: "Reset Password after forced change", + summary: "Change password (after login or forced reset)", body: { type: "object", required: ["old_password", "new_password"], @@ -25,54 +28,69 @@ export default async function authRoutesAuthenticated(server: FastifyInstance) { }, }, }, async (req, reply) => { - const { old_password, new_password } = req.body as { old_password: string; new_password: string }; - console.log(req.user) + try { + const { old_password, new_password } = req.body as { + old_password: string + new_password: string + } - const user_id = req.user?.user_id; // kommt aus JWT Middleware - if (!user_id) { - // @ts-ignore - return reply.code(401).send({ error: "Unauthorized" }); + const userId = req.user?.user_id + if (!userId) { + //@ts-ignore + return reply.code(401).send({ error: "Unauthorized" }) + } + + // ----------------------------------------------------- + // 1) User laden + // ----------------------------------------------------- + const [user] = await server.db + .select({ + id: authUsers.id, + passwordHash: authUsers.passwordHash, + mustChangePassword: authUsers.must_change_password + }) + .from(authUsers) + .where(eq(authUsers.id, userId)) + .limit(1) + + if (!user) { + //@ts-ignore + return reply.code(404).send({ error: "User not found" }) + } + + // ----------------------------------------------------- + // 2) Altes PW prüfen + // ----------------------------------------------------- + const valid = await bcrypt.compare(old_password, user.passwordHash) + if (!valid) { + //@ts-ignore + return reply.code(401).send({ error: "Old password incorrect" }) + } + + // ----------------------------------------------------- + // 3) Neues PW hashen + // ----------------------------------------------------- + const newHash = await bcrypt.hash(new_password, 10) + + // ----------------------------------------------------- + // 4) Updaten + // ----------------------------------------------------- + await server.db + .update(authUsers) + .set({ + passwordHash: newHash, + must_change_password: false, + updatedAt: new Date(), + }) + .where(eq(authUsers.id, userId)) + + return { success: true } + + } catch (err) { + console.error("POST /auth/password/change ERROR:", err) + //@ts-ignore + return reply.code(500).send({ error: "Internal Server Error" }) } - - // Nutzer laden - const { data: user, error } = await server.supabase - .from("auth_users") - .select("id, password_hash, must_change_password") - .eq("id", user_id) - .single(); - - if (error || !user) { - // @ts-ignore - return reply.code(404).send({ error: "User not found" }); - } - - // Altes Passwort prüfen - const valid = await bcrypt.compare(old_password, user.password_hash); - if (!valid) { - // @ts-ignore - return reply.code(401).send({ error: "Old password incorrect" }); - } - - // Neues Passwort hashen - const newHash = await bcrypt.hash(new_password, 10); - - // Speichern + Flag zurücksetzen - const { error: updateError } = await server.supabase - .from("auth_users") - .update({ - password_hash: newHash, - must_change_password: false, - updated_at: new Date().toISOString(), - }) - .eq("id", user_id); - - if (updateError) { - console.log(updateError); - // @ts-ignore - return reply.code(500).send({ error: "Password update failed" }); - } - - return { success: true }; - }); -} \ No newline at end of file + }) +} diff --git a/src/routes/emailAsUser.ts b/src/routes/emailAsUser.ts index 45693b3..525fa25 100644 --- a/src/routes/emailAsUser.ts +++ b/src/routes/emailAsUser.ts @@ -1,169 +1,31 @@ import nodemailer from "nodemailer" +import { FastifyInstance } from "fastify" +import { eq } from "drizzle-orm" + +import { sendMailAsUser } from "../utils/emailengine" +import { encrypt, decrypt } from "../utils/crypt" +import { userCredentials } from "../../db/schema" +// Pfad ggf. anpassen -import { FastifyInstance } from "fastify"; -import {sendMailAsUser} from "../utils/emailengine"; -import {encrypt, decrypt} from "../utils/crypt" -import {secrets} from "../utils/secrets"; // @ts-ignore -import MailComposer from 'nodemailer/lib/mail-composer/index.js' - -import {ImapFlow} from "imapflow" +import MailComposer from "nodemailer/lib/mail-composer/index.js" +import { ImapFlow } from "imapflow" export default async function emailAsUserRoutes(server: FastifyInstance) { - // Create E-Mail Account + + // ====================================================================== + // CREATE OR UPDATE EMAIL ACCOUNT + // ====================================================================== server.post("/email/accounts/:id?", async (req, reply) => { - if (!req.user?.tenant_id) { - return reply.code(400).send({ error: "No tenant selected" }); - } - - const { id } = req.params as { id: string }; - - - const body = req.body as { - email: string - password: string - smtp_host: string - smtp_port: number - smtp_ssl: boolean - imap_host: string - imap_port: number - imap_ssl: boolean - }; - - if(id) { - //SAVE Existing - let saveData = { - email_encrypted: body.email ? encrypt(body.email) : undefined, - password_encrypted: body.password ? encrypt(body.password) : undefined, - smtp_host_encrypted: body.smtp_host ? encrypt(body.smtp_host) : undefined, - smtp_port: body.smtp_port, - smtp_ssl: body.smtp_ssl, - imap_host_encrypted: body.imap_host ? encrypt(body.imap_host) : undefined, - imap_port: body.imap_port, - imap_ssl: body.imap_ssl, - } - - - const { data, error } = await server.supabase - .from("user_credentials") - .update(saveData) - .eq("id", id) - .select("*") - .single(); - - if (error) { - return reply.code(400).send({ error: error.message }); - } else { - return reply.send({success: true}) - } - } else { - //Create New - let createData = { - user_id: req.user.user_id, - email_encrypted: encrypt(body.email), - password_encrypted: encrypt(body.password), - tenant_id: req.user.tenant_id, - smtp_host_encrypted: encrypt(body.smtp_host), - smtp_port: body.smtp_port, - smtp_ssl: body.smtp_ssl, - type: "mail", - imap_host_encrypted: encrypt(body.imap_host), - imap_port: body.imap_port, - imap_ssl: body.imap_ssl, - } - - - const { data, error } = await server.supabase - .from("user_credentials") - .insert(createData) - .select("*") - .single(); - - if (error) { - return reply.code(400).send({ error: error.message }); - } else { - return reply.send({success: true}) - } - } - - - - }); - - server.get("/email/accounts/:id?", async (req, reply) => { - if (!req.user?.tenant_id) { - return reply.code(400).send({ error: "No tenant selected" }); - } - - const { id } = req.params as { id: string }; - - if(id) { - let returnData = {} - // @ts-ignore - const { data, error } = await server.supabase - .from("user_credentials") - .select("id, email_encrypted, smtp_host_encrypted, smtp_port, smtp_ssl, imap_host_encrypted, imap_port, imap_ssl, user_id, tenant_id") - .eq("id", id) - .eq("tenant_id", req.user.tenant_id) - .eq("type", "mail") - .single(); - - if (error || !data) { - return reply.code(404).send({ error: "Not found" }); - } else { - Object.keys(data).forEach((key) => { - if(key.includes("encrypted")){ - returnData[key.substring(0,key.length-10)] = decrypt(data[key]) - } else { - returnData[key] = data[key] - } - }) - } - - return returnData; - } else { - - const { data, error } = await server.supabase - .from("user_credentials") - .select("id, email_encrypted, user_id, tenant_id") - .eq("tenant_id", req.user.tenant_id) - .eq("type", "mail") - - let accounts = [] - data.forEach(item => { - let temp = {} - Object.keys(item).forEach((key) => { - if(key.includes("encrypted")){ - temp[key.substring(0,key.length-10)] = decrypt(item[key]) - } else { - temp[key] = item[key] - } - }) - accounts.push(temp) - }) - - - - return accounts - } - }); - - server.post("/email/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 { + if (!req.user?.tenant_id) { + return reply.code(400).send({ error: "No tenant selected" }) + } - let accountData = {} as { + const { id } = req.params as { id?: string } + + const body = req.body as { email: string password: string smtp_host: string @@ -173,32 +35,173 @@ export default async function emailAsUserRoutes(server: FastifyInstance) { imap_port: number imap_ssl: boolean } - // @ts-ignore - const { data, error } = await server.supabase - .from("user_credentials") - .select("id, email_encrypted,password_encrypted, smtp_host_encrypted, smtp_port, smtp_ssl,imap_host_encrypted,imap_port, imap_ssl, user_id, tenant_id") - .eq("id", body.account) - .eq("tenant_id", req.user.tenant_id) - .eq("type", "mail") - .single(); - if (error || !data) { - return reply.code(404).send({ error: "Not found" }); - } else { - Object.keys(data).forEach((key) => { - if(key.includes("encrypted")){ - accountData[key.substring(0,key.length-10)] = decrypt(data[key]) - } else { - accountData[key] = data[key] - } - }) + // ----------------------------- + // UPDATE EXISTING + // ----------------------------- + if (id) { + const saveData = { + emailEncrypted: body.email ? encrypt(body.email) : undefined, + passwordEncrypted: body.password ? encrypt(body.password) : undefined, + smtpHostEncrypted: body.smtp_host ? encrypt(body.smtp_host) : undefined, + smtpPort: body.smtp_port, + smtpSsl: body.smtp_ssl, + imapHostEncrypted: body.imap_host ? encrypt(body.imap_host) : undefined, + imapPort: body.imap_port, + imapSsl: body.imap_ssl, + } + + await server.db + .update(userCredentials) + .set(saveData) + .where(eq(userCredentials.id, id)) + + return reply.send({ success: true }) } + // ----------------------------- + // CREATE NEW + // ----------------------------- + const insertData = { + userId: req.user.user_id, + tenantId: req.user.tenant_id, + type: "mail", + emailEncrypted: encrypt(body.email), + passwordEncrypted: encrypt(body.password), + + smtpHostEncrypted: encrypt(body.smtp_host), + smtpPort: body.smtp_port, + smtpSsl: body.smtp_ssl, + + imapHostEncrypted: encrypt(body.imap_host), + imapPort: body.imap_port, + imapSsl: body.imap_ssl, + } + + await server.db.insert(userCredentials).values(insertData) + + return reply.send({ success: true }) + } catch (err) { + console.error("POST /email/accounts error:", err) + return reply.code(500).send({ error: "Internal Server Error" }) + } + }) + + + + // ====================================================================== + // GET SINGLE OR ALL ACCOUNTS + // ====================================================================== + server.get("/email/accounts/:id?", async (req, reply) => { + try { + if (!req.user?.tenant_id) { + return reply.code(400).send({ error: "No tenant selected" }) + } + + const { id } = req.params as { id?: string } + + // ============================================================ + // LOAD SINGLE ACCOUNT + // ============================================================ + if (id) { + const rows = await server.db + .select() + .from(userCredentials) + .where(eq(userCredentials.id, id)) + + const row = rows[0] + if (!row) return reply.code(404).send({ error: "Not found" }) + + const returnData: any = {} + + Object.entries(row).forEach(([key, val]) => { + if (key.endsWith("Encrypted")) { + const cleanKey = key.replace("Encrypted", "") + // @ts-ignore + returnData[cleanKey] = decrypt(val as string) + } else { + returnData[key] = val + } + }) + + return reply.send(returnData) + } + + // ============================================================ + // LOAD ALL ACCOUNTS FOR TENANT + // ============================================================ + const rows = await server.db + .select() + .from(userCredentials) + .where(eq(userCredentials.tenantId, req.user.tenant_id)) + + const accounts = rows.map(row => { + const temp: any = {} + Object.entries(row).forEach(([key, val]) => { + if (key.endsWith("Encrypted")) { + // @ts-ignore + temp[key.replace("Encrypted", "")] = decrypt(val as string) + } else { + temp[key] = val + } + }) + return temp + }) + + return reply.send(accounts) + + } catch (err) { + console.error("GET /email/accounts error:", err) + return reply.code(500).send({ error: "Internal Server Error" }) + } + }) + + + + // ====================================================================== + // SEND EMAIL + SAVE IN IMAP SENT FOLDER + // ====================================================================== + server.post("/email/send", async (req, reply) => { + try { + const body = req.body as { + to: string + cc?: string + bcc?: string + subject?: string + text?: string + html?: string + attachments?: any + account: string + } + + // Fetch email credentials + const rows = await server.db + .select() + .from(userCredentials) + .where(eq(userCredentials.id, body.account)) + + const row = rows[0] + if (!row) return reply.code(404).send({ error: "Account not found" }) + + const accountData: any = {} + + Object.entries(row).forEach(([key, val]) => { + if (key.endsWith("Encrypted")) { + // @ts-ignore + accountData[key.replace("Encrypted", "")] = decrypt(val as string) + } else { + accountData[key] = val + } + }) + + // ------------------------- + // SEND EMAIL VIA SMTP + // ------------------------- const transporter = nodemailer.createTransport({ - host: accountData.smtp_host, - port: accountData.smtp_port, - secure: accountData.smtp_ssl, + host: accountData.smtpHost, + port: accountData.smtpPort, + secure: accountData.smtpSsl, auth: { user: accountData.email, pass: accountData.password, @@ -208,62 +211,48 @@ export default async function emailAsUserRoutes(server: FastifyInstance) { const message = { from: accountData.email, to: body.to, - cc: body.cc ? body.cc : undefined, - bcc: body.bcc ? body.bcc : undefined, + cc: body.cc, + bcc: body.bcc, subject: body.subject, - html: body.html ? body.html : undefined, + html: body.html, text: body.text, - attachments: body.attachments ? body.attachments : undefined, + attachments: body.attachments, } - const info = await transporter.sendMail(message) - const imapClient = new ImapFlow({ - host: accountData.imap_host, - port: accountData.imap_port, - secure: accountData.imap_ssl, + // ------------------------- + // SAVE TO IMAP SENT FOLDER + // ------------------------- + const imap = new ImapFlow({ + host: accountData.imapHost, + port: accountData.imapPort, + secure: accountData.imapSsl, auth: { user: accountData.email, pass: accountData.password, }, - logger: false }) - await imapClient.connect() + await imap.connect() const mail = new MailComposer(message) + const raw = await mail.compile().build() - const raw = await mail.compile().build() // → Buffer mit kompletter MIME - - - for await (const mailbox of await imapClient.list()) { - // mailbox.flags enthält z. B. ['\\Sent', '\\HasChildren'] - console.log(mailbox.specialUse) - if (mailbox.specialUse == '\\Sent') { - console.log('📨 Sent folder gefunden:', mailbox.path) - await imapClient.mailboxOpen(mailbox.path) - - await imapClient.append(mailbox.path, raw, ['\\Seen']) - - await imapClient.logout() - - break + for await (const mailbox of await imap.list()) { + if (mailbox.specialUse === "\\Sent") { + await imap.mailboxOpen(mailbox.path) + await imap.append(mailbox.path, raw, ["\\Seen"]) + await imap.logout() } } - if(info.response.includes("OK")){ - reply.send({success: true}) - }{ - reply.status(500) - } - + return reply.send({ success: true }) } catch (err) { - console.log(err) - reply.code(500).send({ error: "Failed to send E-Mail as User" }) + console.error("POST /email/send error:", err) + return reply.code(500).send({ error: "Failed to send email" }) } }) - -} \ No newline at end of file +} diff --git a/src/routes/files.ts b/src/routes/files.ts index 886fd2d..d3abce1 100644 --- a/src/routes/files.ts +++ b/src/routes/files.ts @@ -1,184 +1,200 @@ import { FastifyInstance } from "fastify" import multipart from "@fastify/multipart" import { s3 } from "../utils/s3" -import {GetObjectCommand, PutObjectCommand} from "@aws-sdk/client-s3" -import {getSignedUrl} from "@aws-sdk/s3-request-presigner"; +import { + GetObjectCommand, + PutObjectCommand +} from "@aws-sdk/client-s3" +import { getSignedUrl } from "@aws-sdk/s3-request-presigner" import archiver from "archiver" -import {secrets} from "../utils/secrets" +import { secrets } from "../utils/secrets" + +import { eq, inArray } from "drizzle-orm" +import { + files, + createddocuments, + customers +} from "../../db/schema" + export default async function fileRoutes(server: FastifyInstance) { - await server.register(multipart,{ - limits: { - fileSize: 20 * 1024 * 1024, // 20 MB - } + + // ------------------------------------------------------------- + // MULTIPART INIT + // ------------------------------------------------------------- + await server.register(multipart, { + limits: { fileSize: 20 * 1024 * 1024 } // 20 MB }) + + // ------------------------------------------------------------- + // UPLOAD FILE + // ------------------------------------------------------------- server.post("/files/upload", async (req, reply) => { - const tenantId = req.user?.tenant_id - if (!tenantId) return reply.code(401).send({ error: "Unauthorized" }) + try { + const tenantId = req.user?.tenant_id + if (!tenantId) return reply.code(401).send({ error: "Unauthorized" }) - const data:any = await req.file() - const fileBuffer = await data.toBuffer() + const data: any = await req.file() + if (!data?.file) return reply.code(400).send({ error: "No file uploaded" }) + const fileBuffer = await data.toBuffer() - console.log(data) + const meta = data.fields?.meta?.value ? JSON.parse(data.fields.meta.value) : {} + // 1️⃣ DB-Eintrag erzeugen + const inserted = await server.db + .insert(files) + .values({ tenant: tenantId }) + .returning() - let meta = JSON.parse(data.fields?.meta?.value) + const created = inserted[0] + if (!created) throw new Error("Could not create DB entry") - if (!data.file) return reply.code(400).send({ error: "No file uploaded" }) - - - const {data:createdFileData,error:createdFileError} = await server.supabase - .from("files") - .insert({ - tenant: tenantId, - }) - .select() - .single() - - if(createdFileError) { - console.log(createdFileError) - return reply.code(500).send({ error: "Internal Server Error" }) - } else if(createdFileData && data.file) { - const fileKey = `${tenantId}/filesbyid/${createdFileData.id}/${data.filename}` + // 2️⃣ Datei in S3 speichern + const fileKey = `${tenantId}/filesbyid/${created.id}/${data.filename}` await s3.send(new PutObjectCommand({ Bucket: secrets.S3_BUCKET, Key: fileKey, Body: fileBuffer, - ContentType: data.mimetype, + ContentType: data.mimetype })) - //Update File with Corresponding Path - const {data:updateFileData, error:updateFileError} = await server.supabase - .from("files") - .update({ + // 3️⃣ DB updaten: meta + path + await server.db + .update(files) + .set({ ...meta, - path: fileKey, + path: fileKey }) - .eq("id", createdFileData.id) - - if(updateFileError) { - console.log(updateFileError) - return reply.code(500).send({ error: "Internal Server Error" }) - - } else { - /*const {data:tagData, error:tagError} = await server.supabase - .from("filetagmembers") - .insert(tags.map(tag => { - return { - file_id: createdFileData.id, - tag_id: tag - } - }))*/ - - return { id: createdFileData.id, filename: data.filename, path: fileKey } + .where(eq(files.id, created.id)) + return { + id: created.id, + filename: data.filename, + path: fileKey } - + } catch (err) { + console.error(err) + return reply.code(500).send({ error: "Upload failed" }) } }) + + + // ------------------------------------------------------------- + // GET FILE OR LIST FILES + // ------------------------------------------------------------- server.get("/files/:id?", async (req, reply) => { - const { id } = req.params as { id?: string } + try { + const { id } = req.params as { id?: string } - if(id) { - try { - const {data,error} = await server.supabase.from("files").select("*").eq("id", id).single() + // 🔹 EINZELNE DATEI + if (id) { + const rows = await server.db + .select() + .from(files) + .where(eq(files.id, id)) - return {...data} - } catch (err) { - req.log.error(err); - reply.code(500).send({ error: "Could not generate presigned URL" }); + const file = rows[0] + if (!file) return reply.code(404).send({ error: "Not found" }) + + return file } - } else { - try { - const {data:supabaseFileEntries,error} = await server.supabase.from("files").select("*, createddocument(*, customer(*))").eq("tenant",req.user.tenant_id) + // 🔹 ALLE DATEIEN DES TENANTS (mit createddocument + customer) + const tenantId = req.user?.tenant_id + if (!tenantId) return reply.code(401).send({ error: "Unauthorized" }) + const list = await server.db + .select({ + ...files, + createddocument: createddocuments, + customer: customers + }) + .from(files) + .leftJoin( + createddocuments, + eq(files.createddocument, createddocuments.id) + ) + .leftJoin( + customers, + eq(createddocuments.customer, customers.id) + ) + .where(eq(files.tenant, tenantId)) - return { files: supabaseFileEntries } - } catch (err) { - req.log.error(err) - reply.code(500).send({ error: "Could not generate presigned URLs" }) - } + return { files: list } + } catch (err) { + console.error(err) + return reply.code(500).send({ error: "Could not load files" }) } - }) + + + // ------------------------------------------------------------- + // DOWNLOAD (SINGLE OR MULTI ZIP) + // ------------------------------------------------------------- server.post("/files/download/:id?", async (req, reply) => { - const { id } = req.params as { id?: string } - - // @ts-ignore - const ids = req.body?.ids || [] - - try { - if (id) { - // 🔹 Einzeldownload - const { data, error } = await server.supabase - .from("files") - .select("*") - .eq("id", id) - .single() + const { id } = req.params as { id?: string } + const ids = req.body?.ids || [] - if (error || !data) { - return reply.code(404).send({ error: "File not found" }) - } + // ------------------------------------------------- + // 1️⃣ SINGLE DOWNLOAD + // ------------------------------------------------- + if (id) { + const rows = await server.db + .select() + .from(files) + .where(eq(files.id, id)) + + const file = rows[0] + if (!file) return reply.code(404).send({ error: "File not found" }) const command = new GetObjectCommand({ Bucket: secrets.S3_BUCKET, - Key: data.path, + Key: file.path! }) const { Body, ContentType } = await s3.send(command) const chunks: any[] = [] - // @ts-ignore - for await (const chunk of Body) { - chunks.push(chunk) - } + for await (const chunk of Body as any) chunks.push(chunk) const buffer = Buffer.concat(chunks) reply.header("Content-Type", ContentType || "application/octet-stream") - reply.header( - "Content-Disposition", - `attachment; filename="${data.path.split("/").pop()}"` - ) + reply.header("Content-Disposition", `attachment; filename="${file.path?.split("/").pop()}"`) return reply.send(buffer) } - console.log(ids) + // ------------------------------------------------- + // 2️⃣ MULTI DOWNLOAD → ZIP + // ------------------------------------------------- if (Array.isArray(ids) && ids.length > 0) { - // 🔹 Multi-Download → ZIP zurückgeben - const { data: supabaseFiles, error } = await server.supabase - .from("files") - .select("*") - .in("id", ids) + const rows = await server.db + .select() + .from(files) + .where(inArray(files.id, ids)) - if (error || !supabaseFiles?.length) { - return reply.code(404).send({ error: "Files not found" }) - } - - console.log(supabaseFiles) + if (!rows.length) return reply.code(404).send({ error: "Files not found" }) reply.header("Content-Type", "application/zip") - reply.header("Content-Disposition", "attachment; filename=dateien.zip") + reply.header("Content-Disposition", `attachment; filename="dateien.zip"`) const archive = archiver("zip", { zlib: { level: 9 } }) - archive.on("warning", console.warn) - for (const entry of supabaseFiles) { - const command = new GetObjectCommand({ + for (const entry of rows) { + const cmd = new GetObjectCommand({ Bucket: secrets.S3_BUCKET, - Key: entry.path, + Key: entry.path! }) + const { Body } = await s3.send(cmd) - const { Body } = await s3.send(command) - const filename = entry.path.split("/").pop() || entry.id - console.log(filename) - archive.append(Body as any, { name: filename }) + archive.append(Body as any, { + name: entry.path?.split("/").pop() || entry.id + }) } await archive.finalize() @@ -186,80 +202,80 @@ export default async function fileRoutes(server: FastifyInstance) { } return reply.code(400).send({ error: "No id or ids provided" }) + } catch (err) { - console.log(err) - reply.code(500).send({ error: "Download failed" }) + console.error(err) + return reply.code(500).send({ error: "Download failed" }) } }) + + + // ------------------------------------------------------------- + // GENERATE PRESIGNED URL(S) + // ------------------------------------------------------------- server.post("/files/presigned/:id?", async (req, reply) => { - const { id } = req.params as { id: string }; - const { ids } = req.body as { ids: string[] } + try { + const { id } = req.params as { id?: string } + const { ids } = req.body as { ids?: string[] } + const tenantId = req.user?.tenant_id - if(id) { - try { - const {data,error} = await server.supabase.from("files").select("*").eq("id", id).single() + if (!tenantId) return reply.code(401).send({ error: "Unauthorized" }) - const command = new GetObjectCommand({ - Bucket: secrets.S3_BUCKET, - Key: data.path, - }); + // ------------------------------------------------- + // SINGLE FILE PRESIGNED URL + // ------------------------------------------------- + if (id) { + const rows = await server.db + .select() + .from(files) + .where(eq(files.id, id)) - // URL für 15 Minuten gültig - const url = await getSignedUrl(s3, command, { expiresIn: 900 }); + const file = rows[0] + if (!file) return reply.code(404).send({ error: "Not found" }) - return { ...data, url }; - } catch (err) { - req.log.error(err); - reply.code(500).send({ error: "Could not generate presigned URL" }); - } - } else { - if (!Array.isArray(ids) || ids.length === 0) { - return reply.code(400).send({ error: "No file keys provided" }) - } + const url = await getSignedUrl( + s3, + new GetObjectCommand({ Bucket: secrets.S3_BUCKET, Key: file.path! }), + { expiresIn: 900 } + ) - try { - const {data:supabaseFileEntries,error} = await server.supabase.from("files").select("*, createddocument(*, customer(*))").eq("tenant",req.user.tenant_id).is("archived",false) + return { ...file, url } + } else { + // ------------------------------------------------- + // MULTIPLE PRESIGNED URLs + // ------------------------------------------------- + if (!ids || !Array.isArray(ids) || ids.length === 0) { + return reply.code(400).send({ error: "No ids provided" }) + } - console.log(error) + const rows = await server.db + .select() + .from(files) + .where(eq(files.tenant, tenantId)) - let filteredFiles = supabaseFileEntries.filter(i => ids.includes(i.id)) - filteredFiles = filteredFiles.filter(i => i.path) + const selected = rows.filter(f => ids.includes(f.id)) - console.log(filteredFiles.filter(i => !i.path)) - - - - let urls = await Promise.all( - ids.map(async (id) => { - let file = filteredFiles.find(i => i.id === id) - - if(!file) return - - let key = file.path - if(!key) console.log(file) - - const command = new GetObjectCommand({ - Bucket: secrets.S3_BUCKET, - Key: key, - }) - - const url = await getSignedUrl(s3, command, { expiresIn: 900 }) // 15 min gültig - - - return {...filteredFiles.find(i => i.id === id), url} + const output = await Promise.all( + selected.map(async (file) => { + const url = await getSignedUrl( + s3, + new GetObjectCommand({ Bucket: secrets.S3_BUCKET, Key: file.path! }), + { expiresIn: 900 } + ) + return { ...file, url } }) ) - urls = urls.filter(i => i) - - return { files: urls } - } catch (err) { - console.log(err) - reply.code(500).send({ error: "Could not generate presigned URLs" }) + return { files: output } } + + + + } catch (err) { + console.error(err) + return reply.code(500).send({ error: "Could not create presigned URLs" }) } - - }) -} \ No newline at end of file + +} diff --git a/src/routes/functions.ts b/src/routes/functions.ts index 96cc111..651aba4 100644 --- a/src/routes/functions.ts +++ b/src/routes/functions.ts @@ -13,6 +13,8 @@ import isSameOrBefore from "dayjs/plugin/isSameOrBefore.js" import duration from "dayjs/plugin/duration.js"; import timezone from "dayjs/plugin/timezone.js"; import {generateTimesEvaluation} from "../modules/time/evaluation.service"; +import {citys} from "../../db/schema"; +import {eq} from "drizzle-orm"; dayjs.extend(customParseFormat) dayjs.extend(isoWeek) dayjs.extend(isBetween) @@ -102,7 +104,11 @@ export default async function functionRoutes(server: FastifyInstance) { } try { - const { data, error } = await server.supabase + //@ts-ignore + 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) @@ -111,7 +117,7 @@ export default async function functionRoutes(server: FastifyInstance) { if (error) { console.log(error) return reply.code(500).send({ error: 'Database error' }) - } + }*/ if (!data) { return reply.code(404).send({ error: 'ZIP not found' }) diff --git a/src/routes/profiles.ts b/src/routes/profiles.ts index 17e9158..833f748 100644 --- a/src/routes/profiles.ts +++ b/src/routes/profiles.ts @@ -1,54 +1,120 @@ import { FastifyInstance } from "fastify"; +import { eq, and } from "drizzle-orm"; + +import { + authProfiles, +} from "../../db/schema"; export default async function authProfilesRoutes(server: FastifyInstance) { - // Ein einzelnes Profil laden (nur im aktuellen Tenant) + + // ------------------------------------------------------------- + // GET SINGLE PROFILE + // ------------------------------------------------------------- server.get("/profiles/:id", async (req, reply) => { - const { id } = req.params as {id:string}; - const tenantId = (req.user as any)?.tenant_id; + try { + const { id } = req.params as { id: string }; + const tenantId = (req.user as any)?.tenant_id; - if (!tenantId) { - return reply.code(400).send({ error: "No tenant selected" }); + if (!tenantId) { + return reply.code(400).send({ error: "No tenant selected" }); + } + + const rows = await server.db + .select() + .from(authProfiles) + .where( + and( + eq(authProfiles.id, id), + eq(authProfiles.tenant_id, tenantId) + ) + ) + .limit(1); + + if (!rows.length) { + return reply.code(404).send({ error: "User not found or not in tenant" }); + } + + return rows[0]; + + } catch (error) { + console.error("GET /profiles/:id ERROR:", error); + return reply.code(500).send({ error: "Internal Server Error" }); } - - const { data, error } = await server.supabase - .from("auth_profiles") - .select() - .eq("id", id) - .eq("tenant_id", tenantId) - .single(); - - if (error || !data) { - console.log(error) - return reply.code(404).send({ error: "User not found or not in tenant" }); - } - - console.log(data); - - reply.send(data) }); - server.put("/profiles/:id", async (req, reply) => { - if (!req.user.tenant_id) { - return reply.code(400).send({ error: "No tenant selected" }); + function sanitizeProfileUpdate(body: any) { + const cleaned: any = { ...body } + + // ❌ Systemfelder entfernen + const forbidden = [ + "id", "user_id", "tenant_id", "created_at", "updated_at", + "updatedAt", "updatedBy", "old_profile_id", "full_name" + ] + forbidden.forEach(f => delete cleaned[f]) + + // ❌ Falls NULL Strings vorkommen → in null umwandeln + for (const key of Object.keys(cleaned)) { + if (cleaned[key] === "") cleaned[key] = null } - const { id } = req.params as { id: string }; - const body = req.body as any + // ✅ Date-Felder sauber konvertieren, falls vorhanden + const dateFields = ["birthday", "entry_date"] - delete body.full_name + for (const field of dateFields) { + if (cleaned[field]) { + const d = new Date(cleaned[field]) + if (!isNaN(d.getTime())) cleaned[field] = d + else delete cleaned[field] // invalid → entfernen + } + } + return cleaned + } - const { data, error } = await server.supabase - .from("auth_profiles") - .update(body) - .eq("id", id) - .eq("tenant_id", req.user.tenant_id) - .select("*") - .single(); + // ------------------------------------------------------------- + // UPDATE PROFILE + // ------------------------------------------------------------- + server.put("/profiles/:id", async (req, reply) => { + try { + const tenantId = req.user?.tenant_id + const userId = req.user?.user_id - if (error || !data) { - console.log(error) - return reply.code(404).send({ error: "User not found or not in tenant" }); + if (!tenantId) { + return reply.code(400).send({ error: "No tenant selected" }) + } + + const { id } = req.params as { id: string } + let body = req.body as any + + // Clean + Normalize + body = sanitizeProfileUpdate(body) + + const updateData = { + ...body, + updatedAt: new Date(), + updatedBy: userId + } + + const updated = await server.db + .update(authProfiles) + .set(updateData) + .where( + and( + eq(authProfiles.id, id), + eq(authProfiles.tenant_id, tenantId) + ) + ) + .returning() + + if (!updated.length) { + return reply.code(404).send({ error: "User not found or not in tenant" }) + } + + return updated[0] + + } catch (err) { + console.error("PUT /profiles/:id ERROR:", err) + return reply.code(500).send({ error: "Internal Server Error" }) } }) -} \ No newline at end of file +} diff --git a/src/routes/resources/main.ts b/src/routes/resources/main.ts index 0121e42..4a77cba 100644 --- a/src/routes/resources/main.ts +++ b/src/routes/resources/main.ts @@ -11,18 +11,8 @@ import { } from "drizzle-orm" -import { - projects, - customers, - plants, - contracts, - projecttypes, - createddocuments, - files, - events, - tasks, contacts, vendors -} from "../../../db/schema" -import * as sea from "node:sea"; + +import {resourceConfig} from "../../resource.config"; // ------------------------------------------------------------- // SQL Volltextsuche auf mehreren Feldern @@ -50,7 +40,7 @@ export default async function resourceRoutes(server: FastifyInstance) { // ------------------------------------------------------------- // LIST // ------------------------------------------------------------- - /*server.get("/resource/:resource", async (req, reply) => { + server.get("/resource/:resource", async (req, reply) => { try { const tenantId = req.user?.tenant_id if (!tenantId) @@ -62,34 +52,85 @@ export default async function resourceRoutes(server: FastifyInstance) { asc?: string } + const {resource} = req.params as {resource: string} + const table = resourceConfig[resource].table + // WHERE-Basis - let whereCond: any = eq(projects.tenant, tenantId) + let whereCond: any = eq(table.tenant, tenantId) // 🔍 SQL Search - const searchCond = buildProjectSearch(search) - if (searchCond) whereCond = and(whereCond, searchCond) + if(search) { + const searchCond = buildSearchCondition( + table, + resourceConfig[resource].searchColumns, + search.trim() + ) + + if (searchCond) { + whereCond = and(whereCond, searchCond) + } + } // Base Query - let q = server.db.select().from(projects).where(whereCond) + let q = server.db.select().from(table).where(whereCond) // Sortierung if (sort) { - const col = (projects as any)[sort] + const col = (table as any)[sort] if (col) { + //@ts-ignore q = ascQuery === "true" ? q.orderBy(asc(col)) : q.orderBy(desc(col)) } } - const data = await q + const queryData = await q + + + // RELATION LOADING (MANY-TO-ONE) + + let ids = {} + let lists = {} + let maps = {} + let data = [] + + if(resourceConfig[resource].mtoLoad) { + resourceConfig[resource].mtoLoad.forEach(relation => { + ids[relation] = [...new Set(queryData.map(r => r[relation]).filter(Boolean))]; + }) + + for await (const relation of resourceConfig[resource].mtoLoad ) { + lists[relation] = ids[relation].length ? await server.db.select().from(resourceConfig[relation + "s"].table).where(inArray(resourceConfig[relation + "s"].table.id, ids[relation])) : [] + + } + + resourceConfig[resource].mtoLoad.forEach(relation => { + maps[relation] = Object.fromEntries(lists[relation].map(i => [i.id, i])); + }) + + data = queryData.map(row => { + let toReturn = { + ...row + } + + resourceConfig[resource].mtoLoad.forEach(relation => { + toReturn[relation] = row[relation] ? maps[relation][row[relation]] : null + }) + + return toReturn + }); + } else { + data = queryData + } + return data } catch (err) { - console.error("ERROR /resource/projects", err) + console.error("ERROR /resource/:resource", err) return reply.code(500).send({ error: "Internal Server Error" }) } - })*/ + }) // ------------------------------------------------------------- @@ -118,41 +159,10 @@ export default async function resourceRoutes(server: FastifyInstance) { }; - const config = { - projects: { - searchColumns: ["name"], - mtoLoad: ["customer","plant","contract","projecttype"], - table: projects - }, - customers: { - searchColumns: ["name", "customerNumber", "firstname", "lastname", "notes"], - table: customers, - }, - contacts: { - searchColumns: ["firstName", "lastName", "email", "phone", "notes"], - table: contacts, - mtoLoad: ["customer","vendor"] - }, - contracts: { - table: contracts, - searchColumns: ["name", "notes", "contractNumber", "paymentType", "sepaRef", "bankingName"] - }, - plants: { - table: plants - }, - projecttypes: { - table: projecttypes - }, - vendors: { - table: vendors, - searchColumns: ["name","vendorNumber","notes","defaultPaymentType"], - }, - files: { - table: files - } - } - let table = config[resource].table + + let table = resourceConfig[resource].table + let whereCond: any = eq(table.tenant, tenantId); @@ -160,7 +170,7 @@ export default async function resourceRoutes(server: FastifyInstance) { if(search) { const searchCond = buildSearchCondition( table, - config[resource].searchColumns, + resourceConfig[resource].searchColumns, search.trim() ) @@ -225,7 +235,7 @@ export default async function resourceRoutes(server: FastifyInstance) { if (sort?.length > 0) { const s = sort[0]; - const col = (projects as any)[s.field]; + const col = (table as any)[s.field]; if (col) { orderField = col; direction = s.direction === "asc" ? "asc" : "desc"; @@ -268,17 +278,17 @@ export default async function resourceRoutes(server: FastifyInstance) { let maps = {} let data = [] - if(config[resource].mtoLoad) { - config[resource].mtoLoad.forEach(relation => { + if(resourceConfig[resource].mtoLoad) { + resourceConfig[resource].mtoLoad.forEach(relation => { ids[relation] = [...new Set(rows.map(r => r[relation]).filter(Boolean))]; }) - for await (const relation of config[resource].mtoLoad ) { - lists[relation] = ids[relation].length ? await server.db.select().from(config[relation + "s"].table).where(inArray(config[relation + "s"].table.id, ids[relation])) : [] + for await (const relation of resourceConfig[resource].mtoLoad ) { + lists[relation] = ids[relation].length ? await server.db.select().from(resourceConfig[relation + "s"].table).where(inArray(resourceConfig[relation + "s"].table.id, ids[relation])) : [] } - config[resource].mtoLoad.forEach(relation => { + resourceConfig[resource].mtoLoad.forEach(relation => { maps[relation] = Object.fromEntries(lists[relation].map(i => [i.id, i])); }) @@ -287,12 +297,14 @@ export default async function resourceRoutes(server: FastifyInstance) { ...row } - config[resource].mtoLoad.forEach(relation => { + resourceConfig[resource].mtoLoad.forEach(relation => { toReturn[relation] = row[relation] ? maps[relation][row[relation]] : null }) return toReturn }); + } else { + data = rows } // ----------------------------------------------- @@ -318,96 +330,55 @@ export default async function resourceRoutes(server: FastifyInstance) { // ------------------------------------------------------------- // DETAIL (mit JOINS) // ------------------------------------------------------------- - /*server.get("/resource/projects/:id", async (req, reply) => { + server.get("/resource/:resource/:id", async (req, reply) => { try { const { id } = req.params as { id: string } const tenantId = req.user?.tenant_id if (!tenantId) return reply.code(400).send({ error: "No tenant selected" }) - const pid = Number(id) + const {resource} = req.params as { resource: string } + const table = resourceConfig[resource].table const projRows = await server.db .select() - .from(projects) - .where(and(eq(projects.id, pid), eq(projects.tenant, tenantId))) + .from(table) + .where(and(eq(table.id, id), eq(table.tenant, tenantId))) .limit(1) if (!projRows.length) - return reply.code(404).send({ error: "Project not found" }) - - const project = projRows[0] + return reply.code(404).send({ error: "Resource not found" }) // ------------------------------------ // LOAD RELATIONS // ------------------------------------ - const [ - customerRecord, - plantRecord, - contractRecord, - projectTypeRecord, - projectTasks, - projectFiles, - projectDocuments, - projectEvents, - ] = await Promise.all([ - project.customer - ? server.db.select().from(customers).where(eq(customers.id, project.customer)) - : [], - - project.plant - ? server.db.select().from(plants).where(eq(plants.id, project.plant)) - : [], - - project.contract - ? server.db.select().from(contracts).where(eq(contracts.id, project.contract)) - : [], - - project.projecttype - ? server.db.select().from(projecttypes).where(eq(projecttypes.id, project.projecttype)) - : [], - - // Tasks - server.db - .select() - .from(tasks) - .where(eq(tasks.project, pid)), - - // Files - server.db - .select() - .from(files) - .where(eq(files.project, pid)), - - // Documents - server.db - .select() - .from(createddocuments) - .where(eq(createddocuments.project, pid)), - - // Events - server.db - .select() - .from(events) - .where(eq(events.project, pid)), - - ]) - - return { - ...project, - customer: customerRecord[0] ?? null, - plant: plantRecord[0] ?? null, - contract: contractRecord[0] ?? null, - projecttype: projectTypeRecord[0] ?? null, - tasks: projectTasks, - files: projectFiles, - createddocuments: projectDocuments, - events: projectEvents, + let ids = {} + let lists = {} + let maps = {} + let data = { + ...projRows[0] } + if(resourceConfig[resource].mtoLoad) { + for await (const relation of resourceConfig[resource].mtoLoad ) { + if(data[relation]) { + data[relation] = await server.db.select().from(resourceConfig[relation + "s"].table).where(eq(resourceConfig[relation + "s"].table.id, data[relation])) + } + } + + for await (const relation of resourceConfig[resource].mtmLoad ) { + console.log(relation) + data[relation] = await server.db.select().from(resourceConfig[relation].table).where(eq(resourceConfig[relation].table[resource.substring(0,resource.length - 1)],id)) + } + + + } + + return data + } catch (err) { console.error("ERROR /resource/projects/:id", err) return reply.code(500).send({ error: "Internal Server Error" }) } - })*/ + }) } diff --git a/src/routes/resources/productsServices.ts b/src/routes/resources/productsServices.ts deleted file mode 100644 index dd25bb5..0000000 --- a/src/routes/resources/productsServices.ts +++ /dev/null @@ -1,858 +0,0 @@ -import { FastifyInstance } from "fastify" -import { - eq, - ilike, - asc, - desc, - and, - count, - inArray, -} from "drizzle-orm" - -import { - products, - productcategories, - services, - servicecategories, -} from "../../../db/schema" - -// ----------------------------------------------------------------------------- -// PRODUCTS -// ----------------------------------------------------------------------------- -export default async function productsAndServicesRoutes(server: FastifyInstance) { - // ------------------------------------------------------------- - // LIST: /resource/products - // ------------------------------------------------------------- - server.get("/resource/products", async (req, reply) => { - const tenantId = req.user?.tenant_id - if (!tenantId) return reply.code(400).send({ error: "No tenant selected" }) - - const { search, sort, asc: ascQuery } = req.query as { - search?: string - sort?: string - asc?: string - } - - let baseQuery = server.db - .select() - .from(products) - .where(eq(products.tenant, tenantId)) - - if (search) { - baseQuery = server.db - .select() - .from(products) - .where( - and( - eq(products.tenant, tenantId), - ilike(products.name, `%${search}%`) - ) - ) - } - - if (sort) { - const field = (products as any)[sort] - if (field) { - // @ts-ignore - baseQuery = baseQuery.orderBy( - ascQuery === "true" ? asc(field) : desc(field) - ) - } - } - - const list = await baseQuery - return list - }) - - // ------------------------------------------------------------- - // PAGINATED: /resource/products/paginated - // ------------------------------------------------------------- - server.get("/resource/products/paginated", async (req, reply) => { - try { - const tenantId = req.user?.tenant_id - if (!tenantId) { - return reply.code(400).send({ error: "No tenant selected" }) - } - - const queryConfig = req.queryConfig - const { - pagination, - sort, - filters, - paginationDisabled, - } = queryConfig - - const { - select, // aktuell ignoriert, wie bei customers - search, - searchColumns, - distinctColumns, - } = req.query as { - select?: string - search?: string - searchColumns?: string - distinctColumns?: string - } - - let whereCond: any = eq(products.tenant, tenantId) - - if (filters) { - for (const [key, val] of Object.entries(filters)) { - const col = (products as any)[key] - if (!col) continue - - if (Array.isArray(val)) { - whereCond = and(whereCond, inArray(col, val)) - } else if (val === true || val === false || val === null) { - whereCond = and(whereCond, eq(col, val as any)) - } else { - whereCond = and(whereCond, eq(col, val as any)) - } - } - } - - if (search && search.trim().length > 0) { - const searchTerm = `%${search.trim().toLowerCase()}%` - whereCond = and( - whereCond, - ilike(products.name, searchTerm) - ) - } - - const totalRes = await server.db - .select({ value: count(products.id) }) - .from(products) - .where(whereCond) - - const total = Number(totalRes[0]?.value ?? 0) - - const distinctValues: Record = {} - - if (distinctColumns) { - for (const colName of distinctColumns.split(",").map(v => v.trim())) { - const col = (products as any)[colName] - if (!col) continue - - const rows = await server.db - .select({ v: col }) - .from(products) - .where(eq(products.tenant, tenantId)) - - const values = rows - .map(r => r.v) - .filter(v => v != null && v !== "") - - distinctValues[colName] = [...new Set(values)].sort() - } - } - - let offset = 0 - let limit = 999999 - - if (!paginationDisabled && pagination) { - offset = pagination.offset - limit = pagination.limit - } - - let orderField: any = null - let orderDirection: "asc" | "desc" = "asc" - - if (sort?.length > 0) { - const s = sort[0] - const col = (products as any)[s.field] - if (col) { - orderField = col - orderDirection = s.direction === "asc" ? "asc" : "desc" - } - } - - let dataQuery = server.db - .select() - .from(products) - .where(whereCond) - .offset(offset) - .limit(limit) - - if (orderField) { - dataQuery = - orderDirection === "asc" - ? dataQuery.orderBy(asc(orderField)) - : dataQuery.orderBy(desc(orderField)) - } - - const data = await dataQuery - - const totalPages = pagination?.limit - ? Math.ceil(total / pagination.limit) - : 1 - - const enrichedConfig = { - ...queryConfig, - total, - totalPages, - distinctValues, - search: search || null, - } - - return { - data, - queryConfig: enrichedConfig, - } - } catch (e) { - server.log.error(e) - return reply.code(500).send({ error: "Internal Server Error" }) - } - }) - - // ------------------------------------------------------------- - // DETAIL: /resource/products/:id - // (aktuell ohne weitere Joins) - // ------------------------------------------------------------- - server.get("/resource/products/:id", async (req, reply) => { - const { id } = req.params as { id: string } - const tenantId = req.user?.tenant_id - - if (!tenantId) return reply.code(400).send({ error: "No tenant selected" }) - - const rows = await server.db - .select() - .from(products) - .where( - and( - eq(products.id, Number(id)), - eq(products.tenant, tenantId) - ) - ) - .limit(1) - - if (!rows.length) { - return reply.code(404).send({ error: "Product not found" }) - } - - return rows[0] - }) - - - // --------------------------------------------------------------------------- - // PRODUCTCATEGORIES - // --------------------------------------------------------------------------- - server.get("/resource/productcategories", async (req, reply) => { - const tenantId = req.user?.tenant_id - if (!tenantId) return reply.code(400).send({ error: "No tenant selected" }) - - const { search, sort, asc: ascQuery } = req.query as { - search?: string - sort?: string - asc?: string - } - - let baseQuery = server.db - .select() - .from(productcategories) - .where(eq(productcategories.tenant, tenantId)) - - if (search) { - const searchTerm = `%${search}%` - baseQuery = server.db - .select() - .from(productcategories) - .where( - and( - eq(productcategories.tenant, tenantId), - ilike(productcategories.name, searchTerm) - ) - ) - } - - if (sort) { - const field = (productcategories as any)[sort] - if (field) { - // @ts-ignore - baseQuery = baseQuery.orderBy( - ascQuery === "true" ? asc(field) : desc(field) - ) - } - } - - const list = await baseQuery - return list - }) - - server.get("/resource/productcategories/paginated", async (req, reply) => { - try { - const tenantId = req.user?.tenant_id - if (!tenantId) { - return reply.code(400).send({ error: "No tenant selected" }) - } - - const queryConfig = req.queryConfig - const { - pagination, - sort, - filters, - paginationDisabled, - } = queryConfig - - const { - select, - search, - searchColumns, - distinctColumns, - } = req.query as { - select?: string - search?: string - searchColumns?: string - distinctColumns?: string - } - - let whereCond: any = eq(productcategories.tenant, tenantId) - - if (filters) { - for (const [key, val] of Object.entries(filters)) { - const col = (productcategories as any)[key] - if (!col) continue - - if (Array.isArray(val)) { - whereCond = and(whereCond, inArray(col, val)) - } else if (val === true || val === false || val === null) { - whereCond = and(whereCond, eq(col, val as any)) - } else { - whereCond = and(whereCond, eq(col, val as any)) - } - } - } - - if (search && search.trim().length > 0) { - const searchTerm = `%${search.trim().toLowerCase()}%` - whereCond = and( - whereCond, - ilike(productcategories.name, searchTerm) - ) - } - - const totalRes = await server.db - .select({ value: count(productcategories.id) }) - .from(productcategories) - .where(whereCond) - - const total = Number(totalRes[0]?.value ?? 0) - - const distinctValues: Record = {} - - if (distinctColumns) { - for (const colName of distinctColumns.split(",").map(v => v.trim())) { - const col = (productcategories as any)[colName] - if (!col) continue - - const rows = await server.db - .select({ v: col }) - .from(productcategories) - .where(eq(productcategories.tenant, tenantId)) - - const values = rows - .map(r => r.v) - .filter(v => v != null && v !== "") - - distinctValues[colName] = [...new Set(values)].sort() - } - } - - let offset = 0 - let limit = 999999 - - if (!paginationDisabled && pagination) { - offset = pagination.offset - limit = pagination.limit - } - - let orderField: any = null - let orderDirection: "asc" | "desc" = "asc" - - if (sort?.length > 0) { - const s = sort[0] - const col = (productcategories as any)[s.field] - if (col) { - orderField = col - orderDirection = s.direction === "asc" ? "asc" : "desc" - } - } - - let dataQuery = server.db - .select() - .from(productcategories) - .where(whereCond) - .offset(offset) - .limit(limit) - - if (orderField) { - dataQuery = - orderDirection === "asc" - ? dataQuery.orderBy(asc(orderField)) - : dataQuery.orderBy(desc(orderField)) - } - - const data = await dataQuery - - const totalPages = pagination?.limit - ? Math.ceil(total / pagination.limit) - : 1 - - const enrichedConfig = { - ...queryConfig, - total, - totalPages, - distinctValues, - search: search || null, - } - - return { - data, - queryConfig: enrichedConfig, - } - } catch (e) { - server.log.error(e) - return reply.code(500).send({ error: "Internal Server Error" }) - } - }) - - server.get("/resource/productcategories/:id", async (req, reply) => { - const { id } = req.params as { id: string } - const tenantId = req.user?.tenant_id - - if (!tenantId) return reply.code(400).send({ error: "No tenant selected" }) - - const rows = await server.db - .select() - .from(productcategories) - .where( - and( - eq(productcategories.id, Number(id)), - eq(productcategories.tenant, tenantId) - ) - ) - .limit(1) - - if (!rows.length) { - return reply.code(404).send({ error: "Product category not found" }) - } - - // Später hier: products mit Join-Tabelle - return rows[0] - }) - - - // --------------------------------------------------------------------------- - // SERVICES - // --------------------------------------------------------------------------- - server.get("/resource/services", async (req, reply) => { - const tenantId = req.user?.tenant_id - if (!tenantId) return reply.code(400).send({ error: "No tenant selected" }) - - const { search, sort, asc: ascQuery } = req.query as { - search?: string - sort?: string - asc?: string - } - - let baseQuery = server.db - .select() - .from(services) - .where(eq(services.tenant, tenantId)) - - if (search) { - const searchTerm = `%${search}%` - baseQuery = server.db - .select() - .from(services) - .where( - and( - eq(services.tenant, tenantId), - ilike(services.name, searchTerm) - ) - ) - } - - if (sort) { - const field = (services as any)[sort] - if (field) { - // @ts-ignore - baseQuery = baseQuery.orderBy( - ascQuery === "true" ? asc(field) : desc(field) - ) - } - } - - const list = await baseQuery - return list - }) - - server.get("/resource/services/paginated", async (req, reply) => { - try { - const tenantId = req.user?.tenant_id - if (!tenantId) { - return reply.code(400).send({ error: "No tenant selected" }) - } - - const queryConfig = req.queryConfig - const { - pagination, - sort, - filters, - paginationDisabled, - } = queryConfig - - const { - select, - search, - searchColumns, - distinctColumns, - } = req.query as { - select?: string - search?: string - searchColumns?: string - distinctColumns?: string - } - - let whereCond: any = eq(services.tenant, tenantId) - - if (filters) { - for (const [key, val] of Object.entries(filters)) { - const col = (services as any)[key] - if (!col) continue - - if (Array.isArray(val)) { - whereCond = and(whereCond, inArray(col, val)) - } else if (val === true || val === false || val === null) { - whereCond = and(whereCond, eq(col, val as any)) - } else { - whereCond = and(whereCond, eq(col, val as any)) - } - } - } - - if (search && search.trim().length > 0) { - const searchTerm = `%${search.trim().toLowerCase()}%` - whereCond = and( - whereCond, - ilike(services.name, searchTerm) - ) - } - - const totalRes = await server.db - .select({ value: count(services.id) }) - .from(services) - .where(whereCond) - - const total = Number(totalRes[0]?.value ?? 0) - - const distinctValues: Record = {} - - if (distinctColumns) { - for (const colName of distinctColumns.split(",").map(v => v.trim())) { - const col = (services as any)[colName] - if (!col) continue - - const rows = await server.db - .select({ v: col }) - .from(services) - .where(eq(services.tenant, tenantId)) - - const values = rows - .map(r => r.v) - .filter(v => v != null && v !== "") - - distinctValues[colName] = [...new Set(values)].sort() - } - } - - let offset = 0 - let limit = 999999 - - if (!paginationDisabled && pagination) { - offset = pagination.offset - limit = pagination.limit - } - - let orderField: any = null - let orderDirection: "asc" | "desc" = "asc" - - if (sort?.length > 0) { - const s = sort[0] - const col = (services as any)[s.field] - if (col) { - orderField = col - orderDirection = s.direction === "asc" ? "asc" : "desc" - } - } - - let dataQuery = server.db - .select() - .from(services) - .where(whereCond) - .offset(offset) - .limit(limit) - - if (orderField) { - dataQuery = - orderDirection === "asc" - ? dataQuery.orderBy(asc(orderField)) - : dataQuery.orderBy(desc(orderField)) - } - - const data = await dataQuery - - const totalPages = pagination?.limit - ? Math.ceil(total / pagination.limit) - : 1 - - const enrichedConfig = { - ...queryConfig, - total, - totalPages, - distinctValues, - search: search || null, - } - - return { - data, - queryConfig: enrichedConfig, - } - } catch (e) { - server.log.error(e) - return reply.code(500).send({ error: "Internal Server Error" }) - } - }) - - server.get("/resource/services/:id", async (req, reply) => { - const { id } = req.params as { id: string } - const tenantId = req.user?.tenant_id - - if (!tenantId) return reply.code(400).send({ error: "No tenant selected" }) - - const rows = await server.db - .select() - .from(services) - .where( - and( - eq(services.id, Number(id)), - eq(services.tenant, tenantId) - ) - ) - .limit(1) - - if (!rows.length) { - return reply.code(404).send({ error: "Service not found" }) - } - - // Später: Unit, Kategorien, etc. als Joins - return rows[0] - }) - - - // --------------------------------------------------------------------------- - // SERVICECATEGORIES - // --------------------------------------------------------------------------- - server.get("/resource/servicecategories", async (req, reply) => { - const tenantId = req.user?.tenant_id - if (!tenantId) return reply.code(400).send({ error: "No tenant selected" }) - - const { search, sort, asc: ascQuery } = req.query as { - search?: string - sort?: string - asc?: string - } - - let baseQuery = server.db - .select() - .from(servicecategories) - .where(eq(servicecategories.tenant, tenantId)) - - if (search) { - const searchTerm = `%${search}%` - baseQuery = server.db - .select() - .from(servicecategories) - .where( - and( - eq(servicecategories.tenant, tenantId), - ilike(servicecategories.name, searchTerm) - ) - ) - } - - if (sort) { - const field = (servicecategories as any)[sort] - if (field) { - // @ts-ignore - baseQuery = baseQuery.orderBy( - ascQuery === "true" ? asc(field) : desc(field) - ) - } - } - - const list = await baseQuery - return list - }) - - server.get("/resource/servicecategories/paginated", async (req, reply) => { - try { - const tenantId = req.user?.tenant_id - if (!tenantId) { - return reply.code(400).send({ error: "No tenant selected" }) - } - - const queryConfig = req.queryConfig - const { - pagination, - sort, - filters, - paginationDisabled, - } = queryConfig - - const { - select, - search, - searchColumns, - distinctColumns, - } = req.query as { - select?: string - search?: string - searchColumns?: string - distinctColumns?: string - } - - let whereCond: any = eq(servicecategories.tenant, tenantId) - - if (filters) { - for (const [key, val] of Object.entries(filters)) { - const col = (servicecategories as any)[key] - if (!col) continue - - if (Array.isArray(val)) { - whereCond = and(whereCond, inArray(col, val)) - } else if (val === true || val === false || val === null) { - whereCond = and(whereCond, eq(col, val as any)) - } else { - whereCond = and(whereCond, eq(col, val as any)) - } - } - } - - if (search && search.trim().length > 0) { - const searchTerm = `%${search.trim().toLowerCase()}%` - whereCond = and( - whereCond, - ilike(servicecategories.name, searchTerm) - ) - } - - const totalRes = await server.db - .select({ value: count(servicecategories.id) }) - .from(servicecategories) - .where(whereCond) - - const total = Number(totalRes[0]?.value ?? 0) - - const distinctValues: Record = {} - - if (distinctColumns) { - for (const colName of distinctColumns.split(",").map(v => v.trim())) { - const col = (servicecategories as any)[colName] - if (!col) continue - - const rows = await server.db - .select({ v: col }) - .from(servicecategories) - .where(eq(servicecategories.tenant, tenantId)) - - const values = rows - .map(r => r.v) - .filter(v => v != null && v !== "") - - distinctValues[colName] = [...new Set(values)].sort() - } - } - - let offset = 0 - let limit = 999999 - - if (!paginationDisabled && pagination) { - offset = pagination.offset - limit = pagination.limit - } - - let orderField: any = null - let orderDirection: "asc" | "desc" = "asc" - - if (sort?.length > 0) { - const s = sort[0] - const col = (servicecategories as any)[s.field] - if (col) { - orderField = col - orderDirection = s.direction === "asc" ? "asc" : "desc" - } - } - - let dataQuery = server.db - .select() - .from(servicecategories) - .where(whereCond) - .offset(offset) - .limit(limit) - - if (orderField) { - dataQuery = - orderDirection === "asc" - ? dataQuery.orderBy(asc(orderField)) - : dataQuery.orderBy(desc(orderField)) - } - - const data = await dataQuery - - const totalPages = pagination?.limit - ? Math.ceil(total / pagination.limit) - : 1 - - const enrichedConfig = { - ...queryConfig, - total, - totalPages, - distinctValues, - search: search || null, - } - - return { - data, - queryConfig: enrichedConfig, - } - } catch (e) { - server.log.error(e) - return reply.code(500).send({ error: "Internal Server Error" }) - } - }) - - server.get("/resource/servicecategories/:id", async (req, reply) => { - const { id } = req.params as { id: string } - const tenantId = req.user?.tenant_id - - if (!tenantId) return reply.code(400).send({ error: "No tenant selected" }) - - const rows = await server.db - .select() - .from(servicecategories) - .where( - and( - eq(servicecategories.id, Number(id)), - eq(servicecategories.tenant, tenantId) - ) - ) - .limit(1) - - if (!rows.length) { - return reply.code(404).send({ error: "Service category not found" }) - } - - // Später: zugehörige Services über Join-Tabelle - return rows[0] - }) -} From 09d67a75227deb57d1e804bd873c09f04603f642 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sun, 7 Dec 2025 22:27:57 +0100 Subject: [PATCH 093/149] redone routes --- src/routes/files.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/routes/files.ts b/src/routes/files.ts index d3abce1..0467766 100644 --- a/src/routes/files.ts +++ b/src/routes/files.ts @@ -254,7 +254,17 @@ export default async function fileRoutes(server: FastifyInstance) { .from(files) .where(eq(files.tenant, tenantId)) - const selected = rows.filter(f => ids.includes(f.id)) + const selected = rows.filter(f => ids.includes(f.id) && f.path) + + console.log(selected) + + const url = await getSignedUrl( + s3, + new GetObjectCommand({ Bucket: secrets.S3_BUCKET, Key: selected[0].path! }), + { expiresIn: 900 } + ) + console.log(url) + console.log(selected.filter(f => !f.path)) const output = await Promise.all( selected.map(async (file) => { From 5968329c8ff7d6f9e3b43de7633a13dfdc5e0953 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sun, 7 Dec 2025 22:34:48 +0100 Subject: [PATCH 094/149] package changes --- package-lock.json | 11531 -------------------------------------------- package.json | 1 - 2 files changed, 11532 deletions(-) delete mode 100644 package-lock.json diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index c6aa764..0000000 --- a/package-lock.json +++ /dev/null @@ -1,11531 +0,0 @@ -{ - "name": "backend", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "backend", - "version": "1.0.0", - "license": "ISC", - "dependencies": { - "@aws-sdk/client-s3": "^3.879.0", - "@aws-sdk/s3-request-presigner": "^3.879.0", - "@fastify/cookie": "^11.0.2", - "@fastify/cors": "^11.1.0", - "@fastify/multipart": "^9.0.3", - "@fastify/swagger": "^9.5.1", - "@fastify/swagger-ui": "^5.2.3", - "@infisical/sdk": "^4.0.6", - "@mmote/niimbluelib": "^0.0.1-alpha.29", - "@prisma/client": "^6.15.0", - "@supabase/supabase-js": "^2.56.1", - "@zip.js/zip.js": "^2.7.73", - "archiver": "^7.0.1", - "axios": "^1.12.1", - "bcrypt": "^6.0.0", - "bwip-js": "^4.8.0", - "canvas": "^3.2.0", - "crypto": "^1.0.1", - "dayjs": "^1.11.18", - "drizzle-orm": "^0.45.0", - "fastify": "^5.5.0", - "fastify-plugin": "^5.0.1", - "imapflow": "^1.1.1", - "jsonwebtoken": "^9.0.2", - "nodemailer": "^7.0.6", - "pdf-lib": "^1.17.1", - "pg": "^8.16.3", - "pngjs": "^7.0.0", - "sharp": "^0.34.5", - "xmlbuilder": "^15.1.1", - "zpl-image": "^0.2.0", - "zpl-renderer-js": "^2.0.2" - }, - "devDependencies": { - "@types/bcrypt": "^6.0.0", - "@types/jsonwebtoken": "^9.0.10", - "@types/node": "^24.3.0", - "drizzle-kit": "^0.31.8", - "prisma": "^6.15.0", - "tsx": "^4.20.5", - "typescript": "^5.9.2" - } - }, - "node_modules/@aws-crypto/crc32": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", - "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-crypto/crc32c": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz", - "integrity": "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/sha1-browser": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-5.2.0.tgz", - "integrity": "sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/supports-web-crypto": "^5.2.0", - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-locate-window": "^3.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/is-array-buffer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", - "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-buffer-from": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", - "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-utf8": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", - "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha256-browser": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", - "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-js": "^5.2.0", - "@aws-crypto/supports-web-crypto": "^5.2.0", - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-locate-window": "^3.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", - "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", - "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", - "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha256-js": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", - "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-crypto/supports-web-crypto": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", - "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/util": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", - "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "^3.222.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", - "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", - "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", - "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity": { - "version": "3.600.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.600.0.tgz", - "integrity": "sha512-8dYsnDLiD0rjujRiZZl0E57heUkHqMSFZHBi0YMs57SM8ODPxK3tahwDYZtS7bqanvFKZwGy+o9jIcij7jBOlA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/client-sso-oidc": "3.600.0", - "@aws-sdk/client-sts": "3.600.0", - "@aws-sdk/core": "3.598.0", - "@aws-sdk/credential-provider-node": "3.600.0", - "@aws-sdk/middleware-host-header": "3.598.0", - "@aws-sdk/middleware-logger": "3.598.0", - "@aws-sdk/middleware-recursion-detection": "3.598.0", - "@aws-sdk/middleware-user-agent": "3.598.0", - "@aws-sdk/region-config-resolver": "3.598.0", - "@aws-sdk/types": "3.598.0", - "@aws-sdk/util-endpoints": "3.598.0", - "@aws-sdk/util-user-agent-browser": "3.598.0", - "@aws-sdk/util-user-agent-node": "3.598.0", - "@smithy/config-resolver": "^3.0.2", - "@smithy/core": "^2.2.1", - "@smithy/fetch-http-handler": "^3.0.2", - "@smithy/hash-node": "^3.0.1", - "@smithy/invalid-dependency": "^3.0.1", - "@smithy/middleware-content-length": "^3.0.1", - "@smithy/middleware-endpoint": "^3.0.2", - "@smithy/middleware-retry": "^3.0.4", - "@smithy/middleware-serde": "^3.0.1", - "@smithy/middleware-stack": "^3.0.1", - "@smithy/node-config-provider": "^3.1.1", - "@smithy/node-http-handler": "^3.0.1", - "@smithy/protocol-http": "^4.0.1", - "@smithy/smithy-client": "^3.1.2", - "@smithy/types": "^3.1.0", - "@smithy/url-parser": "^3.0.1", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.4", - "@smithy/util-defaults-mode-node": "^3.0.4", - "@smithy/util-endpoints": "^2.0.2", - "@smithy/util-middleware": "^3.0.1", - "@smithy/util-retry": "^3.0.1", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/client-sso": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.598.0.tgz", - "integrity": "sha512-nOI5lqPYa+YZlrrzwAJywJSw3MKVjvu6Ge2fCqQUNYMfxFB0NAaDFnl0EPjXi+sEbtCuz/uWE77poHbqiZ+7Iw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.598.0", - "@aws-sdk/middleware-host-header": "3.598.0", - "@aws-sdk/middleware-logger": "3.598.0", - "@aws-sdk/middleware-recursion-detection": "3.598.0", - "@aws-sdk/middleware-user-agent": "3.598.0", - "@aws-sdk/region-config-resolver": "3.598.0", - "@aws-sdk/types": "3.598.0", - "@aws-sdk/util-endpoints": "3.598.0", - "@aws-sdk/util-user-agent-browser": "3.598.0", - "@aws-sdk/util-user-agent-node": "3.598.0", - "@smithy/config-resolver": "^3.0.2", - "@smithy/core": "^2.2.1", - "@smithy/fetch-http-handler": "^3.0.2", - "@smithy/hash-node": "^3.0.1", - "@smithy/invalid-dependency": "^3.0.1", - "@smithy/middleware-content-length": "^3.0.1", - "@smithy/middleware-endpoint": "^3.0.2", - "@smithy/middleware-retry": "^3.0.4", - "@smithy/middleware-serde": "^3.0.1", - "@smithy/middleware-stack": "^3.0.1", - "@smithy/node-config-provider": "^3.1.1", - "@smithy/node-http-handler": "^3.0.1", - "@smithy/protocol-http": "^4.0.1", - "@smithy/smithy-client": "^3.1.2", - "@smithy/types": "^3.1.0", - "@smithy/url-parser": "^3.0.1", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.4", - "@smithy/util-defaults-mode-node": "^3.0.4", - "@smithy/util-endpoints": "^2.0.2", - "@smithy/util-middleware": "^3.0.1", - "@smithy/util-retry": "^3.0.1", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/core": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.598.0.tgz", - "integrity": "sha512-HaSjt7puO5Cc7cOlrXFCW0rtA0BM9lvzjl56x0A20Pt+0wxXGeTOZZOkXQIepbrFkV2e/HYukuT9e99vXDm59g==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^2.2.1", - "@smithy/protocol-http": "^4.0.1", - "@smithy/signature-v4": "^3.1.0", - "@smithy/smithy-client": "^3.1.2", - "@smithy/types": "^3.1.0", - "fast-xml-parser": "4.2.5", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-env": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.598.0.tgz", - "integrity": "sha512-vi1khgn7yXzLCcgSIzQrrtd2ilUM0dWodxj3PQ6BLfP0O+q1imO3hG1nq7DVyJtq7rFHs6+9N8G4mYvTkxby2w==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/property-provider": "^3.1.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-http": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.598.0.tgz", - "integrity": "sha512-N7cIafi4HVlQvEgvZSo1G4T9qb/JMLGMdBsDCT5XkeJrF0aptQWzTFH0jIdZcLrMYvzPcuEyO3yCBe6cy/ba0g==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/fetch-http-handler": "^3.0.2", - "@smithy/node-http-handler": "^3.0.1", - "@smithy/property-provider": "^3.1.1", - "@smithy/protocol-http": "^4.0.1", - "@smithy/smithy-client": "^3.1.2", - "@smithy/types": "^3.1.0", - "@smithy/util-stream": "^3.0.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-node": { - "version": "3.600.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.600.0.tgz", - "integrity": "sha512-1pC7MPMYD45J7yFjA90SxpR0yaSvy+yZiq23aXhAPZLYgJBAxHLu0s0mDCk/piWGPh8+UGur5K0bVdx4B1D5hw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.598.0", - "@aws-sdk/credential-provider-http": "3.598.0", - "@aws-sdk/credential-provider-ini": "3.598.0", - "@aws-sdk/credential-provider-process": "3.598.0", - "@aws-sdk/credential-provider-sso": "3.598.0", - "@aws-sdk/credential-provider-web-identity": "3.598.0", - "@aws-sdk/types": "3.598.0", - "@smithy/credential-provider-imds": "^3.1.1", - "@smithy/property-provider": "^3.1.1", - "@smithy/shared-ini-file-loader": "^3.1.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-node/node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.598.0.tgz", - "integrity": "sha512-/ppcIVUbRwDIwJDoYfp90X3+AuJo2mvE52Y1t2VSrvUovYn6N4v95/vXj6LS8CNDhz2jvEJYmu+0cTMHdhI6eA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.598.0", - "@aws-sdk/credential-provider-http": "3.598.0", - "@aws-sdk/credential-provider-process": "3.598.0", - "@aws-sdk/credential-provider-sso": "3.598.0", - "@aws-sdk/credential-provider-web-identity": "3.598.0", - "@aws-sdk/types": "3.598.0", - "@smithy/credential-provider-imds": "^3.1.1", - "@smithy/property-provider": "^3.1.1", - "@smithy/shared-ini-file-loader": "^3.1.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sts": "^3.598.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-node/node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.598.0.tgz", - "integrity": "sha512-GV5GdiMbz5Tz9JO4NJtRoFXjW0GPEujA0j+5J/B723rTN+REHthJu48HdBKouHGhdzkDWkkh1bu52V02Wprw8w==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/property-provider": "^3.1.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sts": "^3.598.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-process": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.598.0.tgz", - "integrity": "sha512-rM707XbLW8huMk722AgjVyxu2tMZee++fNA8TJVNgs1Ma02Wx6bBrfIvlyK0rCcIRb0WdQYP6fe3Xhiu4e8IBA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/property-provider": "^3.1.1", - "@smithy/shared-ini-file-loader": "^3.1.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.598.0.tgz", - "integrity": "sha512-5InwUmrAuqQdOOgxTccRayMMkSmekdLk6s+az9tmikq0QFAHUCtofI+/fllMXSR9iL6JbGYi1940+EUmS4pHJA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/client-sso": "3.598.0", - "@aws-sdk/token-providers": "3.598.0", - "@aws-sdk/types": "3.598.0", - "@smithy/property-provider": "^3.1.1", - "@smithy/shared-ini-file-loader": "^3.1.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.598.0.tgz", - "integrity": "sha512-TKY1EVdHVBnZqpyxyTHdpZpa1tUpb6nxVeRNn1zWG8QB5MvH4ALLd/jR+gtmWDNQbIG4cVuBOZFVL8hIYicKTA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/property-provider": "^3.1.1", - "@smithy/shared-ini-file-loader": "^3.1.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sso-oidc": "^3.598.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/middleware-host-header": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.598.0.tgz", - "integrity": "sha512-WiaG059YBQwQraNejLIi0gMNkX7dfPZ8hDIhvMr5aVPRbaHH8AYF3iNSsXYCHvA2Cfa1O9haYXsuMF9flXnCmA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/protocol-http": "^4.0.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/middleware-logger": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.598.0.tgz", - "integrity": "sha512-bxBjf/VYiu3zfu8SYM2S9dQQc3tz5uBAOcPz/Bt8DyyK3GgOpjhschH/2XuUErsoUO1gDJqZSdGOmuHGZQn00Q==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.598.0.tgz", - "integrity": "sha512-vjT9BeFY9FeN0f8hm2l6F53tI0N5bUq6RcDkQXKNabXBnQxKptJRad6oP2X5y3FoVfBLOuDkQgiC2940GIPxtQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/protocol-http": "^4.0.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.598.0.tgz", - "integrity": "sha512-4tjESlHG5B5MdjUaLK7tQs/miUtHbb6deauQx8ryqSBYOhfHVgb1ZnzvQR0bTrhpqUg0WlybSkDaZAICf9xctg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@aws-sdk/util-endpoints": "3.598.0", - "@smithy/protocol-http": "^4.0.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/region-config-resolver": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.598.0.tgz", - "integrity": "sha512-oYXhmTokSav4ytmWleCr3rs/1nyvZW/S0tdi6X7u+dLNL5Jee+uMxWGzgOrWK6wrQOzucLVjS4E/wA11Kv2GTw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/node-config-provider": "^3.1.1", - "@smithy/types": "^3.1.0", - "@smithy/util-config-provider": "^3.0.0", - "@smithy/util-middleware": "^3.0.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/types": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.598.0.tgz", - "integrity": "sha512-742uRl6z7u0LFmZwDrFP6r1wlZcgVPw+/TilluDJmCAR8BgRw3IR+743kUXKBGd8QZDRW2n6v/PYsi/AWCDDMQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/util-endpoints": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.598.0.tgz", - "integrity": "sha512-Qo9UoiVVZxcOEdiOMZg3xb1mzkTxrhd4qSlg5QQrfWPJVx/QOg+Iy0NtGxPtHtVZNHZxohYwDwV/tfsnDSE2gQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/types": "^3.1.0", - "@smithy/util-endpoints": "^2.0.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.598.0.tgz", - "integrity": "sha512-36Sxo6F+ykElaL1mWzWjlg+1epMpSe8obwhCN1yGE7Js9ywy5U6k6l+A3q3YM9YRbm740sNxncbwLklMvuhTKw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/types": "^3.1.0", - "bowser": "^2.11.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.598.0.tgz", - "integrity": "sha512-oyWGcOlfTdzkC6SVplyr0AGh54IMrDxbhg5RxJ5P+V4BKfcDoDcZV9xenUk9NsOi9MuUjxMumb9UJGkDhM1m0A==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/node-config-provider": "^3.1.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "aws-crt": ">=1.0.0" - }, - "peerDependenciesMeta": { - "aws-crt": { - "optional": true - } - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/abort-controller": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.9.tgz", - "integrity": "sha512-yiW0WI30zj8ZKoSYNx90no7ugVn3khlyH/z5W8qtKBtVE6awRALbhSG+2SAHA1r6bO/6M9utxYKVZ3PCJ1rWxw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/config-resolver": { - "version": "3.0.13", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.13.tgz", - "integrity": "sha512-Gr/qwzyPaTL1tZcq8WQyHhTZREER5R1Wytmz4WnVGL4onA3dNk6Btll55c8Vr58pLdvWZmtG8oZxJTw3t3q7Jg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^3.1.12", - "@smithy/types": "^3.7.2", - "@smithy/util-config-provider": "^3.0.0", - "@smithy/util-middleware": "^3.0.11", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/core": { - "version": "2.5.7", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.5.7.tgz", - "integrity": "sha512-8olpW6mKCa0v+ibCjoCzgZHQx1SQmZuW/WkrdZo73wiTprTH6qhmskT60QLFdT9DRa5mXxjz89kQPZ7ZSsoqqg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/middleware-serde": "^3.0.11", - "@smithy/protocol-http": "^4.1.8", - "@smithy/types": "^3.7.2", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-middleware": "^3.0.11", - "@smithy/util-stream": "^3.3.4", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/credential-provider-imds": { - "version": "3.2.8", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.8.tgz", - "integrity": "sha512-ZCY2yD0BY+K9iMXkkbnjo+08T2h8/34oHd0Jmh6BZUSZwaaGlGCyBT/3wnS7u7Xl33/EEfN4B6nQr3Gx5bYxgw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^3.1.12", - "@smithy/property-provider": "^3.1.11", - "@smithy/types": "^3.7.2", - "@smithy/url-parser": "^3.0.11", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/fetch-http-handler": { - "version": "3.2.9", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-3.2.9.tgz", - "integrity": "sha512-hYNVQOqhFQ6vOpenifFME546f0GfJn2OiQ3M0FDmuUu8V/Uiwy2wej7ZXxFBNqdx0R5DZAqWM1l6VRhGz8oE6A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/protocol-http": "^4.1.4", - "@smithy/querystring-builder": "^3.0.7", - "@smithy/types": "^3.5.0", - "@smithy/util-base64": "^3.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/hash-node": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.11.tgz", - "integrity": "sha512-emP23rwYyZhQBvklqTtwetkQlqbNYirDiEEwXl2v0GYWMnCzxst7ZaRAnWuy28njp5kAH54lvkdG37MblZzaHA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "@smithy/util-buffer-from": "^3.0.0", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/invalid-dependency": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.11.tgz", - "integrity": "sha512-NuQmVPEJjUX6c+UELyVz8kUx8Q539EDeNwbRyu4IIF8MeV7hUtq1FB3SHVyki2u++5XLMFqngeMKk7ccspnNyQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/is-array-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", - "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/middleware-content-length": { - "version": "3.0.13", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.13.tgz", - "integrity": "sha512-zfMhzojhFpIX3P5ug7jxTjfUcIPcGjcQYzB9t+rv0g1TX7B0QdwONW+ATouaLoD7h7LOw/ZlXfkq4xJ/g2TrIw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/protocol-http": "^4.1.8", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/middleware-endpoint": { - "version": "3.2.8", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.2.8.tgz", - "integrity": "sha512-OEJZKVUEhMOqMs3ktrTWp7UvvluMJEvD5XgQwRePSbDg1VvBaL8pX8mwPltFn6wk1GySbcVwwyldL8S+iqnrEQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^2.5.7", - "@smithy/middleware-serde": "^3.0.11", - "@smithy/node-config-provider": "^3.1.12", - "@smithy/shared-ini-file-loader": "^3.1.12", - "@smithy/types": "^3.7.2", - "@smithy/url-parser": "^3.0.11", - "@smithy/util-middleware": "^3.0.11", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/middleware-retry": { - "version": "3.0.34", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.34.tgz", - "integrity": "sha512-yVRr/AAtPZlUvwEkrq7S3x7Z8/xCd97m2hLDaqdz6ucP2RKHsBjEqaUA2ebNv2SsZoPEi+ZD0dZbOB1u37tGCA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^3.1.12", - "@smithy/protocol-http": "^4.1.8", - "@smithy/service-error-classification": "^3.0.11", - "@smithy/smithy-client": "^3.7.0", - "@smithy/types": "^3.7.2", - "@smithy/util-middleware": "^3.0.11", - "@smithy/util-retry": "^3.0.11", - "tslib": "^2.6.2", - "uuid": "^9.0.1" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/middleware-serde": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.11.tgz", - "integrity": "sha512-KzPAeySp/fOoQA82TpnwItvX8BBURecpx6ZMu75EZDkAcnPtO6vf7q4aH5QHs/F1s3/snQaSFbbUMcFFZ086Mw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/middleware-stack": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.11.tgz", - "integrity": "sha512-1HGo9a6/ikgOMrTrWL/WiN9N8GSVYpuRQO5kjstAq4CvV59bjqnh7TbdXGQ4vxLD3xlSjfBjq5t1SOELePsLnA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/node-config-provider": { - "version": "3.1.12", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.12.tgz", - "integrity": "sha512-O9LVEu5J/u/FuNlZs+L7Ikn3lz7VB9hb0GtPT9MQeiBmtK8RSY3ULmsZgXhe6VAlgTw0YO+paQx4p8xdbs43vQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/property-provider": "^3.1.11", - "@smithy/shared-ini-file-loader": "^3.1.12", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/node-http-handler": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.3.3.tgz", - "integrity": "sha512-BrpZOaZ4RCbcJ2igiSNG16S+kgAc65l/2hmxWdmhyoGWHTLlzQzr06PXavJp9OBlPEG/sHlqdxjWmjzV66+BSQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/abort-controller": "^3.1.9", - "@smithy/protocol-http": "^4.1.8", - "@smithy/querystring-builder": "^3.0.11", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/property-provider": { - "version": "3.1.11", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.11.tgz", - "integrity": "sha512-I/+TMc4XTQ3QAjXfOcUWbSS073oOEAxgx4aZy8jHaf8JQnRkq2SZWw8+PfDtBvLUjcGMdxl+YwtzWe6i5uhL/A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/protocol-http": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.8.tgz", - "integrity": "sha512-hmgIAVyxw1LySOwkgMIUN0kjN8TG9Nc85LJeEmEE/cNEe2rkHDUWhnJf2gxcSRFLWsyqWsrZGw40ROjUogg+Iw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/querystring-builder": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.11.tgz", - "integrity": "sha512-u+5HV/9uJaeLj5XTb6+IEF/dokWWkEqJ0XiaRRogyREmKGUgZnNecLucADLdauWFKUNbQfulHFEZEdjwEBjXRg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "@smithy/util-uri-escape": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/querystring-parser": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.11.tgz", - "integrity": "sha512-Je3kFvCsFMnso1ilPwA7GtlbPaTixa3WwC+K21kmMZHsBEOZYQaqxcMqeFFoU7/slFjKDIpiiPydvdJm8Q/MCw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/service-error-classification": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.11.tgz", - "integrity": "sha512-QnYDPkyewrJzCyaeI2Rmp7pDwbUETe+hU8ADkXmgNusO1bgHBH7ovXJiYmba8t0fNfJx75fE8dlM6SEmZxheog==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/shared-ini-file-loader": { - "version": "3.1.12", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.12.tgz", - "integrity": "sha512-1xKSGI+U9KKdbG2qDvIR9dGrw3CNx+baqJfyr0igKEpjbHL5stsqAesYBzHChYHlelWtb87VnLWlhvfCz13H8Q==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/signature-v4": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-3.1.2.tgz", - "integrity": "sha512-3BcPylEsYtD0esM4Hoyml/+s7WP2LFhcM3J2AGdcL2vx9O60TtfpDOL72gjb4lU8NeRPeKAwR77YNyyGvMbuEA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^3.0.0", - "@smithy/types": "^3.3.0", - "@smithy/util-hex-encoding": "^3.0.0", - "@smithy/util-middleware": "^3.0.3", - "@smithy/util-uri-escape": "^3.0.0", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/smithy-client": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.7.0.tgz", - "integrity": "sha512-9wYrjAZFlqWhgVo3C4y/9kpc68jgiSsKUnsFPzr/MSiRL93+QRDafGTfhhKAb2wsr69Ru87WTiqSfQusSmWipA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^2.5.7", - "@smithy/middleware-endpoint": "^3.2.8", - "@smithy/middleware-stack": "^3.0.11", - "@smithy/protocol-http": "^4.1.8", - "@smithy/types": "^3.7.2", - "@smithy/util-stream": "^3.3.4", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/types": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.7.2.tgz", - "integrity": "sha512-bNwBYYmN8Eh9RyjS1p2gW6MIhSO2rl7X9QeLM8iTdcGRP+eDiIWDt66c9IysCc22gefKszZv+ubV9qZc7hdESg==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/url-parser": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.11.tgz", - "integrity": "sha512-TmlqXkSk8ZPhfc+SQutjmFr5FjC0av3GZP4B/10caK1SbRwe/v+Wzu/R6xEKxoNqL+8nY18s1byiy6HqPG37Aw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/querystring-parser": "^3.0.11", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-base64": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-3.0.0.tgz", - "integrity": "sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^3.0.0", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-body-length-browser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-3.0.0.tgz", - "integrity": "sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-body-length-node": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-3.0.0.tgz", - "integrity": "sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-buffer-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz", - "integrity": "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-config-provider": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-3.0.0.tgz", - "integrity": "sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-defaults-mode-browser": { - "version": "3.0.34", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.34.tgz", - "integrity": "sha512-FumjjF631lR521cX+svMLBj3SwSDh9VdtyynTYDAiBDEf8YPP5xORNXKQ9j0105o5+ARAGnOOP/RqSl40uXddA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/property-provider": "^3.1.11", - "@smithy/smithy-client": "^3.7.0", - "@smithy/types": "^3.7.2", - "bowser": "^2.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-defaults-mode-node": { - "version": "3.0.34", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.34.tgz", - "integrity": "sha512-vN6aHfzW9dVVzkI0wcZoUXvfjkl4CSbM9nE//08lmUMyf00S75uuCpTrqF9uD4bD9eldIXlt53colrlwKAT8Gw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/config-resolver": "^3.0.13", - "@smithy/credential-provider-imds": "^3.2.8", - "@smithy/node-config-provider": "^3.1.12", - "@smithy/property-provider": "^3.1.11", - "@smithy/smithy-client": "^3.7.0", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-endpoints": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.1.7.tgz", - "integrity": "sha512-tSfcqKcN/Oo2STEYCABVuKgJ76nyyr6skGl9t15hs+YaiU06sgMkN7QYjo0BbVw+KT26zok3IzbdSOksQ4YzVw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^3.1.12", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-hex-encoding": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-3.0.0.tgz", - "integrity": "sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-middleware": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.11.tgz", - "integrity": "sha512-dWpyc1e1R6VoXrwLoLDd57U1z6CwNSdkM69Ie4+6uYh2GC7Vg51Qtan7ITzczuVpqezdDTKJGJB95fFvvjU/ow==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-retry": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.11.tgz", - "integrity": "sha512-hJUC6W7A3DQgaee3Hp9ZFcOxVDZzmBIRBPlUAk8/fSOEl7pE/aX7Dci0JycNOnm9Mfr0KV2XjIlUOcGWXQUdVQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/service-error-classification": "^3.0.11", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-stream": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.3.4.tgz", - "integrity": "sha512-SGhGBG/KupieJvJSZp/rfHHka8BFgj56eek9px4pp7lZbOF+fRiVr4U7A3y3zJD8uGhxq32C5D96HxsTC9BckQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/fetch-http-handler": "^4.1.3", - "@smithy/node-http-handler": "^3.3.3", - "@smithy/types": "^3.7.2", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-buffer-from": "^3.0.0", - "@smithy/util-hex-encoding": "^3.0.0", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-stream/node_modules/@smithy/fetch-http-handler": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-4.1.3.tgz", - "integrity": "sha512-6SxNltSncI8s689nvnzZQc/dPXcpHQ34KUj6gR/HBroytKOd/isMG3gJF/zBE1TBmTT18TXyzhg3O3SOOqGEhA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/protocol-http": "^4.1.8", - "@smithy/querystring-builder": "^3.0.11", - "@smithy/types": "^3.7.2", - "@smithy/util-base64": "^3.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-uri-escape": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-3.0.0.tgz", - "integrity": "sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-utf8": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", - "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/fast-xml-parser": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz", - "integrity": "sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==", - "funding": [ - { - "type": "paypal", - "url": "https://paypal.me/naturalintelligence" - }, - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "dependencies": { - "strnum": "^1.0.5" - }, - "bin": { - "fxparser": "src/cli/cli.js" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/strnum": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", - "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT" - }, - "node_modules/@aws-sdk/client-s3": { - "version": "3.879.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.879.0.tgz", - "integrity": "sha512-1bD2Do/OdCIzl72ncHKYamDhPijUErLYpuLvciyYD4Ywt4cVLHjWtVIqb22XOOHYYHE3NqHMd4uRhvXMlsBRoQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha1-browser": "5.2.0", - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.879.0", - "@aws-sdk/credential-provider-node": "3.879.0", - "@aws-sdk/middleware-bucket-endpoint": "3.873.0", - "@aws-sdk/middleware-expect-continue": "3.873.0", - "@aws-sdk/middleware-flexible-checksums": "3.879.0", - "@aws-sdk/middleware-host-header": "3.873.0", - "@aws-sdk/middleware-location-constraint": "3.873.0", - "@aws-sdk/middleware-logger": "3.876.0", - "@aws-sdk/middleware-recursion-detection": "3.873.0", - "@aws-sdk/middleware-sdk-s3": "3.879.0", - "@aws-sdk/middleware-ssec": "3.873.0", - "@aws-sdk/middleware-user-agent": "3.879.0", - "@aws-sdk/region-config-resolver": "3.873.0", - "@aws-sdk/signature-v4-multi-region": "3.879.0", - "@aws-sdk/types": "3.862.0", - "@aws-sdk/util-endpoints": "3.879.0", - "@aws-sdk/util-user-agent-browser": "3.873.0", - "@aws-sdk/util-user-agent-node": "3.879.0", - "@aws-sdk/xml-builder": "3.873.0", - "@smithy/config-resolver": "^4.1.5", - "@smithy/core": "^3.9.0", - "@smithy/eventstream-serde-browser": "^4.0.5", - "@smithy/eventstream-serde-config-resolver": "^4.1.3", - "@smithy/eventstream-serde-node": "^4.0.5", - "@smithy/fetch-http-handler": "^5.1.1", - "@smithy/hash-blob-browser": "^4.0.5", - "@smithy/hash-node": "^4.0.5", - "@smithy/hash-stream-node": "^4.0.5", - "@smithy/invalid-dependency": "^4.0.5", - "@smithy/md5-js": "^4.0.5", - "@smithy/middleware-content-length": "^4.0.5", - "@smithy/middleware-endpoint": "^4.1.19", - "@smithy/middleware-retry": "^4.1.20", - "@smithy/middleware-serde": "^4.0.9", - "@smithy/middleware-stack": "^4.0.5", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/node-http-handler": "^4.1.1", - "@smithy/protocol-http": "^5.1.3", - "@smithy/smithy-client": "^4.5.0", - "@smithy/types": "^4.3.2", - "@smithy/url-parser": "^4.0.5", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.27", - "@smithy/util-defaults-mode-node": "^4.0.27", - "@smithy/util-endpoints": "^3.0.7", - "@smithy/util-middleware": "^4.0.5", - "@smithy/util-retry": "^4.0.7", - "@smithy/util-stream": "^4.2.4", - "@smithy/util-utf8": "^4.0.0", - "@smithy/util-waiter": "^4.0.7", - "@types/uuid": "^9.0.1", - "tslib": "^2.6.2", - "uuid": "^9.0.1" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sso": { - "version": "3.879.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.879.0.tgz", - "integrity": "sha512-+Pc3OYFpRYpKLKRreovPM63FPPud1/SF9vemwIJfz6KwsBCJdvg7vYD1xLSIp5DVZLeetgf4reCyAA5ImBfZuw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.879.0", - "@aws-sdk/middleware-host-header": "3.873.0", - "@aws-sdk/middleware-logger": "3.876.0", - "@aws-sdk/middleware-recursion-detection": "3.873.0", - "@aws-sdk/middleware-user-agent": "3.879.0", - "@aws-sdk/region-config-resolver": "3.873.0", - "@aws-sdk/types": "3.862.0", - "@aws-sdk/util-endpoints": "3.879.0", - "@aws-sdk/util-user-agent-browser": "3.873.0", - "@aws-sdk/util-user-agent-node": "3.879.0", - "@smithy/config-resolver": "^4.1.5", - "@smithy/core": "^3.9.0", - "@smithy/fetch-http-handler": "^5.1.1", - "@smithy/hash-node": "^4.0.5", - "@smithy/invalid-dependency": "^4.0.5", - "@smithy/middleware-content-length": "^4.0.5", - "@smithy/middleware-endpoint": "^4.1.19", - "@smithy/middleware-retry": "^4.1.20", - "@smithy/middleware-serde": "^4.0.9", - "@smithy/middleware-stack": "^4.0.5", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/node-http-handler": "^4.1.1", - "@smithy/protocol-http": "^5.1.3", - "@smithy/smithy-client": "^4.5.0", - "@smithy/types": "^4.3.2", - "@smithy/url-parser": "^4.0.5", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.27", - "@smithy/util-defaults-mode-node": "^4.0.27", - "@smithy/util-endpoints": "^3.0.7", - "@smithy/util-middleware": "^4.0.5", - "@smithy/util-retry": "^4.0.7", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc": { - "version": "3.600.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.600.0.tgz", - "integrity": "sha512-7+I8RWURGfzvChyNQSyj5/tKrqRbzRl7H+BnTOf/4Vsw1nFOi5ROhlhD4X/Y0QCTacxnaoNcIrqnY7uGGvVRzw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/client-sts": "3.600.0", - "@aws-sdk/core": "3.598.0", - "@aws-sdk/credential-provider-node": "3.600.0", - "@aws-sdk/middleware-host-header": "3.598.0", - "@aws-sdk/middleware-logger": "3.598.0", - "@aws-sdk/middleware-recursion-detection": "3.598.0", - "@aws-sdk/middleware-user-agent": "3.598.0", - "@aws-sdk/region-config-resolver": "3.598.0", - "@aws-sdk/types": "3.598.0", - "@aws-sdk/util-endpoints": "3.598.0", - "@aws-sdk/util-user-agent-browser": "3.598.0", - "@aws-sdk/util-user-agent-node": "3.598.0", - "@smithy/config-resolver": "^3.0.2", - "@smithy/core": "^2.2.1", - "@smithy/fetch-http-handler": "^3.0.2", - "@smithy/hash-node": "^3.0.1", - "@smithy/invalid-dependency": "^3.0.1", - "@smithy/middleware-content-length": "^3.0.1", - "@smithy/middleware-endpoint": "^3.0.2", - "@smithy/middleware-retry": "^3.0.4", - "@smithy/middleware-serde": "^3.0.1", - "@smithy/middleware-stack": "^3.0.1", - "@smithy/node-config-provider": "^3.1.1", - "@smithy/node-http-handler": "^3.0.1", - "@smithy/protocol-http": "^4.0.1", - "@smithy/smithy-client": "^3.1.2", - "@smithy/types": "^3.1.0", - "@smithy/url-parser": "^3.0.1", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.4", - "@smithy/util-defaults-mode-node": "^3.0.4", - "@smithy/util-endpoints": "^2.0.2", - "@smithy/util-middleware": "^3.0.1", - "@smithy/util-retry": "^3.0.1", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/client-sso": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.598.0.tgz", - "integrity": "sha512-nOI5lqPYa+YZlrrzwAJywJSw3MKVjvu6Ge2fCqQUNYMfxFB0NAaDFnl0EPjXi+sEbtCuz/uWE77poHbqiZ+7Iw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.598.0", - "@aws-sdk/middleware-host-header": "3.598.0", - "@aws-sdk/middleware-logger": "3.598.0", - "@aws-sdk/middleware-recursion-detection": "3.598.0", - "@aws-sdk/middleware-user-agent": "3.598.0", - "@aws-sdk/region-config-resolver": "3.598.0", - "@aws-sdk/types": "3.598.0", - "@aws-sdk/util-endpoints": "3.598.0", - "@aws-sdk/util-user-agent-browser": "3.598.0", - "@aws-sdk/util-user-agent-node": "3.598.0", - "@smithy/config-resolver": "^3.0.2", - "@smithy/core": "^2.2.1", - "@smithy/fetch-http-handler": "^3.0.2", - "@smithy/hash-node": "^3.0.1", - "@smithy/invalid-dependency": "^3.0.1", - "@smithy/middleware-content-length": "^3.0.1", - "@smithy/middleware-endpoint": "^3.0.2", - "@smithy/middleware-retry": "^3.0.4", - "@smithy/middleware-serde": "^3.0.1", - "@smithy/middleware-stack": "^3.0.1", - "@smithy/node-config-provider": "^3.1.1", - "@smithy/node-http-handler": "^3.0.1", - "@smithy/protocol-http": "^4.0.1", - "@smithy/smithy-client": "^3.1.2", - "@smithy/types": "^3.1.0", - "@smithy/url-parser": "^3.0.1", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.4", - "@smithy/util-defaults-mode-node": "^3.0.4", - "@smithy/util-endpoints": "^2.0.2", - "@smithy/util-middleware": "^3.0.1", - "@smithy/util-retry": "^3.0.1", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/core": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.598.0.tgz", - "integrity": "sha512-HaSjt7puO5Cc7cOlrXFCW0rtA0BM9lvzjl56x0A20Pt+0wxXGeTOZZOkXQIepbrFkV2e/HYukuT9e99vXDm59g==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^2.2.1", - "@smithy/protocol-http": "^4.0.1", - "@smithy/signature-v4": "^3.1.0", - "@smithy/smithy-client": "^3.1.2", - "@smithy/types": "^3.1.0", - "fast-xml-parser": "4.2.5", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/credential-provider-env": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.598.0.tgz", - "integrity": "sha512-vi1khgn7yXzLCcgSIzQrrtd2ilUM0dWodxj3PQ6BLfP0O+q1imO3hG1nq7DVyJtq7rFHs6+9N8G4mYvTkxby2w==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/property-provider": "^3.1.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/credential-provider-http": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.598.0.tgz", - "integrity": "sha512-N7cIafi4HVlQvEgvZSo1G4T9qb/JMLGMdBsDCT5XkeJrF0aptQWzTFH0jIdZcLrMYvzPcuEyO3yCBe6cy/ba0g==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/fetch-http-handler": "^3.0.2", - "@smithy/node-http-handler": "^3.0.1", - "@smithy/property-provider": "^3.1.1", - "@smithy/protocol-http": "^4.0.1", - "@smithy/smithy-client": "^3.1.2", - "@smithy/types": "^3.1.0", - "@smithy/util-stream": "^3.0.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/credential-provider-node": { - "version": "3.600.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.600.0.tgz", - "integrity": "sha512-1pC7MPMYD45J7yFjA90SxpR0yaSvy+yZiq23aXhAPZLYgJBAxHLu0s0mDCk/piWGPh8+UGur5K0bVdx4B1D5hw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.598.0", - "@aws-sdk/credential-provider-http": "3.598.0", - "@aws-sdk/credential-provider-ini": "3.598.0", - "@aws-sdk/credential-provider-process": "3.598.0", - "@aws-sdk/credential-provider-sso": "3.598.0", - "@aws-sdk/credential-provider-web-identity": "3.598.0", - "@aws-sdk/types": "3.598.0", - "@smithy/credential-provider-imds": "^3.1.1", - "@smithy/property-provider": "^3.1.1", - "@smithy/shared-ini-file-loader": "^3.1.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/credential-provider-node/node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.598.0.tgz", - "integrity": "sha512-/ppcIVUbRwDIwJDoYfp90X3+AuJo2mvE52Y1t2VSrvUovYn6N4v95/vXj6LS8CNDhz2jvEJYmu+0cTMHdhI6eA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.598.0", - "@aws-sdk/credential-provider-http": "3.598.0", - "@aws-sdk/credential-provider-process": "3.598.0", - "@aws-sdk/credential-provider-sso": "3.598.0", - "@aws-sdk/credential-provider-web-identity": "3.598.0", - "@aws-sdk/types": "3.598.0", - "@smithy/credential-provider-imds": "^3.1.1", - "@smithy/property-provider": "^3.1.1", - "@smithy/shared-ini-file-loader": "^3.1.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sts": "^3.598.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/credential-provider-node/node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.598.0.tgz", - "integrity": "sha512-GV5GdiMbz5Tz9JO4NJtRoFXjW0GPEujA0j+5J/B723rTN+REHthJu48HdBKouHGhdzkDWkkh1bu52V02Wprw8w==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/property-provider": "^3.1.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sts": "^3.598.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/credential-provider-process": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.598.0.tgz", - "integrity": "sha512-rM707XbLW8huMk722AgjVyxu2tMZee++fNA8TJVNgs1Ma02Wx6bBrfIvlyK0rCcIRb0WdQYP6fe3Xhiu4e8IBA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/property-provider": "^3.1.1", - "@smithy/shared-ini-file-loader": "^3.1.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.598.0.tgz", - "integrity": "sha512-5InwUmrAuqQdOOgxTccRayMMkSmekdLk6s+az9tmikq0QFAHUCtofI+/fllMXSR9iL6JbGYi1940+EUmS4pHJA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/client-sso": "3.598.0", - "@aws-sdk/token-providers": "3.598.0", - "@aws-sdk/types": "3.598.0", - "@smithy/property-provider": "^3.1.1", - "@smithy/shared-ini-file-loader": "^3.1.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/middleware-host-header": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.598.0.tgz", - "integrity": "sha512-WiaG059YBQwQraNejLIi0gMNkX7dfPZ8hDIhvMr5aVPRbaHH8AYF3iNSsXYCHvA2Cfa1O9haYXsuMF9flXnCmA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/protocol-http": "^4.0.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/middleware-logger": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.598.0.tgz", - "integrity": "sha512-bxBjf/VYiu3zfu8SYM2S9dQQc3tz5uBAOcPz/Bt8DyyK3GgOpjhschH/2XuUErsoUO1gDJqZSdGOmuHGZQn00Q==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.598.0.tgz", - "integrity": "sha512-vjT9BeFY9FeN0f8hm2l6F53tI0N5bUq6RcDkQXKNabXBnQxKptJRad6oP2X5y3FoVfBLOuDkQgiC2940GIPxtQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/protocol-http": "^4.0.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.598.0.tgz", - "integrity": "sha512-4tjESlHG5B5MdjUaLK7tQs/miUtHbb6deauQx8ryqSBYOhfHVgb1ZnzvQR0bTrhpqUg0WlybSkDaZAICf9xctg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@aws-sdk/util-endpoints": "3.598.0", - "@smithy/protocol-http": "^4.0.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/region-config-resolver": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.598.0.tgz", - "integrity": "sha512-oYXhmTokSav4ytmWleCr3rs/1nyvZW/S0tdi6X7u+dLNL5Jee+uMxWGzgOrWK6wrQOzucLVjS4E/wA11Kv2GTw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/node-config-provider": "^3.1.1", - "@smithy/types": "^3.1.0", - "@smithy/util-config-provider": "^3.0.0", - "@smithy/util-middleware": "^3.0.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/token-providers": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.598.0.tgz", - "integrity": "sha512-TKY1EVdHVBnZqpyxyTHdpZpa1tUpb6nxVeRNn1zWG8QB5MvH4ALLd/jR+gtmWDNQbIG4cVuBOZFVL8hIYicKTA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/property-provider": "^3.1.1", - "@smithy/shared-ini-file-loader": "^3.1.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sso-oidc": "^3.598.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/types": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.598.0.tgz", - "integrity": "sha512-742uRl6z7u0LFmZwDrFP6r1wlZcgVPw+/TilluDJmCAR8BgRw3IR+743kUXKBGd8QZDRW2n6v/PYsi/AWCDDMQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/util-endpoints": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.598.0.tgz", - "integrity": "sha512-Qo9UoiVVZxcOEdiOMZg3xb1mzkTxrhd4qSlg5QQrfWPJVx/QOg+Iy0NtGxPtHtVZNHZxohYwDwV/tfsnDSE2gQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/types": "^3.1.0", - "@smithy/util-endpoints": "^2.0.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.598.0.tgz", - "integrity": "sha512-36Sxo6F+ykElaL1mWzWjlg+1epMpSe8obwhCN1yGE7Js9ywy5U6k6l+A3q3YM9YRbm740sNxncbwLklMvuhTKw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/types": "^3.1.0", - "bowser": "^2.11.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.598.0.tgz", - "integrity": "sha512-oyWGcOlfTdzkC6SVplyr0AGh54IMrDxbhg5RxJ5P+V4BKfcDoDcZV9xenUk9NsOi9MuUjxMumb9UJGkDhM1m0A==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/node-config-provider": "^3.1.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "aws-crt": ">=1.0.0" - }, - "peerDependenciesMeta": { - "aws-crt": { - "optional": true - } - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/abort-controller": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.9.tgz", - "integrity": "sha512-yiW0WI30zj8ZKoSYNx90no7ugVn3khlyH/z5W8qtKBtVE6awRALbhSG+2SAHA1r6bO/6M9utxYKVZ3PCJ1rWxw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/config-resolver": { - "version": "3.0.13", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.13.tgz", - "integrity": "sha512-Gr/qwzyPaTL1tZcq8WQyHhTZREER5R1Wytmz4WnVGL4onA3dNk6Btll55c8Vr58pLdvWZmtG8oZxJTw3t3q7Jg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^3.1.12", - "@smithy/types": "^3.7.2", - "@smithy/util-config-provider": "^3.0.0", - "@smithy/util-middleware": "^3.0.11", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/core": { - "version": "2.5.7", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.5.7.tgz", - "integrity": "sha512-8olpW6mKCa0v+ibCjoCzgZHQx1SQmZuW/WkrdZo73wiTprTH6qhmskT60QLFdT9DRa5mXxjz89kQPZ7ZSsoqqg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/middleware-serde": "^3.0.11", - "@smithy/protocol-http": "^4.1.8", - "@smithy/types": "^3.7.2", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-middleware": "^3.0.11", - "@smithy/util-stream": "^3.3.4", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/credential-provider-imds": { - "version": "3.2.8", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.8.tgz", - "integrity": "sha512-ZCY2yD0BY+K9iMXkkbnjo+08T2h8/34oHd0Jmh6BZUSZwaaGlGCyBT/3wnS7u7Xl33/EEfN4B6nQr3Gx5bYxgw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^3.1.12", - "@smithy/property-provider": "^3.1.11", - "@smithy/types": "^3.7.2", - "@smithy/url-parser": "^3.0.11", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/fetch-http-handler": { - "version": "3.2.9", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-3.2.9.tgz", - "integrity": "sha512-hYNVQOqhFQ6vOpenifFME546f0GfJn2OiQ3M0FDmuUu8V/Uiwy2wej7ZXxFBNqdx0R5DZAqWM1l6VRhGz8oE6A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/protocol-http": "^4.1.4", - "@smithy/querystring-builder": "^3.0.7", - "@smithy/types": "^3.5.0", - "@smithy/util-base64": "^3.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/hash-node": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.11.tgz", - "integrity": "sha512-emP23rwYyZhQBvklqTtwetkQlqbNYirDiEEwXl2v0GYWMnCzxst7ZaRAnWuy28njp5kAH54lvkdG37MblZzaHA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "@smithy/util-buffer-from": "^3.0.0", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/invalid-dependency": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.11.tgz", - "integrity": "sha512-NuQmVPEJjUX6c+UELyVz8kUx8Q539EDeNwbRyu4IIF8MeV7hUtq1FB3SHVyki2u++5XLMFqngeMKk7ccspnNyQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/is-array-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", - "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/middleware-content-length": { - "version": "3.0.13", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.13.tgz", - "integrity": "sha512-zfMhzojhFpIX3P5ug7jxTjfUcIPcGjcQYzB9t+rv0g1TX7B0QdwONW+ATouaLoD7h7LOw/ZlXfkq4xJ/g2TrIw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/protocol-http": "^4.1.8", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/middleware-endpoint": { - "version": "3.2.8", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.2.8.tgz", - "integrity": "sha512-OEJZKVUEhMOqMs3ktrTWp7UvvluMJEvD5XgQwRePSbDg1VvBaL8pX8mwPltFn6wk1GySbcVwwyldL8S+iqnrEQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^2.5.7", - "@smithy/middleware-serde": "^3.0.11", - "@smithy/node-config-provider": "^3.1.12", - "@smithy/shared-ini-file-loader": "^3.1.12", - "@smithy/types": "^3.7.2", - "@smithy/url-parser": "^3.0.11", - "@smithy/util-middleware": "^3.0.11", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/middleware-retry": { - "version": "3.0.34", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.34.tgz", - "integrity": "sha512-yVRr/AAtPZlUvwEkrq7S3x7Z8/xCd97m2hLDaqdz6ucP2RKHsBjEqaUA2ebNv2SsZoPEi+ZD0dZbOB1u37tGCA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^3.1.12", - "@smithy/protocol-http": "^4.1.8", - "@smithy/service-error-classification": "^3.0.11", - "@smithy/smithy-client": "^3.7.0", - "@smithy/types": "^3.7.2", - "@smithy/util-middleware": "^3.0.11", - "@smithy/util-retry": "^3.0.11", - "tslib": "^2.6.2", - "uuid": "^9.0.1" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/middleware-serde": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.11.tgz", - "integrity": "sha512-KzPAeySp/fOoQA82TpnwItvX8BBURecpx6ZMu75EZDkAcnPtO6vf7q4aH5QHs/F1s3/snQaSFbbUMcFFZ086Mw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/middleware-stack": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.11.tgz", - "integrity": "sha512-1HGo9a6/ikgOMrTrWL/WiN9N8GSVYpuRQO5kjstAq4CvV59bjqnh7TbdXGQ4vxLD3xlSjfBjq5t1SOELePsLnA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/node-config-provider": { - "version": "3.1.12", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.12.tgz", - "integrity": "sha512-O9LVEu5J/u/FuNlZs+L7Ikn3lz7VB9hb0GtPT9MQeiBmtK8RSY3ULmsZgXhe6VAlgTw0YO+paQx4p8xdbs43vQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/property-provider": "^3.1.11", - "@smithy/shared-ini-file-loader": "^3.1.12", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/node-http-handler": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.3.3.tgz", - "integrity": "sha512-BrpZOaZ4RCbcJ2igiSNG16S+kgAc65l/2hmxWdmhyoGWHTLlzQzr06PXavJp9OBlPEG/sHlqdxjWmjzV66+BSQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/abort-controller": "^3.1.9", - "@smithy/protocol-http": "^4.1.8", - "@smithy/querystring-builder": "^3.0.11", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/property-provider": { - "version": "3.1.11", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.11.tgz", - "integrity": "sha512-I/+TMc4XTQ3QAjXfOcUWbSS073oOEAxgx4aZy8jHaf8JQnRkq2SZWw8+PfDtBvLUjcGMdxl+YwtzWe6i5uhL/A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/protocol-http": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.8.tgz", - "integrity": "sha512-hmgIAVyxw1LySOwkgMIUN0kjN8TG9Nc85LJeEmEE/cNEe2rkHDUWhnJf2gxcSRFLWsyqWsrZGw40ROjUogg+Iw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/querystring-builder": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.11.tgz", - "integrity": "sha512-u+5HV/9uJaeLj5XTb6+IEF/dokWWkEqJ0XiaRRogyREmKGUgZnNecLucADLdauWFKUNbQfulHFEZEdjwEBjXRg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "@smithy/util-uri-escape": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/querystring-parser": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.11.tgz", - "integrity": "sha512-Je3kFvCsFMnso1ilPwA7GtlbPaTixa3WwC+K21kmMZHsBEOZYQaqxcMqeFFoU7/slFjKDIpiiPydvdJm8Q/MCw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/service-error-classification": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.11.tgz", - "integrity": "sha512-QnYDPkyewrJzCyaeI2Rmp7pDwbUETe+hU8ADkXmgNusO1bgHBH7ovXJiYmba8t0fNfJx75fE8dlM6SEmZxheog==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/shared-ini-file-loader": { - "version": "3.1.12", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.12.tgz", - "integrity": "sha512-1xKSGI+U9KKdbG2qDvIR9dGrw3CNx+baqJfyr0igKEpjbHL5stsqAesYBzHChYHlelWtb87VnLWlhvfCz13H8Q==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/signature-v4": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-3.1.2.tgz", - "integrity": "sha512-3BcPylEsYtD0esM4Hoyml/+s7WP2LFhcM3J2AGdcL2vx9O60TtfpDOL72gjb4lU8NeRPeKAwR77YNyyGvMbuEA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^3.0.0", - "@smithy/types": "^3.3.0", - "@smithy/util-hex-encoding": "^3.0.0", - "@smithy/util-middleware": "^3.0.3", - "@smithy/util-uri-escape": "^3.0.0", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/smithy-client": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.7.0.tgz", - "integrity": "sha512-9wYrjAZFlqWhgVo3C4y/9kpc68jgiSsKUnsFPzr/MSiRL93+QRDafGTfhhKAb2wsr69Ru87WTiqSfQusSmWipA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^2.5.7", - "@smithy/middleware-endpoint": "^3.2.8", - "@smithy/middleware-stack": "^3.0.11", - "@smithy/protocol-http": "^4.1.8", - "@smithy/types": "^3.7.2", - "@smithy/util-stream": "^3.3.4", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/types": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.7.2.tgz", - "integrity": "sha512-bNwBYYmN8Eh9RyjS1p2gW6MIhSO2rl7X9QeLM8iTdcGRP+eDiIWDt66c9IysCc22gefKszZv+ubV9qZc7hdESg==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/url-parser": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.11.tgz", - "integrity": "sha512-TmlqXkSk8ZPhfc+SQutjmFr5FjC0av3GZP4B/10caK1SbRwe/v+Wzu/R6xEKxoNqL+8nY18s1byiy6HqPG37Aw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/querystring-parser": "^3.0.11", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-base64": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-3.0.0.tgz", - "integrity": "sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^3.0.0", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-body-length-browser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-3.0.0.tgz", - "integrity": "sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-body-length-node": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-3.0.0.tgz", - "integrity": "sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-buffer-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz", - "integrity": "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-config-provider": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-3.0.0.tgz", - "integrity": "sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-defaults-mode-browser": { - "version": "3.0.34", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.34.tgz", - "integrity": "sha512-FumjjF631lR521cX+svMLBj3SwSDh9VdtyynTYDAiBDEf8YPP5xORNXKQ9j0105o5+ARAGnOOP/RqSl40uXddA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/property-provider": "^3.1.11", - "@smithy/smithy-client": "^3.7.0", - "@smithy/types": "^3.7.2", - "bowser": "^2.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-defaults-mode-node": { - "version": "3.0.34", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.34.tgz", - "integrity": "sha512-vN6aHfzW9dVVzkI0wcZoUXvfjkl4CSbM9nE//08lmUMyf00S75uuCpTrqF9uD4bD9eldIXlt53colrlwKAT8Gw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/config-resolver": "^3.0.13", - "@smithy/credential-provider-imds": "^3.2.8", - "@smithy/node-config-provider": "^3.1.12", - "@smithy/property-provider": "^3.1.11", - "@smithy/smithy-client": "^3.7.0", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-endpoints": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.1.7.tgz", - "integrity": "sha512-tSfcqKcN/Oo2STEYCABVuKgJ76nyyr6skGl9t15hs+YaiU06sgMkN7QYjo0BbVw+KT26zok3IzbdSOksQ4YzVw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^3.1.12", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-hex-encoding": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-3.0.0.tgz", - "integrity": "sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-middleware": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.11.tgz", - "integrity": "sha512-dWpyc1e1R6VoXrwLoLDd57U1z6CwNSdkM69Ie4+6uYh2GC7Vg51Qtan7ITzczuVpqezdDTKJGJB95fFvvjU/ow==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-retry": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.11.tgz", - "integrity": "sha512-hJUC6W7A3DQgaee3Hp9ZFcOxVDZzmBIRBPlUAk8/fSOEl7pE/aX7Dci0JycNOnm9Mfr0KV2XjIlUOcGWXQUdVQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/service-error-classification": "^3.0.11", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-stream": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.3.4.tgz", - "integrity": "sha512-SGhGBG/KupieJvJSZp/rfHHka8BFgj56eek9px4pp7lZbOF+fRiVr4U7A3y3zJD8uGhxq32C5D96HxsTC9BckQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/fetch-http-handler": "^4.1.3", - "@smithy/node-http-handler": "^3.3.3", - "@smithy/types": "^3.7.2", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-buffer-from": "^3.0.0", - "@smithy/util-hex-encoding": "^3.0.0", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-stream/node_modules/@smithy/fetch-http-handler": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-4.1.3.tgz", - "integrity": "sha512-6SxNltSncI8s689nvnzZQc/dPXcpHQ34KUj6gR/HBroytKOd/isMG3gJF/zBE1TBmTT18TXyzhg3O3SOOqGEhA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/protocol-http": "^4.1.8", - "@smithy/querystring-builder": "^3.0.11", - "@smithy/types": "^3.7.2", - "@smithy/util-base64": "^3.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-uri-escape": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-3.0.0.tgz", - "integrity": "sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-utf8": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", - "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/fast-xml-parser": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz", - "integrity": "sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==", - "funding": [ - { - "type": "paypal", - "url": "https://paypal.me/naturalintelligence" - }, - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "dependencies": { - "strnum": "^1.0.5" - }, - "bin": { - "fxparser": "src/cli/cli.js" - } - }, - "node_modules/@aws-sdk/client-sso-oidc/node_modules/strnum": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", - "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT" - }, - "node_modules/@aws-sdk/client-sts": { - "version": "3.600.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.600.0.tgz", - "integrity": "sha512-KQG97B7LvTtTiGmjlrG1LRAY8wUvCQzrmZVV5bjrJ/1oXAU7DITYwVbSJeX9NWg6hDuSk0VE3MFwIXS2SvfLIA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/client-sso-oidc": "3.600.0", - "@aws-sdk/core": "3.598.0", - "@aws-sdk/credential-provider-node": "3.600.0", - "@aws-sdk/middleware-host-header": "3.598.0", - "@aws-sdk/middleware-logger": "3.598.0", - "@aws-sdk/middleware-recursion-detection": "3.598.0", - "@aws-sdk/middleware-user-agent": "3.598.0", - "@aws-sdk/region-config-resolver": "3.598.0", - "@aws-sdk/types": "3.598.0", - "@aws-sdk/util-endpoints": "3.598.0", - "@aws-sdk/util-user-agent-browser": "3.598.0", - "@aws-sdk/util-user-agent-node": "3.598.0", - "@smithy/config-resolver": "^3.0.2", - "@smithy/core": "^2.2.1", - "@smithy/fetch-http-handler": "^3.0.2", - "@smithy/hash-node": "^3.0.1", - "@smithy/invalid-dependency": "^3.0.1", - "@smithy/middleware-content-length": "^3.0.1", - "@smithy/middleware-endpoint": "^3.0.2", - "@smithy/middleware-retry": "^3.0.4", - "@smithy/middleware-serde": "^3.0.1", - "@smithy/middleware-stack": "^3.0.1", - "@smithy/node-config-provider": "^3.1.1", - "@smithy/node-http-handler": "^3.0.1", - "@smithy/protocol-http": "^4.0.1", - "@smithy/smithy-client": "^3.1.2", - "@smithy/types": "^3.1.0", - "@smithy/url-parser": "^3.0.1", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.4", - "@smithy/util-defaults-mode-node": "^3.0.4", - "@smithy/util-endpoints": "^2.0.2", - "@smithy/util-middleware": "^3.0.1", - "@smithy/util-retry": "^3.0.1", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/client-sso": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.598.0.tgz", - "integrity": "sha512-nOI5lqPYa+YZlrrzwAJywJSw3MKVjvu6Ge2fCqQUNYMfxFB0NAaDFnl0EPjXi+sEbtCuz/uWE77poHbqiZ+7Iw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.598.0", - "@aws-sdk/middleware-host-header": "3.598.0", - "@aws-sdk/middleware-logger": "3.598.0", - "@aws-sdk/middleware-recursion-detection": "3.598.0", - "@aws-sdk/middleware-user-agent": "3.598.0", - "@aws-sdk/region-config-resolver": "3.598.0", - "@aws-sdk/types": "3.598.0", - "@aws-sdk/util-endpoints": "3.598.0", - "@aws-sdk/util-user-agent-browser": "3.598.0", - "@aws-sdk/util-user-agent-node": "3.598.0", - "@smithy/config-resolver": "^3.0.2", - "@smithy/core": "^2.2.1", - "@smithy/fetch-http-handler": "^3.0.2", - "@smithy/hash-node": "^3.0.1", - "@smithy/invalid-dependency": "^3.0.1", - "@smithy/middleware-content-length": "^3.0.1", - "@smithy/middleware-endpoint": "^3.0.2", - "@smithy/middleware-retry": "^3.0.4", - "@smithy/middleware-serde": "^3.0.1", - "@smithy/middleware-stack": "^3.0.1", - "@smithy/node-config-provider": "^3.1.1", - "@smithy/node-http-handler": "^3.0.1", - "@smithy/protocol-http": "^4.0.1", - "@smithy/smithy-client": "^3.1.2", - "@smithy/types": "^3.1.0", - "@smithy/url-parser": "^3.0.1", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.4", - "@smithy/util-defaults-mode-node": "^3.0.4", - "@smithy/util-endpoints": "^2.0.2", - "@smithy/util-middleware": "^3.0.1", - "@smithy/util-retry": "^3.0.1", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/core": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.598.0.tgz", - "integrity": "sha512-HaSjt7puO5Cc7cOlrXFCW0rtA0BM9lvzjl56x0A20Pt+0wxXGeTOZZOkXQIepbrFkV2e/HYukuT9e99vXDm59g==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^2.2.1", - "@smithy/protocol-http": "^4.0.1", - "@smithy/signature-v4": "^3.1.0", - "@smithy/smithy-client": "^3.1.2", - "@smithy/types": "^3.1.0", - "fast-xml-parser": "4.2.5", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/credential-provider-env": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.598.0.tgz", - "integrity": "sha512-vi1khgn7yXzLCcgSIzQrrtd2ilUM0dWodxj3PQ6BLfP0O+q1imO3hG1nq7DVyJtq7rFHs6+9N8G4mYvTkxby2w==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/property-provider": "^3.1.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/credential-provider-http": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.598.0.tgz", - "integrity": "sha512-N7cIafi4HVlQvEgvZSo1G4T9qb/JMLGMdBsDCT5XkeJrF0aptQWzTFH0jIdZcLrMYvzPcuEyO3yCBe6cy/ba0g==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/fetch-http-handler": "^3.0.2", - "@smithy/node-http-handler": "^3.0.1", - "@smithy/property-provider": "^3.1.1", - "@smithy/protocol-http": "^4.0.1", - "@smithy/smithy-client": "^3.1.2", - "@smithy/types": "^3.1.0", - "@smithy/util-stream": "^3.0.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.598.0.tgz", - "integrity": "sha512-/ppcIVUbRwDIwJDoYfp90X3+AuJo2mvE52Y1t2VSrvUovYn6N4v95/vXj6LS8CNDhz2jvEJYmu+0cTMHdhI6eA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.598.0", - "@aws-sdk/credential-provider-http": "3.598.0", - "@aws-sdk/credential-provider-process": "3.598.0", - "@aws-sdk/credential-provider-sso": "3.598.0", - "@aws-sdk/credential-provider-web-identity": "3.598.0", - "@aws-sdk/types": "3.598.0", - "@smithy/credential-provider-imds": "^3.1.1", - "@smithy/property-provider": "^3.1.1", - "@smithy/shared-ini-file-loader": "^3.1.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sts": "^3.598.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/credential-provider-node": { - "version": "3.600.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.600.0.tgz", - "integrity": "sha512-1pC7MPMYD45J7yFjA90SxpR0yaSvy+yZiq23aXhAPZLYgJBAxHLu0s0mDCk/piWGPh8+UGur5K0bVdx4B1D5hw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.598.0", - "@aws-sdk/credential-provider-http": "3.598.0", - "@aws-sdk/credential-provider-ini": "3.598.0", - "@aws-sdk/credential-provider-process": "3.598.0", - "@aws-sdk/credential-provider-sso": "3.598.0", - "@aws-sdk/credential-provider-web-identity": "3.598.0", - "@aws-sdk/types": "3.598.0", - "@smithy/credential-provider-imds": "^3.1.1", - "@smithy/property-provider": "^3.1.1", - "@smithy/shared-ini-file-loader": "^3.1.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/credential-provider-process": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.598.0.tgz", - "integrity": "sha512-rM707XbLW8huMk722AgjVyxu2tMZee++fNA8TJVNgs1Ma02Wx6bBrfIvlyK0rCcIRb0WdQYP6fe3Xhiu4e8IBA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/property-provider": "^3.1.1", - "@smithy/shared-ini-file-loader": "^3.1.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.598.0.tgz", - "integrity": "sha512-5InwUmrAuqQdOOgxTccRayMMkSmekdLk6s+az9tmikq0QFAHUCtofI+/fllMXSR9iL6JbGYi1940+EUmS4pHJA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/client-sso": "3.598.0", - "@aws-sdk/token-providers": "3.598.0", - "@aws-sdk/types": "3.598.0", - "@smithy/property-provider": "^3.1.1", - "@smithy/shared-ini-file-loader": "^3.1.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.598.0.tgz", - "integrity": "sha512-TKY1EVdHVBnZqpyxyTHdpZpa1tUpb6nxVeRNn1zWG8QB5MvH4ALLd/jR+gtmWDNQbIG4cVuBOZFVL8hIYicKTA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/property-provider": "^3.1.1", - "@smithy/shared-ini-file-loader": "^3.1.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sso-oidc": "^3.598.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.598.0.tgz", - "integrity": "sha512-GV5GdiMbz5Tz9JO4NJtRoFXjW0GPEujA0j+5J/B723rTN+REHthJu48HdBKouHGhdzkDWkkh1bu52V02Wprw8w==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/property-provider": "^3.1.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sts": "^3.598.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/middleware-host-header": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.598.0.tgz", - "integrity": "sha512-WiaG059YBQwQraNejLIi0gMNkX7dfPZ8hDIhvMr5aVPRbaHH8AYF3iNSsXYCHvA2Cfa1O9haYXsuMF9flXnCmA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/protocol-http": "^4.0.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/middleware-logger": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.598.0.tgz", - "integrity": "sha512-bxBjf/VYiu3zfu8SYM2S9dQQc3tz5uBAOcPz/Bt8DyyK3GgOpjhschH/2XuUErsoUO1gDJqZSdGOmuHGZQn00Q==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.598.0.tgz", - "integrity": "sha512-vjT9BeFY9FeN0f8hm2l6F53tI0N5bUq6RcDkQXKNabXBnQxKptJRad6oP2X5y3FoVfBLOuDkQgiC2940GIPxtQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/protocol-http": "^4.0.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.598.0.tgz", - "integrity": "sha512-4tjESlHG5B5MdjUaLK7tQs/miUtHbb6deauQx8ryqSBYOhfHVgb1ZnzvQR0bTrhpqUg0WlybSkDaZAICf9xctg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@aws-sdk/util-endpoints": "3.598.0", - "@smithy/protocol-http": "^4.0.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/region-config-resolver": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.598.0.tgz", - "integrity": "sha512-oYXhmTokSav4ytmWleCr3rs/1nyvZW/S0tdi6X7u+dLNL5Jee+uMxWGzgOrWK6wrQOzucLVjS4E/wA11Kv2GTw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/node-config-provider": "^3.1.1", - "@smithy/types": "^3.1.0", - "@smithy/util-config-provider": "^3.0.0", - "@smithy/util-middleware": "^3.0.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/types": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.598.0.tgz", - "integrity": "sha512-742uRl6z7u0LFmZwDrFP6r1wlZcgVPw+/TilluDJmCAR8BgRw3IR+743kUXKBGd8QZDRW2n6v/PYsi/AWCDDMQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/util-endpoints": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.598.0.tgz", - "integrity": "sha512-Qo9UoiVVZxcOEdiOMZg3xb1mzkTxrhd4qSlg5QQrfWPJVx/QOg+Iy0NtGxPtHtVZNHZxohYwDwV/tfsnDSE2gQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/types": "^3.1.0", - "@smithy/util-endpoints": "^2.0.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.598.0.tgz", - "integrity": "sha512-36Sxo6F+ykElaL1mWzWjlg+1epMpSe8obwhCN1yGE7Js9ywy5U6k6l+A3q3YM9YRbm740sNxncbwLklMvuhTKw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/types": "^3.1.0", - "bowser": "^2.11.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.598.0.tgz", - "integrity": "sha512-oyWGcOlfTdzkC6SVplyr0AGh54IMrDxbhg5RxJ5P+V4BKfcDoDcZV9xenUk9NsOi9MuUjxMumb9UJGkDhM1m0A==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/node-config-provider": "^3.1.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "aws-crt": ">=1.0.0" - }, - "peerDependenciesMeta": { - "aws-crt": { - "optional": true - } - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@smithy/abort-controller": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.9.tgz", - "integrity": "sha512-yiW0WI30zj8ZKoSYNx90no7ugVn3khlyH/z5W8qtKBtVE6awRALbhSG+2SAHA1r6bO/6M9utxYKVZ3PCJ1rWxw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@smithy/config-resolver": { - "version": "3.0.13", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.13.tgz", - "integrity": "sha512-Gr/qwzyPaTL1tZcq8WQyHhTZREER5R1Wytmz4WnVGL4onA3dNk6Btll55c8Vr58pLdvWZmtG8oZxJTw3t3q7Jg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^3.1.12", - "@smithy/types": "^3.7.2", - "@smithy/util-config-provider": "^3.0.0", - "@smithy/util-middleware": "^3.0.11", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@smithy/core": { - "version": "2.5.7", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.5.7.tgz", - "integrity": "sha512-8olpW6mKCa0v+ibCjoCzgZHQx1SQmZuW/WkrdZo73wiTprTH6qhmskT60QLFdT9DRa5mXxjz89kQPZ7ZSsoqqg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/middleware-serde": "^3.0.11", - "@smithy/protocol-http": "^4.1.8", - "@smithy/types": "^3.7.2", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-middleware": "^3.0.11", - "@smithy/util-stream": "^3.3.4", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@smithy/credential-provider-imds": { - "version": "3.2.8", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.8.tgz", - "integrity": "sha512-ZCY2yD0BY+K9iMXkkbnjo+08T2h8/34oHd0Jmh6BZUSZwaaGlGCyBT/3wnS7u7Xl33/EEfN4B6nQr3Gx5bYxgw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^3.1.12", - "@smithy/property-provider": "^3.1.11", - "@smithy/types": "^3.7.2", - "@smithy/url-parser": "^3.0.11", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@smithy/fetch-http-handler": { - "version": "3.2.9", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-3.2.9.tgz", - "integrity": "sha512-hYNVQOqhFQ6vOpenifFME546f0GfJn2OiQ3M0FDmuUu8V/Uiwy2wej7ZXxFBNqdx0R5DZAqWM1l6VRhGz8oE6A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/protocol-http": "^4.1.4", - "@smithy/querystring-builder": "^3.0.7", - "@smithy/types": "^3.5.0", - "@smithy/util-base64": "^3.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@smithy/hash-node": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.11.tgz", - "integrity": "sha512-emP23rwYyZhQBvklqTtwetkQlqbNYirDiEEwXl2v0GYWMnCzxst7ZaRAnWuy28njp5kAH54lvkdG37MblZzaHA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "@smithy/util-buffer-from": "^3.0.0", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@smithy/invalid-dependency": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.11.tgz", - "integrity": "sha512-NuQmVPEJjUX6c+UELyVz8kUx8Q539EDeNwbRyu4IIF8MeV7hUtq1FB3SHVyki2u++5XLMFqngeMKk7ccspnNyQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@smithy/is-array-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", - "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@smithy/middleware-content-length": { - "version": "3.0.13", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.13.tgz", - "integrity": "sha512-zfMhzojhFpIX3P5ug7jxTjfUcIPcGjcQYzB9t+rv0g1TX7B0QdwONW+ATouaLoD7h7LOw/ZlXfkq4xJ/g2TrIw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/protocol-http": "^4.1.8", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@smithy/middleware-endpoint": { - "version": "3.2.8", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.2.8.tgz", - "integrity": "sha512-OEJZKVUEhMOqMs3ktrTWp7UvvluMJEvD5XgQwRePSbDg1VvBaL8pX8mwPltFn6wk1GySbcVwwyldL8S+iqnrEQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^2.5.7", - "@smithy/middleware-serde": "^3.0.11", - "@smithy/node-config-provider": "^3.1.12", - "@smithy/shared-ini-file-loader": "^3.1.12", - "@smithy/types": "^3.7.2", - "@smithy/url-parser": "^3.0.11", - "@smithy/util-middleware": "^3.0.11", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@smithy/middleware-retry": { - "version": "3.0.34", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.34.tgz", - "integrity": "sha512-yVRr/AAtPZlUvwEkrq7S3x7Z8/xCd97m2hLDaqdz6ucP2RKHsBjEqaUA2ebNv2SsZoPEi+ZD0dZbOB1u37tGCA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^3.1.12", - "@smithy/protocol-http": "^4.1.8", - "@smithy/service-error-classification": "^3.0.11", - "@smithy/smithy-client": "^3.7.0", - "@smithy/types": "^3.7.2", - "@smithy/util-middleware": "^3.0.11", - "@smithy/util-retry": "^3.0.11", - "tslib": "^2.6.2", - "uuid": "^9.0.1" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@smithy/middleware-serde": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.11.tgz", - "integrity": "sha512-KzPAeySp/fOoQA82TpnwItvX8BBURecpx6ZMu75EZDkAcnPtO6vf7q4aH5QHs/F1s3/snQaSFbbUMcFFZ086Mw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@smithy/middleware-stack": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.11.tgz", - "integrity": "sha512-1HGo9a6/ikgOMrTrWL/WiN9N8GSVYpuRQO5kjstAq4CvV59bjqnh7TbdXGQ4vxLD3xlSjfBjq5t1SOELePsLnA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@smithy/node-config-provider": { - "version": "3.1.12", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.12.tgz", - "integrity": "sha512-O9LVEu5J/u/FuNlZs+L7Ikn3lz7VB9hb0GtPT9MQeiBmtK8RSY3ULmsZgXhe6VAlgTw0YO+paQx4p8xdbs43vQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/property-provider": "^3.1.11", - "@smithy/shared-ini-file-loader": "^3.1.12", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@smithy/node-http-handler": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.3.3.tgz", - "integrity": "sha512-BrpZOaZ4RCbcJ2igiSNG16S+kgAc65l/2hmxWdmhyoGWHTLlzQzr06PXavJp9OBlPEG/sHlqdxjWmjzV66+BSQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/abort-controller": "^3.1.9", - "@smithy/protocol-http": "^4.1.8", - "@smithy/querystring-builder": "^3.0.11", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@smithy/property-provider": { - "version": "3.1.11", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.11.tgz", - "integrity": "sha512-I/+TMc4XTQ3QAjXfOcUWbSS073oOEAxgx4aZy8jHaf8JQnRkq2SZWw8+PfDtBvLUjcGMdxl+YwtzWe6i5uhL/A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@smithy/protocol-http": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.8.tgz", - "integrity": "sha512-hmgIAVyxw1LySOwkgMIUN0kjN8TG9Nc85LJeEmEE/cNEe2rkHDUWhnJf2gxcSRFLWsyqWsrZGw40ROjUogg+Iw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@smithy/querystring-builder": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.11.tgz", - "integrity": "sha512-u+5HV/9uJaeLj5XTb6+IEF/dokWWkEqJ0XiaRRogyREmKGUgZnNecLucADLdauWFKUNbQfulHFEZEdjwEBjXRg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "@smithy/util-uri-escape": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@smithy/querystring-parser": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.11.tgz", - "integrity": "sha512-Je3kFvCsFMnso1ilPwA7GtlbPaTixa3WwC+K21kmMZHsBEOZYQaqxcMqeFFoU7/slFjKDIpiiPydvdJm8Q/MCw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@smithy/service-error-classification": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.11.tgz", - "integrity": "sha512-QnYDPkyewrJzCyaeI2Rmp7pDwbUETe+hU8ADkXmgNusO1bgHBH7ovXJiYmba8t0fNfJx75fE8dlM6SEmZxheog==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@smithy/shared-ini-file-loader": { - "version": "3.1.12", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.12.tgz", - "integrity": "sha512-1xKSGI+U9KKdbG2qDvIR9dGrw3CNx+baqJfyr0igKEpjbHL5stsqAesYBzHChYHlelWtb87VnLWlhvfCz13H8Q==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@smithy/signature-v4": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-3.1.2.tgz", - "integrity": "sha512-3BcPylEsYtD0esM4Hoyml/+s7WP2LFhcM3J2AGdcL2vx9O60TtfpDOL72gjb4lU8NeRPeKAwR77YNyyGvMbuEA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^3.0.0", - "@smithy/types": "^3.3.0", - "@smithy/util-hex-encoding": "^3.0.0", - "@smithy/util-middleware": "^3.0.3", - "@smithy/util-uri-escape": "^3.0.0", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@smithy/smithy-client": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.7.0.tgz", - "integrity": "sha512-9wYrjAZFlqWhgVo3C4y/9kpc68jgiSsKUnsFPzr/MSiRL93+QRDafGTfhhKAb2wsr69Ru87WTiqSfQusSmWipA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^2.5.7", - "@smithy/middleware-endpoint": "^3.2.8", - "@smithy/middleware-stack": "^3.0.11", - "@smithy/protocol-http": "^4.1.8", - "@smithy/types": "^3.7.2", - "@smithy/util-stream": "^3.3.4", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@smithy/types": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.7.2.tgz", - "integrity": "sha512-bNwBYYmN8Eh9RyjS1p2gW6MIhSO2rl7X9QeLM8iTdcGRP+eDiIWDt66c9IysCc22gefKszZv+ubV9qZc7hdESg==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@smithy/url-parser": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.11.tgz", - "integrity": "sha512-TmlqXkSk8ZPhfc+SQutjmFr5FjC0av3GZP4B/10caK1SbRwe/v+Wzu/R6xEKxoNqL+8nY18s1byiy6HqPG37Aw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/querystring-parser": "^3.0.11", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@smithy/util-base64": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-3.0.0.tgz", - "integrity": "sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^3.0.0", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@smithy/util-body-length-browser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-3.0.0.tgz", - "integrity": "sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@smithy/util-body-length-node": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-3.0.0.tgz", - "integrity": "sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@smithy/util-buffer-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz", - "integrity": "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@smithy/util-config-provider": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-3.0.0.tgz", - "integrity": "sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@smithy/util-defaults-mode-browser": { - "version": "3.0.34", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.34.tgz", - "integrity": "sha512-FumjjF631lR521cX+svMLBj3SwSDh9VdtyynTYDAiBDEf8YPP5xORNXKQ9j0105o5+ARAGnOOP/RqSl40uXddA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/property-provider": "^3.1.11", - "@smithy/smithy-client": "^3.7.0", - "@smithy/types": "^3.7.2", - "bowser": "^2.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@smithy/util-defaults-mode-node": { - "version": "3.0.34", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.34.tgz", - "integrity": "sha512-vN6aHfzW9dVVzkI0wcZoUXvfjkl4CSbM9nE//08lmUMyf00S75uuCpTrqF9uD4bD9eldIXlt53colrlwKAT8Gw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/config-resolver": "^3.0.13", - "@smithy/credential-provider-imds": "^3.2.8", - "@smithy/node-config-provider": "^3.1.12", - "@smithy/property-provider": "^3.1.11", - "@smithy/smithy-client": "^3.7.0", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@smithy/util-endpoints": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.1.7.tgz", - "integrity": "sha512-tSfcqKcN/Oo2STEYCABVuKgJ76nyyr6skGl9t15hs+YaiU06sgMkN7QYjo0BbVw+KT26zok3IzbdSOksQ4YzVw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^3.1.12", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@smithy/util-hex-encoding": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-3.0.0.tgz", - "integrity": "sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@smithy/util-middleware": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.11.tgz", - "integrity": "sha512-dWpyc1e1R6VoXrwLoLDd57U1z6CwNSdkM69Ie4+6uYh2GC7Vg51Qtan7ITzczuVpqezdDTKJGJB95fFvvjU/ow==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@smithy/util-retry": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.11.tgz", - "integrity": "sha512-hJUC6W7A3DQgaee3Hp9ZFcOxVDZzmBIRBPlUAk8/fSOEl7pE/aX7Dci0JycNOnm9Mfr0KV2XjIlUOcGWXQUdVQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/service-error-classification": "^3.0.11", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@smithy/util-stream": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.3.4.tgz", - "integrity": "sha512-SGhGBG/KupieJvJSZp/rfHHka8BFgj56eek9px4pp7lZbOF+fRiVr4U7A3y3zJD8uGhxq32C5D96HxsTC9BckQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/fetch-http-handler": "^4.1.3", - "@smithy/node-http-handler": "^3.3.3", - "@smithy/types": "^3.7.2", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-buffer-from": "^3.0.0", - "@smithy/util-hex-encoding": "^3.0.0", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@smithy/util-stream/node_modules/@smithy/fetch-http-handler": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-4.1.3.tgz", - "integrity": "sha512-6SxNltSncI8s689nvnzZQc/dPXcpHQ34KUj6gR/HBroytKOd/isMG3gJF/zBE1TBmTT18TXyzhg3O3SOOqGEhA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/protocol-http": "^4.1.8", - "@smithy/querystring-builder": "^3.0.11", - "@smithy/types": "^3.7.2", - "@smithy/util-base64": "^3.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@smithy/util-uri-escape": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-3.0.0.tgz", - "integrity": "sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/@smithy/util-utf8": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", - "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/fast-xml-parser": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz", - "integrity": "sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==", - "funding": [ - { - "type": "paypal", - "url": "https://paypal.me/naturalintelligence" - }, - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "dependencies": { - "strnum": "^1.0.5" - }, - "bin": { - "fxparser": "src/cli/cli.js" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/strnum": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", - "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT" - }, - "node_modules/@aws-sdk/core": { - "version": "3.879.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.879.0.tgz", - "integrity": "sha512-AhNmLCrx980LsK+SfPXGh7YqTyZxsK0Qmy18mWmkfY0TSq7WLaSDB5zdQbgbnQCACCHy8DUYXbi4KsjlIhv3PA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.862.0", - "@aws-sdk/xml-builder": "3.873.0", - "@smithy/core": "^3.9.0", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/property-provider": "^4.0.5", - "@smithy/protocol-http": "^5.1.3", - "@smithy/signature-v4": "^5.1.3", - "@smithy/smithy-client": "^4.5.0", - "@smithy/types": "^4.3.2", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-middleware": "^4.0.5", - "@smithy/util-utf8": "^4.0.0", - "fast-xml-parser": "5.2.5", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-cognito-identity": { - "version": "3.600.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.600.0.tgz", - "integrity": "sha512-AIM+B06d1+71EuBrk2UR9ZZgRS3a+ARxE3oZKMZYlfqtZ3kY8w4DkhEt7OVruc6uSsMhkrcQT6nxsOxFSi4RtA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/client-cognito-identity": "3.600.0", - "@aws-sdk/types": "3.598.0", - "@smithy/property-provider": "^3.1.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-cognito-identity/node_modules/@aws-sdk/types": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.598.0.tgz", - "integrity": "sha512-742uRl6z7u0LFmZwDrFP6r1wlZcgVPw+/TilluDJmCAR8BgRw3IR+743kUXKBGd8QZDRW2n6v/PYsi/AWCDDMQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-cognito-identity/node_modules/@smithy/property-provider": { - "version": "3.1.11", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.11.tgz", - "integrity": "sha512-I/+TMc4XTQ3QAjXfOcUWbSS073oOEAxgx4aZy8jHaf8JQnRkq2SZWw8+PfDtBvLUjcGMdxl+YwtzWe6i5uhL/A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-cognito-identity/node_modules/@smithy/types": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.7.2.tgz", - "integrity": "sha512-bNwBYYmN8Eh9RyjS1p2gW6MIhSO2rl7X9QeLM8iTdcGRP+eDiIWDt66c9IysCc22gefKszZv+ubV9qZc7hdESg==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.879.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.879.0.tgz", - "integrity": "sha512-JgG7A8SSbr5IiCYL8kk39Y9chdSB5GPwBorDW8V8mr19G9L+qd6ohED4fAocoNFaDnYJ5wGAHhCfSJjzcsPBVQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.879.0", - "@aws-sdk/types": "3.862.0", - "@smithy/property-provider": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.879.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.879.0.tgz", - "integrity": "sha512-2hM5ByLpyK+qORUexjtYyDZsgxVCCUiJQZRMGkNXFEGz6zTpbjfTIWoh3zRgWHEBiqyPIyfEy50eIF69WshcuA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.879.0", - "@aws-sdk/types": "3.862.0", - "@smithy/fetch-http-handler": "^5.1.1", - "@smithy/node-http-handler": "^4.1.1", - "@smithy/property-provider": "^4.0.5", - "@smithy/protocol-http": "^5.1.3", - "@smithy/smithy-client": "^4.5.0", - "@smithy/types": "^4.3.2", - "@smithy/util-stream": "^4.2.4", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.879.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.879.0.tgz", - "integrity": "sha512-07M8zfb73KmMBqVO5/V3Ea9kqDspMX0fO0kaI1bsjWI6ngnMye8jCE0/sIhmkVAI0aU709VA0g+Bzlopnw9EoQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.879.0", - "@aws-sdk/credential-provider-env": "3.879.0", - "@aws-sdk/credential-provider-http": "3.879.0", - "@aws-sdk/credential-provider-process": "3.879.0", - "@aws-sdk/credential-provider-sso": "3.879.0", - "@aws-sdk/credential-provider-web-identity": "3.879.0", - "@aws-sdk/nested-clients": "3.879.0", - "@aws-sdk/types": "3.862.0", - "@smithy/credential-provider-imds": "^4.0.7", - "@smithy/property-provider": "^4.0.5", - "@smithy/shared-ini-file-loader": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.879.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.879.0.tgz", - "integrity": "sha512-FYaAqJbnSTrVL2iZkNDj2hj5087yMv2RN2GA8DJhe7iOJjzhzRojrtlfpWeJg6IhK0sBKDH+YXbdeexCzUJvtA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.879.0", - "@aws-sdk/credential-provider-http": "3.879.0", - "@aws-sdk/credential-provider-ini": "3.879.0", - "@aws-sdk/credential-provider-process": "3.879.0", - "@aws-sdk/credential-provider-sso": "3.879.0", - "@aws-sdk/credential-provider-web-identity": "3.879.0", - "@aws-sdk/types": "3.862.0", - "@smithy/credential-provider-imds": "^4.0.7", - "@smithy/property-provider": "^4.0.5", - "@smithy/shared-ini-file-loader": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.879.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.879.0.tgz", - "integrity": "sha512-7r360x1VyEt35Sm1JFOzww2WpnfJNBbvvnzoyLt7WRfK0S/AfsuWhu5ltJ80QvJ0R3AiSNbG+q/btG2IHhDYPQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.879.0", - "@aws-sdk/types": "3.862.0", - "@smithy/property-provider": "^4.0.5", - "@smithy/shared-ini-file-loader": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.879.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.879.0.tgz", - "integrity": "sha512-gd27B0NsgtKlaPNARj4IX7F7US5NuU691rGm0EUSkDsM7TctvJULighKoHzPxDQlrDbVI11PW4WtKS/Zg5zPlQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/client-sso": "3.879.0", - "@aws-sdk/core": "3.879.0", - "@aws-sdk/token-providers": "3.879.0", - "@aws-sdk/types": "3.862.0", - "@smithy/property-provider": "^4.0.5", - "@smithy/shared-ini-file-loader": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.879.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.879.0.tgz", - "integrity": "sha512-Jy4uPFfGzHk1Mxy+/Wr43vuw9yXsE2yiF4e4598vc3aJfO0YtA2nSfbKD3PNKRORwXbeKqWPfph9SCKQpWoxEg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.879.0", - "@aws-sdk/nested-clients": "3.879.0", - "@aws-sdk/types": "3.862.0", - "@smithy/property-provider": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers": { - "version": "3.600.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.600.0.tgz", - "integrity": "sha512-cC9uqmX0rgx1efiJGqeR+i0EXr8RQ5SAzH7M45WNBZpYiLEe6reWgIYJY9hmOxuaoMdWSi8kekuN3IjTIORRjw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/client-cognito-identity": "3.600.0", - "@aws-sdk/client-sso": "3.598.0", - "@aws-sdk/client-sts": "3.600.0", - "@aws-sdk/credential-provider-cognito-identity": "3.600.0", - "@aws-sdk/credential-provider-env": "3.598.0", - "@aws-sdk/credential-provider-http": "3.598.0", - "@aws-sdk/credential-provider-ini": "3.598.0", - "@aws-sdk/credential-provider-node": "3.600.0", - "@aws-sdk/credential-provider-process": "3.598.0", - "@aws-sdk/credential-provider-sso": "3.598.0", - "@aws-sdk/credential-provider-web-identity": "3.598.0", - "@aws-sdk/types": "3.598.0", - "@smithy/credential-provider-imds": "^3.1.1", - "@smithy/property-provider": "^3.1.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/client-sso": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.598.0.tgz", - "integrity": "sha512-nOI5lqPYa+YZlrrzwAJywJSw3MKVjvu6Ge2fCqQUNYMfxFB0NAaDFnl0EPjXi+sEbtCuz/uWE77poHbqiZ+7Iw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.598.0", - "@aws-sdk/middleware-host-header": "3.598.0", - "@aws-sdk/middleware-logger": "3.598.0", - "@aws-sdk/middleware-recursion-detection": "3.598.0", - "@aws-sdk/middleware-user-agent": "3.598.0", - "@aws-sdk/region-config-resolver": "3.598.0", - "@aws-sdk/types": "3.598.0", - "@aws-sdk/util-endpoints": "3.598.0", - "@aws-sdk/util-user-agent-browser": "3.598.0", - "@aws-sdk/util-user-agent-node": "3.598.0", - "@smithy/config-resolver": "^3.0.2", - "@smithy/core": "^2.2.1", - "@smithy/fetch-http-handler": "^3.0.2", - "@smithy/hash-node": "^3.0.1", - "@smithy/invalid-dependency": "^3.0.1", - "@smithy/middleware-content-length": "^3.0.1", - "@smithy/middleware-endpoint": "^3.0.2", - "@smithy/middleware-retry": "^3.0.4", - "@smithy/middleware-serde": "^3.0.1", - "@smithy/middleware-stack": "^3.0.1", - "@smithy/node-config-provider": "^3.1.1", - "@smithy/node-http-handler": "^3.0.1", - "@smithy/protocol-http": "^4.0.1", - "@smithy/smithy-client": "^3.1.2", - "@smithy/types": "^3.1.0", - "@smithy/url-parser": "^3.0.1", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.4", - "@smithy/util-defaults-mode-node": "^3.0.4", - "@smithy/util-endpoints": "^2.0.2", - "@smithy/util-middleware": "^3.0.1", - "@smithy/util-retry": "^3.0.1", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/core": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.598.0.tgz", - "integrity": "sha512-HaSjt7puO5Cc7cOlrXFCW0rtA0BM9lvzjl56x0A20Pt+0wxXGeTOZZOkXQIepbrFkV2e/HYukuT9e99vXDm59g==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^2.2.1", - "@smithy/protocol-http": "^4.0.1", - "@smithy/signature-v4": "^3.1.0", - "@smithy/smithy-client": "^3.1.2", - "@smithy/types": "^3.1.0", - "fast-xml-parser": "4.2.5", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/credential-provider-env": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.598.0.tgz", - "integrity": "sha512-vi1khgn7yXzLCcgSIzQrrtd2ilUM0dWodxj3PQ6BLfP0O+q1imO3hG1nq7DVyJtq7rFHs6+9N8G4mYvTkxby2w==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/property-provider": "^3.1.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/credential-provider-http": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.598.0.tgz", - "integrity": "sha512-N7cIafi4HVlQvEgvZSo1G4T9qb/JMLGMdBsDCT5XkeJrF0aptQWzTFH0jIdZcLrMYvzPcuEyO3yCBe6cy/ba0g==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/fetch-http-handler": "^3.0.2", - "@smithy/node-http-handler": "^3.0.1", - "@smithy/property-provider": "^3.1.1", - "@smithy/protocol-http": "^4.0.1", - "@smithy/smithy-client": "^3.1.2", - "@smithy/types": "^3.1.0", - "@smithy/util-stream": "^3.0.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.598.0.tgz", - "integrity": "sha512-/ppcIVUbRwDIwJDoYfp90X3+AuJo2mvE52Y1t2VSrvUovYn6N4v95/vXj6LS8CNDhz2jvEJYmu+0cTMHdhI6eA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.598.0", - "@aws-sdk/credential-provider-http": "3.598.0", - "@aws-sdk/credential-provider-process": "3.598.0", - "@aws-sdk/credential-provider-sso": "3.598.0", - "@aws-sdk/credential-provider-web-identity": "3.598.0", - "@aws-sdk/types": "3.598.0", - "@smithy/credential-provider-imds": "^3.1.1", - "@smithy/property-provider": "^3.1.1", - "@smithy/shared-ini-file-loader": "^3.1.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sts": "^3.598.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/credential-provider-node": { - "version": "3.600.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.600.0.tgz", - "integrity": "sha512-1pC7MPMYD45J7yFjA90SxpR0yaSvy+yZiq23aXhAPZLYgJBAxHLu0s0mDCk/piWGPh8+UGur5K0bVdx4B1D5hw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.598.0", - "@aws-sdk/credential-provider-http": "3.598.0", - "@aws-sdk/credential-provider-ini": "3.598.0", - "@aws-sdk/credential-provider-process": "3.598.0", - "@aws-sdk/credential-provider-sso": "3.598.0", - "@aws-sdk/credential-provider-web-identity": "3.598.0", - "@aws-sdk/types": "3.598.0", - "@smithy/credential-provider-imds": "^3.1.1", - "@smithy/property-provider": "^3.1.1", - "@smithy/shared-ini-file-loader": "^3.1.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/credential-provider-process": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.598.0.tgz", - "integrity": "sha512-rM707XbLW8huMk722AgjVyxu2tMZee++fNA8TJVNgs1Ma02Wx6bBrfIvlyK0rCcIRb0WdQYP6fe3Xhiu4e8IBA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/property-provider": "^3.1.1", - "@smithy/shared-ini-file-loader": "^3.1.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.598.0.tgz", - "integrity": "sha512-5InwUmrAuqQdOOgxTccRayMMkSmekdLk6s+az9tmikq0QFAHUCtofI+/fllMXSR9iL6JbGYi1940+EUmS4pHJA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/client-sso": "3.598.0", - "@aws-sdk/token-providers": "3.598.0", - "@aws-sdk/types": "3.598.0", - "@smithy/property-provider": "^3.1.1", - "@smithy/shared-ini-file-loader": "^3.1.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.598.0.tgz", - "integrity": "sha512-GV5GdiMbz5Tz9JO4NJtRoFXjW0GPEujA0j+5J/B723rTN+REHthJu48HdBKouHGhdzkDWkkh1bu52V02Wprw8w==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/property-provider": "^3.1.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sts": "^3.598.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/middleware-host-header": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.598.0.tgz", - "integrity": "sha512-WiaG059YBQwQraNejLIi0gMNkX7dfPZ8hDIhvMr5aVPRbaHH8AYF3iNSsXYCHvA2Cfa1O9haYXsuMF9flXnCmA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/protocol-http": "^4.0.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/middleware-logger": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.598.0.tgz", - "integrity": "sha512-bxBjf/VYiu3zfu8SYM2S9dQQc3tz5uBAOcPz/Bt8DyyK3GgOpjhschH/2XuUErsoUO1gDJqZSdGOmuHGZQn00Q==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.598.0.tgz", - "integrity": "sha512-vjT9BeFY9FeN0f8hm2l6F53tI0N5bUq6RcDkQXKNabXBnQxKptJRad6oP2X5y3FoVfBLOuDkQgiC2940GIPxtQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/protocol-http": "^4.0.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.598.0.tgz", - "integrity": "sha512-4tjESlHG5B5MdjUaLK7tQs/miUtHbb6deauQx8ryqSBYOhfHVgb1ZnzvQR0bTrhpqUg0WlybSkDaZAICf9xctg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@aws-sdk/util-endpoints": "3.598.0", - "@smithy/protocol-http": "^4.0.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/region-config-resolver": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.598.0.tgz", - "integrity": "sha512-oYXhmTokSav4ytmWleCr3rs/1nyvZW/S0tdi6X7u+dLNL5Jee+uMxWGzgOrWK6wrQOzucLVjS4E/wA11Kv2GTw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/node-config-provider": "^3.1.1", - "@smithy/types": "^3.1.0", - "@smithy/util-config-provider": "^3.0.0", - "@smithy/util-middleware": "^3.0.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/token-providers": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.598.0.tgz", - "integrity": "sha512-TKY1EVdHVBnZqpyxyTHdpZpa1tUpb6nxVeRNn1zWG8QB5MvH4ALLd/jR+gtmWDNQbIG4cVuBOZFVL8hIYicKTA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/property-provider": "^3.1.1", - "@smithy/shared-ini-file-loader": "^3.1.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sso-oidc": "^3.598.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/types": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.598.0.tgz", - "integrity": "sha512-742uRl6z7u0LFmZwDrFP6r1wlZcgVPw+/TilluDJmCAR8BgRw3IR+743kUXKBGd8QZDRW2n6v/PYsi/AWCDDMQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/util-endpoints": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.598.0.tgz", - "integrity": "sha512-Qo9UoiVVZxcOEdiOMZg3xb1mzkTxrhd4qSlg5QQrfWPJVx/QOg+Iy0NtGxPtHtVZNHZxohYwDwV/tfsnDSE2gQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/types": "^3.1.0", - "@smithy/util-endpoints": "^2.0.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.598.0.tgz", - "integrity": "sha512-36Sxo6F+ykElaL1mWzWjlg+1epMpSe8obwhCN1yGE7Js9ywy5U6k6l+A3q3YM9YRbm740sNxncbwLklMvuhTKw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/types": "^3.1.0", - "bowser": "^2.11.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.598.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.598.0.tgz", - "integrity": "sha512-oyWGcOlfTdzkC6SVplyr0AGh54IMrDxbhg5RxJ5P+V4BKfcDoDcZV9xenUk9NsOi9MuUjxMumb9UJGkDhM1m0A==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.598.0", - "@smithy/node-config-provider": "^3.1.1", - "@smithy/types": "^3.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "aws-crt": ">=1.0.0" - }, - "peerDependenciesMeta": { - "aws-crt": { - "optional": true - } - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/abort-controller": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.9.tgz", - "integrity": "sha512-yiW0WI30zj8ZKoSYNx90no7ugVn3khlyH/z5W8qtKBtVE6awRALbhSG+2SAHA1r6bO/6M9utxYKVZ3PCJ1rWxw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/config-resolver": { - "version": "3.0.13", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.13.tgz", - "integrity": "sha512-Gr/qwzyPaTL1tZcq8WQyHhTZREER5R1Wytmz4WnVGL4onA3dNk6Btll55c8Vr58pLdvWZmtG8oZxJTw3t3q7Jg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^3.1.12", - "@smithy/types": "^3.7.2", - "@smithy/util-config-provider": "^3.0.0", - "@smithy/util-middleware": "^3.0.11", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/core": { - "version": "2.5.7", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.5.7.tgz", - "integrity": "sha512-8olpW6mKCa0v+ibCjoCzgZHQx1SQmZuW/WkrdZo73wiTprTH6qhmskT60QLFdT9DRa5mXxjz89kQPZ7ZSsoqqg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/middleware-serde": "^3.0.11", - "@smithy/protocol-http": "^4.1.8", - "@smithy/types": "^3.7.2", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-middleware": "^3.0.11", - "@smithy/util-stream": "^3.3.4", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/credential-provider-imds": { - "version": "3.2.8", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.8.tgz", - "integrity": "sha512-ZCY2yD0BY+K9iMXkkbnjo+08T2h8/34oHd0Jmh6BZUSZwaaGlGCyBT/3wnS7u7Xl33/EEfN4B6nQr3Gx5bYxgw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^3.1.12", - "@smithy/property-provider": "^3.1.11", - "@smithy/types": "^3.7.2", - "@smithy/url-parser": "^3.0.11", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/fetch-http-handler": { - "version": "3.2.9", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-3.2.9.tgz", - "integrity": "sha512-hYNVQOqhFQ6vOpenifFME546f0GfJn2OiQ3M0FDmuUu8V/Uiwy2wej7ZXxFBNqdx0R5DZAqWM1l6VRhGz8oE6A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/protocol-http": "^4.1.4", - "@smithy/querystring-builder": "^3.0.7", - "@smithy/types": "^3.5.0", - "@smithy/util-base64": "^3.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/hash-node": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.11.tgz", - "integrity": "sha512-emP23rwYyZhQBvklqTtwetkQlqbNYirDiEEwXl2v0GYWMnCzxst7ZaRAnWuy28njp5kAH54lvkdG37MblZzaHA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "@smithy/util-buffer-from": "^3.0.0", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/invalid-dependency": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.11.tgz", - "integrity": "sha512-NuQmVPEJjUX6c+UELyVz8kUx8Q539EDeNwbRyu4IIF8MeV7hUtq1FB3SHVyki2u++5XLMFqngeMKk7ccspnNyQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/is-array-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", - "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/middleware-content-length": { - "version": "3.0.13", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.13.tgz", - "integrity": "sha512-zfMhzojhFpIX3P5ug7jxTjfUcIPcGjcQYzB9t+rv0g1TX7B0QdwONW+ATouaLoD7h7LOw/ZlXfkq4xJ/g2TrIw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/protocol-http": "^4.1.8", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/middleware-endpoint": { - "version": "3.2.8", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.2.8.tgz", - "integrity": "sha512-OEJZKVUEhMOqMs3ktrTWp7UvvluMJEvD5XgQwRePSbDg1VvBaL8pX8mwPltFn6wk1GySbcVwwyldL8S+iqnrEQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^2.5.7", - "@smithy/middleware-serde": "^3.0.11", - "@smithy/node-config-provider": "^3.1.12", - "@smithy/shared-ini-file-loader": "^3.1.12", - "@smithy/types": "^3.7.2", - "@smithy/url-parser": "^3.0.11", - "@smithy/util-middleware": "^3.0.11", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/middleware-retry": { - "version": "3.0.34", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.34.tgz", - "integrity": "sha512-yVRr/AAtPZlUvwEkrq7S3x7Z8/xCd97m2hLDaqdz6ucP2RKHsBjEqaUA2ebNv2SsZoPEi+ZD0dZbOB1u37tGCA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^3.1.12", - "@smithy/protocol-http": "^4.1.8", - "@smithy/service-error-classification": "^3.0.11", - "@smithy/smithy-client": "^3.7.0", - "@smithy/types": "^3.7.2", - "@smithy/util-middleware": "^3.0.11", - "@smithy/util-retry": "^3.0.11", - "tslib": "^2.6.2", - "uuid": "^9.0.1" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/middleware-serde": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.11.tgz", - "integrity": "sha512-KzPAeySp/fOoQA82TpnwItvX8BBURecpx6ZMu75EZDkAcnPtO6vf7q4aH5QHs/F1s3/snQaSFbbUMcFFZ086Mw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/middleware-stack": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.11.tgz", - "integrity": "sha512-1HGo9a6/ikgOMrTrWL/WiN9N8GSVYpuRQO5kjstAq4CvV59bjqnh7TbdXGQ4vxLD3xlSjfBjq5t1SOELePsLnA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/node-config-provider": { - "version": "3.1.12", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.12.tgz", - "integrity": "sha512-O9LVEu5J/u/FuNlZs+L7Ikn3lz7VB9hb0GtPT9MQeiBmtK8RSY3ULmsZgXhe6VAlgTw0YO+paQx4p8xdbs43vQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/property-provider": "^3.1.11", - "@smithy/shared-ini-file-loader": "^3.1.12", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/node-http-handler": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.3.3.tgz", - "integrity": "sha512-BrpZOaZ4RCbcJ2igiSNG16S+kgAc65l/2hmxWdmhyoGWHTLlzQzr06PXavJp9OBlPEG/sHlqdxjWmjzV66+BSQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/abort-controller": "^3.1.9", - "@smithy/protocol-http": "^4.1.8", - "@smithy/querystring-builder": "^3.0.11", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/property-provider": { - "version": "3.1.11", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.11.tgz", - "integrity": "sha512-I/+TMc4XTQ3QAjXfOcUWbSS073oOEAxgx4aZy8jHaf8JQnRkq2SZWw8+PfDtBvLUjcGMdxl+YwtzWe6i5uhL/A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/protocol-http": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.8.tgz", - "integrity": "sha512-hmgIAVyxw1LySOwkgMIUN0kjN8TG9Nc85LJeEmEE/cNEe2rkHDUWhnJf2gxcSRFLWsyqWsrZGw40ROjUogg+Iw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/querystring-builder": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.11.tgz", - "integrity": "sha512-u+5HV/9uJaeLj5XTb6+IEF/dokWWkEqJ0XiaRRogyREmKGUgZnNecLucADLdauWFKUNbQfulHFEZEdjwEBjXRg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "@smithy/util-uri-escape": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/querystring-parser": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.11.tgz", - "integrity": "sha512-Je3kFvCsFMnso1ilPwA7GtlbPaTixa3WwC+K21kmMZHsBEOZYQaqxcMqeFFoU7/slFjKDIpiiPydvdJm8Q/MCw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/service-error-classification": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.11.tgz", - "integrity": "sha512-QnYDPkyewrJzCyaeI2Rmp7pDwbUETe+hU8ADkXmgNusO1bgHBH7ovXJiYmba8t0fNfJx75fE8dlM6SEmZxheog==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/shared-ini-file-loader": { - "version": "3.1.12", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.12.tgz", - "integrity": "sha512-1xKSGI+U9KKdbG2qDvIR9dGrw3CNx+baqJfyr0igKEpjbHL5stsqAesYBzHChYHlelWtb87VnLWlhvfCz13H8Q==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/signature-v4": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-3.1.2.tgz", - "integrity": "sha512-3BcPylEsYtD0esM4Hoyml/+s7WP2LFhcM3J2AGdcL2vx9O60TtfpDOL72gjb4lU8NeRPeKAwR77YNyyGvMbuEA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^3.0.0", - "@smithy/types": "^3.3.0", - "@smithy/util-hex-encoding": "^3.0.0", - "@smithy/util-middleware": "^3.0.3", - "@smithy/util-uri-escape": "^3.0.0", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/smithy-client": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.7.0.tgz", - "integrity": "sha512-9wYrjAZFlqWhgVo3C4y/9kpc68jgiSsKUnsFPzr/MSiRL93+QRDafGTfhhKAb2wsr69Ru87WTiqSfQusSmWipA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^2.5.7", - "@smithy/middleware-endpoint": "^3.2.8", - "@smithy/middleware-stack": "^3.0.11", - "@smithy/protocol-http": "^4.1.8", - "@smithy/types": "^3.7.2", - "@smithy/util-stream": "^3.3.4", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/types": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.7.2.tgz", - "integrity": "sha512-bNwBYYmN8Eh9RyjS1p2gW6MIhSO2rl7X9QeLM8iTdcGRP+eDiIWDt66c9IysCc22gefKszZv+ubV9qZc7hdESg==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/url-parser": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.11.tgz", - "integrity": "sha512-TmlqXkSk8ZPhfc+SQutjmFr5FjC0av3GZP4B/10caK1SbRwe/v+Wzu/R6xEKxoNqL+8nY18s1byiy6HqPG37Aw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/querystring-parser": "^3.0.11", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-base64": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-3.0.0.tgz", - "integrity": "sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^3.0.0", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-body-length-browser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-3.0.0.tgz", - "integrity": "sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-body-length-node": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-3.0.0.tgz", - "integrity": "sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-buffer-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz", - "integrity": "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-config-provider": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-3.0.0.tgz", - "integrity": "sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-defaults-mode-browser": { - "version": "3.0.34", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.34.tgz", - "integrity": "sha512-FumjjF631lR521cX+svMLBj3SwSDh9VdtyynTYDAiBDEf8YPP5xORNXKQ9j0105o5+ARAGnOOP/RqSl40uXddA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/property-provider": "^3.1.11", - "@smithy/smithy-client": "^3.7.0", - "@smithy/types": "^3.7.2", - "bowser": "^2.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-defaults-mode-node": { - "version": "3.0.34", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.34.tgz", - "integrity": "sha512-vN6aHfzW9dVVzkI0wcZoUXvfjkl4CSbM9nE//08lmUMyf00S75uuCpTrqF9uD4bD9eldIXlt53colrlwKAT8Gw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/config-resolver": "^3.0.13", - "@smithy/credential-provider-imds": "^3.2.8", - "@smithy/node-config-provider": "^3.1.12", - "@smithy/property-provider": "^3.1.11", - "@smithy/smithy-client": "^3.7.0", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-endpoints": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.1.7.tgz", - "integrity": "sha512-tSfcqKcN/Oo2STEYCABVuKgJ76nyyr6skGl9t15hs+YaiU06sgMkN7QYjo0BbVw+KT26zok3IzbdSOksQ4YzVw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^3.1.12", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-hex-encoding": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-3.0.0.tgz", - "integrity": "sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-middleware": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.11.tgz", - "integrity": "sha512-dWpyc1e1R6VoXrwLoLDd57U1z6CwNSdkM69Ie4+6uYh2GC7Vg51Qtan7ITzczuVpqezdDTKJGJB95fFvvjU/ow==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-retry": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.11.tgz", - "integrity": "sha512-hJUC6W7A3DQgaee3Hp9ZFcOxVDZzmBIRBPlUAk8/fSOEl7pE/aX7Dci0JycNOnm9Mfr0KV2XjIlUOcGWXQUdVQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/service-error-classification": "^3.0.11", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-stream": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.3.4.tgz", - "integrity": "sha512-SGhGBG/KupieJvJSZp/rfHHka8BFgj56eek9px4pp7lZbOF+fRiVr4U7A3y3zJD8uGhxq32C5D96HxsTC9BckQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/fetch-http-handler": "^4.1.3", - "@smithy/node-http-handler": "^3.3.3", - "@smithy/types": "^3.7.2", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-buffer-from": "^3.0.0", - "@smithy/util-hex-encoding": "^3.0.0", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-stream/node_modules/@smithy/fetch-http-handler": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-4.1.3.tgz", - "integrity": "sha512-6SxNltSncI8s689nvnzZQc/dPXcpHQ34KUj6gR/HBroytKOd/isMG3gJF/zBE1TBmTT18TXyzhg3O3SOOqGEhA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/protocol-http": "^4.1.8", - "@smithy/querystring-builder": "^3.0.11", - "@smithy/types": "^3.7.2", - "@smithy/util-base64": "^3.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-uri-escape": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-3.0.0.tgz", - "integrity": "sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-utf8": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", - "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/fast-xml-parser": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz", - "integrity": "sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==", - "funding": [ - { - "type": "paypal", - "url": "https://paypal.me/naturalintelligence" - }, - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "dependencies": { - "strnum": "^1.0.5" - }, - "bin": { - "fxparser": "src/cli/cli.js" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/strnum": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", - "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT" - }, - "node_modules/@aws-sdk/eventstream-codec": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-codec/-/eventstream-codec-3.370.0.tgz", - "integrity": "sha512-PiaDMum7TNsIE3DGECSsNYwibBIPN2/e13BJbTwi6KgVx8BV2mYA3kQkaUDiy++tEpzN81Nh5OPTFVb7bvgYYg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/crc32": "3.0.0", - "@aws-sdk/types": "3.370.0", - "@aws-sdk/util-hex-encoding": "3.310.0", - "tslib": "^2.5.0" - } - }, - "node_modules/@aws-sdk/eventstream-codec/node_modules/@aws-crypto/crc32": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-3.0.0.tgz", - "integrity": "sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/util": "^3.0.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^1.11.1" - } - }, - "node_modules/@aws-sdk/eventstream-codec/node_modules/@aws-crypto/crc32/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "license": "0BSD" - }, - "node_modules/@aws-sdk/eventstream-codec/node_modules/@aws-crypto/util": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz", - "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-utf8-browser": "^3.0.0", - "tslib": "^1.11.1" - } - }, - "node_modules/@aws-sdk/eventstream-codec/node_modules/@aws-crypto/util/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "license": "0BSD" - }, - "node_modules/@aws-sdk/eventstream-codec/node_modules/@aws-sdk/types": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.370.0.tgz", - "integrity": "sha512-8PGMKklSkRKjunFhzM2y5Jm0H2TBu7YRNISdYzXLUHKSP9zlMEYagseKVdmox0zKHf1LXVNuSlUV2b6SRrieCQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^1.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/eventstream-codec/node_modules/@smithy/types": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-1.2.0.tgz", - "integrity": "sha512-z1r00TvBqF3dh4aHhya7nz1HhvCg4TRmw51fjMrh5do3h+ngSstt/yKlNbHeb9QxJmFbmN8KEVSWgb1bRvfEoA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/is-array-buffer": { - "version": "3.310.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/is-array-buffer/-/is-array-buffer-3.310.0.tgz", - "integrity": "sha512-urnbcCR+h9NWUnmOtet/s4ghvzsidFmspfhYaHAmSRdy9yDjdjBJMFjjsn85A1ODUktztm+cVncXjQ38WCMjMQ==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/middleware-bucket-endpoint": { - "version": "3.873.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.873.0.tgz", - "integrity": "sha512-b4bvr0QdADeTUs+lPc9Z48kXzbKHXQKgTvxx/jXDgSW9tv4KmYPO1gIj6Z9dcrBkRWQuUtSW3Tu2S5n6pe+zeg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.862.0", - "@aws-sdk/util-arn-parser": "3.873.0", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/protocol-http": "^5.1.3", - "@smithy/types": "^4.3.2", - "@smithy/util-config-provider": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-expect-continue": { - "version": "3.873.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.873.0.tgz", - "integrity": "sha512-GIqoc8WgRcf/opBOZXFLmplJQKwOMjiOMmDz9gQkaJ8FiVJoAp8EGVmK2TOWZMQUYsavvHYsHaor5R2xwPoGVg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.862.0", - "@smithy/protocol-http": "^5.1.3", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-flexible-checksums": { - "version": "3.879.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.879.0.tgz", - "integrity": "sha512-U1rcWToy2rlQPQLsx5h73uTC1XYo/JpnlJGCc3Iw7b1qrK8Mke4+rgMPKCfnXELD5TTazGrbT03frxH4Y1Ycvw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/crc32": "5.2.0", - "@aws-crypto/crc32c": "5.2.0", - "@aws-crypto/util": "5.2.0", - "@aws-sdk/core": "3.879.0", - "@aws-sdk/types": "3.862.0", - "@smithy/is-array-buffer": "^4.0.0", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/protocol-http": "^5.1.3", - "@smithy/types": "^4.3.2", - "@smithy/util-middleware": "^4.0.5", - "@smithy/util-stream": "^4.2.4", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-host-header": { - "version": "3.873.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.873.0.tgz", - "integrity": "sha512-KZ/W1uruWtMOs7D5j3KquOxzCnV79KQW9MjJFZM/M0l6KI8J6V3718MXxFHsTjUE4fpdV6SeCNLV1lwGygsjJA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.862.0", - "@smithy/protocol-http": "^5.1.3", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-location-constraint": { - "version": "3.873.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.873.0.tgz", - "integrity": "sha512-r+hIaORsW/8rq6wieDordXnA/eAu7xAPLue2InhoEX6ML7irP52BgiibHLpt9R0psiCzIHhju8qqKa4pJOrmiw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.862.0", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-logger": { - "version": "3.876.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.876.0.tgz", - "integrity": "sha512-cpWJhOuMSyz9oV25Z/CMHCBTgafDCbv7fHR80nlRrPdPZ8ETNsahwRgltXP1QJJ8r3X/c1kwpOR7tc+RabVzNA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.862.0", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.873.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.873.0.tgz", - "integrity": "sha512-OtgY8EXOzRdEWR//WfPkA/fXl0+WwE8hq0y9iw2caNyKPtca85dzrrZWnPqyBK/cpImosrpR1iKMYr41XshsCg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.862.0", - "@smithy/protocol-http": "^5.1.3", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-sdk-s3": { - "version": "3.879.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.879.0.tgz", - "integrity": "sha512-ZTpLr2AbZcCsEzu18YCtB8Tp8tjAWHT0ccfwy3HiL6g9ncuSMW+7BVi1hDYmBidFwpPbnnIMtM0db3pDMR6/WA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.879.0", - "@aws-sdk/types": "3.862.0", - "@aws-sdk/util-arn-parser": "3.873.0", - "@smithy/core": "^3.9.0", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/protocol-http": "^5.1.3", - "@smithy/signature-v4": "^5.1.3", - "@smithy/smithy-client": "^4.5.0", - "@smithy/types": "^4.3.2", - "@smithy/util-config-provider": "^4.0.0", - "@smithy/util-middleware": "^4.0.5", - "@smithy/util-stream": "^4.2.4", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-ssec": { - "version": "3.873.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.873.0.tgz", - "integrity": "sha512-AF55J94BoiuzN7g3hahy0dXTVZahVi8XxRBLgzNp6yQf0KTng+hb/V9UQZVYY1GZaDczvvvnqC54RGe9OZZ9zQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.862.0", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.879.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.879.0.tgz", - "integrity": "sha512-DDSV8228lQxeMAFKnigkd0fHzzn5aauZMYC3CSj6e5/qE7+9OwpkUcjHfb7HZ9KWG6L2/70aKZXHqiJ4xKhOZw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.879.0", - "@aws-sdk/types": "3.862.0", - "@aws-sdk/util-endpoints": "3.879.0", - "@smithy/core": "^3.9.0", - "@smithy/protocol-http": "^5.1.3", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/nested-clients": { - "version": "3.879.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.879.0.tgz", - "integrity": "sha512-7+n9NpIz9QtKYnxmw1fHi9C8o0GrX8LbBR4D50c7bH6Iq5+XdSuL5AFOWWQ5cMD0JhqYYJhK/fJsVau3nUtC4g==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.879.0", - "@aws-sdk/middleware-host-header": "3.873.0", - "@aws-sdk/middleware-logger": "3.876.0", - "@aws-sdk/middleware-recursion-detection": "3.873.0", - "@aws-sdk/middleware-user-agent": "3.879.0", - "@aws-sdk/region-config-resolver": "3.873.0", - "@aws-sdk/types": "3.862.0", - "@aws-sdk/util-endpoints": "3.879.0", - "@aws-sdk/util-user-agent-browser": "3.873.0", - "@aws-sdk/util-user-agent-node": "3.879.0", - "@smithy/config-resolver": "^4.1.5", - "@smithy/core": "^3.9.0", - "@smithy/fetch-http-handler": "^5.1.1", - "@smithy/hash-node": "^4.0.5", - "@smithy/invalid-dependency": "^4.0.5", - "@smithy/middleware-content-length": "^4.0.5", - "@smithy/middleware-endpoint": "^4.1.19", - "@smithy/middleware-retry": "^4.1.20", - "@smithy/middleware-serde": "^4.0.9", - "@smithy/middleware-stack": "^4.0.5", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/node-http-handler": "^4.1.1", - "@smithy/protocol-http": "^5.1.3", - "@smithy/smithy-client": "^4.5.0", - "@smithy/types": "^4.3.2", - "@smithy/url-parser": "^4.0.5", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.27", - "@smithy/util-defaults-mode-node": "^4.0.27", - "@smithy/util-endpoints": "^3.0.7", - "@smithy/util-middleware": "^4.0.5", - "@smithy/util-retry": "^4.0.7", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/protocol-http": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/protocol-http/-/protocol-http-3.370.0.tgz", - "integrity": "sha512-MfZCgSsVmir+4kJps7xT0awOPNi+swBpcVp9ZtAP7POduUVV6zVLurMNLXsppKsErggssD5E9HUgQFs5w06U4Q==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.370.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/protocol-http/node_modules/@aws-sdk/types": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.370.0.tgz", - "integrity": "sha512-8PGMKklSkRKjunFhzM2y5Jm0H2TBu7YRNISdYzXLUHKSP9zlMEYagseKVdmox0zKHf1LXVNuSlUV2b6SRrieCQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^1.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/protocol-http/node_modules/@smithy/types": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-1.2.0.tgz", - "integrity": "sha512-z1r00TvBqF3dh4aHhya7nz1HhvCg4TRmw51fjMrh5do3h+ngSstt/yKlNbHeb9QxJmFbmN8KEVSWgb1bRvfEoA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/region-config-resolver": { - "version": "3.873.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.873.0.tgz", - "integrity": "sha512-q9sPoef+BBG6PJnc4x60vK/bfVwvRWsPgcoQyIra057S/QGjq5VkjvNk6H8xedf6vnKlXNBwq9BaANBXnldUJg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.862.0", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/types": "^4.3.2", - "@smithy/util-config-provider": "^4.0.0", - "@smithy/util-middleware": "^4.0.5", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/s3-request-presigner": { - "version": "3.879.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.879.0.tgz", - "integrity": "sha512-WNUrY4UW1ZAkBiSq9HnhJcG/1NdrEy37DDxqE8u0OdIZHhbgU1x1r4iXgQssAZhV6D+Ib70oiQGtPSH/lXeMKg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/signature-v4-multi-region": "3.879.0", - "@aws-sdk/types": "3.862.0", - "@aws-sdk/util-format-url": "3.873.0", - "@smithy/middleware-endpoint": "^4.1.19", - "@smithy/protocol-http": "^5.1.3", - "@smithy/smithy-client": "^4.5.0", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/signature-v4": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4/-/signature-v4-3.370.0.tgz", - "integrity": "sha512-Mh++NJiXoBxMzz4d8GQPNB37nqjS1gsVwjKoSAWFE67sjgsjb8D5JWRCm9CinqPoXi2iN57+1DcQalTDKQGc0A==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/eventstream-codec": "3.370.0", - "@aws-sdk/is-array-buffer": "3.310.0", - "@aws-sdk/types": "3.370.0", - "@aws-sdk/util-hex-encoding": "3.310.0", - "@aws-sdk/util-middleware": "3.370.0", - "@aws-sdk/util-uri-escape": "3.310.0", - "@aws-sdk/util-utf8": "3.310.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.879.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.879.0.tgz", - "integrity": "sha512-MDsw0EWOHyKac75X3gD8tLWtmPuRliS/s4IhWRhsdDCU13wewHIs5IlA5B65kT6ISf49yEIalEH3FHUSVqdmIQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/middleware-sdk-s3": "3.879.0", - "@aws-sdk/types": "3.862.0", - "@smithy/protocol-http": "^5.1.3", - "@smithy/signature-v4": "^5.1.3", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/signature-v4/node_modules/@aws-sdk/types": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.370.0.tgz", - "integrity": "sha512-8PGMKklSkRKjunFhzM2y5Jm0H2TBu7YRNISdYzXLUHKSP9zlMEYagseKVdmox0zKHf1LXVNuSlUV2b6SRrieCQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^1.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/signature-v4/node_modules/@smithy/types": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-1.2.0.tgz", - "integrity": "sha512-z1r00TvBqF3dh4aHhya7nz1HhvCg4TRmw51fjMrh5do3h+ngSstt/yKlNbHeb9QxJmFbmN8KEVSWgb1bRvfEoA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/token-providers": { - "version": "3.879.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.879.0.tgz", - "integrity": "sha512-47J7sCwXdnw9plRZNAGVkNEOlSiLb/kR2slnDIHRK9NB/ECKsoqgz5OZQJ9E2f0yqOs8zSNJjn3T01KxpgW8Qw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.879.0", - "@aws-sdk/nested-clients": "3.879.0", - "@aws-sdk/types": "3.862.0", - "@smithy/property-provider": "^4.0.5", - "@smithy/shared-ini-file-loader": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/types": { - "version": "3.862.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.862.0.tgz", - "integrity": "sha512-Bei+RL0cDxxV+lW2UezLbCYYNeJm6Nzee0TpW0FfyTRBhH9C1XQh4+x+IClriXvgBnRquTMMYsmJfvx8iyLKrg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/util-arn-parser": { - "version": "3.873.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.873.0.tgz", - "integrity": "sha512-qag+VTqnJWDn8zTAXX4wiVioa0hZDQMtbZcGRERVnLar4/3/VIKBhxX2XibNQXFu1ufgcRn4YntT/XEPecFWcg==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/util-buffer-from": { - "version": "3.310.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-buffer-from/-/util-buffer-from-3.310.0.tgz", - "integrity": "sha512-i6LVeXFtGih5Zs8enLrt+ExXY92QV25jtEnTKHsmlFqFAuL3VBeod6boeMXkN2p9lbSVVQ1sAOOYZOHYbYkntw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/is-array-buffer": "3.310.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/util-endpoints": { - "version": "3.879.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.879.0.tgz", - "integrity": "sha512-aVAJwGecYoEmbEFju3127TyJDF9qJsKDUUTRMDuS8tGn+QiWQFnfInmbt+el9GU1gEJupNTXV+E3e74y51fb7A==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.862.0", - "@smithy/types": "^4.3.2", - "@smithy/url-parser": "^4.0.5", - "@smithy/util-endpoints": "^3.0.7", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/util-format-url": { - "version": "3.873.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-format-url/-/util-format-url-3.873.0.tgz", - "integrity": "sha512-v//b9jFnhzTKKV3HFTw2MakdM22uBAs2lBov51BWmFXuFtSTdBLrR7zgfetQPE3PVkFai0cmtJQPdc3MX+T/cQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.862.0", - "@smithy/querystring-builder": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/util-hex-encoding": { - "version": "3.310.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-hex-encoding/-/util-hex-encoding-3.310.0.tgz", - "integrity": "sha512-sVN7mcCCDSJ67pI1ZMtk84SKGqyix6/0A1Ab163YKn+lFBQRMKexleZzpYzNGxYzmQS6VanP/cfU7NiLQOaSfA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/util-locate-window": { - "version": "3.873.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.873.0.tgz", - "integrity": "sha512-xcVhZF6svjM5Rj89T1WzkjQmrTF6dpR2UvIHPMTnSZoNe6CixejPZ6f0JJ2kAhO8H+dUHwNBlsUgOTIKiK/Syg==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/util-middleware": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-middleware/-/util-middleware-3.370.0.tgz", - "integrity": "sha512-Jvs9FZHaQznWGLkRel3PFEP93I1n0Kp6356zxYHk3LIOmjpzoob3R+v96mzyN+dZrnhPdPubYS41qbU2F9lROg==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/util-uri-escape": { - "version": "3.310.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-uri-escape/-/util-uri-escape-3.310.0.tgz", - "integrity": "sha512-drzt+aB2qo2LgtDoiy/3sVG8w63cgLkqFIa2NFlGpUgHFWTXkqtbgf4L5QdjRGKWhmZsnqkbtL7vkSWEcYDJ4Q==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.873.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.873.0.tgz", - "integrity": "sha512-AcRdbK6o19yehEcywI43blIBhOCSo6UgyWcuOJX5CFF8k39xm1ILCjQlRRjchLAxWrm0lU0Q7XV90RiMMFMZtA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.862.0", - "@smithy/types": "^4.3.2", - "bowser": "^2.11.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.879.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.879.0.tgz", - "integrity": "sha512-A5KGc1S+CJRzYnuxJQQmH1BtGsz46AgyHkqReKfGiNQA8ET/9y9LQ5t2ABqnSBHHIh3+MiCcQSkUZ0S3rTodrQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/middleware-user-agent": "3.879.0", - "@aws-sdk/types": "3.862.0", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "aws-crt": ">=1.0.0" - }, - "peerDependenciesMeta": { - "aws-crt": { - "optional": true - } - } - }, - "node_modules/@aws-sdk/util-utf8": { - "version": "3.310.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8/-/util-utf8-3.310.0.tgz", - "integrity": "sha512-DnLfFT8uCO22uOJc0pt0DsSNau1GTisngBCDw8jQuWT5CqogMJu4b/uXmwEqfj8B3GX6Xsz8zOd6JpRlPftQoA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/util-buffer-from": "3.310.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/util-utf8-browser": { - "version": "3.259.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", - "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.3.1" - } - }, - "node_modules/@aws-sdk/xml-builder": { - "version": "3.873.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.873.0.tgz", - "integrity": "sha512-kLO7k7cGJ6KaHiExSJWojZurF7SnGMDHXRuQunFnEoD0n1yB6Lqy/S/zHiQ7oJnBhPr9q0TW9qFkrsZb1Uc54w==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@capacitor-community/bluetooth-le": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@capacitor-community/bluetooth-le/-/bluetooth-le-7.2.0.tgz", - "integrity": "sha512-eBM0Ia6C68YKqYbGjy6xgjaBrCvfH9EICn77EqxpR3Pu9yniRGTlCuZQdcCAonLwp16uuyAh/5tESxkkVJMccA==", - "license": "MIT", - "dependencies": { - "@types/web-bluetooth": "^0.0.20" - }, - "peerDependencies": { - "@capacitor/core": ">=7.0.0" - } - }, - "node_modules/@capacitor/core": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@capacitor/core/-/core-7.4.4.tgz", - "integrity": "sha512-xzjxpr+d2zwTpCaN0k+C6wKSZzWFAb9OVEUtmO72ihjr/NEDoLvsGl4WLfjWPcCO2zOy0b2X52tfRWjECFUjtw==", - "license": "MIT", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/@drizzle-team/brocli": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@drizzle-team/brocli/-/brocli-0.10.2.tgz", - "integrity": "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/@emnapi/runtime": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.0.tgz", - "integrity": "sha512-oAYoQnCYaQZKVS53Fq23ceWMRxq5EhQsE0x0RdQ55jT7wagMu5k+fS39v1fiSLrtrLQlXwVINenqhLMtTrV/1Q==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@esbuild-kit/core-utils": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/@esbuild-kit/core-utils/-/core-utils-3.3.2.tgz", - "integrity": "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==", - "deprecated": "Merged into tsx: https://tsx.is", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "~0.18.20", - "source-map-support": "^0.5.21" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/android-arm": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", - "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/android-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", - "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/android-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", - "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/darwin-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", - "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/darwin-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", - "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/freebsd-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", - "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/freebsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", - "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-arm": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", - "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", - "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-ia32": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", - "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-loong64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", - "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-mips64el": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", - "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-ppc64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", - "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-riscv64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", - "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-s390x": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", - "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", - "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/netbsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", - "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/openbsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", - "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/sunos-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", - "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/win32-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", - "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/win32-ia32": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", - "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/win32-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", - "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/esbuild": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", - "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/android-arm": "0.18.20", - "@esbuild/android-arm64": "0.18.20", - "@esbuild/android-x64": "0.18.20", - "@esbuild/darwin-arm64": "0.18.20", - "@esbuild/darwin-x64": "0.18.20", - "@esbuild/freebsd-arm64": "0.18.20", - "@esbuild/freebsd-x64": "0.18.20", - "@esbuild/linux-arm": "0.18.20", - "@esbuild/linux-arm64": "0.18.20", - "@esbuild/linux-ia32": "0.18.20", - "@esbuild/linux-loong64": "0.18.20", - "@esbuild/linux-mips64el": "0.18.20", - "@esbuild/linux-ppc64": "0.18.20", - "@esbuild/linux-riscv64": "0.18.20", - "@esbuild/linux-s390x": "0.18.20", - "@esbuild/linux-x64": "0.18.20", - "@esbuild/netbsd-x64": "0.18.20", - "@esbuild/openbsd-x64": "0.18.20", - "@esbuild/sunos-x64": "0.18.20", - "@esbuild/win32-arm64": "0.18.20", - "@esbuild/win32-ia32": "0.18.20", - "@esbuild/win32-x64": "0.18.20" - } - }, - "node_modules/@esbuild-kit/esm-loader": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/@esbuild-kit/esm-loader/-/esm-loader-2.6.5.tgz", - "integrity": "sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==", - "deprecated": "Merged into tsx: https://tsx.is", - "dev": true, - "license": "MIT", - "dependencies": { - "@esbuild-kit/core-utils": "^3.3.2", - "get-tsconfig": "^4.7.0" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", - "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz", - "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz", - "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz", - "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz", - "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz", - "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz", - "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz", - "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz", - "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz", - "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz", - "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz", - "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz", - "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz", - "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz", - "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz", - "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz", - "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz", - "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz", - "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz", - "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz", - "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz", - "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz", - "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz", - "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz", - "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz", - "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@fastify/accept-negotiator": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@fastify/accept-negotiator/-/accept-negotiator-2.0.1.tgz", - "integrity": "sha512-/c/TW2bO/v9JeEgoD/g1G5GxGeCF1Hafdf79WPmUlgYiBXummY0oX3VVq4yFkKKVBKDNlaDUYoab7g38RpPqCQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT" - }, - "node_modules/@fastify/ajv-compiler": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-4.0.2.tgz", - "integrity": "sha512-Rkiu/8wIjpsf46Rr+Fitd3HRP+VsxUFDDeag0hs9L0ksfnwx2g7SPQQTFL0E8Qv+rfXzQOxBJnjUB9ITUDjfWQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT", - "dependencies": { - "ajv": "^8.12.0", - "ajv-formats": "^3.0.1", - "fast-uri": "^3.0.0" - } - }, - "node_modules/@fastify/busboy": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.2.0.tgz", - "integrity": "sha512-m9FVDXU3GT2ITSe0UaMA5rU3QkfC/UXtCU8y0gSN/GugTqtVldOBWIB5V6V3sbmenVZUIpU6f+mPEO2+m5iTaA==", - "license": "MIT" - }, - "node_modules/@fastify/cookie": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/@fastify/cookie/-/cookie-11.0.2.tgz", - "integrity": "sha512-GWdwdGlgJxyvNv+QcKiGNevSspMQXncjMZ1J8IvuDQk0jvkzgWWZFNC2En3s+nHndZBGV8IbLwOI/sxCZw/mzA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT", - "dependencies": { - "cookie": "^1.0.0", - "fastify-plugin": "^5.0.0" - } - }, - "node_modules/@fastify/cors": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/@fastify/cors/-/cors-11.1.0.tgz", - "integrity": "sha512-sUw8ed8wP2SouWZTIbA7V2OQtMNpLj2W6qJOYhNdcmINTu6gsxVYXjQiM9mdi8UUDlcoDDJ/W2syPo1WB2QjYA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT", - "dependencies": { - "fastify-plugin": "^5.0.0", - "toad-cache": "^3.7.0" - } - }, - "node_modules/@fastify/deepmerge": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@fastify/deepmerge/-/deepmerge-2.0.2.tgz", - "integrity": "sha512-3wuLdX5iiiYeZWP6bQrjqhrcvBIf0NHbQH1Ur1WbHvoiuTYUEItgygea3zs8aHpiitn0lOB8gX20u1qO+FDm7Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT" - }, - "node_modules/@fastify/error": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@fastify/error/-/error-4.2.0.tgz", - "integrity": "sha512-RSo3sVDXfHskiBZKBPRgnQTtIqpi/7zhJOEmAxCiBcM7d0uwdGdxLlsCaLzGs8v8NnxIRlfG0N51p5yFaOentQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT" - }, - "node_modules/@fastify/fast-json-stringify-compiler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-5.0.3.tgz", - "integrity": "sha512-uik7yYHkLr6fxd8hJSZ8c+xF4WafPK+XzneQDPU+D10r5X19GW8lJcom2YijX2+qtFF1ENJlHXKFM9ouXNJYgQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT", - "dependencies": { - "fast-json-stringify": "^6.0.0" - } - }, - "node_modules/@fastify/forwarded": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@fastify/forwarded/-/forwarded-3.0.0.tgz", - "integrity": "sha512-kJExsp4JCms7ipzg7SJ3y8DwmePaELHxKYtg+tZow+k0znUTf3cb+npgyqm8+ATZOdmfgfydIebPDWM172wfyA==", - "license": "MIT" - }, - "node_modules/@fastify/merge-json-schemas": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@fastify/merge-json-schemas/-/merge-json-schemas-0.2.1.tgz", - "integrity": "sha512-OA3KGBCy6KtIvLf8DINC5880o5iBlDX4SxzLQS8HorJAbqluzLRn80UXU0bxZn7UOFhFgpRJDasfwn9nG4FG4A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT", - "dependencies": { - "dequal": "^2.0.3" - } - }, - "node_modules/@fastify/multipart": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/@fastify/multipart/-/multipart-9.0.3.tgz", - "integrity": "sha512-pJogxQCrT12/6I5Fh6jr3narwcymA0pv4B0jbC7c6Bl9wnrxomEUnV0d26w6gUls7gSXmhG8JGRMmHFIPsxt1g==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT", - "dependencies": { - "@fastify/busboy": "^3.0.0", - "@fastify/deepmerge": "^2.0.0", - "@fastify/error": "^4.0.0", - "fastify-plugin": "^5.0.0", - "secure-json-parse": "^3.0.0" - } - }, - "node_modules/@fastify/multipart/node_modules/secure-json-parse": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-3.0.2.tgz", - "integrity": "sha512-H6nS2o8bWfpFEV6U38sOSjS7bTbdgbCGU9wEM6W14P5H0QOsz94KCusifV44GpHDTu2nqZbuDNhTzu+mjDSw1w==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/@fastify/proxy-addr": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@fastify/proxy-addr/-/proxy-addr-5.0.0.tgz", - "integrity": "sha512-37qVVA1qZ5sgH7KpHkkC4z9SK6StIsIcOmpjvMPXNb3vx2GQxhZocogVYbr2PbbeLCQxYIPDok307xEvRZOzGA==", - "license": "MIT", - "dependencies": { - "@fastify/forwarded": "^3.0.0", - "ipaddr.js": "^2.1.0" - } - }, - "node_modules/@fastify/send": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@fastify/send/-/send-4.1.0.tgz", - "integrity": "sha512-TMYeQLCBSy2TOFmV95hQWkiTYgC/SEx7vMdV+wnZVX4tt8VBLKzmH8vV9OzJehV0+XBfg+WxPMt5wp+JBUKsVw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT", - "dependencies": { - "@lukeed/ms": "^2.0.2", - "escape-html": "~1.0.3", - "fast-decode-uri-component": "^1.0.1", - "http-errors": "^2.0.0", - "mime": "^3" - } - }, - "node_modules/@fastify/static": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@fastify/static/-/static-8.2.0.tgz", - "integrity": "sha512-PejC/DtT7p1yo3p+W7LiUtLMsV8fEvxAK15sozHy9t8kwo5r0uLYmhV/inURmGz1SkHZFz/8CNtHLPyhKcx4SQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT", - "dependencies": { - "@fastify/accept-negotiator": "^2.0.0", - "@fastify/send": "^4.0.0", - "content-disposition": "^0.5.4", - "fastify-plugin": "^5.0.0", - "fastq": "^1.17.1", - "glob": "^11.0.0" - } - }, - "node_modules/@fastify/swagger": { - "version": "9.5.1", - "resolved": "https://registry.npmjs.org/@fastify/swagger/-/swagger-9.5.1.tgz", - "integrity": "sha512-EGjYLA7vDmCPK7XViAYMF6y4+K3XUy5soVTVxsyXolNe/Svb4nFQxvtuQvvoQb2Gzc9pxiF3+ZQN/iZDHhKtTg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT", - "dependencies": { - "fastify-plugin": "^5.0.0", - "json-schema-resolver": "^3.0.0", - "openapi-types": "^12.1.3", - "rfdc": "^1.3.1", - "yaml": "^2.4.2" - } - }, - "node_modules/@fastify/swagger-ui": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/@fastify/swagger-ui/-/swagger-ui-5.2.3.tgz", - "integrity": "sha512-e7ivEJi9EpFcxTONqICx4llbpB2jmlI+LI1NQ/mR7QGQnyDOqZybPK572zJtcdHZW4YyYTBHcP3a03f1pOh0SA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT", - "dependencies": { - "@fastify/static": "^8.0.0", - "fastify-plugin": "^5.0.0", - "openapi-types": "^12.1.3", - "rfdc": "^1.3.1", - "yaml": "^2.4.1" - } - }, - "node_modules/@img/colour": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", - "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", - "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.2.4" - } - }, - "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", - "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.2.4" - } - }, - "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", - "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", - "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", - "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", - "cpu": [ - "arm" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", - "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", - "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", - "cpu": [ - "ppc64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-riscv64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", - "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", - "cpu": [ - "riscv64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", - "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", - "cpu": [ - "s390x" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", - "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", - "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", - "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-linux-arm": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", - "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", - "cpu": [ - "arm" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", - "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-ppc64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", - "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", - "cpu": [ - "ppc64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-riscv64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", - "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", - "cpu": [ - "riscv64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-riscv64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", - "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", - "cpu": [ - "s390x" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", - "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.2.4" - } - }, - "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", - "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" - } - }, - "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", - "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.2.4" - } - }, - "node_modules/@img/sharp-wasm32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", - "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", - "cpu": [ - "wasm32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", - "optional": true, - "dependencies": { - "@emnapi/runtime": "^1.7.0" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", - "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", - "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", - "cpu": [ - "ia32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", - "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@infisical/sdk": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@infisical/sdk/-/sdk-4.0.6.tgz", - "integrity": "sha512-aK/oQj0prIx8jTybcwQfPYow3/KsBGPbHCyK8zCIWGvUjHzYU2is34AWjRvxQ6GhZFpW1LaXfgxgrmbWrsgWZA==", - "license": "ISC", - "dependencies": { - "@aws-crypto/sha256-js": "^5.2.0", - "@aws-sdk/credential-providers": "3.600.0", - "@aws-sdk/protocol-http": "^3.370.0", - "@aws-sdk/signature-v4": "^3.370.0", - "axios": "^1.11.0", - "typescript": "^5.5.4", - "zod": "^3.23.8" - } - }, - "node_modules/@isaacs/balanced-match": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", - "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", - "license": "MIT", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/@isaacs/brace-expansion": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", - "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", - "license": "MIT", - "dependencies": { - "@isaacs/balanced-match": "^4.0.1" - }, - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@lukeed/ms": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@lukeed/ms/-/ms-2.0.2.tgz", - "integrity": "sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@mmote/niimbluelib": { - "version": "0.0.1-alpha.29", - "resolved": "https://registry.npmjs.org/@mmote/niimbluelib/-/niimbluelib-0.0.1-alpha.29.tgz", - "integrity": "sha512-/uE3thCSbRKiak1biuB4HiFRxmM+ROAiDAkz8+fqdOQ4mhlhLTLSAklFQKcWpfkSTEyj0tng8kssHaGum8ACyg==", - "license": "MIT", - "dependencies": { - "@capacitor-community/bluetooth-le": "^7.1.0", - "@capacitor/core": "^7.2.0", - "async-mutex": "^0.5.0", - "crc-32": "^1.2.2", - "eventemitter3": "^5.0.1" - } - }, - "node_modules/@pdf-lib/standard-fonts": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@pdf-lib/standard-fonts/-/standard-fonts-1.0.0.tgz", - "integrity": "sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==", - "license": "MIT", - "dependencies": { - "pako": "^1.0.6" - } - }, - "node_modules/@pdf-lib/upng": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@pdf-lib/upng/-/upng-1.0.1.tgz", - "integrity": "sha512-dQK2FUMQtowVP00mtIksrlZhdFXQZPC+taih1q4CvPZ5vqdxR/LKBaFg0oAfzd1GlHZXXSPdQfzQnt+ViGvEIQ==", - "license": "MIT", - "dependencies": { - "pako": "^1.0.10" - } - }, - "node_modules/@pinojs/redact": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz", - "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==", - "license": "MIT" - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@prisma/client": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.15.0.tgz", - "integrity": "sha512-wR2LXUbOH4cL/WToatI/Y2c7uzni76oNFND7+23ypLllBmIS8e3ZHhO+nud9iXSXKFt1SoM3fTZvHawg63emZw==", - "hasInstallScript": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "peerDependencies": { - "prisma": "*", - "typescript": ">=5.1.0" - }, - "peerDependenciesMeta": { - "prisma": { - "optional": true - }, - "typescript": { - "optional": true - } - } - }, - "node_modules/@prisma/config": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.15.0.tgz", - "integrity": "sha512-KMEoec9b2u6zX0EbSEx/dRpx1oNLjqJEBZYyK0S3TTIbZ7GEGoVyGyFRk4C72+A38cuPLbfQGQvgOD+gBErKlA==", - "devOptional": true, - "license": "Apache-2.0", - "dependencies": { - "c12": "3.1.0", - "deepmerge-ts": "7.1.5", - "effect": "3.16.12", - "empathic": "2.0.0" - } - }, - "node_modules/@prisma/debug": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.15.0.tgz", - "integrity": "sha512-y7cSeLuQmyt+A3hstAs6tsuAiVXSnw9T55ra77z0nbNkA8Lcq9rNcQg6PI00by/+WnE/aMRJ/W7sZWn2cgIy1g==", - "devOptional": true, - "license": "Apache-2.0" - }, - "node_modules/@prisma/engines": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.15.0.tgz", - "integrity": "sha512-opITiR5ddFJ1N2iqa7mkRlohCZqVSsHhRcc29QXeldMljOf4FSellLT0J5goVb64EzRTKcIDeIsJBgmilNcKxA==", - "devOptional": true, - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "@prisma/debug": "6.15.0", - "@prisma/engines-version": "6.15.0-5.85179d7826409ee107a6ba334b5e305ae3fba9fb", - "@prisma/fetch-engine": "6.15.0", - "@prisma/get-platform": "6.15.0" - } - }, - "node_modules/@prisma/engines-version": { - "version": "6.15.0-5.85179d7826409ee107a6ba334b5e305ae3fba9fb", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.15.0-5.85179d7826409ee107a6ba334b5e305ae3fba9fb.tgz", - "integrity": "sha512-a/46aK5j6L3ePwilZYEgYDPrhBQ/n4gYjLxT5YncUTJJNRnTCVjPF86QdzUOLRdYjCLfhtZp9aum90W0J+trrg==", - "devOptional": true, - "license": "Apache-2.0" - }, - "node_modules/@prisma/fetch-engine": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.15.0.tgz", - "integrity": "sha512-xcT5f6b+OWBq6vTUnRCc7qL+Im570CtwvgSj+0MTSGA1o9UDSKZ/WANvwtiRXdbYWECpyC3CukoG3A04VTAPHw==", - "devOptional": true, - "license": "Apache-2.0", - "dependencies": { - "@prisma/debug": "6.15.0", - "@prisma/engines-version": "6.15.0-5.85179d7826409ee107a6ba334b5e305ae3fba9fb", - "@prisma/get-platform": "6.15.0" - } - }, - "node_modules/@prisma/get-platform": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.15.0.tgz", - "integrity": "sha512-Jbb+Xbxyp05NSR1x2epabetHiXvpO8tdN2YNoWoA/ZsbYyxxu/CO/ROBauIFuMXs3Ti+W7N7SJtWsHGaWte9Rg==", - "devOptional": true, - "license": "Apache-2.0", - "dependencies": { - "@prisma/debug": "6.15.0" - } - }, - "node_modules/@smithy/abort-controller": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.5.tgz", - "integrity": "sha512-jcrqdTQurIrBbUm4W2YdLVMQDoL0sA9DTxYd2s+R/y+2U9NLOP7Xf/YqfSg1FZhlZIYEnvk2mwbyvIfdLEPo8g==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/chunked-blob-reader": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-5.0.0.tgz", - "integrity": "sha512-+sKqDBQqb036hh4NPaUiEkYFkTUGYzRsn3EuFhyfQfMy6oGHEUJDurLP9Ufb5dasr/XiAmPNMr6wa9afjQB+Gw==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/chunked-blob-reader-native": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-4.0.0.tgz", - "integrity": "sha512-R9wM2yPmfEMsUmlMlIgSzOyICs0x9uu7UTHoccMyt7BWw8shcGM8HqB355+BZCPBcySvbTYMs62EgEQkNxz2ig==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-base64": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/config-resolver": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.1.5.tgz", - "integrity": "sha512-viuHMxBAqydkB0AfWwHIdwf/PRH2z5KHGUzqyRtS/Wv+n3IHI993Sk76VCA7dD/+GzgGOmlJDITfPcJC1nIVIw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^4.1.4", - "@smithy/types": "^4.3.2", - "@smithy/util-config-provider": "^4.0.0", - "@smithy/util-middleware": "^4.0.5", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/core": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.9.0.tgz", - "integrity": "sha512-B/GknvCfS3llXd/b++hcrwIuqnEozQDnRL4sBmOac5/z/dr0/yG1PURNPOyU4Lsiy1IyTj8scPxVqRs5dYWf6A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/middleware-serde": "^4.0.9", - "@smithy/protocol-http": "^5.1.3", - "@smithy/types": "^4.3.2", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-middleware": "^4.0.5", - "@smithy/util-stream": "^4.2.4", - "@smithy/util-utf8": "^4.0.0", - "@types/uuid": "^9.0.1", - "tslib": "^2.6.2", - "uuid": "^9.0.1" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/credential-provider-imds": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.0.7.tgz", - "integrity": "sha512-dDzrMXA8d8riFNiPvytxn0mNwR4B3h8lgrQ5UjAGu6T9z/kRg/Xncf4tEQHE/+t25sY8IH3CowcmWi+1U5B1Gw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^4.1.4", - "@smithy/property-provider": "^4.0.5", - "@smithy/types": "^4.3.2", - "@smithy/url-parser": "^4.0.5", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/eventstream-codec": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.0.5.tgz", - "integrity": "sha512-miEUN+nz2UTNoRYRhRqVTJCx7jMeILdAurStT2XoS+mhokkmz1xAPp95DFW9Gxt4iF2VBqpeF9HbTQ3kY1viOA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/crc32": "5.2.0", - "@smithy/types": "^4.3.2", - "@smithy/util-hex-encoding": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/eventstream-serde-browser": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.0.5.tgz", - "integrity": "sha512-LCUQUVTbM6HFKzImYlSB9w4xafZmpdmZsOh9rIl7riPC3osCgGFVP+wwvYVw6pXda9PPT9TcEZxaq3XE81EdJQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/eventstream-serde-universal": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/eventstream-serde-config-resolver": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.1.3.tgz", - "integrity": "sha512-yTTzw2jZjn/MbHu1pURbHdpjGbCuMHWncNBpJnQAPxOVnFUAbSIUSwafiphVDjNV93TdBJWmeVAds7yl5QCkcA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/eventstream-serde-node": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.0.5.tgz", - "integrity": "sha512-lGS10urI4CNzz6YlTe5EYG0YOpsSp3ra8MXyco4aqSkQDuyZPIw2hcaxDU82OUVtK7UY9hrSvgWtpsW5D4rb4g==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/eventstream-serde-universal": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/eventstream-serde-universal": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.0.5.tgz", - "integrity": "sha512-JFnmu4SU36YYw3DIBVao3FsJh4Uw65vVDIqlWT4LzR6gXA0F3KP0IXFKKJrhaVzCBhAuMsrUUaT5I+/4ZhF7aw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/eventstream-codec": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/fetch-http-handler": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.1.1.tgz", - "integrity": "sha512-61WjM0PWmZJR+SnmzaKI7t7G0UkkNFboDpzIdzSoy7TByUzlxo18Qlh9s71qug4AY4hlH/CwXdubMtkcNEb/sQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/protocol-http": "^5.1.3", - "@smithy/querystring-builder": "^4.0.5", - "@smithy/types": "^4.3.2", - "@smithy/util-base64": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/hash-blob-browser": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-4.0.5.tgz", - "integrity": "sha512-F7MmCd3FH/Q2edhcKd+qulWkwfChHbc9nhguBlVjSUE6hVHhec3q6uPQ+0u69S6ppvLtR3eStfCuEKMXBXhvvA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/chunked-blob-reader": "^5.0.0", - "@smithy/chunked-blob-reader-native": "^4.0.0", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/hash-node": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.0.5.tgz", - "integrity": "sha512-cv1HHkKhpyRb6ahD8Vcfb2Hgz67vNIXEp2vnhzfxLFGRukLCNEA5QdsorbUEzXma1Rco0u3rx5VTqbM06GcZqQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2", - "@smithy/util-buffer-from": "^4.0.0", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/hash-stream-node": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-4.0.5.tgz", - "integrity": "sha512-IJuDS3+VfWB67UC0GU0uYBG/TA30w+PlOaSo0GPm9UHS88A6rCP6uZxNjNYiyRtOcjv7TXn/60cW8ox1yuZsLg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/invalid-dependency": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.0.5.tgz", - "integrity": "sha512-IVnb78Qtf7EJpoEVo7qJ8BEXQwgC4n3igeJNNKEj/MLYtapnx8A67Zt/J3RXAj2xSO1910zk0LdFiygSemuLow==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/is-array-buffer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.0.0.tgz", - "integrity": "sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/md5-js": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-4.0.5.tgz", - "integrity": "sha512-8n2XCwdUbGr8W/XhMTaxILkVlw2QebkVTn5tm3HOcbPbOpWg89zr6dPXsH8xbeTsbTXlJvlJNTQsKAIoqQGbdA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-content-length": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.0.5.tgz", - "integrity": "sha512-l1jlNZoYzoCC7p0zCtBDE5OBXZ95yMKlRlftooE5jPWQn4YBPLgsp+oeHp7iMHaTGoUdFqmHOPa8c9G3gBsRpQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/protocol-http": "^5.1.3", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-endpoint": { - "version": "4.1.19", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.1.19.tgz", - "integrity": "sha512-EAlEPncqo03siNZJ9Tm6adKCQ+sw5fNU8ncxWwaH0zTCwMPsgmERTi6CEKaermZdgJb+4Yvh0NFm36HeO4PGgQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.9.0", - "@smithy/middleware-serde": "^4.0.9", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/shared-ini-file-loader": "^4.0.5", - "@smithy/types": "^4.3.2", - "@smithy/url-parser": "^4.0.5", - "@smithy/util-middleware": "^4.0.5", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-retry": { - "version": "4.1.20", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.1.20.tgz", - "integrity": "sha512-T3maNEm3Masae99eFdx1Q7PIqBBEVOvRd5hralqKZNeIivnoGNx5OFtI3DiZ5gCjUkl0mNondlzSXeVxkinh7Q==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^4.1.4", - "@smithy/protocol-http": "^5.1.3", - "@smithy/service-error-classification": "^4.0.7", - "@smithy/smithy-client": "^4.5.0", - "@smithy/types": "^4.3.2", - "@smithy/util-middleware": "^4.0.5", - "@smithy/util-retry": "^4.0.7", - "@types/uuid": "^9.0.1", - "tslib": "^2.6.2", - "uuid": "^9.0.1" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-serde": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.0.9.tgz", - "integrity": "sha512-uAFFR4dpeoJPGz8x9mhxp+RPjo5wW0QEEIPPPbLXiRRWeCATf/Km3gKIVR5vaP8bN1kgsPhcEeh+IZvUlBv6Xg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/protocol-http": "^5.1.3", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-stack": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.0.5.tgz", - "integrity": "sha512-/yoHDXZPh3ocRVyeWQFvC44u8seu3eYzZRveCMfgMOBcNKnAmOvjbL9+Cp5XKSIi9iYA9PECUuW2teDAk8T+OQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/node-config-provider": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.1.4.tgz", - "integrity": "sha512-+UDQV/k42jLEPPHSn39l0Bmc4sB1xtdI9Gd47fzo/0PbXzJ7ylgaOByVjF5EeQIumkepnrJyfx86dPa9p47Y+w==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/property-provider": "^4.0.5", - "@smithy/shared-ini-file-loader": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/node-http-handler": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.1.1.tgz", - "integrity": "sha512-RHnlHqFpoVdjSPPiYy/t40Zovf3BBHc2oemgD7VsVTFFZrU5erFFe0n52OANZZ/5sbshgD93sOh5r6I35Xmpaw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/abort-controller": "^4.0.5", - "@smithy/protocol-http": "^5.1.3", - "@smithy/querystring-builder": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/property-provider": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.0.5.tgz", - "integrity": "sha512-R/bswf59T/n9ZgfgUICAZoWYKBHcsVDurAGX88zsiUtOTA/xUAPyiT+qkNCPwFn43pZqN84M4MiUsbSGQmgFIQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/protocol-http": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.1.3.tgz", - "integrity": "sha512-fCJd2ZR7D22XhDY0l+92pUag/7je2BztPRQ01gU5bMChcyI0rlly7QFibnYHzcxDvccMjlpM/Q1ev8ceRIb48w==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/querystring-builder": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.0.5.tgz", - "integrity": "sha512-NJeSCU57piZ56c+/wY+AbAw6rxCCAOZLCIniRE7wqvndqxcKKDOXzwWjrY7wGKEISfhL9gBbAaWWgHsUGedk+A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2", - "@smithy/util-uri-escape": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/querystring-parser": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.0.5.tgz", - "integrity": "sha512-6SV7md2CzNG/WUeTjVe6Dj8noH32r4MnUeFKZrnVYsQxpGSIcphAanQMayi8jJLZAWm6pdM9ZXvKCpWOsIGg0w==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/service-error-classification": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.0.7.tgz", - "integrity": "sha512-XvRHOipqpwNhEjDf2L5gJowZEm5nsxC16pAZOeEcsygdjv9A2jdOh3YoDQvOXBGTsaJk6mNWtzWalOB9976Wlg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/shared-ini-file-loader": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.0.5.tgz", - "integrity": "sha512-YVVwehRDuehgoXdEL4r1tAAzdaDgaC9EQvhK0lEbfnbrd0bd5+CTQumbdPryX3J2shT7ZqQE+jPW4lmNBAB8JQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/signature-v4": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.1.3.tgz", - "integrity": "sha512-mARDSXSEgllNzMw6N+mC+r1AQlEBO3meEAkR/UlfAgnMzJUB3goRBWgip1EAMG99wh36MDqzo86SfIX5Y+VEaw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^4.0.0", - "@smithy/protocol-http": "^5.1.3", - "@smithy/types": "^4.3.2", - "@smithy/util-hex-encoding": "^4.0.0", - "@smithy/util-middleware": "^4.0.5", - "@smithy/util-uri-escape": "^4.0.0", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/smithy-client": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.5.0.tgz", - "integrity": "sha512-ZSdE3vl0MuVbEwJBxSftm0J5nL/gw76xp5WF13zW9cN18MFuFXD5/LV0QD8P+sCU5bSWGyy6CTgUupE1HhOo1A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.9.0", - "@smithy/middleware-endpoint": "^4.1.19", - "@smithy/middleware-stack": "^4.0.5", - "@smithy/protocol-http": "^5.1.3", - "@smithy/types": "^4.3.2", - "@smithy/util-stream": "^4.2.4", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/types": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.3.2.tgz", - "integrity": "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/url-parser": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.0.5.tgz", - "integrity": "sha512-j+733Um7f1/DXjYhCbvNXABV53NyCRRA54C7bNEIxNPs0YjfRxeMKjjgm2jvTYrciZyCjsicHwQ6Q0ylo+NAUw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/querystring-parser": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-base64": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.0.0.tgz", - "integrity": "sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^4.0.0", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-body-length-browser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.0.0.tgz", - "integrity": "sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-body-length-node": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.0.0.tgz", - "integrity": "sha512-q0iDP3VsZzqJyje8xJWEJCNIu3lktUGVoSy1KB0UWym2CL1siV3artm+u1DFYTLejpsrdGyCSWBdGNjJzfDPjg==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-buffer-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.0.0.tgz", - "integrity": "sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-config-provider": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.0.0.tgz", - "integrity": "sha512-L1RBVzLyfE8OXH+1hsJ8p+acNUSirQnWQ6/EgpchV88G6zGBTDPdXiiExei6Z1wR2RxYvxY/XLw6AMNCCt8H3w==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-defaults-mode-browser": { - "version": "4.0.27", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.27.tgz", - "integrity": "sha512-i/Fu6AFT5014VJNgWxKomBJP/GB5uuOsM4iHdcmplLm8B1eAqnRItw4lT2qpdO+mf+6TFmf6dGcggGLAVMZJsQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/property-provider": "^4.0.5", - "@smithy/smithy-client": "^4.5.0", - "@smithy/types": "^4.3.2", - "bowser": "^2.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-defaults-mode-node": { - "version": "4.0.27", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.27.tgz", - "integrity": "sha512-3W0qClMyxl/ELqTA39aNw1N+pN0IjpXT7lPFvZ8zTxqVFP7XCpACB9QufmN4FQtd39xbgS7/Lekn7LmDa63I5w==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/config-resolver": "^4.1.5", - "@smithy/credential-provider-imds": "^4.0.7", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/property-provider": "^4.0.5", - "@smithy/smithy-client": "^4.5.0", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-endpoints": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.0.7.tgz", - "integrity": "sha512-klGBP+RpBp6V5JbrY2C/VKnHXn3d5V2YrifZbmMY8os7M6m8wdYFoO6w/fe5VkP+YVwrEktW3IWYaSQVNZJ8oQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^4.1.4", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-hex-encoding": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.0.0.tgz", - "integrity": "sha512-Yk5mLhHtfIgW2W2WQZWSg5kuMZCVbvhFmC7rV4IO2QqnZdbEFPmQnCcGMAX2z/8Qj3B9hYYNjZOhWym+RwhePw==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-middleware": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.0.5.tgz", - "integrity": "sha512-N40PfqsZHRSsByGB81HhSo+uvMxEHT+9e255S53pfBw/wI6WKDI7Jw9oyu5tJTLwZzV5DsMha3ji8jk9dsHmQQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-retry": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.0.7.tgz", - "integrity": "sha512-TTO6rt0ppK70alZpkjwy+3nQlTiqNfoXja+qwuAchIEAIoSZW8Qyd76dvBv3I5bCpE38APafG23Y/u270NspiQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/service-error-classification": "^4.0.7", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-stream": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.2.4.tgz", - "integrity": "sha512-vSKnvNZX2BXzl0U2RgCLOwWaAP9x/ddd/XobPK02pCbzRm5s55M53uwb1rl/Ts7RXZvdJZerPkA+en2FDghLuQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/fetch-http-handler": "^5.1.1", - "@smithy/node-http-handler": "^4.1.1", - "@smithy/types": "^4.3.2", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-buffer-from": "^4.0.0", - "@smithy/util-hex-encoding": "^4.0.0", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-uri-escape": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.0.0.tgz", - "integrity": "sha512-77yfbCbQMtgtTylO9itEAdpPXSog3ZxMe09AEhm0dU0NLTalV70ghDZFR+Nfi1C60jnJoh/Re4090/DuZh2Omg==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-utf8": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.0.0.tgz", - "integrity": "sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-waiter": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.0.7.tgz", - "integrity": "sha512-mYqtQXPmrwvUljaHyGxYUIIRI3qjBTEb/f5QFi3A6VlxhpmZd5mWXn9W+qUkf2pVE1Hv3SqxefiZOPGdxmO64A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/abort-controller": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@standard-schema/spec": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", - "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/@supabase/auth-js": { - "version": "2.71.1", - "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.71.1.tgz", - "integrity": "sha512-mMIQHBRc+SKpZFRB2qtupuzulaUhFYupNyxqDj5Jp/LyPvcWvjaJzZzObv6URtL/O6lPxkanASnotGtNpS3H2Q==", - "license": "MIT", - "dependencies": { - "@supabase/node-fetch": "^2.6.14" - } - }, - "node_modules/@supabase/functions-js": { - "version": "2.4.5", - "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.4.5.tgz", - "integrity": "sha512-v5GSqb9zbosquTo6gBwIiq7W9eQ7rE5QazsK/ezNiQXdCbY+bH8D9qEaBIkhVvX4ZRW5rP03gEfw5yw9tiq4EQ==", - "license": "MIT", - "dependencies": { - "@supabase/node-fetch": "^2.6.14" - } - }, - "node_modules/@supabase/node-fetch": { - "version": "2.6.15", - "resolved": "https://registry.npmjs.org/@supabase/node-fetch/-/node-fetch-2.6.15.tgz", - "integrity": "sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ==", - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - } - }, - "node_modules/@supabase/postgrest-js": { - "version": "1.21.3", - "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-1.21.3.tgz", - "integrity": "sha512-rg3DmmZQKEVCreXq6Am29hMVe1CzemXyIWVYyyua69y6XubfP+DzGfLxME/1uvdgwqdoaPbtjBDpEBhqxq1ZwA==", - "license": "MIT", - "dependencies": { - "@supabase/node-fetch": "^2.6.14" - } - }, - "node_modules/@supabase/realtime-js": { - "version": "2.15.4", - "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.15.4.tgz", - "integrity": "sha512-e/FYIWjvQJHOCNACWehnKvg26zosju3694k0NMUNb+JGLdvHJzEa29ZVVLmawd2kvx4hdbv8mxSqfttRnH3+DA==", - "license": "MIT", - "dependencies": { - "@supabase/node-fetch": "^2.6.13", - "@types/phoenix": "^1.6.6", - "@types/ws": "^8.18.1", - "ws": "^8.18.2" - } - }, - "node_modules/@supabase/storage-js": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.11.0.tgz", - "integrity": "sha512-Y+kx/wDgd4oasAgoAq0bsbQojwQ+ejIif8uczZ9qufRHWFLMU5cODT+ApHsSrDufqUcVKt+eyxtOXSkeh2v9ww==", - "license": "MIT", - "dependencies": { - "@supabase/node-fetch": "^2.6.14" - } - }, - "node_modules/@supabase/supabase-js": { - "version": "2.56.1", - "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.56.1.tgz", - "integrity": "sha512-cb/kS0d6G/qbcmUFItkqVrQbxQHWXzfRZuoiSDv/QiU6RbGNTn73XjjvmbBCZ4MMHs+5teihjhpEVluqbXISEg==", - "license": "MIT", - "dependencies": { - "@supabase/auth-js": "2.71.1", - "@supabase/functions-js": "2.4.5", - "@supabase/node-fetch": "2.6.15", - "@supabase/postgrest-js": "1.21.3", - "@supabase/realtime-js": "2.15.4", - "@supabase/storage-js": "^2.10.4" - } - }, - "node_modules/@types/bcrypt": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-6.0.0.tgz", - "integrity": "sha512-/oJGukuH3D2+D+3H4JWLaAsJ/ji86dhRidzZ/Od7H/i8g+aCmvkeCc6Ni/f9uxGLSQVCRZkX2/lqEFG2BvWtlQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/jsonwebtoken": { - "version": "9.0.10", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz", - "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/ms": "*", - "@types/node": "*" - } - }, - "node_modules/@types/ms": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", - "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.0.tgz", - "integrity": "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==", - "license": "MIT", - "dependencies": { - "undici-types": "~7.10.0" - } - }, - "node_modules/@types/phoenix": { - "version": "1.6.6", - "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.6.tgz", - "integrity": "sha512-PIzZZlEppgrpoT2QgbnDU+MMzuR6BbCjllj0bM70lWoejMeNJAxCchxnv7J3XFkI8MpygtRpzXrIlmWUBclP5A==", - "license": "MIT" - }, - "node_modules/@types/uuid": { - "version": "9.0.8", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", - "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", - "license": "MIT" - }, - "node_modules/@types/web-bluetooth": { - "version": "0.0.20", - "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz", - "integrity": "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==", - "license": "MIT" - }, - "node_modules/@types/ws": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", - "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@zip.js/zip.js": { - "version": "2.7.73", - "resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.7.73.tgz", - "integrity": "sha512-I2UP8/rdQE5hTtVVL08B7P8XuwXiKuuMUPjNuFOVL/9b+8IsExR9S5jz2H58u0rJjU4M1BikLgqEMG8gZJZVBw==", - "license": "BSD-3-Clause", - "engines": { - "bun": ">=0.7.0", - "deno": ">=1.0.0", - "node": ">=16.5.0" - } - }, - "node_modules/@zone-eu/mailsplit": { - "version": "5.4.7", - "resolved": "https://registry.npmjs.org/@zone-eu/mailsplit/-/mailsplit-5.4.7.tgz", - "integrity": "sha512-jApX86aDgolMz08pP20/J2zcns02NSK3zSiYouf01QQg4250L+GUAWSWicmS7eRvs+Z7wP7QfXrnkaTBGrIpwQ==", - "license": "(MIT OR EUPL-1.1+)", - "dependencies": { - "libbase64": "1.3.0", - "libmime": "5.3.7", - "libqp": "2.1.1" - } - }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "license": "MIT", - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, - "node_modules/abstract-logging": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz", - "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==", - "license": "MIT" - }, - "node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", - "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ansi-regex": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz", - "integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/archiver": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/archiver/-/archiver-7.0.1.tgz", - "integrity": "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==", - "license": "MIT", - "dependencies": { - "archiver-utils": "^5.0.2", - "async": "^3.2.4", - "buffer-crc32": "^1.0.0", - "readable-stream": "^4.0.0", - "readdir-glob": "^1.1.2", - "tar-stream": "^3.0.0", - "zip-stream": "^6.0.1" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/archiver-utils": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-5.0.2.tgz", - "integrity": "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==", - "license": "MIT", - "dependencies": { - "glob": "^10.0.0", - "graceful-fs": "^4.2.0", - "is-stream": "^2.0.1", - "lazystream": "^1.0.0", - "lodash": "^4.17.15", - "normalize-path": "^3.0.0", - "readable-stream": "^4.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/archiver-utils/node_modules/glob": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", - "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/archiver-utils/node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/archiver-utils/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "license": "ISC" - }, - "node_modules/archiver-utils/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/archiver-utils/node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/async": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", - "license": "MIT" - }, - "node_modules/async-mutex": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz", - "integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==", - "license": "MIT", - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" - }, - "node_modules/atomic-sleep": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", - "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", - "license": "MIT", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/avvio": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/avvio/-/avvio-9.1.0.tgz", - "integrity": "sha512-fYASnYi600CsH/j9EQov7lECAniYiBFiiAtBNuZYLA2leLe9qOvZzqYHFjtIj6gD2VMoMLP14834LFWvr4IfDw==", - "license": "MIT", - "dependencies": { - "@fastify/error": "^4.0.0", - "fastq": "^1.17.1" - } - }, - "node_modules/axios": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.1.tgz", - "integrity": "sha512-Kn4kbSXpkFHCGE6rBFNwIv0GQs4AvDT80jlveJDKFxjbTYMUeB4QtsdPCv6H8Cm19Je7IU6VFtRl2zWZI0rudQ==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.4", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/b4a": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz", - "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==", - "license": "Apache-2.0" - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" - }, - "node_modules/bare-events": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.6.1.tgz", - "integrity": "sha512-AuTJkq9XmE6Vk0FJVNq5QxETrSA/vKHarWVBG5l/JbdCL1prJemiyJqUS0jrlXO0MftuPq4m3YVYhoNc5+aE/g==", - "license": "Apache-2.0", - "optional": true - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/bcrypt": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-6.0.0.tgz", - "integrity": "sha512-cU8v/EGSrnH+HnxV2z0J7/blxH8gq7Xh2JFT6Aroax7UohdmiJJlxApMxtKfuI7z68NvvVcmR78k2LbT6efhRg==", - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "node-addon-api": "^8.3.0", - "node-gyp-build": "^4.8.4" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "license": "MIT", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/bl/node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/bl/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/bowser": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.12.1.tgz", - "integrity": "sha512-z4rE2Gxh7tvshQ4hluIT7XcFrgLIQaw9X3A+kTTRdovCz5PMukm/0QC/BKSYPj3omF5Qfypn9O/c5kgpmvYUCw==", - "license": "MIT" - }, - "node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/buffer-crc32": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", - "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", - "license": "MIT", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", - "license": "BSD-3-Clause" - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/bwip-js": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/bwip-js/-/bwip-js-4.8.0.tgz", - "integrity": "sha512-gUDkDHSTv8/DJhomSIbO0fX/Dx0MO/sgllLxJyJfu4WixCQe9nfGJzmHm64ZCbxo+gUYQEsQcRmqcwcwPRwUkg==", - "license": "MIT", - "bin": { - "bwip-js": "bin/bwip-js.js" - } - }, - "node_modules/c12": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/c12/-/c12-3.1.0.tgz", - "integrity": "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "chokidar": "^4.0.3", - "confbox": "^0.2.2", - "defu": "^6.1.4", - "dotenv": "^16.6.1", - "exsolve": "^1.0.7", - "giget": "^2.0.0", - "jiti": "^2.4.2", - "ohash": "^2.0.11", - "pathe": "^2.0.3", - "perfect-debounce": "^1.0.0", - "pkg-types": "^2.2.0", - "rc9": "^2.1.2" - }, - "peerDependencies": { - "magicast": "^0.3.5" - }, - "peerDependenciesMeta": { - "magicast": { - "optional": true - } - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/canvas": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/canvas/-/canvas-3.2.0.tgz", - "integrity": "sha512-jk0GxrLtUEmW/TmFsk2WghvgHe8B0pxGilqCL21y8lHkPUGa6FTsnCNtHPOzT8O3y+N+m3espawV80bbBlgfTA==", - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "node-addon-api": "^7.0.0", - "prebuild-install": "^7.1.3" - }, - "engines": { - "node": "^18.12.0 || >= 20.9.0" - } - }, - "node_modules/canvas/node_modules/node-addon-api": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", - "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", - "license": "MIT" - }, - "node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "license": "ISC" - }, - "node_modules/citty": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", - "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "consola": "^3.2.3" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/compress-commons": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-6.0.2.tgz", - "integrity": "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==", - "license": "MIT", - "dependencies": { - "crc-32": "^1.2.0", - "crc32-stream": "^6.0.0", - "is-stream": "^2.0.1", - "normalize-path": "^3.0.0", - "readable-stream": "^4.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/confbox": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz", - "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/consola": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", - "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": "^14.18.0 || >=16.10.0" - } - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", - "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "license": "MIT" - }, - "node_modules/crc-32": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", - "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", - "license": "Apache-2.0", - "bin": { - "crc32": "bin/crc32.njs" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/crc32-stream": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-6.0.0.tgz", - "integrity": "sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==", - "license": "MIT", - "dependencies": { - "crc-32": "^1.2.0", - "readable-stream": "^4.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/crypto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", - "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==", - "deprecated": "This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in.", - "license": "ISC" - }, - "node_modules/dayjs": { - "version": "1.11.18", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.18.tgz", - "integrity": "sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA==", - "license": "MIT" - }, - "node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "license": "MIT", - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "license": "MIT", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/deepmerge-ts": { - "version": "7.1.5", - "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz", - "integrity": "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==", - "devOptional": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/defu": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", - "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/destr": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", - "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, - "node_modules/dotenv": { - "version": "16.6.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", - "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", - "devOptional": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/drizzle-kit": { - "version": "0.31.8", - "resolved": "https://registry.npmjs.org/drizzle-kit/-/drizzle-kit-0.31.8.tgz", - "integrity": "sha512-O9EC/miwdnRDY10qRxM8P3Pg8hXe3LyU4ZipReKOgTwn4OqANmftj8XJz1UPUAS6NMHf0E2htjsbQujUTkncCg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@drizzle-team/brocli": "^0.10.2", - "@esbuild-kit/esm-loader": "^2.5.5", - "esbuild": "^0.25.4", - "esbuild-register": "^3.5.0" - }, - "bin": { - "drizzle-kit": "bin.cjs" - } - }, - "node_modules/drizzle-orm": { - "version": "0.45.0", - "resolved": "https://registry.npmjs.org/drizzle-orm/-/drizzle-orm-0.45.0.tgz", - "integrity": "sha512-lyd9VRk3SXKRjV/gQckQzmJgkoYMvVG3A2JAV0vh3L+Lwk+v9+rK5Gj0H22y+ZBmxsrRBgJ5/RbQCN7DWd1dtQ==", - "license": "Apache-2.0", - "peerDependencies": { - "@aws-sdk/client-rds-data": ">=3", - "@cloudflare/workers-types": ">=4", - "@electric-sql/pglite": ">=0.2.0", - "@libsql/client": ">=0.10.0", - "@libsql/client-wasm": ">=0.10.0", - "@neondatabase/serverless": ">=0.10.0", - "@op-engineering/op-sqlite": ">=2", - "@opentelemetry/api": "^1.4.1", - "@planetscale/database": ">=1.13", - "@prisma/client": "*", - "@tidbcloud/serverless": "*", - "@types/better-sqlite3": "*", - "@types/pg": "*", - "@types/sql.js": "*", - "@upstash/redis": ">=1.34.7", - "@vercel/postgres": ">=0.8.0", - "@xata.io/client": "*", - "better-sqlite3": ">=7", - "bun-types": "*", - "expo-sqlite": ">=14.0.0", - "gel": ">=2", - "knex": "*", - "kysely": "*", - "mysql2": ">=2", - "pg": ">=8", - "postgres": ">=3", - "sql.js": ">=1", - "sqlite3": ">=5" - }, - "peerDependenciesMeta": { - "@aws-sdk/client-rds-data": { - "optional": true - }, - "@cloudflare/workers-types": { - "optional": true - }, - "@electric-sql/pglite": { - "optional": true - }, - "@libsql/client": { - "optional": true - }, - "@libsql/client-wasm": { - "optional": true - }, - "@neondatabase/serverless": { - "optional": true - }, - "@op-engineering/op-sqlite": { - "optional": true - }, - "@opentelemetry/api": { - "optional": true - }, - "@planetscale/database": { - "optional": true - }, - "@prisma/client": { - "optional": true - }, - "@tidbcloud/serverless": { - "optional": true - }, - "@types/better-sqlite3": { - "optional": true - }, - "@types/pg": { - "optional": true - }, - "@types/sql.js": { - "optional": true - }, - "@upstash/redis": { - "optional": true - }, - "@vercel/postgres": { - "optional": true - }, - "@xata.io/client": { - "optional": true - }, - "better-sqlite3": { - "optional": true - }, - "bun-types": { - "optional": true - }, - "expo-sqlite": { - "optional": true - }, - "gel": { - "optional": true - }, - "knex": { - "optional": true - }, - "kysely": { - "optional": true - }, - "mysql2": { - "optional": true - }, - "pg": { - "optional": true - }, - "postgres": { - "optional": true - }, - "prisma": { - "optional": true - }, - "sql.js": { - "optional": true - }, - "sqlite3": { - "optional": true - } - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "license": "MIT" - }, - "node_modules/ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "license": "Apache-2.0", - "dependencies": { - "safe-buffer": "^5.0.1" - } - }, - "node_modules/effect": { - "version": "3.16.12", - "resolved": "https://registry.npmjs.org/effect/-/effect-3.16.12.tgz", - "integrity": "sha512-N39iBk0K71F9nb442TLbTkjl24FLUzuvx2i1I2RsEAQsdAdUTuUoW0vlfUXgkMTUOnYqKnWcFfqw4hK4Pw27hg==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "@standard-schema/spec": "^1.0.0", - "fast-check": "^3.23.1" - } - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "license": "MIT" - }, - "node_modules/empathic": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/empathic/-/empathic-2.0.0.tgz", - "integrity": "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">=14" - } - }, - "node_modules/encoding-japanese": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/encoding-japanese/-/encoding-japanese-2.2.0.tgz", - "integrity": "sha512-EuJWwlHPZ1LbADuKTClvHtwbaFn4rOD+dRAbWysqEOXRc2Uui0hJInNJrsdH0c+OhJA4nrCBdSkW4DD5YxAo6A==", - "license": "MIT", - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/end-of-stream": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", - "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", - "license": "MIT", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/esbuild": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", - "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.9", - "@esbuild/android-arm": "0.25.9", - "@esbuild/android-arm64": "0.25.9", - "@esbuild/android-x64": "0.25.9", - "@esbuild/darwin-arm64": "0.25.9", - "@esbuild/darwin-x64": "0.25.9", - "@esbuild/freebsd-arm64": "0.25.9", - "@esbuild/freebsd-x64": "0.25.9", - "@esbuild/linux-arm": "0.25.9", - "@esbuild/linux-arm64": "0.25.9", - "@esbuild/linux-ia32": "0.25.9", - "@esbuild/linux-loong64": "0.25.9", - "@esbuild/linux-mips64el": "0.25.9", - "@esbuild/linux-ppc64": "0.25.9", - "@esbuild/linux-riscv64": "0.25.9", - "@esbuild/linux-s390x": "0.25.9", - "@esbuild/linux-x64": "0.25.9", - "@esbuild/netbsd-arm64": "0.25.9", - "@esbuild/netbsd-x64": "0.25.9", - "@esbuild/openbsd-arm64": "0.25.9", - "@esbuild/openbsd-x64": "0.25.9", - "@esbuild/openharmony-arm64": "0.25.9", - "@esbuild/sunos-x64": "0.25.9", - "@esbuild/win32-arm64": "0.25.9", - "@esbuild/win32-ia32": "0.25.9", - "@esbuild/win32-x64": "0.25.9" - } - }, - "node_modules/esbuild-register": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/esbuild-register/-/esbuild-register-3.6.0.tgz", - "integrity": "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.3.4" - }, - "peerDependencies": { - "esbuild": ">=0.12 <1" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", - "license": "MIT" - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "license": "MIT", - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/expand-template": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", - "license": "(MIT OR WTFPL)", - "engines": { - "node": ">=6" - } - }, - "node_modules/exsolve": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.7.tgz", - "integrity": "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/fast-check": { - "version": "3.23.2", - "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.23.2.tgz", - "integrity": "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==", - "devOptional": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ], - "license": "MIT", - "dependencies": { - "pure-rand": "^6.1.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/fast-decode-uri-component": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz", - "integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==", - "license": "MIT" - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "license": "MIT" - }, - "node_modules/fast-fifo": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", - "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", - "license": "MIT" - }, - "node_modules/fast-json-stringify": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-6.0.1.tgz", - "integrity": "sha512-s7SJE83QKBZwg54dIbD5rCtzOBVD43V1ReWXXYqBgwCwHLYAAT0RQc/FmrQglXqWPpz6omtryJQOau5jI4Nrvg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT", - "dependencies": { - "@fastify/merge-json-schemas": "^0.2.0", - "ajv": "^8.12.0", - "ajv-formats": "^3.0.1", - "fast-uri": "^3.0.0", - "json-schema-ref-resolver": "^2.0.0", - "rfdc": "^1.2.0" - } - }, - "node_modules/fast-querystring": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/fast-querystring/-/fast-querystring-1.1.2.tgz", - "integrity": "sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==", - "license": "MIT", - "dependencies": { - "fast-decode-uri-component": "^1.0.1" - } - }, - "node_modules/fast-redact": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz", - "integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/fast-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", - "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/fast-xml-parser": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz", - "integrity": "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "dependencies": { - "strnum": "^2.1.0" - }, - "bin": { - "fxparser": "src/cli/cli.js" - } - }, - "node_modules/fastify": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/fastify/-/fastify-5.5.0.tgz", - "integrity": "sha512-ZWSWlzj3K/DcULCnCjEiC2zn2FBPdlZsSA/pnPa/dbUfLvxkD/Nqmb0XXMXLrWkeM4uQPUvjdJpwtXmTfriXqw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT", - "dependencies": { - "@fastify/ajv-compiler": "^4.0.0", - "@fastify/error": "^4.0.0", - "@fastify/fast-json-stringify-compiler": "^5.0.0", - "@fastify/proxy-addr": "^5.0.0", - "abstract-logging": "^2.0.1", - "avvio": "^9.0.0", - "fast-json-stringify": "^6.0.0", - "find-my-way": "^9.0.0", - "light-my-request": "^6.0.0", - "pino": "^9.0.0", - "process-warning": "^5.0.0", - "rfdc": "^1.3.1", - "secure-json-parse": "^4.0.0", - "semver": "^7.6.0", - "toad-cache": "^3.7.0" - } - }, - "node_modules/fastify-plugin": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/fastify-plugin/-/fastify-plugin-5.0.1.tgz", - "integrity": "sha512-HCxs+YnRaWzCl+cWRYFnHmeRFyR5GVnJTAaCJQiYzQSDwK9MgJdyAsuL3nh0EWRCYMgQ5MeziymvmAhUHYHDUQ==", - "license": "MIT" - }, - "node_modules/fastq": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", - "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/find-my-way": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-9.3.0.tgz", - "integrity": "sha512-eRoFWQw+Yv2tuYlK2pjFS2jGXSxSppAs3hSQjfxVKxM5amECzIgYYc1FEI8ZmhSh/Ig+FrKEz43NLRKJjYCZVg==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-querystring": "^1.0.0", - "safe-regex2": "^5.0.0" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/follow-redirects": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", - "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/form-data": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "license": "MIT" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/get-tsconfig": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz", - "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve-pkg-maps": "^1.0.0" - }, - "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" - } - }, - "node_modules/giget": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz", - "integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "citty": "^0.1.6", - "consola": "^3.4.0", - "defu": "^6.1.4", - "node-fetch-native": "^1.6.6", - "nypm": "^0.6.0", - "pathe": "^2.0.3" - }, - "bin": { - "giget": "dist/cli.mjs" - } - }, - "node_modules/github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", - "license": "MIT" - }, - "node_modules/glob": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", - "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", - "license": "BlueOak-1.0.0", - "dependencies": { - "foreground-child": "^3.3.1", - "jackspeak": "^4.1.1", - "minimatch": "^10.1.1", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^2.0.0" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "license": "ISC" - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "license": "MIT", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/iconv-lite": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", - "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/imapflow": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/imapflow/-/imapflow-1.1.1.tgz", - "integrity": "sha512-cgK0eGagmw8C3pY6IiVLSF52N4bwvNRJ7mog4bdZOTafG90yNgUkShQBjo4/CRqvl/isPcaV1Gc0OmFBdXF0HQ==", - "license": "MIT", - "dependencies": { - "@zone-eu/mailsplit": "5.4.7", - "encoding-japanese": "2.2.0", - "iconv-lite": "0.7.0", - "libbase64": "1.3.0", - "libmime": "5.3.7", - "libqp": "2.1.1", - "nodemailer": "7.0.10", - "pino": "10.1.0", - "socks": "2.8.7" - } - }, - "node_modules/imapflow/node_modules/pino": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/pino/-/pino-10.1.0.tgz", - "integrity": "sha512-0zZC2ygfdqvqK8zJIr1e+wT1T/L+LF6qvqvbzEQ6tiMAoTqEVK9a1K3YRu8HEUvGEvNqZyPJTtb2sNIoTkB83w==", - "license": "MIT", - "dependencies": { - "@pinojs/redact": "^0.4.0", - "atomic-sleep": "^1.0.0", - "on-exit-leak-free": "^2.1.0", - "pino-abstract-transport": "^2.0.0", - "pino-std-serializers": "^7.0.0", - "process-warning": "^5.0.0", - "quick-format-unescaped": "^4.0.3", - "real-require": "^0.2.0", - "safe-stable-stringify": "^2.3.1", - "sonic-boom": "^4.0.1", - "thread-stream": "^3.0.0" - }, - "bin": { - "pino": "bin.js" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "license": "ISC" - }, - "node_modules/ip-address": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", - "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==", - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, - "node_modules/ipaddr.js": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", - "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", - "license": "MIT", - "engines": { - "node": ">= 10" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "license": "MIT" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "license": "ISC" - }, - "node_modules/jackspeak": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", - "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/jiti": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz", - "integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==", - "devOptional": true, - "license": "MIT", - "bin": { - "jiti": "lib/jiti-cli.mjs" - } - }, - "node_modules/json-schema-ref-resolver": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/json-schema-ref-resolver/-/json-schema-ref-resolver-2.0.1.tgz", - "integrity": "sha512-HG0SIB9X4J8bwbxCbnd5FfPEbcXAJYTi1pBJeP/QPON+w8ovSME8iRG+ElHNxZNX2Qh6eYn1GdzJFS4cDFfx0Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT", - "dependencies": { - "dequal": "^2.0.3" - } - }, - "node_modules/json-schema-resolver": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-schema-resolver/-/json-schema-resolver-3.0.0.tgz", - "integrity": "sha512-HqMnbz0tz2DaEJ3ntsqtx3ezzZyDE7G56A/pPY/NGmrPu76UzsWquOpHFRAf5beTNXoH2LU5cQePVvRli1nchA==", - "license": "MIT", - "dependencies": { - "debug": "^4.1.1", - "fast-uri": "^3.0.5", - "rfdc": "^1.1.4" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/Eomm/json-schema-resolver?sponsor=1" - } - }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "license": "MIT" - }, - "node_modules/jsonwebtoken": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", - "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", - "license": "MIT", - "dependencies": { - "jws": "^3.2.2", - "lodash.includes": "^4.3.0", - "lodash.isboolean": "^3.0.3", - "lodash.isinteger": "^4.0.4", - "lodash.isnumber": "^3.0.3", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.once": "^4.0.0", - "ms": "^2.1.1", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=12", - "npm": ">=6" - } - }, - "node_modules/jwa": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", - "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", - "license": "MIT", - "dependencies": { - "buffer-equal-constant-time": "^1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/jws": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.3.tgz", - "integrity": "sha512-byiJ0FLRdLdSVSReO/U4E7RoEyOCKnEnEPMjq3HxWtvzLsV08/i5RQKsFVNkCldrCaPr2vDNAOMsfs8T/Hze7g==", - "license": "MIT", - "dependencies": { - "jwa": "^1.4.2", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/lazystream": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", - "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", - "license": "MIT", - "dependencies": { - "readable-stream": "^2.0.5" - }, - "engines": { - "node": ">= 0.6.3" - } - }, - "node_modules/lazystream/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/lazystream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "license": "MIT" - }, - "node_modules/lazystream/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/libbase64": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/libbase64/-/libbase64-1.3.0.tgz", - "integrity": "sha512-GgOXd0Eo6phYgh0DJtjQ2tO8dc0IVINtZJeARPeiIJqge+HdsWSuaDTe8ztQ7j/cONByDZ3zeB325AHiv5O0dg==", - "license": "MIT" - }, - "node_modules/libmime": { - "version": "5.3.7", - "resolved": "https://registry.npmjs.org/libmime/-/libmime-5.3.7.tgz", - "integrity": "sha512-FlDb3Wtha8P01kTL3P9M+ZDNDWPKPmKHWaU/cG/lg5pfuAwdflVpZE+wm9m7pKmC5ww6s+zTxBKS1p6yl3KpSw==", - "license": "MIT", - "dependencies": { - "encoding-japanese": "2.2.0", - "iconv-lite": "0.6.3", - "libbase64": "1.3.0", - "libqp": "2.1.1" - } - }, - "node_modules/libmime/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/libqp": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/libqp/-/libqp-2.1.1.tgz", - "integrity": "sha512-0Wd+GPz1O134cP62YU2GTOPNA7Qgl09XwCqM5zpBv87ERCXdfDtyKXvV7c9U22yWJh44QZqBocFnXN11K96qow==", - "license": "MIT" - }, - "node_modules/light-my-request": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-6.6.0.tgz", - "integrity": "sha512-CHYbu8RtboSIoVsHZ6Ye4cj4Aw/yg2oAFimlF7mNvfDV192LR7nDiKtSIfCuLT7KokPSTn/9kfVLm5OGN0A28A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause", - "dependencies": { - "cookie": "^1.0.1", - "process-warning": "^4.0.0", - "set-cookie-parser": "^2.6.0" - } - }, - "node_modules/light-my-request/node_modules/process-warning": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-4.0.1.tgz", - "integrity": "sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT" - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "license": "MIT" - }, - "node_modules/lodash.includes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", - "license": "MIT" - }, - "node_modules/lodash.isboolean": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", - "license": "MIT" - }, - "node_modules/lodash.isinteger": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", - "license": "MIT" - }, - "node_modules/lodash.isnumber": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", - "license": "MIT" - }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", - "license": "MIT" - }, - "node_modules/lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", - "license": "MIT" - }, - "node_modules/lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", - "license": "MIT" - }, - "node_modules/lru-cache": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz", - "integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==", - "license": "ISC", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/mime": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", - "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/minimatch": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", - "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/brace-expansion": "^5.0.0" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "license": "MIT" - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/napi-build-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", - "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", - "license": "MIT" - }, - "node_modules/node-abi": { - "version": "3.80.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.80.0.tgz", - "integrity": "sha512-LyPuZJcI9HVwzXK1GPxWNzrr+vr8Hp/3UqlmWxxh8p54U1ZbclOqbSog9lWHaCX+dBaiGi6n/hIX+mKu74GmPA==", - "license": "MIT", - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-addon-api": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.5.0.tgz", - "integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==", - "license": "MIT", - "engines": { - "node": "^18 || ^20 || >= 21" - } - }, - "node_modules/node-fetch-native": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", - "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/node-gyp-build": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", - "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", - "license": "MIT", - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" - } - }, - "node_modules/nodemailer": { - "version": "7.0.10", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.10.tgz", - "integrity": "sha512-Us/Se1WtT0ylXgNFfyFSx4LElllVLJXQjWi2Xz17xWw7amDKO2MLtFnVp1WACy7GkVGs+oBlRopVNUzlrGSw1w==", - "license": "MIT-0", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/nypm": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.1.tgz", - "integrity": "sha512-hlacBiRiv1k9hZFiphPUkfSQ/ZfQzZDzC+8z0wL3lvDAOUu/2NnChkKuMoMjNur/9OpKuz2QsIeiPVN0xM5Q0w==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "citty": "^0.1.6", - "consola": "^3.4.2", - "pathe": "^2.0.3", - "pkg-types": "^2.2.0", - "tinyexec": "^1.0.1" - }, - "bin": { - "nypm": "dist/cli.mjs" - }, - "engines": { - "node": "^14.16.0 || >=16.10.0" - } - }, - "node_modules/ohash": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", - "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/on-exit-leak-free": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", - "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/openapi-types": { - "version": "12.1.3", - "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", - "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==", - "license": "MIT" - }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "license": "BlueOak-1.0.0" - }, - "node_modules/pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "license": "(MIT AND Zlib)" - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-scurry": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", - "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/pdf-lib": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/pdf-lib/-/pdf-lib-1.17.1.tgz", - "integrity": "sha512-V/mpyJAoTsN4cnP31vc0wfNA1+p20evqqnap0KLoRUN0Yk/p3wN52DOEsL4oBFcLdb76hlpKPtzJIgo67j/XLw==", - "license": "MIT", - "dependencies": { - "@pdf-lib/standard-fonts": "^1.0.0", - "@pdf-lib/upng": "^1.0.1", - "pako": "^1.0.11", - "tslib": "^1.11.1" - } - }, - "node_modules/pdf-lib/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "license": "0BSD" - }, - "node_modules/perfect-debounce": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", - "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/pg": { - "version": "8.16.3", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz", - "integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==", - "license": "MIT", - "dependencies": { - "pg-connection-string": "^2.9.1", - "pg-pool": "^3.10.1", - "pg-protocol": "^1.10.3", - "pg-types": "2.2.0", - "pgpass": "1.0.5" - }, - "engines": { - "node": ">= 16.0.0" - }, - "optionalDependencies": { - "pg-cloudflare": "^1.2.7" - }, - "peerDependencies": { - "pg-native": ">=3.0.1" - }, - "peerDependenciesMeta": { - "pg-native": { - "optional": true - } - } - }, - "node_modules/pg-cloudflare": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.7.tgz", - "integrity": "sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==", - "license": "MIT", - "optional": true - }, - "node_modules/pg-connection-string": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz", - "integrity": "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==", - "license": "MIT" - }, - "node_modules/pg-int8": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", - "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", - "license": "ISC", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/pg-pool": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.1.tgz", - "integrity": "sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==", - "license": "MIT", - "peerDependencies": { - "pg": ">=8.0" - } - }, - "node_modules/pg-protocol": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.3.tgz", - "integrity": "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==", - "license": "MIT" - }, - "node_modules/pg-types": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", - "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", - "license": "MIT", - "dependencies": { - "pg-int8": "1.0.1", - "postgres-array": "~2.0.0", - "postgres-bytea": "~1.0.0", - "postgres-date": "~1.0.4", - "postgres-interval": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pgpass": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", - "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", - "license": "MIT", - "dependencies": { - "split2": "^4.1.0" - } - }, - "node_modules/pino": { - "version": "9.9.0", - "resolved": "https://registry.npmjs.org/pino/-/pino-9.9.0.tgz", - "integrity": "sha512-zxsRIQG9HzG+jEljmvmZupOMDUQ0Jpj0yAgE28jQvvrdYTlEaiGwelJpdndMl/MBuRr70heIj83QyqJUWaU8mQ==", - "license": "MIT", - "dependencies": { - "atomic-sleep": "^1.0.0", - "fast-redact": "^3.1.1", - "on-exit-leak-free": "^2.1.0", - "pino-abstract-transport": "^2.0.0", - "pino-std-serializers": "^7.0.0", - "process-warning": "^5.0.0", - "quick-format-unescaped": "^4.0.3", - "real-require": "^0.2.0", - "safe-stable-stringify": "^2.3.1", - "sonic-boom": "^4.0.1", - "thread-stream": "^3.0.0" - }, - "bin": { - "pino": "bin.js" - } - }, - "node_modules/pino-abstract-transport": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz", - "integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==", - "license": "MIT", - "dependencies": { - "split2": "^4.0.0" - } - }, - "node_modules/pino-std-serializers": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz", - "integrity": "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==", - "license": "MIT" - }, - "node_modules/pkg-types": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", - "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "confbox": "^0.2.2", - "exsolve": "^1.0.7", - "pathe": "^2.0.3" - } - }, - "node_modules/pngjs": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz", - "integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==", - "license": "MIT", - "engines": { - "node": ">=14.19.0" - } - }, - "node_modules/postgres-array": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", - "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/postgres-bytea": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", - "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postgres-date": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", - "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postgres-interval": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", - "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", - "license": "MIT", - "dependencies": { - "xtend": "^4.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/prebuild-install": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", - "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", - "license": "MIT", - "dependencies": { - "detect-libc": "^2.0.0", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^2.0.0", - "node-abi": "^3.3.0", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^4.0.0", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - }, - "bin": { - "prebuild-install": "bin.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/prisma": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.15.0.tgz", - "integrity": "sha512-E6RCgOt+kUVtjtZgLQDBJ6md2tDItLJNExwI0XJeBc1FKL+Vwb+ovxXxuok9r8oBgsOXBA33fGDuE/0qDdCWqQ==", - "devOptional": true, - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "@prisma/config": "6.15.0", - "@prisma/engines": "6.15.0" - }, - "bin": { - "prisma": "build/index.js" - }, - "engines": { - "node": ">=18.18" - }, - "peerDependencies": { - "typescript": ">=5.1.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", - "license": "MIT", - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "license": "MIT" - }, - "node_modules/process-warning": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz", - "integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT" - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" - }, - "node_modules/pump": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", - "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", - "license": "MIT", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/pure-rand": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", - "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", - "devOptional": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ], - "license": "MIT" - }, - "node_modules/quick-format-unescaped": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", - "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", - "license": "MIT" - }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/rc9": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz", - "integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "defu": "^6.1.4", - "destr": "^2.0.3" - } - }, - "node_modules/readable-stream": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", - "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", - "license": "MIT", - "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/readdir-glob": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", - "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", - "license": "Apache-2.0", - "dependencies": { - "minimatch": "^5.1.0" - } - }, - "node_modules/readdir-glob/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/real-require": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", - "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", - "license": "MIT", - "engines": { - "node": ">= 12.13.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" - } - }, - "node_modules/ret": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.5.0.tgz", - "integrity": "sha512-I1XxrZSQ+oErkRR4jYbAyEEu2I0avBvvMM5JN+6EBprOGRCs63ENqZ3vjavq8fBw2+62G5LF5XelKwuJpcvcxw==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rfdc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", - "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", - "license": "MIT" - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/safe-regex2": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-5.0.0.tgz", - "integrity": "sha512-YwJwe5a51WlK7KbOJREPdjNrpViQBI3p4T50lfwPuDhZnE3XGVTlGvi+aolc5+RvxDD6bnUmjVsU9n1eboLUYw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT", - "dependencies": { - "ret": "~0.5.0" - } - }, - "node_modules/safe-stable-stringify": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", - "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/secure-json-parse": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-4.0.0.tgz", - "integrity": "sha512-dxtLJO6sc35jWidmLxo7ij+Eg48PM/kleBsxpC8QJE0qJICe+KawkDQmvCMZUr9u7WKVHgMW6vy3fQ7zMiFZMA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/set-cookie-parser": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", - "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", - "license": "MIT" - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" - }, - "node_modules/sharp": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", - "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "@img/colour": "^1.0.0", - "detect-libc": "^2.1.2", - "semver": "^7.7.3" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.5", - "@img/sharp-darwin-x64": "0.34.5", - "@img/sharp-libvips-darwin-arm64": "1.2.4", - "@img/sharp-libvips-darwin-x64": "1.2.4", - "@img/sharp-libvips-linux-arm": "1.2.4", - "@img/sharp-libvips-linux-arm64": "1.2.4", - "@img/sharp-libvips-linux-ppc64": "1.2.4", - "@img/sharp-libvips-linux-riscv64": "1.2.4", - "@img/sharp-libvips-linux-s390x": "1.2.4", - "@img/sharp-libvips-linux-x64": "1.2.4", - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", - "@img/sharp-libvips-linuxmusl-x64": "1.2.4", - "@img/sharp-linux-arm": "0.34.5", - "@img/sharp-linux-arm64": "0.34.5", - "@img/sharp-linux-ppc64": "0.34.5", - "@img/sharp-linux-riscv64": "0.34.5", - "@img/sharp-linux-s390x": "0.34.5", - "@img/sharp-linux-x64": "0.34.5", - "@img/sharp-linuxmusl-arm64": "0.34.5", - "@img/sharp-linuxmusl-x64": "0.34.5", - "@img/sharp-wasm32": "0.34.5", - "@img/sharp-win32-arm64": "0.34.5", - "@img/sharp-win32-ia32": "0.34.5", - "@img/sharp-win32-x64": "0.34.5" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/simple-get": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", - "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "decompress-response": "^6.0.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, - "node_modules/smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "license": "MIT", - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks": { - "version": "2.8.7", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", - "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", - "license": "MIT", - "dependencies": { - "ip-address": "^10.0.1", - "smart-buffer": "^4.2.0" - }, - "engines": { - "node": ">= 10.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/sonic-boom": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz", - "integrity": "sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==", - "license": "MIT", - "dependencies": { - "atomic-sleep": "^1.0.0" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/split2": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", - "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", - "license": "ISC", - "engines": { - "node": ">= 10.x" - } - }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/streamx": { - "version": "2.22.1", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.22.1.tgz", - "integrity": "sha512-znKXEBxfatz2GBNK02kRnCXjV+AA4kjZIUxeWSr3UGirZMJfTE9uiwKHobnbgxWyL/JWro8tTq+vOqAK1/qbSA==", - "license": "MIT", - "dependencies": { - "fast-fifo": "^1.3.2", - "text-decoder": "^1.1.0" - }, - "optionalDependencies": { - "bare-events": "^2.2.0" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/string-width-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/strnum": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.1.tgz", - "integrity": "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT" - }, - "node_modules/tar-fs": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", - "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", - "license": "MIT", - "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, - "node_modules/tar-fs/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/tar-fs/node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "license": "MIT", - "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tar-stream": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", - "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", - "license": "MIT", - "dependencies": { - "b4a": "^1.6.4", - "fast-fifo": "^1.2.0", - "streamx": "^2.15.0" - } - }, - "node_modules/text-decoder": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", - "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", - "license": "Apache-2.0", - "dependencies": { - "b4a": "^1.6.4" - } - }, - "node_modules/thread-stream": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz", - "integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==", - "license": "MIT", - "dependencies": { - "real-require": "^0.2.0" - } - }, - "node_modules/tinyexec": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.1.tgz", - "integrity": "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/toad-cache": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/toad-cache/-/toad-cache-3.7.0.tgz", - "integrity": "sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==", - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT" - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/tsx": { - "version": "4.20.5", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.5.tgz", - "integrity": "sha512-+wKjMNU9w/EaQayHXb7WA7ZaHY6hN8WgfvHNQ3t1PnU91/7O8TcTnIhCDYTZwnt8JsO9IBqZ30Ln1r7pPF52Aw==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "~0.25.0", - "get-tsconfig": "^4.7.5" - }, - "bin": { - "tsx": "dist/cli.mjs" - }, - "engines": { - "node": ">=18.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - } - }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "license": "Apache-2.0", - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/typescript": { - "version": "5.9.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", - "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "7.10.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", - "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==", - "license": "MIT" - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT" - }, - "node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/wrap-ansi-cjs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "license": "ISC" - }, - "node_modules/ws": { - "version": "8.18.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", - "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/xmlbuilder": { - "version": "15.1.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", - "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", - "license": "MIT", - "engines": { - "node": ">=8.0" - } - }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "license": "MIT", - "engines": { - "node": ">=0.4" - } - }, - "node_modules/yaml": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", - "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - } - }, - "node_modules/zip-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-6.0.1.tgz", - "integrity": "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==", - "license": "MIT", - "dependencies": { - "archiver-utils": "^5.0.0", - "compress-commons": "^6.0.2", - "readable-stream": "^4.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, - "node_modules/zpl-image": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/zpl-image/-/zpl-image-0.2.0.tgz", - "integrity": "sha512-6EqJEvVmfHGAgNt2dIA1t3C8uow/ceinDAzAlAOd3FDGNRN1L+ElY5ddfbwN4eyZykSeqwc0AC8fKus40mkinw==", - "license": "MIT" - }, - "node_modules/zpl-renderer-js": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/zpl-renderer-js/-/zpl-renderer-js-2.0.2.tgz", - "integrity": "sha512-zGGbLxwSFcYDsytuQlDl/tuIve80Ww2jnOkM3jAzxDqHhZ+jUCxMyNeflBHMAyCV0wRvTSIcL2TpHx1WgauWqA==", - "license": "MIT" - } - } -} diff --git a/package.json b/package.json index e70ea9c..5e69b5d 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,6 @@ "axios": "^1.12.1", "bcrypt": "^6.0.0", "bwip-js": "^4.8.0", - "canvas": "^3.2.0", "crypto": "^1.0.1", "dayjs": "^1.11.18", "drizzle-orm": "^0.45.0", From d33b908775a2c9ec8a2b7d8df4e8eeab3e38ffb6 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sun, 7 Dec 2025 22:40:15 +0100 Subject: [PATCH 095/149] TS fixes --- db/schema/checks.ts | 4 ++-- db/schema/files.ts | 4 ++-- db/schema/historyitems.ts | 4 ++-- db/schema/staff_time_entry_connects.ts | 4 ++-- db/schema/staff_zeitstromtimestamps.ts | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/db/schema/checks.ts b/db/schema/checks.ts index 744b615..45f2a26 100644 --- a/db/schema/checks.ts +++ b/db/schema/checks.ts @@ -10,7 +10,7 @@ import { import { tenants } from "./tenants" import { vehicles } from "./vehicles" -import { inventoryItems } from "./inventoryitems" +import { inventoryitems } from "./inventoryitems" import { authUsers } from "./auth_users" export const checks = pgTable("checks", { @@ -26,7 +26,7 @@ export const checks = pgTable("checks", { // ❌ profile removed (old 0_profiles reference) inventoryItem: bigint("inventoryitem", { mode: "number" }) - .references(() => inventoryItems.id), + .references(() => inventoryitems.id), tenant: bigint("tenant", { mode: "number" }) .references(() => tenants.id), diff --git a/db/schema/files.ts b/db/schema/files.ts index f118834..a151421 100644 --- a/db/schema/files.ts +++ b/db/schema/files.ts @@ -17,7 +17,7 @@ import { plants } from "./plants" import { createddocuments } from "./createddocuments" import { vehicles } from "./vehicles" import { products } from "./products" -import { inventoryItems } from "./inventoryitems" +import { inventoryitems } from "./inventoryitems" import { folders } from "./folders" import { filetags } from "./filetags" import { authUsers } from "./auth_users" @@ -51,7 +51,7 @@ export const files = pgTable("files", { check: uuid("check").references(() => checks.id), - inventoryitem: bigint("inventoryitem", { mode: "number" }).references(() => inventoryItems.id), + inventoryitem: bigint("inventoryitem", { mode: "number" }).references(() => inventoryitems.id), folder: uuid("folder").references(() => folders.id), diff --git a/db/schema/historyitems.ts b/db/schema/historyitems.ts index 40da4c9..4f82235 100644 --- a/db/schema/historyitems.ts +++ b/db/schema/historyitems.ts @@ -14,7 +14,7 @@ import { projects } from "./projects" import { plants } from "./plants" import { incominginvoices } from "./incominginvoices" import { contacts } from "./contacts" -import { inventoryItems } from "./inventoryitems" +import { inventoryitems } from "./inventoryitems" import { products } from "./products" import { tasks } from "./tasks" import { vehicles } from "./vehicles" @@ -75,7 +75,7 @@ export const historyitems = pgTable("historyitems", { }), inventoryitem: bigint("inventoryitem", { mode: "number" }).references( - () => inventoryItems.id, + () => inventoryitems.id, { onDelete: "cascade" } ), diff --git a/db/schema/staff_time_entry_connects.ts b/db/schema/staff_time_entry_connects.ts index 5569605..13cb51f 100644 --- a/db/schema/staff_time_entry_connects.ts +++ b/db/schema/staff_time_entry_connects.ts @@ -7,7 +7,7 @@ import { text, } from "drizzle-orm/pg-core" -import { staffTimeEntries } from "./staff_time_entries" +import { stafftimeentries } from "./staff_time_entries" import {sql} from "drizzle-orm"; export const staffTimeEntryConnects = pgTable("staff_time_entry_connects", { @@ -15,7 +15,7 @@ export const staffTimeEntryConnects = pgTable("staff_time_entry_connects", { timeEntryId: uuid("time_entry_id") .notNull() - .references(() => staffTimeEntries.id, { onDelete: "cascade" }), + .references(() => stafftimeentries.id, { onDelete: "cascade" }), projectId: bigint("project_id", { mode: "number" }), // referenziert später projects.id diff --git a/db/schema/staff_zeitstromtimestamps.ts b/db/schema/staff_zeitstromtimestamps.ts index 2e70480..174e843 100644 --- a/db/schema/staff_zeitstromtimestamps.ts +++ b/db/schema/staff_zeitstromtimestamps.ts @@ -8,7 +8,7 @@ import { import { tenants } from "./tenants" import { authProfiles } from "./auth_profiles" -import { staffTimeEntries } from "./staff_time_entries" +import { stafftimeentries } from "./staff_time_entries" export const staffZeitstromTimestamps = pgTable("staff_zeitstromtimestamps", { id: uuid("id").primaryKey().defaultRandom(), @@ -32,7 +32,7 @@ export const staffZeitstromTimestamps = pgTable("staff_zeitstromtimestamps", { time: timestamp("time", { withTimezone: true }).notNull(), staffTimeEntry: uuid("staff_time_entry").references( - () => staffTimeEntries.id + () => stafftimeentries.id ), internalNote: text("internal_note"), From 8faaef1ef525ac7bdd4bb7d4c08daacae4d5f9a7 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sun, 7 Dec 2025 22:42:23 +0100 Subject: [PATCH 096/149] TS fixes --- src/routes/functions.ts | 10 +++++----- src/routes/staff/time.ts | 2 +- src/utils/functions.ts | 17 +++++++++-------- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/routes/functions.ts b/src/routes/functions.ts index 651aba4..dc1551b 100644 --- a/src/routes/functions.ts +++ b/src/routes/functions.ts @@ -1,9 +1,9 @@ import { FastifyInstance } from "fastify"; import {createInvoicePDF, createTimeSheetPDF} from "../utils/pdf"; -import {encodeBase64ToNiimbot, generateLabel, useNextNumberRangeNumber} from "../utils/functions"; +//import {encodeBase64ToNiimbot, generateLabel, useNextNumberRangeNumber} from "../utils/functions"; import dayjs from "dayjs"; -import { ready as zplReady } from 'zpl-renderer-js' -import { renderZPL } from "zpl-image"; +//import { ready as zplReady } from 'zpl-renderer-js' +//import { renderZPL } from "zpl-image"; import customParseFormat from "dayjs/plugin/customParseFormat.js"; import isoWeek from "dayjs/plugin/isoWeek.js"; @@ -155,7 +155,7 @@ export default async function functionRoutes(server: FastifyInstance) { } }) - server.post('/print/zpl/preview', async (req, reply) => { + /*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} console.log(widthMm,heightMm,dpmm) @@ -192,6 +192,6 @@ export default async function functionRoutes(server: FastifyInstance) { console.error('[ZPL Preview Error]', err) return reply.code(500).send({ error: err.message || 'Failed to render ZPL' }) } - }) + })*/ } \ No newline at end of file diff --git a/src/routes/staff/time.ts b/src/routes/staff/time.ts index d5d2cda..3daa30c 100644 --- a/src/routes/staff/time.ts +++ b/src/routes/staff/time.ts @@ -7,7 +7,7 @@ export default async function staffTimeRoutes(server: FastifyInstance) { server.post( '/staff/time', async (req, reply) => { - const { started_at, stopped_at, type = 'work', description, user_id } = req.body + const { started_at, stopped_at, type = 'work', description, user_id } = req.body as any const userId = req.user.user_id const tenantId = req.user.tenant_id diff --git a/src/utils/functions.ts b/src/utils/functions.ts index 740db75..3190a87 100644 --- a/src/utils/functions.ts +++ b/src/utils/functions.ts @@ -1,11 +1,11 @@ import {FastifyInstance} from "fastify"; -import { PNG } from 'pngjs' -import { ready as zplReady } from 'zpl-renderer-js' -import { Utils } from '@mmote/niimbluelib' -import { createCanvas } from 'canvas' -import bwipjs from 'bwip-js' -import Sharp from 'sharp' -import fs from 'fs' +// import { PNG } from 'pngjs' +// import { ready as zplReady } from 'zpl-renderer-js' +// import { Utils } from '@mmote/niimbluelib' +// import { createCanvas } from 'canvas' +// import bwipjs from 'bwip-js' +// import Sharp from 'sharp' +// import fs from 'fs' export const useNextNumberRangeNumber = async (server:FastifyInstance, tenantId:number,numberRange)=> { const {data:tenant} = await server.supabase.from("tenants").select().eq("id",tenantId).single() @@ -29,6 +29,7 @@ export const useNextNumberRangeNumber = async (server:FastifyInstance, tenantId: } } +/* export async function encodeBase64ToNiimbot(base64Png, printDirection = 'top') { // 1️⃣ PNG dekodieren const buffer = Buffer.from(base64Png, 'base64') @@ -140,4 +141,4 @@ export async function generateLabel(context,width,height) { const base64 = final.toString('base64') return base64 -} \ No newline at end of file +}*/ From af5f9567f139896aac36db23f49e764f1dcf87bb Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sun, 7 Dec 2025 22:43:01 +0100 Subject: [PATCH 097/149] TS fixes --- src/routes/functions.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/routes/functions.ts b/src/routes/functions.ts index dc1551b..471ea1d 100644 --- a/src/routes/functions.ts +++ b/src/routes/functions.ts @@ -15,6 +15,7 @@ import timezone from "dayjs/plugin/timezone.js"; import {generateTimesEvaluation} from "../modules/time/evaluation.service"; import {citys} from "../../db/schema"; import {eq} from "drizzle-orm"; +import {useNextNumberRangeNumber} from "../utils/functions"; dayjs.extend(customParseFormat) dayjs.extend(isoWeek) dayjs.extend(isBetween) @@ -147,6 +148,7 @@ export default async function functionRoutes(server: FastifyInstance) { return reply.send({ ...data, + //@ts-ignore state_code: bundeslaender.find(i => i.name === data.countryName) }) } catch (err) { From e92eccc7d31a3cf61b31adfed1ac1683845f0c01 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sun, 7 Dec 2025 22:45:02 +0100 Subject: [PATCH 098/149] TS fixes --- src/routes/auth/auth.ts | 2 ++ src/routes/emailAsUser.ts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/routes/auth/auth.ts b/src/routes/auth/auth.ts index 5b1d1d9..dd4eb14 100644 --- a/src/routes/auth/auth.ts +++ b/src/routes/auth/auth.ts @@ -198,10 +198,12 @@ export default async function authRoutes(server: FastifyInstance) { const plainPassword = generateRandomPassword(); const passwordHash = await hashPassword(plainPassword); + await server.db .update(authUsers) .set({ passwordHash, + // @ts-ignore mustChangePassword: true, }) .where(eq(authUsers.id, user.id)); diff --git a/src/routes/emailAsUser.ts b/src/routes/emailAsUser.ts index 525fa25..0017688 100644 --- a/src/routes/emailAsUser.ts +++ b/src/routes/emailAsUser.ts @@ -53,6 +53,7 @@ export default async function emailAsUserRoutes(server: FastifyInstance) { await server.db .update(userCredentials) + //@ts-ignore .set(saveData) .where(eq(userCredentials.id, id)) @@ -79,6 +80,7 @@ export default async function emailAsUserRoutes(server: FastifyInstance) { imapSsl: body.imap_ssl, } + //@ts-ignore await server.db.insert(userCredentials).values(insertData) return reply.send({ success: true }) From 32b46f58a4bb0483ac8d7de09da06d8de0e8844b Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sun, 7 Dec 2025 22:45:49 +0100 Subject: [PATCH 099/149] TS fixes --- src/routes/files.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/routes/files.ts b/src/routes/files.ts index 0467766..5c7ac9d 100644 --- a/src/routes/files.ts +++ b/src/routes/files.ts @@ -107,6 +107,7 @@ export default async function fileRoutes(server: FastifyInstance) { if (!tenantId) return reply.code(401).send({ error: "Unauthorized" }) const list = await server.db + //@ts-ignore .select({ ...files, createddocument: createddocuments, @@ -138,6 +139,7 @@ export default async function fileRoutes(server: FastifyInstance) { server.post("/files/download/:id?", async (req, reply) => { try { const { id } = req.params as { id?: string } + //@ts-ignore const ids = req.body?.ids || [] // ------------------------------------------------- From 206bdc6392df6f2110667e9ba0e4f7034ab6dfac Mon Sep 17 00:00:00 2001 From: Florian Federspiel Date: Sun, 7 Dec 2025 22:03:06 +0000 Subject: [PATCH 100/149] docker-compose.yml bearbeiten --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index c6a115c..6490d95 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,6 @@ services: backend: - image: reg.federspiel.software/fedeo/backend:main + image: reg.federspiel.software/fedeo/backend:orm restart: always environment: From 607024c8130e48b2d06c6e557a6d52d145a399e5 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sun, 7 Dec 2025 23:06:29 +0100 Subject: [PATCH 101/149] Changed Dockerfile --- Dockerfile | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/Dockerfile b/Dockerfile index 12566db..cfcb8aa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,29 +1,20 @@ -# Basis-Image -FROM node:20-alpine AS base +FROM node:20-alpine WORKDIR /usr/src/app -# Dependencies installieren (dev deps für Build erforderlich) +# Package-Dateien COPY package*.json ./ + +# Dev + Prod Dependencies (für TS-Build nötig) RUN npm install -# Quellcode kopieren +# Restlicher Sourcecode COPY . . -# Build ausführen (TypeScript -> dist) -RUN npx tsc --skipLibCheck --noEmitOnError false - -# --------- Production Stage --------- -FROM node:20-alpine AS production -WORKDIR /usr/src/app - -# Nur production dependencies installieren -COPY package*.json ./ -RUN npm install --omit=dev - -COPY --from=base /usr/src/app/dist ./dist +# TypeScript Build +RUN npm run build # Port freigeben EXPOSE 3100 -# App starten -CMD ["npm", "start"] \ No newline at end of file +# Start der App +CMD ["node", "dist/index.js"] From f5825f9a18df217fc842632933dd81433f081133 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sun, 7 Dec 2025 23:14:31 +0100 Subject: [PATCH 102/149] tsconfig.json --- tsconfig.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tsconfig.json b/tsconfig.json index bd70a7e..268fa98 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,8 +8,9 @@ "esModuleInterop": true, "skipLibCheck": true, "noEmitOnError": false, - "forceConsistentCasingInFileNames": true + "forceConsistentCasingInFileNames": true, + "rootDir": "." }, - "include": ["src"], + "include": ["src","db"], "exclude": ["node_modules", "dist"] } \ No newline at end of file From aa1f3b1cb34d63d170601a219c0bbc9a9f387d05 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sun, 7 Dec 2025 23:23:18 +0100 Subject: [PATCH 103/149] tsconfig.json --- package.json | 2 +- src/routes/resources/main.ts | 2 +- src/{ => utils}/resource.config.ts | 2 +- tsconfig.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename src/{ => utils}/resource.config.ts (99%) diff --git a/package.json b/package.json index 5e69b5d..984f697 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "scripts": { "dev": "tsx watch src/index.ts", "build": "tsc", - "start": "node dist/index.js", + "start": "node dist/src/index.js", "schema:index": "ts-node scripts/generate-schema-index.ts" }, "repository": { diff --git a/src/routes/resources/main.ts b/src/routes/resources/main.ts index 4a77cba..514aa9f 100644 --- a/src/routes/resources/main.ts +++ b/src/routes/resources/main.ts @@ -12,7 +12,7 @@ import { -import {resourceConfig} from "../../resource.config"; +import {resourceConfig} from "../../utils/resource.config"; // ------------------------------------------------------------- // SQL Volltextsuche auf mehreren Feldern diff --git a/src/resource.config.ts b/src/utils/resource.config.ts similarity index 99% rename from src/resource.config.ts rename to src/utils/resource.config.ts index 47be3c3..9c62711 100644 --- a/src/resource.config.ts +++ b/src/utils/resource.config.ts @@ -8,7 +8,7 @@ import { projects, projecttypes, servicecategories, services, spaces, tasks, texttemplates, units, vehicles, vendors -} from "../db/schema"; +} from "../../db/schema"; export const resourceConfig = { projects: { diff --git a/tsconfig.json b/tsconfig.json index 268fa98..29f598d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,6 +11,6 @@ "forceConsistentCasingInFileNames": true, "rootDir": "." }, - "include": ["src","db"], + "include": ["src","db","*.ts"], "exclude": ["node_modules", "dist"] } \ No newline at end of file From 899f8dce2052fe2c4b3a59e9e1dcc54916a7bfda Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sun, 7 Dec 2025 23:28:36 +0100 Subject: [PATCH 104/149] tsconfig.json --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index cfcb8aa..015f7ec 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,4 +17,4 @@ RUN npm run build EXPOSE 3100 # Start der App -CMD ["node", "dist/index.js"] +CMD ["node", "dist/src/index.js"] From e760bd5f97cae15dcc4cae14f1a34aeadc051b7c Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sun, 7 Dec 2025 23:30:33 +0100 Subject: [PATCH 105/149] tsconfig.json --- src/plugins/db.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/db.ts b/src/plugins/db.ts index 092393a..8c1f61a 100644 --- a/src/plugins/db.ts +++ b/src/plugins/db.ts @@ -5,7 +5,7 @@ import * as schema from "../../db/schema" export default fp(async (server, opts) => { const pool = new Pool({ - host: "db-001.netbird.cloud", + host: "100.102.185.225", port: Number(process.env.DB_PORT || 5432), user: "postgres", password: "wJw7aNpEBJdcxgoct6GXNpvY4Cn6ECqu", From 8f0efc0d7283d2c93b3ed01c512f2612f5a1d3f5 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Mon, 8 Dec 2025 11:45:37 +0100 Subject: [PATCH 106/149] schema changes --- db/schema/statementallocations.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/db/schema/statementallocations.ts b/db/schema/statementallocations.ts index 9a5d27f..c0db9bc 100644 --- a/db/schema/statementallocations.ts +++ b/db/schema/statementallocations.ts @@ -23,15 +23,15 @@ export const statementallocations = pgTable("statementallocations", { id: uuid("id").primaryKey().defaultRandom(), // foreign keys - bs_id: integer("bs_id") + bankstatement: integer("bs_id") .notNull() .references(() => bankstatements.id), - cd_id: integer("cd_id").references(() => createddocuments.id), + createddocument: integer("cd_id").references(() => createddocuments.id), amount: doublePrecision("amount").notNull().default(0), - ii_id: bigint("ii_id", { mode: "number" }).references( + incominginvoice: bigint("ii_id", { mode: "number" }).references( () => incominginvoices.id ), @@ -43,7 +43,7 @@ export const statementallocations = pgTable("statementallocations", { () => accounts.id ), - createdAt: timestamp("created_at", { + created_at: timestamp("created_at", { withTimezone: false, }).defaultNow(), @@ -57,9 +57,9 @@ export const statementallocations = pgTable("statementallocations", { vendor: bigint("vendor", { mode: "number" }).references(() => vendors.id), - updatedAt: timestamp("updated_at", { withTimezone: true }), + updated_at: timestamp("updated_at", { withTimezone: true }), - updatedBy: uuid("updated_by").references(() => authUsers.id), + updated_by: uuid("updated_by").references(() => authUsers.id), archived: boolean("archived").notNull().default(false), }) From 1d3bf94b8895864c5a455665fbe526bbd47f6b45 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Mon, 8 Dec 2025 11:45:50 +0100 Subject: [PATCH 107/149] Route Changes --- src/routes/banking.ts | 293 +++++++++++++------------- src/routes/resources/main.ts | 67 ++++-- src/routes/tenant.ts | 386 ++++++++++++++++++++--------------- 3 files changed, 430 insertions(+), 316 deletions(-) diff --git a/src/routes/banking.ts b/src/routes/banking.ts index fd228b7..f06ab60 100644 --- a/src/routes/banking.ts +++ b/src/routes/banking.ts @@ -1,217 +1,236 @@ -import { FastifyInstance } from "fastify"; -import {insertHistoryItem} from "../utils/history"; +import { FastifyInstance } from "fastify" import axios from "axios" import dayjs from "dayjs" -import {secrets} from "../utils/secrets"; + +import { secrets } from "../utils/secrets" +import { insertHistoryItem } from "../utils/history" + +import { + bankrequisitions, + statementallocations, +} from "../../db/schema" + +import { + eq, + and, +} from "drizzle-orm" + export default async function bankingRoutes(server: FastifyInstance) { + // ------------------------------------------------------------------ + // 🔐 GoCardLess Token Handling + // ------------------------------------------------------------------ + const goCardLessBaseUrl = secrets.GOCARDLESS_BASE_URL const goCardLessSecretId = secrets.GOCARDLESS_SECRET_ID const goCardLessSecretKey = secrets.GOCARDLESS_SECRET_KEY - let tokenData = null + let tokenData: any = null const getToken = async () => { - const res = await axios({ - url: goCardLessBaseUrl + "/token/new/", - method: "POST", - data: { - secret_id: goCardLessSecretId, - secret_key: goCardLessSecretKey, - }, + const res = await axios.post(`${goCardLessBaseUrl}/token/new/`, { + secret_id: goCardLessSecretId, + secret_key: goCardLessSecretKey, }) tokenData = res.data tokenData.created_at = new Date().toISOString() - server.log.info("Got new GoCardless token") + + server.log.info("GoCardless token refreshed.") } const checkToken = async () => { - if (tokenData) { - const expired = dayjs(tokenData.created_at) - .add(tokenData.access_expires, "seconds") - .isBefore(dayjs()) - if (expired) { - server.log.info("Token expired — refreshing…") - await getToken() - } - } else { + if (!tokenData) return await getToken() + + const expired = dayjs(tokenData.created_at) + .add(tokenData.access_expires, "seconds") + .isBefore(dayjs()) + + if (expired) { + server.log.info("Refreshing expired GoCardless token …") await getToken() } } - // 🔹 Generate Link + // ------------------------------------------------------------------ + // 🔗 Create GoCardless Banking Link + // ------------------------------------------------------------------ server.get("/banking/link/:institutionid", async (req, reply) => { - await checkToken() - - const {institutionid} = req.params as {institutionid: string} - try { - const { data } = await axios({ - url: `${goCardLessBaseUrl}/requisitions/`, - method: "POST", - headers: { - Authorization: `Bearer ${tokenData.access}`, - accept: "application/json", - }, - data: { + await checkToken() + + const { institutionid } = req.params as { institutionid: string } + const tenantId = req.user?.tenant_id + + if (!tenantId) return reply.code(401).send({ error: "Unauthorized" }) + + const { data } = await axios.post( + `${goCardLessBaseUrl}/requisitions/`, + { redirect: "https://app.fedeo.de/settings/banking", institution_id: institutionid, user_language: "de", }, + { + headers: { Authorization: `Bearer ${tokenData.access}` }, + } + ) + + // DB: Requisition speichern + await server.db.insert(bankrequisitions).values({ + id: data.id, + tenant: tenantId, + institutionId: institutionid, + status: data.status, }) - await server.supabase - .from("bankrequisitions") - .insert({ - tenant: req.user.tenant_id, - institutionId: institutionid, - id: data.id, - status: data.status, - }) - return reply.send({ link: data.link }) - } catch (err) { - server.log.error(err.response?.data || err.message) + } catch (err: any) { + server.log.error(err?.response?.data || err) return reply.code(500).send({ error: "Failed to generate link" }) } }) - // 🔹 Check Institution + // ------------------------------------------------------------------ + // 🏦 Check Bank Institutions + // ------------------------------------------------------------------ server.get("/banking/institutions/:bic", async (req, reply) => { - const { bic } = req.params as {bic: string} - if (!bic) return reply.code(400).send("BIC not provided") - - await checkToken() - try { - const { data } = await axios({ - url: `${goCardLessBaseUrl}/institutions/?country=de`, - method: "GET", - headers: { - Authorization: `Bearer ${tokenData.access}`, - }, - }) + const { bic } = req.params as { bic: string } + if (!bic) return reply.code(400).send("BIC missing") + + await checkToken() + + const { data } = await axios.get( + `${goCardLessBaseUrl}/institutions/?country=de`, + { headers: { Authorization: `Bearer ${tokenData.access}` } } + ) + + const bank = data.find((i: any) => i.bic.toLowerCase() === bic.toLowerCase()) - const bank = data.find((i) => i.bic.toLowerCase() === bic.toLowerCase()) if (!bank) return reply.code(404).send("Bank not found") return reply.send(bank) - } catch (err) { - server.log.error(err.response?.data || err.message) + } catch (err: any) { + server.log.error(err?.response?.data || err) return reply.code(500).send("Failed to fetch institutions") } }) - // 🔹 List Requisitions + + // ------------------------------------------------------------------ + // 📄 Get Requisition Details + // ------------------------------------------------------------------ server.get("/banking/requisitions/:reqId", async (req, reply) => { - const { reqId } = req.params as {reqId: string} - if (!reqId) return reply.code(400).send("Requisition ID not provided") - - await checkToken() - try { - const { data } = await axios({ - url: `${goCardLessBaseUrl}/requisitions/${reqId}`, - method: "GET", - headers: { - Authorization: `Bearer ${tokenData.access}`, - }, - }) + const { reqId } = req.params as { reqId: string } + if (!reqId) return reply.code(400).send("Requisition ID missing") + await checkToken() + + const { data } = await axios.get( + `${goCardLessBaseUrl}/requisitions/${reqId}`, + { headers: { Authorization: `Bearer ${tokenData.access}` } } + ) + + // Load account details if (data.accounts) { data.accounts = await Promise.all( - data.accounts.map(async (accId) => { - const { data: accountData } = await axios({ - url: `${goCardLessBaseUrl}/accounts/${accId}`, - method: "GET", - headers: { - Authorization: `Bearer ${tokenData.access}`, - accept: "application/json", - }, - }) - return accountData + data.accounts.map(async (accId: string) => { + const { data: acc } = await axios.get( + `${goCardLessBaseUrl}/accounts/${accId}`, + { headers: { Authorization: `Bearer ${tokenData.access}` } } + ) + return acc }) ) } return reply.send(data) - } catch (err) { - server.log.error(err.response?.data || err.message) - return reply.code(500).send("Failed to fetch requisition data") + } catch (err: any) { + server.log.error(err?.response?.data || err) + return reply.code(500).send("Failed to fetch requisition details") } }) - //Create Banking Statement + // ------------------------------------------------------------------ + // 💰 Create Statement Allocation + // ------------------------------------------------------------------ server.post("/banking/statements", async (req, reply) => { - if (!req.user) { - return reply.code(401).send({ error: "Unauthorized" }); + try { + if (!req.user) return reply.code(401).send({ error: "Unauthorized" }) + + const { data: payload } = req.body as { data: any } + + const inserted = await server.db.insert(statementallocations).values({ + ...payload, + tenant: req.user.tenant_id + }).returning() + + const createdRecord = inserted[0] + + await insertHistoryItem(server, { + entity: "bankstatements", + entityId: createdRecord.id, + action: "created", + created_by: req.user.user_id, + tenant_id: req.user.tenant_id, + oldVal: null, + newVal: createdRecord, + text: "Buchung erstellt", + }) + + return reply.send(createdRecord) + + } catch (err) { + console.error(err) + return reply.code(500).send({ error: "Failed to create statement" }) } + }) - const body = req.body as { data: string }; - console.log(body); - const {data,error} = await server.supabase.from("statementallocations").insert({ - //@ts-ignore - ...body.data, - tenant: req.user.tenant_id, - }).select() - - await insertHistoryItem(server,{ - entity: "bankstatements", - //@ts-ignore - 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 + // ------------------------------------------------------------------ + // 🗑 Delete Statement Allocation + // ------------------------------------------------------------------ server.delete("/banking/statements/:id", async (req, reply) => { - if (!req.user) { - return reply.code(401).send({ error: "Unauthorized" }); - } + try { + if (!req.user) return reply.code(401).send({ error: "Unauthorized" }) - const { id } = req.params as { id?: string } + const { id } = req.params as { id: string } - const {data} = await server.supabase.from("statementallocations").select().eq("id",id).single() + const oldRecord = await server.db + .select() + .from(statementallocations) + .where(eq(statementallocations.id, id)) + .limit(1) - const {error} = await server.supabase.from("statementallocations").delete().eq("id",id) + const old = oldRecord[0] - if(!error){ + if (!old) return reply.code(404).send({ error: "Record not found" }) - await insertHistoryItem(server,{ + await server.db + .delete(statementallocations) + .where(eq(statementallocations.id, id)) + + await insertHistoryItem(server, { entity: "bankstatements", entityId: id, action: "deleted", created_by: req.user.user_id, tenant_id: req.user.tenant_id, - oldVal: data, + oldVal: old, newVal: null, - text: `Buchung gelöscht`, - }); + text: "Buchung gelöscht", + }) - return reply.send({success:true}) - } else { - return reply.code(500).send({error:"Fehler beim löschen"}) + return reply.send({ success: true }) + + } catch (err) { + console.error(err) + return reply.code(500).send({ error: "Failed to delete statement" }) } - - }) - - - - } - diff --git a/src/routes/resources/main.ts b/src/routes/resources/main.ts index 514aa9f..bad18a7 100644 --- a/src/routes/resources/main.ts +++ b/src/routes/resources/main.ts @@ -87,13 +87,12 @@ export default async function resourceRoutes(server: FastifyInstance) { const queryData = await q - // RELATION LOADING (MANY-TO-ONE) let ids = {} let lists = {} let maps = {} - let data = [] + let data = [...queryData] if(resourceConfig[resource].mtoLoad) { resourceConfig[resource].mtoLoad.forEach(relation => { @@ -101,6 +100,7 @@ export default async function resourceRoutes(server: FastifyInstance) { }) for await (const relation of resourceConfig[resource].mtoLoad ) { + console.log(relation) lists[relation] = ids[relation].length ? await server.db.select().from(resourceConfig[relation + "s"].table).where(inArray(resourceConfig[relation + "s"].table.id, ids[relation])) : [] } @@ -120,8 +120,29 @@ export default async function resourceRoutes(server: FastifyInstance) { return toReturn }); - } else { - data = queryData + } + + if(resourceConfig[resource].mtmListLoad) { + for await (const relation of resourceConfig[resource].mtmListLoad) { + console.log(relation) + console.log(resource.substring(0,resource.length-1)) + + const relationRows = await server.db.select().from(resourceConfig[relation].table).where(inArray(resourceConfig[relation].table[resource.substring(0,resource.length-1)],data.map(i => i.id))) + + console.log(relationRows.length) + + data = data.map(row => { + let toReturn = { + ...row + } + + toReturn[relation] = relationRows.filter(i => i[resource.substring(0,resource.length-1)] === row.id) + + return toReturn + }) + + + } } return data @@ -271,14 +292,14 @@ export default async function resourceRoutes(server: FastifyInstance) { }; } - // RELATION LOADING (MANY-TO-ONE) - let ids = {} - let lists = {} - let maps = {} - let data = [] + let data = [...rows] + //Many to One if(resourceConfig[resource].mtoLoad) { + let ids = {} + let lists = {} + let maps = {} resourceConfig[resource].mtoLoad.forEach(relation => { ids[relation] = [...new Set(rows.map(r => r[relation]).filter(Boolean))]; }) @@ -303,8 +324,28 @@ export default async function resourceRoutes(server: FastifyInstance) { return toReturn }); - } else { - data = rows + } + + if(resourceConfig[resource].mtmListLoad) { + for await (const relation of resourceConfig[resource].mtmListLoad) { + console.log(relation) + + const relationRows = await server.db.select().from(resourceConfig[relation].table).where(inArray(resourceConfig[relation].table[resource.substring(0,resource.length-1)],data.map(i => i.id))) + + console.log(relationRows) + + data = data.map(row => { + let toReturn = { + ...row + } + + toReturn[relation] = relationRows.filter(i => i[resource.substring(0,resource.length-1)] === row.id) + + return toReturn + }) + + + } } // ----------------------------------------------- @@ -365,13 +406,13 @@ export default async function resourceRoutes(server: FastifyInstance) { data[relation] = await server.db.select().from(resourceConfig[relation + "s"].table).where(eq(resourceConfig[relation + "s"].table.id, data[relation])) } } + } + if(resourceConfig[resource].mtmLoad) { for await (const relation of resourceConfig[resource].mtmLoad ) { console.log(relation) data[relation] = await server.db.select().from(resourceConfig[relation].table).where(eq(resourceConfig[relation].table[resource.substring(0,resource.length - 1)],id)) } - - } return data diff --git a/src/routes/tenant.ts b/src/routes/tenant.ts index 36634ab..8b83203 100644 --- a/src/routes/tenant.ts +++ b/src/routes/tenant.ts @@ -1,188 +1,242 @@ -import { FastifyInstance } from "fastify"; -import jwt from "jsonwebtoken"; -import {secrets} from "../utils/secrets"; +import { FastifyInstance } from "fastify" +import jwt from "jsonwebtoken" +import { secrets } from "../utils/secrets" -export default async function routes(server: FastifyInstance) { +import { + authTenantUsers, + authUsers, + authProfiles, + tenants +} from "../../db/schema" + +import { eq } from "drizzle-orm" + + +export default async function tenantRoutes(server: FastifyInstance) { + + + // ------------------------------------------------------------- + // GET CURRENT TENANT + // ------------------------------------------------------------- server.get("/tenant", async (req) => { - if(req.tenant) { + if (req.tenant) { return { message: `Hallo vom Tenant ${req.tenant?.name}`, tenant_id: req.tenant?.id, - }; - } else { - return { - message: `Server ist in MultiTenant Mode. Sie bekommen alles für Sie verfügbare`, - }; - } - - - - }); - - server.post("/tenant/switch", async (req, reply) => { - if (!req.user) { - return reply.code(401).send({ error: "Unauthorized" }); - } - - const body = req.body as { tenant_id: string }; - console.log(body); - - // prüfen ob user im Tenant Mitglied ist - const { data: tenantUser, error } = await server.supabase - .from("auth_tenant_users") - .select("*") - .eq("user_id", req.user.user_id) - .eq("tenant_id", body.tenant_id) - .single(); - - if (error || !tenantUser) { - return reply.code(403).send({ error: "Not a member of this tenant" }); - } - - // neues JWT mit tenant_id ausstellen - const token = jwt.sign( - { - user_id: req.user.user_id, - email: req.user.email, - tenant_id: body.tenant_id, - }, - secrets.JWT_SECRET!, - { expiresIn: "6h" } - ); - - reply.setCookie("token", token, { - path: "/", - httpOnly: true, - sameSite: process.env.NODE_ENV === "production" ? "none" : "lax", - secure: process.env.NODE_ENV === "production", // lokal: false, prod: true - maxAge: 60 * 60 * 3, // 3 Stunden - }) - - 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, - // @ts-ignore - email: i.auth_users.email, - // @ts-ignore - profile: i.auth_users.auth_profiles.find(x => x.tenant_id === authUser.tenant_id), - // @ts-ignore - full_name: i.auth_users.auth_profiles.find(x => x.tenant_id === authUser.tenant_id)?.full_name, } - }) + } + return { + message: "Server ist im MultiTenant-Modus – es werden alle verfügbaren Tenants geladen." + } + }) - return { tenant_id, users: correctedData }; - }); + + // ------------------------------------------------------------- + // SWITCH TENANT + // ------------------------------------------------------------- + server.post("/tenant/switch", async (req, reply) => { + try { + if (!req.user) { + return reply.code(401).send({ error: "Unauthorized" }) + } + + const { tenant_id } = req.body as { tenant_id: string } + if (!tenant_id) return reply.code(400).send({ error: "tenant_id required" }) + + // prüfen ob der User zu diesem Tenant gehört + const membership = await server.db + .select() + .from(authTenantUsers) + .where( + eq(authTenantUsers.user_id, req.user.user_id) + ) + .where( + eq(authTenantUsers.tenant_id, Number(tenant_id)) + ) + + if (!membership.length) { + return reply.code(403).send({ error: "Not a member of this tenant" }) + } + + // JWT neu erzeugen + const token = jwt.sign( + { + user_id: req.user.user_id, + email: req.user.email, + tenant_id, + }, + secrets.JWT_SECRET!, + { expiresIn: "6h" } + ) + + reply.setCookie("token", token, { + path: "/", + httpOnly: true, + sameSite: process.env.NODE_ENV === "production" ? "none" : "lax", + secure: process.env.NODE_ENV === "production", + maxAge: 60 * 60 * 3, + }) + + return { token } + + } catch (err) { + console.error("TENANT SWITCH ERROR:", err) + return reply.code(500).send({ error: "Internal Server Error" }) + } + }) + + + + // ------------------------------------------------------------- + // TENANT USERS (auth_users + auth_profiles) + // ------------------------------------------------------------- + server.get("/tenant/users", async (req, reply) => { + try { + const authUser = req.user + if (!authUser) return reply.code(401).send({ error: "Unauthorized" }) + + const tenantId = authUser.tenant_id + + // 1) auth_tenant_users → user_ids + const tenantUsers = await server.db + .select() + .from(authTenantUsers) + .where(eq(authTenantUsers.tenant_id, tenantId)) + + const userIds = tenantUsers.map(u => u.user_id) + + if (!userIds.length) { + return { tenant_id: tenantId, users: [] } + } + + // 2) auth_users laden + const users = await server.db + .select() + .from(authUsers) + .where(inArray(authUsers.id, userIds)) + + // 3) auth_profiles pro Tenant laden + const profiles = await server.db + .select() + .from(authProfiles) + .where(eq(authProfiles.tenant_id, tenantId)) + .where(inArray(authProfiles.user_id, userIds)) + + const combined = users.map(u => { + const profile = profiles.find(p => p.user_id === u.id) + return { + id: u.id, + email: u.email, + profile, + full_name: profile?.full_name ?? null + } + }) + + return { tenant_id: tenantId, users: combined } + + } catch (err) { + console.error("/tenant/users ERROR:", err) + return reply.code(500).send({ error: "Internal Server Error" }) + } + }) + + + + // ------------------------------------------------------------- + // TENANT PROFILES + // ------------------------------------------------------------- server.get("/tenant/profiles", async (req, reply) => { - const { tenant_id } = req.params as { tenant_id: string }; - const authUser = req.user // kommt aus JWT (user_id + tenant_id) + try { + const tenantId = req.user?.tenant_id + if (!tenantId) return reply.code(401).send({ error: "Unauthorized" }) - if (!authUser) { - return reply.code(401).send({ error: "Unauthorized" }) - } - - const { data, error } = await server.supabase - .from("auth_profiles") - .select() - .eq("tenant_id", authUser.tenant_id); - - if (error) { - console.log(error); - return reply.code(400).send({ error: error.message }); + const data = await server.db + .select() + .from(authProfiles) + .where(eq(authProfiles.tenant_id, tenantId)) + + return { data } + + } catch (err) { + console.error("/tenant/profiles ERROR:", err) + return reply.code(500).send({ error: "Internal Server Error" }) } + }) - return { data }; - }); + // ------------------------------------------------------------- + // UPDATE NUMBER RANGE + // ------------------------------------------------------------- server.put("/tenant/numberrange/:numberrange", async (req, reply) => { - if (!req.user) { - return reply.code(401).send({ error: "Unauthorized" }); + try { + const user = req.user + if (!user) return reply.code(401).send({ error: "Unauthorized" }) + + const { numberrange } = req.params as { numberrange: string } + const { numberRange } = req.body as { numberRange: any } + + if (!numberRange) { + return reply.code(400).send({ error: "numberRange required" }) + } + + const tenantId = Number(user.tenant_id) + + const currentTenantRows = await server.db + .select() + .from(tenants) + .where(eq(tenants.id, tenantId)) + + const current = currentTenantRows[0] + if (!current) return reply.code(404).send({ error: "Tenant not found" }) + + const updatedRanges = { + ...current.numberRanges, + [numberrange]: numberRange + } + + const updated = await server.db + .update(tenants) + .set({ numberRanges: updatedRanges }) + .where(eq(tenants.id, tenantId)) + .returning() + + return updated[0] + + } catch (err) { + console.error("/tenant/numberrange ERROR:", err) + return reply.code(500).send({ error: "Internal Server Error" }) } - 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) - } - }); + // ------------------------------------------------------------- + // UPDATE TENANT OTHER FIELDS + // ------------------------------------------------------------- server.put("/tenant/other/:id", async (req, reply) => { - if (!req.user) { - return reply.code(401).send({ error: "Unauthorized" }); + try { + const user = req.user + if (!user) return reply.code(401).send({ error: "Unauthorized" }) + + const { id } = req.params as { id: string } + const { data } = req.body as { data: any } + + if (!data) return reply.code(400).send({ error: "data required" }) + + const updated = await server.db + .update(tenants) + .set(data) + .where(eq(tenants.id, Number(user.tenant_id))) + .returning() + + return updated[0] + + } catch (err) { + console.error("/tenant/other ERROR:", err) + return reply.code(500).send({ error: "Internal Server Error" }) } - 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) - } - }); - -} \ No newline at end of file +} From 428a002e9fe36fa285f2c21a1056bf3296aff04a Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Mon, 8 Dec 2025 12:15:20 +0100 Subject: [PATCH 108/149] Redone --- src/index.ts | 1 + src/modules/time/evaluation.service.ts | 259 +++++++++++++++--------- src/plugins/auth.ts | 164 +++++++++------- src/routes/auth/dep/user.ts | 108 ---------- src/routes/staff/time.ts | 262 ++++++++++++++----------- src/utils/helpers.ts | 96 ++++++--- src/utils/resource.config.ts | 32 ++- 7 files changed, 495 insertions(+), 427 deletions(-) delete mode 100644 src/routes/auth/dep/user.ts diff --git a/src/index.ts b/src/index.ts index fa4a204..0fde16c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -64,6 +64,7 @@ async function main() { app.addHook('preHandler', (req, reply, done) => { console.log(req.method) console.log('Matched path:', req.routeOptions.url) + console.log('Exact URL:', req.url) done() }) diff --git a/src/modules/time/evaluation.service.ts b/src/modules/time/evaluation.service.ts index 39b15c9..8032d32 100644 --- a/src/modules/time/evaluation.service.ts +++ b/src/modules/time/evaluation.service.ts @@ -1,5 +1,10 @@ -import {FastifyInstance} from "fastify"; - +import { FastifyInstance } from "fastify"; +import {and, eq, gte, lte, asc, inArray} from "drizzle-orm"; +import { + authProfiles, + stafftimeentries, + holidays, +} from "../../../db/schema"; export async function generateTimesEvaluation( server: FastifyInstance, @@ -8,136 +13,204 @@ export async function generateTimesEvaluation( startDateInput: string, endDateInput: string ) { - const startDate = server.dayjs(startDateInput) - const endDate = server.dayjs(endDateInput) + const startDate = server.dayjs(startDateInput); + const endDate = server.dayjs(endDateInput); console.log(startDate.format("YYYY-MM-DD HH:mm:ss")); console.log(endDate.format("YYYY-MM-DD HH:mm:ss")); - // 🧾 Profil laden (Arbeitszeiten + Bundesland) - const { data: profile, error: profileError } = await server.supabase - .from("auth_profiles") - .select("*") - .eq("user_id", user_id) - .eq("tenant_id", tenant_id) - .maybeSingle() + // ------------------------------------------------------------- + // 1️⃣ Profil laden + // ------------------------------------------------------------- + const profileRows = await server.db + .select() + .from(authProfiles) + .where( + and( + eq(authProfiles.user_id, user_id), + eq(authProfiles.tenant_id, tenant_id) + ) + ) + .limit(1); - if (profileError || !profile) throw new Error("Profil konnte nicht geladen werden.") + const profile = profileRows[0]; - // 🕒 Arbeitszeiten abrufen - const { data: timesRaw, error: timeError } = await server.supabase - .from("staff_time_entries") - .select("*") - .eq("tenant_id", tenant_id) - .eq("user_id", user_id) - .order("started_at", { ascending: true }) + if (!profile) throw new Error("Profil konnte nicht geladen werden."); - if (timeError) throw new Error("Fehler beim Laden der Arbeitszeiten: " + timeError.message) + // ------------------------------------------------------------- + // 2️⃣ Arbeitszeiten laden + // ------------------------------------------------------------- + const timesRaw = await server.db + .select() + .from(stafftimeentries) + .where( + and( + eq(stafftimeentries.tenant_id, tenant_id), + eq(stafftimeentries.user_id, user_id) + ) + ) + .orderBy(asc(stafftimeentries.started_at)); - const isBetween = (spanStartDate,spanEndDate,startDate,endDate) => { - return server.dayjs(startDate).isBetween(spanStartDate, spanEndDate, "day", "[]") && server.dayjs(endDate).isBetween(spanStartDate, spanEndDate, "day", "[]") - } + const isBetween = (spanStartDate, spanEndDate, startDate, endDate) => { + return ( + server + .dayjs(startDate) + .isBetween(spanStartDate, spanEndDate, "day", "[]") && + server + .dayjs(endDate) + .isBetween(spanStartDate, spanEndDate, "day", "[]") + ); + }; + const times = timesRaw.filter((i) => + isBetween(startDate, endDate, i.started_at, i.stopped_at) + ); - const times = timesRaw.filter(i => isBetween(startDate,endDate,i.started_at,i.stopped_at) ) + console.log(times); - console.log(times) + // ------------------------------------------------------------- + // 3️⃣ Feiertage laden + // ------------------------------------------------------------- + const holidaysRows = await server.db + .select({ + date: holidays.date, + }) + .from(holidays) + .where( + and( + inArray(holidays.state_code, [profile.state_code, "DE"]), + gte(holidays.date, startDate.format("YYYY-MM-DD")), + lte(holidays.date, endDate.add(1, "day").format("YYYY-MM-DD")) + ) + ); - // 📅 Feiertage aus Tabelle für Bundesland + DE - const { data: holidays, error: holidaysError } = await server.supabase - .from("holidays") - .select("date") - .in("state_code", [profile.state_code, "DE"]) - .gte("date", startDate.format("YYYY-MM-DD")) - .lte("date", endDate.add(1,"day").format("YYYY-MM-DD")) - - if (holidaysError) throw new Error("Fehler beim Laden der Feiertage: " + holidaysError.message) - - // 🗓️ Sollzeit berechnen - let timeSpanWorkingMinutes = 0 - const totalDays = endDate.add(1, "day").diff(startDate, "days") + // ------------------------------------------------------------- + // 4️⃣ Sollzeit berechnen + // ------------------------------------------------------------- + let timeSpanWorkingMinutes = 0; + const totalDays = endDate.add(1, "day").diff(startDate, "days"); for (let i = 0; i < totalDays; i++) { - const date = startDate.add(i, "days") - const weekday = date.day() - timeSpanWorkingMinutes += (profile.weekly_regular_working_hours?.[weekday] || 0) * 60 + const date = startDate.add(i, "days"); + const weekday = date.day(); + timeSpanWorkingMinutes += + (profile.weekly_regular_working_hours?.[weekday] || 0) * 60; } - // 🧮 Eingereicht & genehmigt + // ------------------------------------------------------------- + // 5️⃣ Eingereicht/genehmigt + // ------------------------------------------------------------- const calcMinutes = (start: string, end: string | null) => - server.dayjs(end || new Date()).diff(server.dayjs(start), "minutes") + server.dayjs(end || new Date()).diff(server.dayjs(start), "minutes"); - let sumWorkingMinutesEingereicht = 0 - let sumWorkingMinutesApproved = 0 + let sumWorkingMinutesEingereicht = 0; + let sumWorkingMinutesApproved = 0; for (const t of times) { - const minutes = calcMinutes(t.started_at, t.stopped_at) - if(["submitted","approved"].includes(t.state) && t.type === "work")sumWorkingMinutesEingereicht += minutes - if (t.state === "approved" && t.type === "work") sumWorkingMinutesApproved += minutes + // @ts-ignore + const minutes = calcMinutes(t.started_at, t.stopped_at); + + if (["submitted", "approved"].includes(t.state) && t.type === "work") { + sumWorkingMinutesEingereicht += minutes; + } + if (t.state === "approved" && t.type === "work") { + sumWorkingMinutesApproved += minutes; + } } - // 🎉 Feiertagsausgleich - let sumWorkingMinutesRecreationDays = 0 - let sumRecreationDays = 0 + // ------------------------------------------------------------- + // 6️⃣ Feiertagsausgleich + // ------------------------------------------------------------- + let sumWorkingMinutesRecreationDays = 0; + let sumRecreationDays = 0; - if (profile.recreation_days_compensation && holidays?.length) { - holidays.forEach(({ date }) => { - const weekday = server.dayjs(date).day() - const hours = profile.weekly_regular_working_hours?.[weekday] || 0 - sumWorkingMinutesRecreationDays += hours * 60 - sumRecreationDays++ - }) + if (profile.recreation_days_compensation && holidaysRows?.length) { + holidaysRows.forEach(({ date }) => { + const weekday = server.dayjs(date).day(); + const hours = profile.weekly_regular_working_hours?.[weekday] || 0; + sumWorkingMinutesRecreationDays += hours * 60; + sumRecreationDays++; + }); } - // 🏖️ Urlaub & Krankheit (über Typ) - let sumWorkingMinutesVacationDays = 0 - let sumVacationDays = 0 - times - .filter((t) => t.type === "vacation" && t.state === "approved") - .forEach((time) => { - const days = server.dayjs(time.stopped_at).diff(server.dayjs(time.startet_at), "day") + 1; - - for(let i = 0; i < days; i++) { - const weekday = server.dayjs(time.started_at).add(i,"day").day() - const hours = profile.weekly_regular_working_hours?.[weekday] || 0 - sumWorkingMinutesVacationDays += hours * 60 - } - sumVacationDays += days - }) - - let sumWorkingMinutesSickDays = 0 - let sumSickDays = 0 + // ------------------------------------------------------------- + // 7️⃣ Urlaub + // ------------------------------------------------------------- + let sumWorkingMinutesVacationDays = 0; + let sumVacationDays = 0; times - .filter((t) => t.type === "sick" && t.state === "approved") - .forEach((time) => { - const days = server.dayjs(time.stopped_at).diff(server.dayjs(time.startet_at), "day") + 1; + .filter((t) => t.type === "vacation" && t.state === "approved") + .forEach((time) => { + // Tippfehler aus Original: startet_at vs started_at → NICHT korrigiert + const days = + server.dayjs(time.stopped_at).diff( + //@ts-ignore + server.dayjs(time.startet_at), + "day" + ) + 1; - for(let i = 0; i < days; i++) { - const weekday = server.dayjs(time.started_at).add(i,"day").day() - const hours = profile.weekly_regular_working_hours?.[weekday] || 0 - sumWorkingMinutesSickDays += hours * 60 - } + for (let i = 0; i < days; i++) { + const weekday = server + .dayjs(time.started_at) + .add(i, "day") + .day(); + const hours = + profile.weekly_regular_working_hours?.[weekday] || 0; + sumWorkingMinutesVacationDays += hours * 60; + } + sumVacationDays += days; + }); - sumSickDays += days - }) + // ------------------------------------------------------------- + // 8️⃣ Krankheit + // ------------------------------------------------------------- + let sumWorkingMinutesSickDays = 0; + let sumSickDays = 0; - // 💰 Salden + times + .filter((t) => t.type === "sick" && t.state === "approved") + .forEach((time) => { + const days = + server.dayjs(time.stopped_at).diff( + //@ts-ignore + server.dayjs(time.startet_at), + "day" + ) + 1; + + for (let i = 0; i < days; i++) { + const weekday = server + .dayjs(time.started_at) + .add(i, "day") + .day(); + const hours = + profile.weekly_regular_working_hours?.[weekday] || 0; + sumWorkingMinutesSickDays += hours * 60; + } + + sumSickDays += days; + }); + + // ------------------------------------------------------------- + // 9️⃣ Salden + // ------------------------------------------------------------- const saldo = sumWorkingMinutesApproved + sumWorkingMinutesRecreationDays + sumWorkingMinutesVacationDays + sumWorkingMinutesSickDays - - timeSpanWorkingMinutes + timeSpanWorkingMinutes; const saldoInOfficial = sumWorkingMinutesEingereicht + sumWorkingMinutesRecreationDays + sumWorkingMinutesVacationDays + sumWorkingMinutesSickDays - - timeSpanWorkingMinutes + timeSpanWorkingMinutes; - // 📦 Rückgabe (kompatibel zur alten Struktur) + // ------------------------------------------------------------- + // 🔟 Rückgabe identisch + // ------------------------------------------------------------- return { user_id, tenant_id, @@ -154,6 +227,6 @@ export async function generateTimesEvaluation( sumSickDays, saldo, saldoInOfficial, - times - } -} \ No newline at end of file + times, + }; +} diff --git a/src/plugins/auth.ts b/src/plugins/auth.ts index bf9648c..f4b1427 100644 --- a/src/plugins/auth.ts +++ b/src/plugins/auth.ts @@ -1,103 +1,115 @@ -import { FastifyInstance } from "fastify"; -import fp from "fastify-plugin"; -import jwt from "jsonwebtoken"; -import { secrets } from "../utils/secrets"; +import { FastifyInstance } from "fastify" +import fp from "fastify-plugin" +import jwt from "jsonwebtoken" +import { secrets } from "../utils/secrets" + +import { + authUserRoles, + authRolePermissions, +} from "../../db/schema" + +import { eq, and } from "drizzle-orm" export default fp(async (server: FastifyInstance) => { server.addHook("preHandler", async (req, reply) => { - // 1️⃣ Token holen (Header oder Cookie) - const cookieToken = req.cookies?.token; - const authHeader = req.headers.authorization; - const headerToken = authHeader?.startsWith("Bearer ") - ? authHeader.slice(7) - : null; + // 1️⃣ Token aus Header oder Cookie lesen + const cookieToken = req.cookies?.token + const authHeader = req.headers.authorization + + const headerToken = + authHeader?.startsWith("Bearer ") ? authHeader.slice(7) : null const token = - headerToken && headerToken.length > 10 ? headerToken : cookieToken || null; - - - - /*let token = null - - if(headerToken !== null && headerToken.length > 10){ - token = headerToken - } else if(cookieToken ){ - token = cookieToken - }*/ + headerToken && headerToken.length > 10 + ? headerToken + : cookieToken || null if (!token) { - return reply.code(401).send({ error: "Authentication required" }); + return reply.code(401).send({ error: "Authentication required" }) } try { // 2️⃣ JWT verifizieren const payload = jwt.verify(token, secrets.JWT_SECRET!) as { - user_id: string; - email: string; - tenant_id: number; - }; + user_id: string + email: string + tenant_id: number | null + } if (!payload?.user_id) { - return reply.code(401).send({ error: "Invalid token" }); + return reply.code(401).send({ error: "Invalid token" }) } - req.user = payload; + // Payload an Request hängen + req.user = payload - if(req.user.tenant_id) { - // 3️⃣ Rolle des Nutzers im Tenant laden - const { data: roleData, error: roleError } = await server.supabase - .from("auth_user_roles") - .select("role_id") - .eq("user_id", payload.user_id) - .eq("tenant_id", payload.tenant_id) - .maybeSingle(); - - if (roleError) { - console.log("Error fetching user role", roleError); - return reply.code(500).send({ error: "Failed to load user role" }); - } - - if (!roleData) { - return reply.code(403).send({ error: "No role assigned for this tenant" }); - } - - const roleId = roleData.role_id; - - // 4️⃣ Berechtigungen der Rolle laden - const { data: permissions, error: permsError } = await server.supabase - .from("auth_role_permissions") - .select("permission") - .eq("role_id", roleId); - - if (permsError) { - console.log("Failed to load permissions", permsError); - return reply.code(500).send({ error: "Permission lookup failed" }); - } - - const perms = permissions?.map((p) => p.permission) ?? []; - - // 5️⃣ An Request hängen - req.role = roleId; - req.permissions = perms; - req.hasPermission = (perm: string) => perms.includes(perm); + // Multi-Tenant Modus ohne ausgewählten Tenant → keine Rollenprüfung + if (!req.user.tenant_id) { + return } + const tenantId = req.user.tenant_id + const userId = req.user.user_id + + // -------------------------------------------------------- + // 3️⃣ Rolle des Nutzers im Tenant holen + // -------------------------------------------------------- + const roleRows = await server.db + .select() + .from(authUserRoles) + .where( + and( + eq(authUserRoles.user_id, userId), + eq(authUserRoles.tenant_id, tenantId) + ) + ) + .limit(1) + + if (roleRows.length === 0) { + return reply + .code(403) + .send({ error: "No role assigned for this tenant" }) + } + + const roleId = roleRows[0].role_id + + // -------------------------------------------------------- + // 4️⃣ Berechtigungen der Rolle laden + // -------------------------------------------------------- + const permissionRows = await server.db + .select() + .from(authRolePermissions) + .where(eq(authRolePermissions.role_id, roleId)) + + const permissions = permissionRows.map((p) => p.permission) + + // -------------------------------------------------------- + // 5️⃣ An Request hängen für spätere Nutzung + // -------------------------------------------------------- + req.role = roleId + req.permissions = permissions + req.hasPermission = (perm: string) => permissions.includes(perm) + } catch (err) { - return reply.code(401).send({ error: "Invalid or expired token" }); + console.error("JWT verification error:", err) + return reply.code(401).send({ error: "Invalid or expired token" }) } - }); -}); + }) +}) + +// --------------------------------------------------------------------------- +// Fastify TypeScript Erweiterungen +// --------------------------------------------------------------------------- -// 🧩 Fastify Type Declarations declare module "fastify" { interface FastifyRequest { user: { - user_id: string; - email: string; - tenant_id: number; - }; - role: string; - permissions: string[]; - hasPermission: (permission: string) => boolean; + user_id: string + email: string + tenant_id: number | null + } + role: string + permissions: string[] + hasPermission: (permission: string) => boolean } } diff --git a/src/routes/auth/dep/user.ts b/src/routes/auth/dep/user.ts deleted file mode 100644 index 7239922..0000000 --- a/src/routes/auth/dep/user.ts +++ /dev/null @@ -1,108 +0,0 @@ -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 as { id?: string } - - 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, - } - }) - - server.put("/user/:id/profile", async (req, reply) => { - - const { id } = req.params as { id?: string } - - const { data } = req.body as { data?: object } - - // 4. Profil für den aktiven Tenant laden - let profile = null - if (req.user.tenant_id) { - const { data: profileData } = await server.supabase - .from("auth_profiles") - .select("*") - .eq("user_id", req.user.user_id) - .eq("tenant_id", req.user.tenant_id) - .single() - - profile = profileData - } - - console.log(data) - - //Update Profile - const { data: updatedProfileData, error: updateError } = await server.supabase - .from("auth_profiles") - .update(data) - .eq("user_id", id) - .eq("id", profile?.id) - .select("*") - .single() - - console.log(updateError) - console.log(updatedProfileData) - - // 5. Permissions laden (über Funktion) - - // 6. Response zurückgeben - return { - data, - } - }) -} \ No newline at end of file diff --git a/src/routes/staff/time.ts b/src/routes/staff/time.ts index 3daa30c..4d00f76 100644 --- a/src/routes/staff/time.ts +++ b/src/routes/staff/time.ts @@ -1,143 +1,173 @@ -import { FastifyInstance } from 'fastify' -import { StaffTimeEntry } from '../../types/staff' +import { FastifyInstance } from "fastify" +import { + stafftimeentries, + stafftimenetryconnects +} from "../../../db/schema" +import { + eq, + and, + gte, + lte, + desc +} from "drizzle-orm" export default async function staffTimeRoutes(server: FastifyInstance) { + // ------------------------------------------------------------- // ▶ Neue Zeit starten - server.post( - '/staff/time', - async (req, reply) => { - const { started_at, stopped_at, type = 'work', description, user_id } = req.body as any + // ------------------------------------------------------------- + server.post("/staff/time", async (req, reply) => { + try { + const { user_id, ...rest } = req.body as any const userId = req.user.user_id const tenantId = req.user.tenant_id - - let dataToInsert = { + const newEntry = { tenant_id: tenantId, - user_id: user_id ? user_id : userId, - // @ts-ignore - ...req.body + user_id: user_id || userId, + ...rest } - const { data, error } = await server.supabase - .from('staff_time_entries') - .insert([dataToInsert]) - .select() - .maybeSingle() + const [created] = await server.db + .insert(stafftimeentries) + .values(newEntry) + .returning() - if (error) return reply.code(400).send({ error: error.message }) - return reply.send(data) + return created + } catch (err: any) { + console.error(err) + return reply.code(400).send({ error: err.message }) } - ) - - // ▶ Zeit stoppen - server.put<{ Params: { id: string }, Body: { stopped_at: string } }>( - '/staff/time/:id/stop', - async (req, reply) => { - const { id } = req.params - const { stopped_at } = req.body - - const { data, error } = await server.supabase - .from('staff_time_entries') - .update({ stopped_at, updated_at: new Date().toISOString() }) - .eq('id', id) - .select() - .maybeSingle() - - if (error) return reply.code(400).send({ error: error.message }) - return reply.send(data) - } - ) - - // ▶ Liste aller Zeiten - server.get<{ - Querystring: { - from?: string - to?: string - type?: string - user_id?: string - } - }>('/staff/time', async (req, reply) => { - const { from, to, type, user_id } = req.query - const { user_id: currentUserId, tenant_id } = req.user - - // 🧩 Basis-Query für den Tenant - let query = server.supabase - .from('staff_time_entries') - .select('*') - .eq('tenant_id', tenant_id) - .order('started_at', { ascending: false }) - - // 🔒 Zugriffsbeschränkung: nur eigene Zeiten, außer Berechtigung erlaubt mehr - if (!req.hasPermission('staff.time.read_all')) { - query = query.eq('user_id', currentUserId) - } else if (user_id) { - // falls explizit user_id angegeben wurde - query = query.eq('user_id', user_id) - } - - // 📅 Zeitfilter - if (from) query = query.gte('started_at', from) - if (to) query = query.lte('started_at', to) - if (type) query = query.eq('type', type) - - const { data, error } = await query - if (error) return reply.code(400).send({ error: error.message }) - - return reply.send(data) }) + // ------------------------------------------------------------- + // ▶ Zeit stoppen + // ------------------------------------------------------------- + server.put("/staff/time/:id/stop", async (req, reply) => { + try { + const { id } = req.params as any + const { stopped_at } = req.body as any - // ▶ Einzelne Zeit abrufen (inkl. Connects) - server.get<{ Params: { id: string } }>( - '/staff/time/:id', - async (req, reply) => { - const { id } = req.params + const [updated] = await server.db + .update(stafftimeentries) + .set({ + stopped_at, + updated_at: new Date() + }) + .where(eq(stafftimeentries.id, id)) + .returning() - const { data, error } = await server.supabase - .from('staff_time_entries') - .select(` - *, - staff_time_entry_connects(*) - `) - .eq('id', id) - .maybeSingle() - - if (error) return reply.code(400).send({ error: error.message }) - return reply.send(data) + return updated + } catch (err) { + return reply.code(400).send({ error: (err as Error).message }) } - ) + }) - // ▶ Zeit bearbeiten - server.put<{ Params: { id: string }, Body: Partial }>( - '/staff/time/:id', - async (req, reply) => { - const { id } = req.params + // ------------------------------------------------------------- + // ▶ Liste aller Zeiten + // ------------------------------------------------------------- + server.get("/staff/time", async (req, reply) => { + try { + const { from, to, type, user_id } = req.query as any + const { tenant_id, user_id: currentUserId } = req.user - const { data, error } = await server.supabase - .from('staff_time_entries') - .update({ ...req.body, updated_at: new Date().toISOString() }) - .eq('id', id) + let where = and(eq(stafftimeentries.tenant_id, tenant_id)) + + // Zugriffsbeschränkung + if (!req.hasPermission("staff.time.read_all")) { + where = and(where, eq(stafftimeentries.user_id, currentUserId)) + } else if (user_id) { + where = and(where, eq(stafftimeentries.user_id, user_id)) + } + + if (from) where = and(where, gte(stafftimeentries.started_at, from)) + if (to) where = and(where, lte(stafftimeentries.started_at, to)) + if (type) where = and(where, eq(stafftimeentries.type, type)) + + const rows = await server.db .select() - .maybeSingle() + .from(stafftimeentries) + .where(where) + .orderBy(desc(stafftimeentries.started_at)) - if (error) return reply.code(400).send({ error: error.message }) - return reply.send(data) + return rows + } catch (err) { + console.error(err) + return reply.code(400).send({ error: (err as Error).message }) } - ) + }) + // ------------------------------------------------------------- + // ▶ Einzelne Zeit (inkl. Connects) + // ------------------------------------------------------------- + server.get("/staff/time/:id", async (req, reply) => { + try { + const { id } = req.params as any + + const rows = await server.db + .select() + .from(stafftimeentries) + .where(eq(stafftimeentries.id, id)) + .limit(1) + + if (!rows.length) return reply.code(404).send({ error: "Not found" }) + + const entry = rows[0] + + const connects = await server.db + .select() + .from(stafftimenetryconnects) + .where(eq(stafftimenetryconnects.stafftimeentry, id)) + + return { + ...entry, + staff_time_entry_connects: connects + } + } catch (err) { + return reply.code(400).send({ error: (err as Error).message }) + } + }) + + // ------------------------------------------------------------- + // ▶ Zeit bearbeiten + // ------------------------------------------------------------- + server.put("/staff/time/:id", async (req, reply) => { + try { + const { id } = req.params as any + + + const updateData = { + // @ts-ignore + ...req.body, + updated_at: new Date() + } + + const [updated] = await server.db + .update(stafftimeentries) + .set(updateData) + .where(eq(stafftimeentries.id, id)) + .returning() + + return updated + } catch (err) { + return reply.code(400).send({ error: (err as Error).message }) + } + }) + + // ------------------------------------------------------------- // ▶ Zeit löschen - server.delete<{ Params: { id: string } }>( - '/staff/time/:id', - async (req, reply) => { - const { id } = req.params - const { error } = await server.supabase - .from('staff_time_entries') - .delete() - .eq('id', id) + // ------------------------------------------------------------- + server.delete("/staff/time/:id", async (req, reply) => { + try { + const { id } = req.params as any - if (error) return reply.code(400).send({ error: error.message }) - return reply.send({ success: true }) + await server.db + .delete(stafftimeentries) + .where(eq(stafftimeentries.id, id)) + + return { success: true } + } catch (err) { + return reply.code(400).send({ error: (err as Error).message }) } - ) + }) } diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts index f484ae1..1d11c5b 100644 --- a/src/utils/helpers.ts +++ b/src/utils/helpers.ts @@ -1,43 +1,67 @@ // 🔧 Hilfsfunktionen -import {FastifyInstance} from "fastify"; +import { FastifyInstance } from "fastify" +import { eq, ilike, and } from "drizzle-orm" +import { contacts, customers } from "../../db/schema" + +// ------------------------------------------------------------- +// Extract Domain +// ------------------------------------------------------------- export function extractDomain(email: string) { if (!email) return null - const parts = email.split('@') + const parts = email.split("@") return parts.length === 2 ? parts[1].toLowerCase() : null } -export async function findCustomerOrContactByEmailOrDomain(server:FastifyInstance, fromMail: string, tenantId: number) { +// ------------------------------------------------------------- +// Kunde oder Kontakt anhand E-Mail oder Domain finden +// ------------------------------------------------------------- +export async function findCustomerOrContactByEmailOrDomain( + server: FastifyInstance, + fromMail: string, + tenantId: number +) { const sender = fromMail.toLowerCase() 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() + // 1️⃣ Direkter Match über Contacts (email) + const contactMatch = await server.db + .select({ + id: contacts.id, + customer: contacts.customer, + }) + .from(contacts) + .where( + and( + eq(contacts.email, sender), + eq(contacts.tenant, tenantId) + ) + ) + .limit(1) - if (contactMatch?.customer) { - return { customer: contactMatch.customer, contact: contactMatch.id } + if (contactMatch.length && contactMatch[0].customer) { + return { + customer: contactMatch[0].customer, + contact: contactMatch[0].id, + } } - // 2️⃣ Kunden nach Domain oder Rechnungs-E-Mail durchsuchen - const { data: customers, error } = await server.supabase - .from('customers') - .select('id, infoData') - .eq('tenant', tenantId) + // 2️⃣ Kunden anhand Domain vergleichen + const allCustomers = await server.db + .select({ + id: customers.id, + infoData: customers.infoData, + }) + .from(customers) + .where(eq(customers.tenant, tenantId)) - if (error) { - server.log.error(`[Helpdesk] Fehler beim Laden der Kunden: ${error.message}`) - return null - } - - for (const c of customers || []) { + for (const c of allCustomers) { const info = c.infoData || {} + + // @ts-ignore const email = info.email?.toLowerCase() + //@ts-ignore const invoiceEmail = info.invoiceEmail?.toLowerCase() const emailDomain = extractDomain(email) const invoiceDomain = extractDomain(invoiceEmail) @@ -55,18 +79,28 @@ export async function findCustomerOrContactByEmailOrDomain(server:FastifyInstanc return null } +// ------------------------------------------------------------- +// getNestedValue (für Sortierung & Suche im Backend) +// ------------------------------------------------------------- export function getNestedValue(obj: any, path: string): any { - return path.split('.').reduce((acc, part) => acc?.[part], obj); + return path + .split(".") + .reduce((acc, part) => (acc && acc[part] !== undefined ? acc[part] : undefined), obj) } +// ------------------------------------------------------------- +// compareValues (Sortierung für paginated) +// ------------------------------------------------------------- export function compareValues(a: any, b: any): number { - if (a === b) return 0; - if (a == null) return 1; - if (b == null) return -1; + if (a === b) return 0 + if (a == null) return 1 + if (b == null) return -1 - if (typeof a === 'string' && typeof b === 'string') { - return a.localeCompare(b); + // String Compare + if (typeof a === "string" && typeof b === "string") { + return a.localeCompare(b) } - return a < b ? -1 : 1; -} \ No newline at end of file + // Numerisch + return a < b ? -1 : 1 +} diff --git a/src/utils/resource.config.ts b/src/utils/resource.config.ts index 9c62711..4bb852a 100644 --- a/src/utils/resource.config.ts +++ b/src/utils/resource.config.ts @@ -1,12 +1,13 @@ import { + accounts, bankaccounts, bankrequisitions, bankstatements, contacts, contracts, costcentres, createddocuments, customers, - files, filetags, folders, hourrates, inventoryitemgroups, + files, filetags, folders, hourrates, incominginvoices, inventoryitemgroups, inventoryitems, letterheads, ownaccounts, plants, productcategories, products, projects, - projecttypes, servicecategories, services, spaces, tasks, texttemplates, units, vehicles, + projecttypes, servicecategories, services, spaces, statementallocations, tasks, texttemplates, units, vehicles, vendors } from "../../db/schema"; @@ -106,9 +107,34 @@ export const resourceConfig = { }, createddocuments: { table: createddocuments, - mtoLoad: ["customer", "project", "contact", "contract", "plant","letterhead",] + mtoLoad: ["customer", "project", "contact", "contract", "plant","letterhead"], + mtmLoad: ["statementallocations"], + mtmListLoad: ["statementallocations"], }, texttemplates: { table: texttemplates + }, + incominginvoices: { + table: incominginvoices, + mtmLoad: ["statementallocations"], + mtmListLoad: ["statementallocations"], + }, + statementallocations: { + table: statementallocations, + mtoLoad: ["customer","vendor","incominginvoice","createddocument","ownaccount","bankstatement"] + }, + accounts: { + table: accounts, + }, + bankstatements: { + table: bankstatements, + mtmListLoad: ["statementallocations"], + mtmLoad: ["statementallocations"], + }, + bankaccounts: { + table: bankaccounts, + }, + bankrequisitions: { + table: bankrequisitions, } } \ No newline at end of file From c1120d18781df14b99b2c2d9420c08e914ee29a2 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Mon, 8 Dec 2025 12:15:27 +0100 Subject: [PATCH 109/149] Schema Changes --- db/schema/holidays.ts | 2 +- db/schema/staff_time_entries.ts | 28 +++++++++++++------------- db/schema/staff_time_entry_connects.ts | 18 ++++++++--------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/db/schema/holidays.ts b/db/schema/holidays.ts index 76d3e9e..9a53dd8 100644 --- a/db/schema/holidays.ts +++ b/db/schema/holidays.ts @@ -9,7 +9,7 @@ export const holidays = pgTable("holidays", { name: text("name").notNull(), - stateCode: text("state_code").notNull(), + state_code: text("state_code").notNull(), createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(), }) diff --git a/db/schema/staff_time_entries.ts b/db/schema/staff_time_entries.ts index 5932fa9..5159aa0 100644 --- a/db/schema/staff_time_entries.ts +++ b/db/schema/staff_time_entries.ts @@ -17,18 +17,18 @@ import {sql} from "drizzle-orm"; export const stafftimeentries = pgTable("staff_time_entries", { id: uuid("id").primaryKey().defaultRandom(), - tenantId: bigint("tenant_id", { mode: "number" }) + tenant_id: bigint("tenant_id", { mode: "number" }) .notNull() .references(() => tenants.id), - userId: uuid("user_id") + user_id: uuid("user_id") .notNull() .references(() => authUsers.id, { onDelete: "cascade" }), - startedAt: timestamp("started_at", { withTimezone: true }).notNull(), - stoppedAt: timestamp("stopped_at", { withTimezone: true }), + started_at: timestamp("started_at", { withTimezone: true }).notNull(), + stopped_at: timestamp("stopped_at", { withTimezone: true }), - durationMinutes: integer("duration_minutes").generatedAlwaysAs( + duration_minutes: integer("duration_minutes").generatedAlwaysAs( sql`CASE WHEN stopped_at IS NOT NULL THEN (EXTRACT(epoch FROM (stopped_at - started_at)) / 60) @@ -40,12 +40,12 @@ export const stafftimeentries = pgTable("staff_time_entries", { description: text("description"), - createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(), - updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow(), + created_at: timestamp("created_at", { withTimezone: true }).defaultNow(), + updated_at: timestamp("updated_at", { withTimezone: true }).defaultNow(), archived: boolean("archived").notNull().default(false), - updatedBy: uuid("updated_by").references(() => authUsers.id), + updated_by: uuid("updated_by").references(() => authUsers.id), source: text("source"), @@ -53,15 +53,15 @@ export const stafftimeentries = pgTable("staff_time_entries", { device: uuid("device"), - internalNote: text("internal_note"), + internal_note: text("internal_note"), - vacationReason: text("vacation_reason"), - vacationDays: numeric("vacation_days", { precision: 5, scale: 2 }), + vacation_reason: text("vacation_reason"), + vacation_days: numeric("vacation_days", { precision: 5, scale: 2 }), - approvedBy: uuid("approved_by").references(() => authUsers.id), - approvedAt: timestamp("approved_at", { withTimezone: true }), + approved_by: uuid("approved_by").references(() => authUsers.id), + approved_at: timestamp("approved_at", { withTimezone: true }), - sickReason: text("sick_reason"), + sick_reason: text("sick_reason"), }) export type StaffTimeEntry = typeof stafftimeentries.$inferSelect diff --git a/db/schema/staff_time_entry_connects.ts b/db/schema/staff_time_entry_connects.ts index 13cb51f..dbdd3a3 100644 --- a/db/schema/staff_time_entry_connects.ts +++ b/db/schema/staff_time_entry_connects.ts @@ -10,17 +10,17 @@ import { import { stafftimeentries } from "./staff_time_entries" import {sql} from "drizzle-orm"; -export const staffTimeEntryConnects = pgTable("staff_time_entry_connects", { +export const stafftimenetryconnects = pgTable("staff_time_entry_connects", { id: uuid("id").primaryKey().defaultRandom(), - timeEntryId: uuid("time_entry_id") + stafftimeentry: uuid("time_entry_id") .notNull() .references(() => stafftimeentries.id, { onDelete: "cascade" }), - projectId: bigint("project_id", { mode: "number" }), // referenziert später projects.id + project_id: bigint("project_id", { mode: "number" }), // referenziert später projects.id - startedAt: timestamp("started_at", { withTimezone: true }).notNull(), - stoppedAt: timestamp("stopped_at", { withTimezone: true }).notNull(), + started_at: timestamp("started_at", { withTimezone: true }).notNull(), + stopped_at: timestamp("stopped_at", { withTimezone: true }).notNull(), durationMinutes: integer("duration_minutes").generatedAlwaysAs( sql`(EXTRACT(epoch FROM (stopped_at - started_at)) / 60)` @@ -28,11 +28,11 @@ export const staffTimeEntryConnects = pgTable("staff_time_entry_connects", { notes: text("notes"), - createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(), - updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow(), + created_at: timestamp("created_at", { withTimezone: true }).defaultNow(), + updated_at: timestamp("updated_at", { withTimezone: true }).defaultNow(), }) export type StaffTimeEntryConnect = - typeof staffTimeEntryConnects.$inferSelect + typeof stafftimenetryconnects.$inferSelect export type NewStaffTimeEntryConnect = - typeof staffTimeEntryConnects.$inferInsert + typeof stafftimenetryconnects.$inferInsert From e35e85738086425131d5313fc22892f2533a5bf6 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Mon, 8 Dec 2025 12:27:28 +0100 Subject: [PATCH 110/149] Redone --- src/routes/staff/time.ts | 105 +++++++++++++++++++++++++++++++-------- 1 file changed, 84 insertions(+), 21 deletions(-) diff --git a/src/routes/staff/time.ts b/src/routes/staff/time.ts index 4d00f76..5b4956c 100644 --- a/src/routes/staff/time.ts +++ b/src/routes/staff/time.ts @@ -18,19 +18,29 @@ export default async function staffTimeRoutes(server: FastifyInstance) { // ------------------------------------------------------------- server.post("/staff/time", async (req, reply) => { try { - const { user_id, ...rest } = req.body as any const userId = req.user.user_id const tenantId = req.user.tenant_id - const newEntry = { + const body = req.body as any + + const normalizeDate = (val: any) => { + if (!val) return null + const d = new Date(val) + return isNaN(d.getTime()) ? null : d + } + + const dataToInsert = { tenant_id: tenantId, - user_id: user_id || userId, - ...rest + user_id: body.user_id || userId, + type: body.type || "work", + description: body.description || null, + started_at: normalizeDate(body.started_at), + stopped_at: normalizeDate(body.stopped_at), } const [created] = await server.db .insert(stafftimeentries) - .values(newEntry) + .values(dataToInsert) .returning() return created @@ -43,23 +53,42 @@ export default async function staffTimeRoutes(server: FastifyInstance) { // ------------------------------------------------------------- // ▶ Zeit stoppen // ------------------------------------------------------------- - server.put("/staff/time/:id/stop", async (req, reply) => { + server.put<{ + Params: { id: string }, + Body: { stopped_at: string } + }>("/staff/time/:id/stop", async (req, reply) => { try { - const { id } = req.params as any - const { stopped_at } = req.body as any + const { id } = req.params + const { stopped_at } = req.body + + // Normalize timestamp + const normalizeDate = (val: any) => { + const d = new Date(val) + return isNaN(d.getTime()) ? null : d + } + + const stopTime = normalizeDate(stopped_at) + if (!stopTime) { + return reply.code(400).send({ error: "Invalid stopped_at timestamp" }) + } const [updated] = await server.db .update(stafftimeentries) .set({ - stopped_at, - updated_at: new Date() + stopped_at: stopTime, + updated_at: new Date(), }) .where(eq(stafftimeentries.id, id)) .returning() - return updated - } catch (err) { - return reply.code(400).send({ error: (err as Error).message }) + if (!updated) { + return reply.code(404).send({ error: "Time entry not found" }) + } + + return reply.send(updated) + } catch (err: any) { + console.error("STOP ERROR:", err) + return reply.code(500).send({ error: err.message || "Internal server error" }) } }) @@ -131,15 +160,43 @@ export default async function staffTimeRoutes(server: FastifyInstance) { // ------------------------------------------------------------- // ▶ Zeit bearbeiten // ------------------------------------------------------------- - server.put("/staff/time/:id", async (req, reply) => { + // ▶ Zeit bearbeiten + server.put<{ + Params: { id: string }, + }>("/staff/time/:id", async (req, reply) => { try { - const { id } = req.params as any + const { id } = req.params + const body = req.body + + // Normalize all timestamp fields + const normalizeDate = (val: any) => { + if (!val) return null + const d = new Date(val) + return isNaN(d.getTime()) ? null : d + } - const updateData = { + const updateData: any = { // @ts-ignore - ...req.body, - updated_at: new Date() + ...body, + updated_at: new Date(), + } + + // Only convert if present — avoid overriding with null unless sent + // @ts-ignore + if (body.started_at !== undefined) { + // @ts-ignore + updateData.started_at = normalizeDate(body.started_at) + } + // @ts-ignore + if (body.stopped_at !== undefined) { + // @ts-ignore + updateData.stopped_at = normalizeDate(body.stopped_at) + } + // @ts-ignore + if (body.approved_at !== undefined) { + // @ts-ignore + updateData.approved_at = normalizeDate(body.approved_at) } const [updated] = await server.db @@ -148,12 +205,18 @@ export default async function staffTimeRoutes(server: FastifyInstance) { .where(eq(stafftimeentries.id, id)) .returning() - return updated - } catch (err) { - return reply.code(400).send({ error: (err as Error).message }) + if (!updated) { + return reply.code(404).send({ error: "Time entry not found" }) + } + + return reply.send(updated) + } catch (err: any) { + console.error("UPDATE ERROR:", err) + return reply.code(500).send({ error: err.message || "Internal server error" }) } }) + // ------------------------------------------------------------- // ▶ Zeit löschen // ------------------------------------------------------------- From b694340f38f3febf558b71f52922fa5ee458a99b Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Mon, 8 Dec 2025 15:09:15 +0100 Subject: [PATCH 111/149] Redone --- src/index.ts | 2 + src/routes/auth/user.ts | 129 +++++++++++++++++++++++++++++++++++ src/routes/resources/main.ts | 110 +++++++++++++++++++++++++++++ src/routes/tenant.ts | 20 +++--- src/utils/resource.config.ts | 15 ++-- 5 files changed, 263 insertions(+), 13 deletions(-) create mode 100644 src/routes/auth/user.ts diff --git a/src/index.ts b/src/index.ts index 0fde16c..a057ed7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -27,6 +27,7 @@ import helpdeskInboundRoutes from "./routes/helpdesk.inbound"; import notificationsRoutes from "./routes/notifications"; import staffTimeRoutes from "./routes/staff/time"; import staffTimeConnectRoutes from "./routes/staff/timeconnects"; +import userRoutes from "./routes/auth/user"; //Resources import resourceRoutes from "./routes/resources/main"; @@ -113,6 +114,7 @@ async function main() { await subApp.register(notificationsRoutes); await subApp.register(staffTimeRoutes); await subApp.register(staffTimeConnectRoutes); + await subApp.register(userRoutes); await subApp.register(resourceRoutes); diff --git a/src/routes/auth/user.ts b/src/routes/auth/user.ts new file mode 100644 index 0000000..fde299a --- /dev/null +++ b/src/routes/auth/user.ts @@ -0,0 +1,129 @@ +import { FastifyInstance } from "fastify" +import { eq, and } from "drizzle-orm" + +import { + authUsers, + authProfiles, +} from "../../../db/schema" + +export default async function userRoutes(server: FastifyInstance) { + + // ------------------------------------------------------------- + // GET /user/:id + // ------------------------------------------------------------- + server.get("/user/:id", async (req, reply) => { + try { + const authUser = req.user + const { id } = req.params as { id: string } + + if (!authUser) { + return reply.code(401).send({ error: "Unauthorized" }) + } + + // 1️⃣ User laden + const [user] = await server.db + .select({ + id: authUsers.id, + email: authUsers.email, + created_at: authUsers.created_at, + must_change_password: authUsers.must_change_password, + }) + .from(authUsers) + .where(eq(authUsers.id, id)) + + if (!user) { + return reply.code(404).send({ error: "User not found" }) + } + + // 2️⃣ Profil im Tenant + let profile = null + + if (authUser.tenant_id) { + const [profileRow] = await server.db + .select() + .from(authProfiles) + .where( + and( + eq(authProfiles.user_id, id), + eq(authProfiles.tenant_id, authUser.tenant_id) + ) + ) + + profile = profileRow || null + } + + return { user, profile } + + } catch (err: any) { + console.error("/user/:id ERROR", err) + return reply.code(500).send({ error: err.message || "Internal error" }) + } + }) + + // ------------------------------------------------------------- + // PUT /user/:id/profile + // ------------------------------------------------------------- + server.put("/user/:id/profile", async (req, reply) => { + try { + const { id } = req.params as { id: string } + const { data } = req.body as { data?: Record } + + if (!req.user?.tenant_id) { + return reply.code(401).send({ error: "Unauthorized" }) + } + + if (!data || typeof data !== "object") { + return reply.code(400).send({ error: "data object required" }) + } + + // 1️⃣ Profil für diesen Tenant laden (damit wir die ID kennen) + const [profile] = await server.db + .select() + .from(authProfiles) + .where( + and( + eq(authProfiles.user_id, id), + eq(authProfiles.tenant_id, req.user.tenant_id) + ) + ) + + if (!profile) { + return reply.code(404).send({ error: "Profile not found in tenant" }) + } + + // 2️⃣ Timestamp-Felder normalisieren (falls welche drin sind) + const normalizeDate = (val: any) => { + if (!val) return null + const d = new Date(val) + return isNaN(d.getTime()) ? null : d + } + + const updateData: any = { ...data } + + // bekannte Date-Felder prüfen + if (data.entry_date !== undefined) + updateData.entry_date = normalizeDate(data.entry_date) + + if (data.birthday !== undefined) + updateData.birthday = normalizeDate(data.birthday) + + if (data.created_at !== undefined) + updateData.created_at = normalizeDate(data.created_at) + + updateData.updated_at = new Date() + + // 3️⃣ Update durchführen + const [updatedProfile] = await server.db + .update(authProfiles) + .set(updateData) + .where(eq(authProfiles.id, profile.id)) + .returning() + + return { profile: updatedProfile } + + } catch (err: any) { + console.error("PUT /user/:id/profile ERROR", err) + return reply.code(500).send({ error: err.message || "Internal server error" }) + } + }) +} diff --git a/src/routes/resources/main.ts b/src/routes/resources/main.ts index bad18a7..966cea0 100644 --- a/src/routes/resources/main.ts +++ b/src/routes/resources/main.ts @@ -13,6 +13,8 @@ import { import {resourceConfig} from "../../utils/resource.config"; +import {useNextNumberRangeNumber} from "../../utils/functions"; +import {stafftimeentries} from "../../../db/schema"; // ------------------------------------------------------------- // SQL Volltextsuche auf mehreren Feldern @@ -422,4 +424,112 @@ export default async function resourceRoutes(server: FastifyInstance) { return reply.code(500).send({ error: "Internal Server Error" }) } }) + + // Create + server.post("/resource/:resource", async (req, reply) => { + if (!req.user?.tenant_id) { + return reply.code(400).send({error: "No tenant selected"}); + } + + const {resource} = req.params as { resource: string }; + const body = req.body as Record; + + const table = resourceConfig[resource].table + + let createData = { + ...body, + tenant: req.user.tenant_id, + archived: false, // Standardwert + } + + if (resourceConfig[resource].numberRangeHolder && !body[resourceConfig[resource]]) { + const result = await useNextNumberRangeNumber(server, req.user.tenant_id, resource) + createData[resourceConfig[resource]] = result.usedNumber + } + + const [created] = await server.db + .insert(table) + .values(createData) + .returning() + + + /*await insertHistoryItem(server, { + entity: resource, + entityId: data.id, + action: "created", + created_by: req.user.user_id, + tenant_id: req.user.tenant_id, + oldVal: null, + newVal: data, + text: `${dataType.labelSingle} erstellt`, + });*/ + + return created; + }); + + // UPDATE (inkl. Soft-Delete/Archive) + server.put("/resource/:resource/:id", async (req, reply) => { + const {resource, id} = req.params as { resource: string; id: string } + const body = req.body as Record + + const tenantId = (req.user as any)?.tenant_id + const userId = (req.user as any)?.user_id + + if (!tenantId || !userId) { + return reply.code(401).send({error: "Unauthorized"}) + } + + const table = resourceConfig[resource].table + + //TODO: HISTORY + + const normalizeDate = (val: any) => { + const d = new Date(val) + return isNaN(d.getTime()) ? null : d + } + + console.log(body) + + Object.keys(body).forEach((key) => { + if(key.includes("_at") || key.includes("At")) { + body[key] = normalizeDate(body[key]) + } + }) + + + // vorherige Version für History laden + /*const {data: oldItem} = await server.supabase + .from(resource) + .select("*") + .eq("id", id) + .eq("tenant", tenantId) + .single()*/ + + const [updated] = await server.db + .update(table) + .set({...body, updated_at: new Date().toISOString(), updated_by: userId}) + .where(and( + eq(table.id, id), + eq(table.tenant, tenantId))) + .returning() + + //const diffs = diffObjects(oldItem, newItem); + + + /*for (const d of diffs) { + await insertHistoryItem(server, { + entity: resource, + entityId: id, + action: d.type, + created_by: userId, + tenant_id: tenantId, + oldVal: d.oldValue ? String(d.oldValue) : null, + newVal: d.newValue ? String(d.newValue) : null, + text: `Feld "${d.label}" ${d.typeLabel}: ${d.oldValue ?? ""} → ${d.newValue ?? ""}`, + }); + }*/ + + return updated + }) + } diff --git a/src/routes/tenant.ts b/src/routes/tenant.ts index 8b83203..93fa7c6 100644 --- a/src/routes/tenant.ts +++ b/src/routes/tenant.ts @@ -9,7 +9,7 @@ import { tenants } from "../../db/schema" -import { eq } from "drizzle-orm" +import {and, eq, inArray} from "drizzle-orm" export default async function tenantRoutes(server: FastifyInstance) { @@ -48,12 +48,10 @@ export default async function tenantRoutes(server: FastifyInstance) { const membership = await server.db .select() .from(authTenantUsers) - .where( - eq(authTenantUsers.user_id, req.user.user_id) - ) - .where( - eq(authTenantUsers.tenant_id, Number(tenant_id)) - ) + .where(and( + eq(authTenantUsers.user_id, req.user.user_id), + eq(authTenantUsers.tenant_id, Number(tenant_id)) + )) if (!membership.length) { return reply.code(403).send({ error: "Not a member of this tenant" }) @@ -120,8 +118,11 @@ export default async function tenantRoutes(server: FastifyInstance) { const profiles = await server.db .select() .from(authProfiles) - .where(eq(authProfiles.tenant_id, tenantId)) - .where(inArray(authProfiles.user_id, userIds)) + .where( + and( + eq(authProfiles.tenant_id, tenantId), + inArray(authProfiles.user_id, userIds) + )) const combined = users.map(u => { const profile = profiles.find(p => p.user_id === u.id) @@ -192,6 +193,7 @@ export default async function tenantRoutes(server: FastifyInstance) { if (!current) return reply.code(404).send({ error: "Tenant not found" }) const updatedRanges = { + //@ts-ignore ...current.numberRanges, [numberrange]: numberRange } diff --git a/src/utils/resource.config.ts b/src/utils/resource.config.ts index 4bb852a..4aa78f1 100644 --- a/src/utils/resource.config.ts +++ b/src/utils/resource.config.ts @@ -16,12 +16,14 @@ export const resourceConfig = { searchColumns: ["name"], mtoLoad: ["customer","plant","contract","projecttype"], mtmLoad: ["tasks", "files"], - table: projects + table: projects, + numberRangeHolder: "projectNumber" }, customers: { searchColumns: ["name", "customerNumber", "firstname", "lastname", "notes"], mtmLoad: ["contacts","projects"], table: customers, + numberRangeHolder: "customerNumber", }, contacts: { searchColumns: ["firstName", "lastName", "email", "phone", "notes"], @@ -30,7 +32,8 @@ export const resourceConfig = { }, contracts: { table: contracts, - searchColumns: ["name", "notes", "contractNumber", "paymentType", "sepaRef", "bankingName"] + searchColumns: ["name", "notes", "contractNumber", "paymentType", "sepaRef", "bankingName"], + numberRangeHolder: "contractNumber", }, plants: { table: plants, @@ -43,6 +46,7 @@ export const resourceConfig = { vendors: { table: vendors, searchColumns: ["name","vendorNumber","notes","defaultPaymentType"], + numberRangeHolder: "vendorNumber", }, files: { table: files @@ -54,7 +58,8 @@ export const resourceConfig = { table: filetags }, inventoryitems: { - table: inventoryitems + table: inventoryitems, + numberRangeHolder: "articleNumber", }, inventoryitemgroups: { table: inventoryitemgroups @@ -88,6 +93,7 @@ export const resourceConfig = { spaces: { table: spaces, searchColumns: ["name","space_number","type","info_data"], + numberRangeHolder: "spaceNumber", }, ownaccounts: { table: ownaccounts, @@ -96,7 +102,8 @@ export const resourceConfig = { costcentres: { table: costcentres, searchColumns: ["name","number","description"], - mtoLoad: ["vehicle","project","inventoryitem"] + mtoLoad: ["vehicle","project","inventoryitem"], + numberRangeHolder: "number", }, tasks: { table: tasks, From 64b3ec626cae2ad3d318a8a66d16469379608ea6 Mon Sep 17 00:00:00 2001 From: Florian Federspiel Date: Mon, 8 Dec 2025 14:11:25 +0000 Subject: [PATCH 112/149] docker-compose.yml bearbeiten --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 6490d95..c6a115c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,6 @@ services: backend: - image: reg.federspiel.software/fedeo/backend:orm + image: reg.federspiel.software/fedeo/backend:main restart: always environment: From a605fdc35123560798f7e37256c6e506a26fc340 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Mon, 8 Dec 2025 20:23:59 +0100 Subject: [PATCH 113/149] Redone --- src/utils/resource.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/resource.config.ts b/src/utils/resource.config.ts index 4aa78f1..dd70738 100644 --- a/src/utils/resource.config.ts +++ b/src/utils/resource.config.ts @@ -115,7 +115,7 @@ export const resourceConfig = { createddocuments: { table: createddocuments, mtoLoad: ["customer", "project", "contact", "contract", "plant","letterhead"], - mtmLoad: ["statementallocations"], + mtmLoad: ["statementallocations","files"], mtmListLoad: ["statementallocations"], }, texttemplates: { From 0c1287d3b92bdeb9cb1c04cf803f5444585a5604 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Tue, 9 Dec 2025 08:17:52 +0100 Subject: [PATCH 114/149] Fix NumberRange --- src/routes/resources/main.ts | 82 ++++++++++++++++++++---------------- src/utils/functions.ts | 54 ++++++++++++++++++------ 2 files changed, 87 insertions(+), 49 deletions(-) diff --git a/src/routes/resources/main.ts b/src/routes/resources/main.ts index 966cea0..4039ede 100644 --- a/src/routes/resources/main.ts +++ b/src/routes/resources/main.ts @@ -427,44 +427,52 @@ export default async function resourceRoutes(server: FastifyInstance) { // Create server.post("/resource/:resource", async (req, reply) => { - if (!req.user?.tenant_id) { - return reply.code(400).send({error: "No tenant selected"}); + try { + if (!req.user?.tenant_id) { + return reply.code(400).send({error: "No tenant selected"}); + } + + const {resource} = req.params as { resource: string }; + const body = req.body as Record; + + const table = resourceConfig[resource].table + + let createData = { + ...body, + tenant: req.user.tenant_id, + archived: false, // Standardwert + } + + console.log(resourceConfig[resource].numberRangeHolder) + + if (resourceConfig[resource].numberRangeHolder && !body[resourceConfig[resource]]) { + const result = await useNextNumberRangeNumber(server, req.user.tenant_id, resource) + console.log(result) + createData[resourceConfig[resource].numberRangeHolder] = result.usedNumber + } + + const [created] = await server.db + .insert(table) + .values(createData) + .returning() + + + /*await insertHistoryItem(server, { + entity: resource, + entityId: data.id, + action: "created", + created_by: req.user.user_id, + tenant_id: req.user.tenant_id, + oldVal: null, + newVal: data, + text: `${dataType.labelSingle} erstellt`, + });*/ + + return created; + } catch (error) { + console.log(error) + reply.status(500) } - - const {resource} = req.params as { resource: string }; - const body = req.body as Record; - - const table = resourceConfig[resource].table - - let createData = { - ...body, - tenant: req.user.tenant_id, - archived: false, // Standardwert - } - - if (resourceConfig[resource].numberRangeHolder && !body[resourceConfig[resource]]) { - const result = await useNextNumberRangeNumber(server, req.user.tenant_id, resource) - createData[resourceConfig[resource]] = result.usedNumber - } - - const [created] = await server.db - .insert(table) - .values(createData) - .returning() - - - /*await insertHistoryItem(server, { - entity: resource, - entityId: data.id, - action: "created", - created_by: req.user.user_id, - tenant_id: req.user.tenant_id, - oldVal: null, - newVal: data, - text: `${dataType.labelSingle} erstellt`, - });*/ - - return created; }); // UPDATE (inkl. Soft-Delete/Archive) diff --git a/src/utils/functions.ts b/src/utils/functions.ts index 3190a87..3b6dc7d 100644 --- a/src/utils/functions.ts +++ b/src/utils/functions.ts @@ -7,28 +7,58 @@ import {FastifyInstance} from "fastify"; // import Sharp from 'sharp' // import fs from 'fs' -export const useNextNumberRangeNumber = async (server:FastifyInstance, tenantId:number,numberRange)=> { - const {data:tenant} = await server.supabase.from("tenants").select().eq("id",tenantId).single() +import { tenants } from "../../db/schema" +import { eq } from "drizzle-orm" - const numberRanges = tenant.numberRanges +export const useNextNumberRangeNumber = async ( + server: FastifyInstance, + tenantId: number, + numberRange: string +) => { + // 1️⃣ Tenant laden + const [tenant] = await server.db + .select() + .from(tenants) + .where(eq(tenants.id, tenantId)) - const usedNumber = (numberRanges[numberRange].prefix ? numberRanges[numberRange].prefix : "") + numberRanges[numberRange].nextNumber + (numberRanges[numberRange].suffix ? numberRanges[numberRange].suffix : "") + if (!tenant) { + throw new Error(`Tenant ${tenantId} not found`) + } - let newNumberRange = numberRanges + const numberRanges = tenant.numberRanges || {} - newNumberRange[numberRange].nextNumber += 1 + if (!numberRanges[numberRange]) { + throw new Error(`Number range '${numberRange}' not found`) + } - const {error} = await server.supabase.from("tenants").update({numberRanges: newNumberRange}).eq("id",tenantId) + const current = numberRanges[numberRange] - if(error) { - console.log(error) - } else { - return { - usedNumber + // 2️⃣ Used Number generieren + const usedNumber = + (current.prefix || "") + + current.nextNumber + + (current.suffix || "") + + // 3️⃣ nextNumber erhöhen + const updatedRanges = { + // @ts-ignore + ...numberRanges, + [numberRange]: { + ...current, + nextNumber: current.nextNumber + 1 } } + + // 4️⃣ Tenant aktualisieren + await server.db + .update(tenants) + .set({ numberRanges: updatedRanges }) + .where(eq(tenants.id, tenantId)) + + return { usedNumber } } + /* export async function encodeBase64ToNiimbot(base64Png, printDirection = 'top') { // 1️⃣ PNG dekodieren From 861984e4b1a7c462dda7e0dece47785a712d0841 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Tue, 9 Dec 2025 08:29:24 +0100 Subject: [PATCH 115/149] fix laoding issue fix missing mtm --- src/routes/resources/main.ts | 2 +- src/utils/resource.config.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/resources/main.ts b/src/routes/resources/main.ts index 4039ede..9dd4cbf 100644 --- a/src/routes/resources/main.ts +++ b/src/routes/resources/main.ts @@ -405,7 +405,7 @@ export default async function resourceRoutes(server: FastifyInstance) { if(resourceConfig[resource].mtoLoad) { for await (const relation of resourceConfig[resource].mtoLoad ) { if(data[relation]) { - data[relation] = await server.db.select().from(resourceConfig[relation + "s"].table).where(eq(resourceConfig[relation + "s"].table.id, data[relation])) + data[relation] = (await server.db.select().from(resourceConfig[relation + "s"].table).where(eq(resourceConfig[relation + "s"].table.id, data[relation])))[0] } } } diff --git a/src/utils/resource.config.ts b/src/utils/resource.config.ts index dd70738..c430fc5 100644 --- a/src/utils/resource.config.ts +++ b/src/utils/resource.config.ts @@ -15,7 +15,7 @@ export const resourceConfig = { projects: { searchColumns: ["name"], mtoLoad: ["customer","plant","contract","projecttype"], - mtmLoad: ["tasks", "files"], + mtmLoad: ["tasks", "files","createddocuments"], table: projects, numberRangeHolder: "projectNumber" }, From 4a3515f6f3eb6d9937b0b8e895fd9b1c69083dd7 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Tue, 9 Dec 2025 12:12:05 +0100 Subject: [PATCH 116/149] Schema and Config Changes --- db/schema/bankstatements.ts | 8 -------- db/schema/createddocuments.ts | 2 +- src/utils/resource.config.ts | 4 ++-- 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/db/schema/bankstatements.ts b/db/schema/bankstatements.ts index bce2af7..c69ee2e 100644 --- a/db/schema/bankstatements.ts +++ b/db/schema/bankstatements.ts @@ -46,20 +46,12 @@ export const bankstatements = pgTable("bankstatements", { currency: text("currency"), valueDate: text("valueDate"), - incomingInvoice: bigint("incomingInvoice", { mode: "number" }).references( - () => incominginvoices.id - ), - mandateId: text("mandateId"), contract: bigint("contract", { mode: "number" }).references( () => contracts.id ), - createdDocument: bigint("createdDocument", { mode: "number" }).references( - () => createddocuments.id - ), - archived: boolean("archived").notNull().default(false), updatedAt: timestamp("updated_at", { withTimezone: true }), diff --git a/db/schema/createddocuments.ts b/db/schema/createddocuments.ts index dca90d6..9f16acf 100644 --- a/db/schema/createddocuments.ts +++ b/db/schema/createddocuments.ts @@ -71,7 +71,7 @@ export const createddocuments = pgTable("createddocuments", { serialConfig: jsonb("serialConfig").default({}), - linkedDocument: bigint("linkedDocument", { mode: "number" }).references( + createddocument: bigint("linkedDocument", { mode: "number" }).references( () => createddocuments.id ), diff --git a/src/utils/resource.config.ts b/src/utils/resource.config.ts index c430fc5..acce9cd 100644 --- a/src/utils/resource.config.ts +++ b/src/utils/resource.config.ts @@ -114,8 +114,8 @@ export const resourceConfig = { }, createddocuments: { table: createddocuments, - mtoLoad: ["customer", "project", "contact", "contract", "plant","letterhead"], - mtmLoad: ["statementallocations","files"], + mtoLoad: ["customer", "project", "contact", "contract", "plant","letterhead","createddocument"], + mtmLoad: ["statementallocations","files","createddocuments"], mtmListLoad: ["statementallocations"], }, texttemplates: { From 6d0500181227b739581a98fa28cacfa029f89b6d Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Tue, 9 Dec 2025 12:26:10 +0100 Subject: [PATCH 117/149] Tidying --- README.md | 93 ------------------------------------ src/routes/resources/main.ts | 9 ---- 2 files changed, 102 deletions(-) diff --git a/README.md b/README.md index 5cd6a60..e69de29 100644 --- a/README.md +++ b/README.md @@ -1,93 +0,0 @@ -# Backend - - - -## Getting started - -To make it easy for you to get started with GitLab, here's a list of recommended next steps. - -Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)! - -## Add your files - -- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files -- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command: - -``` -cd existing_repo -git remote add origin https://git.federspiel.software/fedeo/backend.git -git branch -M main -git push -uf origin main -``` - -## Integrate with your tools - -- [ ] [Set up project integrations](https://git.federspiel.software/fedeo/backend/-/settings/integrations) - -## Collaborate with your team - -- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/) -- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html) -- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically) -- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/) -- [ ] [Set auto-merge](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html) - -## Test and Deploy - -Use the built-in continuous integration in GitLab. - -- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html) -- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing (SAST)](https://docs.gitlab.com/ee/user/application_security/sast/) -- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html) -- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/) -- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html) - -*** - -# Editing this README - -When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thanks to [makeareadme.com](https://www.makeareadme.com/) for this template. - -## Suggestions for a good README - -Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information. - -## Name -Choose a self-explaining name for your project. - -## Description -Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors. - -## Badges -On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge. - -## Visuals -Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method. - -## Installation -Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection. - -## Usage -Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README. - -## Support -Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc. - -## Roadmap -If you have ideas for releases in the future, it is a good idea to list them in the README. - -## Contributing -State if you are open to contributions and what your requirements are for accepting them. - -For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self. - -You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser. - -## Authors and acknowledgment -Show your appreciation to those who have contributed to the project. - -## License -For open source projects, say how it is licensed. - -## Project status -If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers. diff --git a/src/routes/resources/main.ts b/src/routes/resources/main.ts index 9dd4cbf..c0bf1f8 100644 --- a/src/routes/resources/main.ts +++ b/src/routes/resources/main.ts @@ -504,15 +504,6 @@ export default async function resourceRoutes(server: FastifyInstance) { } }) - - // vorherige Version für History laden - /*const {data: oldItem} = await server.supabase - .from(resource) - .select("*") - .eq("id", id) - .eq("tenant", tenantId) - .single()*/ - const [updated] = await server.db .update(table) .set({...body, updated_at: new Date().toISOString(), updated_by: userId}) From 641130e506ba957efaaf2296ab111922a81d8c2f Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Tue, 9 Dec 2025 12:26:18 +0100 Subject: [PATCH 118/149] Redone --- src/routes/resourcesSpecial.ts | 96 ++++++++++++++++++++++++---------- 1 file changed, 68 insertions(+), 28 deletions(-) diff --git a/src/routes/resourcesSpecial.ts b/src/routes/resourcesSpecial.ts index 9fae6ac..18a30bc 100644 --- a/src/routes/resourcesSpecial.ts +++ b/src/routes/resourcesSpecial.ts @@ -1,34 +1,74 @@ -import {FastifyInstance} from "fastify"; -import {sortData} from "../utils/sort"; +import { FastifyInstance } from "fastify" +import { asc, desc } from "drizzle-orm" +import { sortData } from "../utils/sort" +// Schema imports +import { accounts, units,countrys } from "../../db/schema" + +const TABLE_MAP: Record = { + accounts, + units, + countrys, +} export default async function resourceRoutesSpecial(server: FastifyInstance) { - // Liste + server.get("/resource-special/:resource", async (req, reply) => { - if (!req.user?.tenant_id) { - return reply.code(400).send({ error: "No tenant selected" }); + try { + if (!req.user?.tenant_id) { + return reply.code(400).send({ error: "No tenant selected" }) + } + + const { resource } = req.params as { resource: string } + + // ❌ Wenn falsche Ressource + if (!TABLE_MAP[resource]) { + return reply.code(400).send({ error: "Invalid special resource" }) + } + + const table = TABLE_MAP[resource] + + const { select, sort, asc: ascQuery } = req.query as { + select?: string + sort?: string + asc?: string + } + + // --------------------------------------- + // 📌 SELECT: wir ignorieren select string (wie Supabase) + // Drizzle kann kein dynamisches Select aus String! + // Wir geben IMMER alle Spalten zurück → kompatibel zum Frontend + // --------------------------------------- + + let query = server.db.select().from(table) + + // --------------------------------------- + // 📌 Sortierung + // --------------------------------------- + if (sort) { + const col = (table as any)[sort] + if (col) { + query = + ascQuery === "true" + ? query.orderBy(asc(col)) + : query.orderBy(desc(col)) + } + } + + const data = await query + + // Falls sort clientseitig wie früher notwendig ist: + const sorted = sortData( + data, + sort, + ascQuery === "true" + ) + + return sorted } - - const { resource } = req.params as { resource: string }; - - if(!["accounts","units","countrys"].includes(resource)) return reply.code(400).send({ error: "No corrected special resource selected" }); - - const {select, sort, asc } = req.query as { select?: string, sort?: string, asc?: string } - console.log(select, sort, asc) - - - const { data, error } = await server.supabase - .from(resource) - //@ts-ignore - .select(select || "*") - if (error) { - console.log(error) - return reply.code(400).send({ error: error.message }); + catch (err) { + console.error(err) + return reply.code(500).send({ error: "Internal Server Error" }) } - - // @ts-ignore - const sorted =sortData(data,sort,asc === "true" ? true : false) - - return sorted; - }); -} \ No newline at end of file + }) +} From c3467bdd9df3a1cc51f0586708e51865f0996905 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Tue, 9 Dec 2025 12:27:20 +0100 Subject: [PATCH 119/149] Redone --- src/routes/resourcesSpecial.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/routes/resourcesSpecial.ts b/src/routes/resourcesSpecial.ts index 18a30bc..fc29890 100644 --- a/src/routes/resourcesSpecial.ts +++ b/src/routes/resourcesSpecial.ts @@ -48,6 +48,7 @@ export default async function resourceRoutesSpecial(server: FastifyInstance) { if (sort) { const col = (table as any)[sort] if (col) { + //@ts-ignore query = ascQuery === "true" ? query.orderBy(asc(col)) From 4016c8b6b54debace24f4d0aceecfcd457993c64 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Wed, 10 Dec 2025 08:32:22 +0100 Subject: [PATCH 120/149] Redone --- src/routes/resources/main.ts | 93 +++++++++++++++++++----------------- 1 file changed, 49 insertions(+), 44 deletions(-) diff --git a/src/routes/resources/main.ts b/src/routes/resources/main.ts index c0bf1f8..5f279fb 100644 --- a/src/routes/resources/main.ts +++ b/src/routes/resources/main.ts @@ -477,58 +477,63 @@ export default async function resourceRoutes(server: FastifyInstance) { // UPDATE (inkl. Soft-Delete/Archive) server.put("/resource/:resource/:id", async (req, reply) => { - const {resource, id} = req.params as { resource: string; id: string } - const body = req.body as Record + try { + const {resource, id} = req.params as { resource: string; id: string } + const body = req.body as Record - const tenantId = (req.user as any)?.tenant_id - const userId = (req.user as any)?.user_id + const tenantId = (req.user as any)?.tenant_id + const userId = (req.user as any)?.user_id - if (!tenantId || !userId) { - return reply.code(401).send({error: "Unauthorized"}) - } - - const table = resourceConfig[resource].table - - //TODO: HISTORY - - const normalizeDate = (val: any) => { - const d = new Date(val) - return isNaN(d.getTime()) ? null : d - } - - console.log(body) - - Object.keys(body).forEach((key) => { - if(key.includes("_at") || key.includes("At")) { - body[key] = normalizeDate(body[key]) + if (!tenantId || !userId) { + return reply.code(401).send({error: "Unauthorized"}) } - }) - const [updated] = await server.db - .update(table) - .set({...body, updated_at: new Date().toISOString(), updated_by: userId}) - .where(and( - eq(table.id, id), - eq(table.tenant, tenantId))) - .returning() + const table = resourceConfig[resource].table - //const diffs = diffObjects(oldItem, newItem); + //TODO: HISTORY + + const normalizeDate = (val: any) => { + const d = new Date(val) + return isNaN(d.getTime()) ? null : d + } + + console.log(body) + + Object.keys(body).forEach((key) => { + if(key.includes("_at") || key.includes("At")) { + body[key] = normalizeDate(body[key]) + } + }) + + const [updated] = await server.db + .update(table) + .set({...body, updated_at: new Date().toISOString(), updated_by: userId}) + .where(and( + eq(table.id, id), + eq(table.tenant, tenantId))) + .returning() + + //const diffs = diffObjects(oldItem, newItem); - /*for (const d of diffs) { - await insertHistoryItem(server, { - entity: resource, - entityId: id, - action: d.type, - created_by: userId, - tenant_id: tenantId, - oldVal: d.oldValue ? String(d.oldValue) : null, - newVal: d.newValue ? String(d.newValue) : null, - text: `Feld "${d.label}" ${d.typeLabel}: ${d.oldValue ?? ""} → ${d.newValue ?? ""}`, - }); - }*/ + /*for (const d of diffs) { + await insertHistoryItem(server, { + entity: resource, + entityId: id, + action: d.type, + created_by: userId, + tenant_id: tenantId, + oldVal: d.oldValue ? String(d.oldValue) : null, + newVal: d.newValue ? String(d.newValue) : null, + text: `Feld "${d.label}" ${d.typeLabel}: ${d.oldValue ?? ""} → ${d.newValue ?? ""}`, + }); + }*/ + + return updated + } catch (err) { + console.log("ERROR /resource/projects/:id", err) + } - return updated }) } From 58836fb0efa26e2f28e38ef663be580adf9281e9 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Wed, 10 Dec 2025 08:39:44 +0100 Subject: [PATCH 121/149] Redone --- src/routes/resources/main.ts | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/routes/resources/main.ts b/src/routes/resources/main.ts index 5f279fb..7d781bd 100644 --- a/src/routes/resources/main.ts +++ b/src/routes/resources/main.ts @@ -373,13 +373,13 @@ export default async function resourceRoutes(server: FastifyInstance) { // ------------------------------------------------------------- // DETAIL (mit JOINS) // ------------------------------------------------------------- - server.get("/resource/:resource/:id", async (req, reply) => { + server.get("/resource/:resource/:id/:no_relations?", async (req, reply) => { try { const { id } = req.params as { id: string } const tenantId = req.user?.tenant_id if (!tenantId) return reply.code(400).send({ error: "No tenant selected" }) - const {resource} = req.params as { resource: string } + const {resource, no_relations} = req.params as { resource: string, no_relations?: boolean } const table = resourceConfig[resource].table const projRows = await server.db @@ -402,20 +402,24 @@ export default async function resourceRoutes(server: FastifyInstance) { ...projRows[0] } - if(resourceConfig[resource].mtoLoad) { - for await (const relation of resourceConfig[resource].mtoLoad ) { - if(data[relation]) { - data[relation] = (await server.db.select().from(resourceConfig[relation + "s"].table).where(eq(resourceConfig[relation + "s"].table.id, data[relation])))[0] + if(!no_relations) { + if(resourceConfig[resource].mtoLoad) { + for await (const relation of resourceConfig[resource].mtoLoad ) { + if(data[relation]) { + data[relation] = (await server.db.select().from(resourceConfig[relation + "s"].table).where(eq(resourceConfig[relation + "s"].table.id, data[relation])))[0] + } + } + } + + if(resourceConfig[resource].mtmLoad) { + for await (const relation of resourceConfig[resource].mtmLoad ) { + console.log(relation) + data[relation] = await server.db.select().from(resourceConfig[relation].table).where(eq(resourceConfig[relation].table[resource.substring(0,resource.length - 1)],id)) } } } - if(resourceConfig[resource].mtmLoad) { - for await (const relation of resourceConfig[resource].mtmLoad ) { - console.log(relation) - data[relation] = await server.db.select().from(resourceConfig[relation].table).where(eq(resourceConfig[relation].table[resource.substring(0,resource.length - 1)],id)) - } - } + return data From 3f7f8a4498d771cd9401a1ca7da3cac07748aa08 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Wed, 10 Dec 2025 10:28:53 +0100 Subject: [PATCH 122/149] Added Plants to resource config --- src/utils/resource.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/resource.config.ts b/src/utils/resource.config.ts index acce9cd..a0f26a3 100644 --- a/src/utils/resource.config.ts +++ b/src/utils/resource.config.ts @@ -21,7 +21,7 @@ export const resourceConfig = { }, customers: { searchColumns: ["name", "customerNumber", "firstname", "lastname", "notes"], - mtmLoad: ["contacts","projects"], + mtmLoad: ["contacts","projects","plants"], table: customers, numberRangeHolder: "customerNumber", }, From 6d936c4be71c417fc1d76808bcb7f7ac369bb020 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Wed, 10 Dec 2025 12:31:10 +0100 Subject: [PATCH 123/149] Fixed Put --- src/routes/resources/main.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/routes/resources/main.ts b/src/routes/resources/main.ts index 7d781bd..9fc41dc 100644 --- a/src/routes/resources/main.ts +++ b/src/routes/resources/main.ts @@ -501,17 +501,19 @@ export default async function resourceRoutes(server: FastifyInstance) { return isNaN(d.getTime()) ? null : d } - console.log(body) + let data = {...body, updated_at: new Date().toISOString(), updated_by: userId} - Object.keys(body).forEach((key) => { + Object.keys(data).forEach((key) => { if(key.includes("_at") || key.includes("At")) { - body[key] = normalizeDate(body[key]) + data[key] = normalizeDate(data[key]) } }) + console.log(data) + const [updated] = await server.db .update(table) - .set({...body, updated_at: new Date().toISOString(), updated_by: userId}) + .set(data) .where(and( eq(table.id, id), eq(table.tenant, tenantId))) From e5c3863eee6c94c2300e95d8c024bd60c5f65d25 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sun, 14 Dec 2025 16:28:30 +0100 Subject: [PATCH 124/149] Schema Migrations --- db/migrations/0000_brief_dark_beast.sql | 1312 +++ db/migrations/0001_medical_big_bertha.sql | 32 + .../0002_silent_christian_walker.sql | 13 + db/migrations/meta/0000_snapshot.json | 9788 ++++++++++++++++ db/migrations/meta/0001_snapshot.json | 9947 +++++++++++++++++ db/migrations/meta/_journal.json | 41 + 6 files changed, 21133 insertions(+) create mode 100644 db/migrations/0000_brief_dark_beast.sql create mode 100644 db/migrations/0001_medical_big_bertha.sql create mode 100644 db/migrations/0002_silent_christian_walker.sql create mode 100644 db/migrations/meta/0000_snapshot.json create mode 100644 db/migrations/meta/0001_snapshot.json create mode 100644 db/migrations/meta/_journal.json diff --git a/db/migrations/0000_brief_dark_beast.sql b/db/migrations/0000_brief_dark_beast.sql new file mode 100644 index 0000000..adfe24f --- /dev/null +++ b/db/migrations/0000_brief_dark_beast.sql @@ -0,0 +1,1312 @@ +CREATE TYPE "public"."credential_types" AS ENUM('mail', 'm365');--> statement-breakpoint +CREATE TYPE "public"."folderfunctions" AS ENUM('none', 'yearSubCategory', 'incomingInvoices', 'invoices', 'quotes', 'confirmationOrders', 'deliveryNotes', 'vehicleData', 'reminders', 'taxData', 'deposit', 'timeEvaluations');--> statement-breakpoint +CREATE TYPE "public"."locked_tenant" AS ENUM('maintenance_tenant', 'maintenance', 'general', 'no_subscription');--> statement-breakpoint +CREATE TYPE "public"."notification_channel" AS ENUM('email', 'inapp', 'sms', 'push', 'webhook');--> statement-breakpoint +CREATE TYPE "public"."notification_severity" AS ENUM('info', 'success', 'warning', 'error');--> statement-breakpoint +CREATE TYPE "public"."notification_status" AS ENUM('queued', 'sent', 'failed', 'read');--> statement-breakpoint +CREATE TYPE "public"."payment_types" AS ENUM('transfer', 'direct_debit');--> statement-breakpoint +CREATE TYPE "public"."texttemplatepositions" AS ENUM('startText', 'endText');--> statement-breakpoint +CREATE TYPE "public"."times_state" AS ENUM('submitted', 'approved', 'draft');--> statement-breakpoint +CREATE TABLE "accounts" ( + "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "accounts_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1), + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "number" text NOT NULL, + "label" text NOT NULL, + "description" text +); +--> statement-breakpoint +CREATE TABLE "auth_profiles" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "user_id" uuid, + "tenant_id" bigint NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "first_name" text NOT NULL, + "last_name" text NOT NULL, + "full_name" text GENERATED ALWAYS AS (((first_name || ' ') || last_name)) STORED, + "mobile_tel" text, + "fixed_tel" text, + "salutation" text, + "employee_number" text, + "weekly_working_hours" double precision DEFAULT 0, + "annual_paid_leave_days" bigint, + "weekly_regular_working_hours" jsonb DEFAULT '{}', + "clothing_size_top" text, + "clothing_size_bottom" text, + "clothing_size_shoe" text, + "email_signature" text DEFAULT '

Mit freundlichen Grüßen

', + "birthday" date, + "entry_date" date DEFAULT now(), + "automatic_hour_corrections" jsonb DEFAULT '[]', + "recreation_days_compensation" boolean DEFAULT true NOT NULL, + "customer_for_portal" bigint, + "pinned_on_navigation" jsonb DEFAULT '[]' NOT NULL, + "email" text, + "token_id" text, + "weekly_working_days" double precision, + "old_profile_id" uuid, + "temp_config" jsonb, + "state_code" text DEFAULT 'DE-NI', + "contract_type" text, + "position" text, + "qualification" text, + "address_street" text, + "address_zip" text, + "address_city" text, + "active" boolean DEFAULT true NOT NULL +); +--> statement-breakpoint +CREATE TABLE "auth_role_permissions" ( + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "role_id" uuid NOT NULL, + "permission" text NOT NULL +); +--> statement-breakpoint +CREATE TABLE "auth_roles" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "name" text NOT NULL, + "description" text, + "created_by" uuid, + "tenant_id" bigint +); +--> statement-breakpoint +CREATE TABLE "auth_tenant_users" ( + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "tenant_id" bigint NOT NULL, + "user_id" uuid NOT NULL, + "created_by" uuid +); +--> statement-breakpoint +CREATE TABLE "auth_user_roles" ( + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "user_id" uuid NOT NULL, + "role_id" uuid NOT NULL, + "tenant_id" bigint NOT NULL, + "created_by" uuid +); +--> statement-breakpoint +CREATE TABLE "auth_users" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "email" text NOT NULL, + "password_hash" text NOT NULL, + "multi_tenant" boolean DEFAULT true NOT NULL, + "must_change_password" boolean DEFAULT false NOT NULL, + "updated_at" timestamp with time zone, + "ported" boolean DEFAULT true NOT NULL +); +--> statement-breakpoint +CREATE TABLE "bankaccounts" ( + "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "bankaccounts_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1), + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "name" text, + "iban" text NOT NULL, + "tenant" bigint NOT NULL, + "bankId" text NOT NULL, + "ownerName" text, + "accountId" text NOT NULL, + "balance" double precision, + "expired" boolean DEFAULT false NOT NULL, + "datevNumber" text, + "synced_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone, + "updated_by" uuid, + "archived" boolean DEFAULT false NOT NULL +); +--> statement-breakpoint +CREATE TABLE "bankrequisitions" ( + "id" uuid PRIMARY KEY NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "institutionId" text, + "tenant" bigint, + "status" text, + "updated_at" timestamp with time zone, + "updated_by" uuid +); +--> statement-breakpoint +CREATE TABLE "bankstatements" ( + "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "bankstatements_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1), + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "account" bigint NOT NULL, + "date" text NOT NULL, + "credIban" text, + "credName" text, + "text" text, + "amount" double precision NOT NULL, + "tenant" bigint NOT NULL, + "debIban" text, + "debName" text, + "gocardlessId" text, + "currency" text, + "valueDate" text, + "incomingInvoice" bigint, + "mandateId" text, + "contract" bigint, + "createdDocument" bigint, + "archived" boolean DEFAULT false NOT NULL, + "updated_at" timestamp with time zone, + "updated_by" uuid +); +--> statement-breakpoint +CREATE TABLE "checkexecutions" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "check" uuid, + "executed_at" timestamp, + "description" text +); +--> statement-breakpoint +CREATE TABLE "checks" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "vehicle" bigint, + "inventoryitem" bigint, + "tenant" bigint, + "name" text, + "type" text, + "distance" bigint DEFAULT 1, + "distanceUnit" text DEFAULT 'days', + "description" text, + "profiles" jsonb DEFAULT '[]'::jsonb NOT NULL, + "archived" boolean DEFAULT false NOT NULL, + "updated_at" timestamp with time zone, + "updated_by" uuid +); +--> statement-breakpoint +CREATE TABLE "citys" ( + "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "citys_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1), + "name" text, + "short" text, + "long" text, + "geometry" jsonb, + "zip" bigint, + "districtCode" bigint, + "countryName" text, + "countryCode" bigint, + "districtName" text, + "geopoint" text +); +--> statement-breakpoint +CREATE TABLE "contacts" ( + "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "contacts_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1), + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "firstName" text, + "lastName" text, + "email" text, + "customer" bigint, + "tenant" bigint NOT NULL, + "phoneMobile" text, + "phoneHome" text, + "heroId" text, + "role" text, + "fullName" text, + "salutation" text, + "vendor" bigint, + "active" boolean DEFAULT true NOT NULL, + "birthday" date, + "notes" text, + "profiles" jsonb DEFAULT '[]'::jsonb NOT NULL, + "archived" boolean DEFAULT false NOT NULL, + "title" text, + "updated_at" timestamp with time zone, + "updated_by" uuid +); +--> statement-breakpoint +CREATE TABLE "contracts" ( + "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "contracts_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1), + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "tenant" bigint NOT NULL, + "name" text NOT NULL, + "customer" bigint NOT NULL, + "notes" text, + "active" boolean DEFAULT true NOT NULL, + "recurring" boolean DEFAULT false NOT NULL, + "rhythm" jsonb, + "startDate" timestamp with time zone, + "endDate" timestamp with time zone, + "signDate" timestamp with time zone, + "duration" text, + "contact" bigint, + "bankingIban" text, + "bankingBIC" text, + "bankingName" text, + "bankingOwner" text, + "sepaRef" text, + "sepaDate" timestamp with time zone, + "paymentType" text, + "invoiceDispatch" text, + "ownFields" jsonb DEFAULT '{}'::jsonb NOT NULL, + "profiles" jsonb DEFAULT '[]'::jsonb NOT NULL, + "archived" boolean DEFAULT false NOT NULL, + "contractNumber" text, + "updated_at" timestamp with time zone, + "updated_by" uuid +); +--> statement-breakpoint +CREATE TABLE "costcentres" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "tenant" bigint NOT NULL, + "number" text NOT NULL, + "name" text NOT NULL, + "vehicle" bigint, + "project" bigint, + "inventoryitem" bigint, + "description" text, + "archived" boolean DEFAULT false NOT NULL, + "profiles" jsonb DEFAULT '[]'::jsonb NOT NULL, + "updated_at" timestamp with time zone, + "updated_by" uuid +); +--> statement-breakpoint +CREATE TABLE "countrys" ( + "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "countrys_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1), + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "name" text NOT NULL +); +--> statement-breakpoint +CREATE TABLE "createddocuments" ( + "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "createddocuments_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1), + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "tenant" bigint NOT NULL, + "type" text DEFAULT 'INVOICE' NOT NULL, + "customer" bigint, + "contact" bigint, + "address" jsonb, + "project" bigint, + "documentNumber" text, + "documentDate" text, + "state" text DEFAULT 'Entwurf' NOT NULL, + "info" jsonb, + "createdBy" uuid, + "title" text, + "description" text, + "startText" text, + "endText" text, + "rows" jsonb DEFAULT '[]'::jsonb, + "deliveryDateType" text, + "paymentDays" smallint, + "deliveryDate" text, + "contactPerson" uuid, + "serialConfig" jsonb DEFAULT '{}'::jsonb, + "linkedDocument" bigint, + "agriculture" jsonb, + "letterhead" bigint, + "advanceInvoiceResolved" boolean DEFAULT false NOT NULL, + "usedAdvanceInvoices" jsonb DEFAULT '[]'::jsonb NOT NULL, + "archived" boolean DEFAULT false NOT NULL, + "deliveryDateEnd" text, + "plant" bigint, + "taxType" text, + "customSurchargePercentage" smallint DEFAULT 0 NOT NULL, + "report" jsonb DEFAULT '{}'::jsonb NOT NULL, + "availableInPortal" boolean DEFAULT false NOT NULL, + "updated_at" timestamp with time zone, + "updated_by" uuid, + "created_by" uuid, + "payment_type" text DEFAULT 'transfer', + "contract" bigint +); +--> statement-breakpoint +CREATE TABLE "createdletters" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "tenant" bigint, + "customer" bigint, + "vendor" bigint, + "content_json" jsonb DEFAULT '[]'::jsonb, + "content_text" text, + "updated_at" timestamp with time zone, + "updated_by" uuid, + "archived" boolean DEFAULT false NOT NULL +); +--> statement-breakpoint +CREATE TABLE "customers" ( + "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "customers_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1), + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "customerNumber" text NOT NULL, + "name" text NOT NULL, + "tenant" bigint NOT NULL, + "infoData" jsonb DEFAULT '{}'::jsonb, + "active" boolean DEFAULT true NOT NULL, + "notes" text, + "type" text DEFAULT 'Privat', + "heroId" text, + "isCompany" boolean DEFAULT false NOT NULL, + "profiles" jsonb DEFAULT '[]'::jsonb NOT NULL, + "customPaymentDays" smallint, + "firstname" text, + "lastname" text, + "archived" boolean DEFAULT false NOT NULL, + "customSurchargePercentage" smallint DEFAULT 0 NOT NULL, + "salutation" text, + "title" text, + "nameAddition" text, + "availableInPortal" boolean DEFAULT false NOT NULL, + "updated_at" timestamp with time zone, + "updated_by" uuid, + "custom_payment_type" text +); +--> statement-breakpoint +CREATE TABLE "devices" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "name" text NOT NULL, + "type" text NOT NULL, + "tenant" bigint, + "password" text, + "externalId" text +); +--> statement-breakpoint +CREATE TABLE "documentboxes" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "space" bigint, + "key" text NOT NULL, + "tenant" bigint NOT NULL, + "archived" boolean DEFAULT false NOT NULL, + "updated_at" timestamp with time zone, + "updated_by" uuid +); +--> statement-breakpoint +CREATE TABLE "events" ( + "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "events_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1), + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "tenant" bigint NOT NULL, + "name" text NOT NULL, + "startDate" timestamp with time zone NOT NULL, + "endDate" timestamp with time zone, + "eventtype" text DEFAULT 'Umsetzung', + "project" bigint, + "resources" jsonb DEFAULT '[]'::jsonb, + "notes" text, + "link" text, + "profiles" jsonb DEFAULT '[]'::jsonb NOT NULL, + "archived" boolean DEFAULT false NOT NULL, + "vehicles" jsonb DEFAULT '[]'::jsonb NOT NULL, + "inventoryitems" jsonb DEFAULT '[]'::jsonb NOT NULL, + "inventoryitemgroups" jsonb DEFAULT '[]'::jsonb NOT NULL, + "customer" bigint, + "vendor" bigint, + "updated_at" timestamp with time zone, + "updated_by" uuid +); +--> statement-breakpoint +CREATE TABLE "files" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "path" text, + "tenant" bigint NOT NULL, + "project" bigint, + "customer" bigint, + "contract" bigint, + "vendor" bigint, + "incominginvoice" bigint, + "plant" bigint, + "createddocument" bigint, + "vehicle" bigint, + "product" bigint, + "check" uuid, + "inventoryitem" bigint, + "folder" uuid, + "mimeType" text, + "archived" boolean DEFAULT false NOT NULL, + "space" bigint, + "type" uuid, + "documentbox" uuid, + "name" text, + "updated_at" timestamp with time zone, + "updated_by" uuid, + "created_by" uuid, + "auth_profile" uuid +); +--> statement-breakpoint +CREATE TABLE "filetags" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "name" text NOT NULL, + "color" text, + "tenant" bigint NOT NULL, + "createddocumenttype" text DEFAULT '', + "incomingDocumentType" text, + "archived" boolean DEFAULT false NOT NULL +); +--> statement-breakpoint +CREATE TABLE "folders" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "tenant" bigint NOT NULL, + "name" text NOT NULL, + "icon" text, + "parent" uuid, + "isSystemUsed" boolean DEFAULT false NOT NULL, + "function" "folderfunctions", + "year" integer, + "standardFiletype" uuid, + "standardFiletypeIsOptional" boolean DEFAULT true NOT NULL, + "updated_at" timestamp with time zone, + "updated_by" uuid, + "archived" boolean DEFAULT false NOT NULL +); +--> statement-breakpoint +CREATE TABLE "exports" ( + "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "exports_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1), + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "tenant_id" bigint NOT NULL, + "start_date" timestamp with time zone NOT NULL, + "end_date" timestamp with time zone NOT NULL, + "valid_until" timestamp with time zone, + "type" text DEFAULT 'datev' NOT NULL, + "url" text NOT NULL, + "file_path" text +); +--> statement-breakpoint +CREATE TABLE "globalmessages" ( + "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "globalmessages_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1), + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "title" text, + "description" text +); +--> statement-breakpoint +CREATE TABLE "globalmessagesseen" ( + "message" bigint NOT NULL, + "seen_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "helpdesk_channel_instances" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "tenant_id" bigint NOT NULL, + "type_id" text NOT NULL, + "name" text NOT NULL, + "is_active" boolean DEFAULT true NOT NULL, + "config" jsonb NOT NULL, + "public_config" jsonb DEFAULT '{}'::jsonb NOT NULL, + "public_token" text, + "secret_token" text, + "created_by" uuid, + "created_at" timestamp with time zone DEFAULT now(), + CONSTRAINT "helpdesk_channel_instances_public_token_unique" UNIQUE("public_token") +); +--> statement-breakpoint +CREATE TABLE "helpdesk_channel_types" ( + "id" text PRIMARY KEY NOT NULL, + "description" text NOT NULL +); +--> statement-breakpoint +CREATE TABLE "helpdesk_contacts" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "tenant_id" bigint NOT NULL, + "customer_id" bigint, + "email" text, + "phone" text, + "external_ref" jsonb, + "display_name" text, + "created_at" timestamp with time zone DEFAULT now(), + "source_channel_id" uuid, + "contact_id" bigint +); +--> statement-breakpoint +CREATE TABLE "helpdesk_conversation_participants" ( + "conversation_id" uuid NOT NULL, + "user_id" uuid NOT NULL, + "role" text +); +--> statement-breakpoint +CREATE TABLE "helpdesk_conversations" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "tenant_id" bigint NOT NULL, + "channel_instance_id" uuid NOT NULL, + "contact_id" uuid, + "subject" text, + "status" text DEFAULT 'open' NOT NULL, + "priority" text DEFAULT 'normal', + "assignee_user_id" uuid, + "last_message_at" timestamp with time zone, + "created_at" timestamp with time zone DEFAULT now(), + "customer_id" bigint, + "contact_person_id" bigint, + "ticket_number" text +); +--> statement-breakpoint +CREATE TABLE "helpdesk_messages" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "tenant_id" bigint NOT NULL, + "conversation_id" uuid NOT NULL, + "direction" text NOT NULL, + "author_user_id" uuid, + "payload" jsonb NOT NULL, + "raw_meta" jsonb, + "created_at" timestamp with time zone DEFAULT now(), + "contact_id" uuid, + "external_message_id" text, + "received_at" timestamp with time zone DEFAULT now(), + CONSTRAINT "helpdesk_messages_external_message_id_unique" UNIQUE("external_message_id") +); +--> statement-breakpoint +CREATE TABLE "helpdesk_routing_rules" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "tenant_id" bigint NOT NULL, + "name" text NOT NULL, + "condition" jsonb NOT NULL, + "action" jsonb NOT NULL, + "created_by" uuid, + "created_at" timestamp with time zone DEFAULT now() +); +--> statement-breakpoint +CREATE TABLE "historyitems" ( + "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "historyitems_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1), + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "text" text NOT NULL, + "customer" bigint, + "tenant" bigint NOT NULL, + "vendor" bigint, + "project" bigint, + "plant" bigint, + "incomingInvoice" bigint, + "contact" bigint, + "inventoryitem" bigint, + "product" bigint, + "event" bigint, + "newVal" text, + "oldVal" text, + "task" bigint, + "vehicle" bigint, + "bankstatement" bigint, + "space" bigint, + "config" jsonb, + "projecttype" bigint, + "check" uuid, + "service" bigint, + "createddocument" bigint, + "file" uuid, + "inventoryitemgroup" uuid, + "source" text DEFAULT 'Software', + "costcentre" uuid, + "ownaccount" uuid, + "documentbox" uuid, + "hourrate" uuid, + "created_by" uuid, + "action" text +); +--> statement-breakpoint +CREATE TABLE "holidays" ( + "id" bigint PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "holidays_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1), + "date" date NOT NULL, + "name" text NOT NULL, + "state_code" text NOT NULL, + "created_at" timestamp with time zone DEFAULT now() +); +--> statement-breakpoint +CREATE TABLE "hourrates" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "tenant" bigint NOT NULL, + "name" text NOT NULL, + "purchasePrice" double precision NOT NULL, + "sellingPrice" double precision NOT NULL, + "archived" boolean DEFAULT false NOT NULL, + "updated_at" timestamp with time zone, + "updated_by" uuid +); +--> statement-breakpoint +CREATE TABLE "incominginvoices" ( + "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "incominginvoices_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1), + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "tenant" bigint NOT NULL, + "state" text DEFAULT 'Entwurf' NOT NULL, + "vendor" bigint, + "reference" text, + "date" text, + "document" bigint, + "dueDate" text, + "description" text, + "paymentType" text, + "accounts" jsonb DEFAULT '[{"account":null,"taxType":null,"amountNet":null,"amountTax":19,"costCentre":null}]'::jsonb NOT NULL, + "paid" boolean DEFAULT false NOT NULL, + "expense" boolean DEFAULT true NOT NULL, + "updated_at" timestamp with time zone, + "updated_by" uuid, + "archived" boolean DEFAULT false NOT NULL +); +--> statement-breakpoint +CREATE TABLE "inventoryitemgroups" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "tenant" bigint NOT NULL, + "name" text NOT NULL, + "inventoryitems" jsonb DEFAULT '[]'::jsonb NOT NULL, + "description" text, + "archived" boolean DEFAULT false NOT NULL, + "profiles" jsonb DEFAULT '[]'::jsonb NOT NULL, + "usePlanning" boolean DEFAULT false NOT NULL, + "updated_at" timestamp with time zone, + "updated_by" uuid +); +--> statement-breakpoint +CREATE TABLE "inventoryitems" ( + "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "inventoryitems_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1), + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "name" text NOT NULL, + "usePlanning" boolean DEFAULT false NOT NULL, + "description" text, + "tenant" bigint NOT NULL, + "currentSpace" bigint, + "articleNumber" text, + "serialNumber" text, + "purchaseDate" date, + "vendor" bigint, + "quantity" bigint DEFAULT 0 NOT NULL, + "purchasePrice" double precision DEFAULT 0, + "manufacturer" text, + "manufacturerNumber" text, + "currentValue" double precision, + "archived" boolean DEFAULT false NOT NULL, + "profiles" jsonb DEFAULT '[]'::jsonb NOT NULL, + "updated_at" timestamp with time zone, + "updated_by" uuid +); +--> statement-breakpoint +CREATE TABLE "letterheads" ( + "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "letterheads_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1), + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "tenant" bigint NOT NULL, + "name" text DEFAULT 'Standard', + "path" text NOT NULL, + "documentTypes" text[] DEFAULT '{}' NOT NULL, + "updated_at" timestamp with time zone, + "updated_by" uuid, + "archived" boolean DEFAULT false NOT NULL +); +--> statement-breakpoint +CREATE TABLE "movements" ( + "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "movements_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1), + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "quantity" bigint NOT NULL, + "productId" bigint NOT NULL, + "spaceId" bigint, + "tenant" bigint NOT NULL, + "projectId" bigint, + "notes" text, + "serials" text[], + "updated_at" timestamp with time zone, + "updated_by" uuid +); +--> statement-breakpoint +CREATE TABLE "notifications_event_types" ( + "event_key" text PRIMARY KEY NOT NULL, + "display_name" text NOT NULL, + "description" text, + "category" text, + "severity" "notification_severity" DEFAULT 'info' NOT NULL, + "allowed_channels" jsonb DEFAULT '["inapp","email"]'::jsonb NOT NULL, + "payload_schema" jsonb, + "is_active" boolean DEFAULT true NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "notifications_items" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "tenant_id" bigint NOT NULL, + "user_id" uuid NOT NULL, + "event_type" text NOT NULL, + "title" text NOT NULL, + "message" text NOT NULL, + "payload" jsonb, + "channel" "notification_channel" NOT NULL, + "status" "notification_status" DEFAULT 'queued' NOT NULL, + "error" text, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "sent_at" timestamp with time zone, + "read_at" timestamp with time zone +); +--> statement-breakpoint +CREATE TABLE "notifications_preferences" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "tenant_id" bigint NOT NULL, + "user_id" uuid NOT NULL, + "event_type" text NOT NULL, + "channel" "notification_channel" NOT NULL, + "enabled" boolean DEFAULT true NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "notifications_preferences_defaults" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "tenant_id" bigint NOT NULL, + "event_key" text NOT NULL, + "channel" "notification_channel" NOT NULL, + "enabled" boolean DEFAULT true NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "ownaccounts" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "tenant" bigint NOT NULL, + "number" text NOT NULL, + "name" text NOT NULL, + "description" text, + "archived" boolean DEFAULT false NOT NULL, + "profiles" jsonb DEFAULT '[]'::jsonb NOT NULL, + "updated_at" timestamp with time zone, + "updated_by" uuid +); +--> statement-breakpoint +CREATE TABLE "plants" ( + "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "plants_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1), + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "tenant" bigint NOT NULL, + "name" text NOT NULL, + "customer" bigint, + "infoData" jsonb, + "contract" bigint, + "description" jsonb DEFAULT '{"html":"","json":[],"text":""}'::jsonb, + "archived" boolean DEFAULT false NOT NULL, + "profiles" jsonb DEFAULT '[]'::jsonb NOT NULL, + "updated_at" timestamp with time zone, + "updated_by" uuid +); +--> statement-breakpoint +CREATE TABLE "productcategories" ( + "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "productcategories_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1), + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "tenant" bigint NOT NULL, + "name" text NOT NULL, + "description" text, + "archived" boolean DEFAULT false NOT NULL, + "updated_at" timestamp with time zone, + "updated_by" uuid +); +--> statement-breakpoint +CREATE TABLE "products" ( + "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "products_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1), + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "name" text NOT NULL, + "manufacturer" text, + "unit" bigint NOT NULL, + "tags" json DEFAULT '[]'::json NOT NULL, + "tenant" bigint NOT NULL, + "ean" text, + "barcode" text, + "purchasePrice" double precision, + "sellingPrice" double precision, + "description" text, + "manufacturerNumber" text, + "vendorAllocation" jsonb DEFAULT '[]'::jsonb, + "articleNumber" text, + "barcodes" text[] DEFAULT '{}' NOT NULL, + "productcategories" jsonb DEFAULT '[]'::jsonb, + "archived" boolean DEFAULT false NOT NULL, + "taxPercentage" smallint DEFAULT 19 NOT NULL, + "markupPercentage" double precision, + "updated_at" timestamp with time zone, + "updated_by" uuid +); +--> statement-breakpoint +CREATE TABLE "projects" ( + "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "projects_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1), + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "tenant" bigint NOT NULL, + "name" text NOT NULL, + "notes" text, + "customer" bigint, + "phases" jsonb DEFAULT '[]'::jsonb, + "description" json, + "forms" jsonb DEFAULT '[]'::jsonb, + "heroId" text, + "measure" text, + "material" jsonb, + "plant" bigint, + "profiles" uuid[] DEFAULT '{}' NOT NULL, + "projectNumber" text, + "contract" bigint, + "projectType" text DEFAULT 'Projekt', + "projecttype" bigint, + "archived" boolean DEFAULT false NOT NULL, + "customerRef" text, + "updated_at" timestamp with time zone, + "updated_by" uuid, + "active_phase" text +); +--> statement-breakpoint +CREATE TABLE "projecttypes" ( + "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "projecttypes_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1), + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "name" text NOT NULL, + "initialPhases" jsonb, + "addablePhases" jsonb, + "icon" text, + "tenant" bigint NOT NULL, + "archived" boolean DEFAULT false NOT NULL, + "updated_at" timestamp with time zone, + "updated_by" uuid +); +--> statement-breakpoint +CREATE TABLE "servicecategories" ( + "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "servicecategories_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1), + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "tenant" bigint NOT NULL, + "name" text NOT NULL, + "description" text, + "discount" double precision DEFAULT 0, + "archived" boolean DEFAULT false NOT NULL, + "updated_at" timestamp with time zone, + "updated_by" uuid +); +--> statement-breakpoint +CREATE TABLE "services" ( + "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "services_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1), + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "name" text NOT NULL, + "sellingPrice" double precision, + "description" text, + "tenant" bigint NOT NULL, + "unit" bigint, + "serviceNumber" bigint, + "tags" jsonb DEFAULT '[]'::jsonb, + "servicecategories" jsonb DEFAULT '[]'::jsonb NOT NULL, + "archived" boolean DEFAULT false NOT NULL, + "purchasePriceComposed" jsonb DEFAULT '{"total":0}'::jsonb NOT NULL, + "sellingPriceComposed" jsonb DEFAULT '{"total":0}'::jsonb NOT NULL, + "taxPercentage" smallint DEFAULT 19 NOT NULL, + "materialComposition" jsonb DEFAULT '[]'::jsonb NOT NULL, + "personalComposition" jsonb DEFAULT '[]'::jsonb NOT NULL, + "updated_at" timestamp with time zone, + "updated_by" uuid +); +--> statement-breakpoint +CREATE TABLE "spaces" ( + "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "spaces_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1), + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "name" text, + "type" text NOT NULL, + "tenant" bigint NOT NULL, + "spaceNumber" text NOT NULL, + "parentSpace" bigint, + "infoData" jsonb DEFAULT '{"zip":"","city":"","streetNumber":""}'::jsonb NOT NULL, + "description" text, + "archived" boolean DEFAULT false NOT NULL, + "updated_at" timestamp with time zone, + "updated_by" uuid +); +--> statement-breakpoint +CREATE TABLE "staff_time_entries" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "tenant_id" bigint NOT NULL, + "user_id" uuid NOT NULL, + "started_at" timestamp with time zone NOT NULL, + "stopped_at" timestamp with time zone, + "duration_minutes" integer GENERATED ALWAYS AS (CASE + WHEN stopped_at IS NOT NULL + THEN (EXTRACT(epoch FROM (stopped_at - started_at)) / 60) + ELSE NULL + END) STORED, + "type" text DEFAULT 'work', + "description" text, + "created_at" timestamp with time zone DEFAULT now(), + "updated_at" timestamp with time zone DEFAULT now(), + "archived" boolean DEFAULT false NOT NULL, + "updated_by" uuid, + "source" text, + "state" times_state DEFAULT 'draft' NOT NULL, + "device" uuid, + "internal_note" text, + "vacation_reason" text, + "vacation_days" numeric(5, 2), + "approved_by" uuid, + "approved_at" timestamp with time zone, + "sick_reason" text +); +--> statement-breakpoint +CREATE TABLE "staff_time_entry_connects" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "time_entry_id" uuid NOT NULL, + "project_id" bigint, + "started_at" timestamp with time zone NOT NULL, + "stopped_at" timestamp with time zone NOT NULL, + "duration_minutes" integer GENERATED ALWAYS AS ((EXTRACT(epoch FROM (stopped_at - started_at)) / 60)) STORED, + "notes" text, + "created_at" timestamp with time zone DEFAULT now(), + "updated_at" timestamp with time zone DEFAULT now() +); +--> statement-breakpoint +CREATE TABLE "staff_zeitstromtimestamps" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "tenant" bigint NOT NULL, + "profile" uuid NOT NULL, + "key" text NOT NULL, + "intent" text NOT NULL, + "time" timestamp with time zone NOT NULL, + "staff_time_entry" uuid, + "internal_note" text +); +--> statement-breakpoint +CREATE TABLE "statementallocations" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "bs_id" integer NOT NULL, + "cd_id" integer, + "amount" double precision DEFAULT 0 NOT NULL, + "ii_id" bigint, + "tenant" bigint NOT NULL, + "account" bigint, + "created_at" timestamp DEFAULT now(), + "ownaccount" uuid, + "description" text, + "customer" bigint, + "vendor" bigint, + "updated_at" timestamp with time zone, + "updated_by" uuid, + "archived" boolean DEFAULT false NOT NULL +); +--> statement-breakpoint +CREATE TABLE "tasks" ( + "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "tasks_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1), + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "name" text NOT NULL, + "description" text, + "categorie" text, + "tenant" bigint NOT NULL, + "user_id" uuid, + "project" bigint, + "plant" bigint, + "customer" bigint, + "profiles" jsonb DEFAULT '[]'::jsonb NOT NULL, + "archived" boolean DEFAULT false NOT NULL, + "updated_at" timestamp with time zone, + "updated_by" uuid +); +--> statement-breakpoint +CREATE TABLE "taxtypes" ( + "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "taxtypes_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1), + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "label" text NOT NULL, + "percentage" bigint NOT NULL, + "updated_at" timestamp with time zone, + "updated_by" uuid +); +--> statement-breakpoint +CREATE TABLE "tenants" ( + "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "tenants_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1), + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "name" text NOT NULL, + "short" text NOT NULL, + "calendarConfig" jsonb DEFAULT '{"eventTypes":[{"color":"blue","label":"Büro"},{"color":"yellow","label":"Besprechung"},{"color":"green","label":"Umsetzung"},{"color":"red","label":"Vor Ort Termin"}]}'::jsonb, + "timeConfig" jsonb DEFAULT '{}'::jsonb NOT NULL, + "tags" jsonb DEFAULT '{"products":[],"documents":[]}'::jsonb NOT NULL, + "measures" jsonb DEFAULT '[{"name":"Netzwerktechnik","short":"NWT"},{"name":"Elektrotechnik","short":"ELT"},{"name":"Photovoltaik","short":"PV"},{"name":"Videüberwachung","short":"VÜA"},{"name":"Projekt","short":"PRJ"},{"name":"Smart Home","short":"SHO"}]'::jsonb NOT NULL, + "businessInfo" jsonb DEFAULT '{"zip":"","city":"","name":"","street":""}'::jsonb, + "features" jsonb DEFAULT '{"objects":true,"calendar":true,"contacts":true,"projects":true,"vehicles":true,"contracts":true,"inventory":true,"accounting":true,"timeTracking":true,"planningBoard":true,"workingTimeTracking":true}'::jsonb, + "ownFields" jsonb, + "numberRanges" jsonb DEFAULT '{"vendors":{"prefix":"","suffix":"","nextNumber":10000},"customers":{"prefix":"","suffix":"","nextNumber":10000},"products":{"prefix":"AT-","suffix":"","nextNumber":1000},"quotes":{"prefix":"AN-","suffix":"","nextNumber":1000},"confirmationOrders":{"prefix":"AB-","suffix":"","nextNumber":1000},"invoices":{"prefix":"RE-","suffix":"","nextNumber":1000},"spaces":{"prefix":"LP-","suffix":"","nextNumber":1000},"inventoryitems":{"prefix":"IA-","suffix":"","nextNumber":1000},"projects":{"prefix":"PRJ-","suffix":"","nextNumber":1000},"costcentres":{"prefix":"KST-","suffix":"","nextNumber":1000}}'::jsonb NOT NULL, + "standardEmailForInvoices" text, + "extraModules" jsonb DEFAULT '[]'::jsonb NOT NULL, + "isInTrial" boolean DEFAULT false, + "trialEndDate" date, + "stripeCustomerId" text, + "hasActiveLicense" boolean DEFAULT false NOT NULL, + "userLicenseCount" integer DEFAULT 0 NOT NULL, + "workstationLicenseCount" integer DEFAULT 0 NOT NULL, + "standardPaymentDays" smallint DEFAULT 14 NOT NULL, + "dokuboxEmailAddresses" jsonb DEFAULT '[]'::jsonb, + "dokuboxkey" uuid DEFAULT gen_random_uuid() NOT NULL, + "autoPrepareIncomingInvoices" boolean DEFAULT true, + "portalDomain" text, + "portalConfig" jsonb DEFAULT '{"primayColor":"#69c350"}'::jsonb NOT NULL, + "updated_at" timestamp with time zone, + "updated_by" uuid, + "locked" "locked_tenant" +); +--> statement-breakpoint +CREATE TABLE "texttemplates" ( + "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "texttemplates_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1), + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "tenant" bigint NOT NULL, + "name" text NOT NULL, + "text" text NOT NULL, + "documentType" text DEFAULT '', + "default" boolean DEFAULT false NOT NULL, + "pos" texttemplatepositions NOT NULL, + "archived" boolean DEFAULT false NOT NULL, + "updated_at" timestamp with time zone, + "updated_by" uuid +); +--> statement-breakpoint +CREATE TABLE "units" ( + "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "units_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1), + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "name" text NOT NULL, + "single" text NOT NULL, + "multiple" text, + "short" text, + "step" text DEFAULT '1' NOT NULL +); +--> statement-breakpoint +CREATE TABLE "user_credentials" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "user_id" uuid NOT NULL, + "updated_at" timestamp with time zone, + "tenant_id" bigint NOT NULL, + "smtp_port" numeric, + "smtp_ssl" boolean, + "type" "credential_types" NOT NULL, + "imap_port" numeric, + "imap_ssl" boolean, + "email_encrypted" jsonb, + "password_encrypted" jsonb, + "smtp_host_encrypted" jsonb, + "imap_host_encrypted" jsonb, + "access_token_encrypted" jsonb, + "refresh_token_encrypted" jsonb +); +--> statement-breakpoint +CREATE TABLE "vehicles" ( + "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "vehicles_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1), + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "tenant" bigint NOT NULL, + "licensePlate" text, + "name" text, + "type" text, + "active" boolean DEFAULT true, + "driver" uuid, + "vin" text, + "tankSize" double precision DEFAULT 0 NOT NULL, + "archived" boolean DEFAULT false NOT NULL, + "buildYear" text, + "towingCapacity" bigint, + "powerInKW" bigint, + "color" text, + "profiles" jsonb DEFAULT '[]'::jsonb NOT NULL, + "updated_at" timestamp with time zone, + "updated_by" uuid +); +--> statement-breakpoint +CREATE TABLE "vendors" ( + "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "vendors_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1), + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "name" text NOT NULL, + "vendorNumber" text NOT NULL, + "tenant" bigint NOT NULL, + "infoData" jsonb DEFAULT '{}'::jsonb NOT NULL, + "notes" text, + "hasSEPA" boolean DEFAULT false NOT NULL, + "profiles" jsonb DEFAULT '[]'::jsonb NOT NULL, + "archived" boolean DEFAULT false NOT NULL, + "defaultPaymentMethod" text, + "updated_at" timestamp with time zone, + "updated_by" uuid +); +--> statement-breakpoint +ALTER TABLE "auth_profiles" ADD CONSTRAINT "auth_profiles_user_id_auth_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "auth_role_permissions" ADD CONSTRAINT "auth_role_permissions_role_id_auth_roles_id_fk" FOREIGN KEY ("role_id") REFERENCES "public"."auth_roles"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "auth_roles" ADD CONSTRAINT "auth_roles_created_by_auth_users_id_fk" FOREIGN KEY ("created_by") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "auth_tenant_users" ADD CONSTRAINT "auth_tenant_users_created_by_auth_users_id_fk" FOREIGN KEY ("created_by") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "auth_user_roles" ADD CONSTRAINT "auth_user_roles_user_id_auth_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "auth_user_roles" ADD CONSTRAINT "auth_user_roles_role_id_auth_roles_id_fk" FOREIGN KEY ("role_id") REFERENCES "public"."auth_roles"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "auth_user_roles" ADD CONSTRAINT "auth_user_roles_created_by_auth_users_id_fk" FOREIGN KEY ("created_by") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "bankaccounts" ADD CONSTRAINT "bankaccounts_tenant_tenants_id_fk" FOREIGN KEY ("tenant") REFERENCES "public"."tenants"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "bankaccounts" ADD CONSTRAINT "bankaccounts_updated_by_auth_users_id_fk" FOREIGN KEY ("updated_by") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "bankrequisitions" ADD CONSTRAINT "bankrequisitions_tenant_tenants_id_fk" FOREIGN KEY ("tenant") REFERENCES "public"."tenants"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "bankrequisitions" ADD CONSTRAINT "bankrequisitions_updated_by_auth_users_id_fk" FOREIGN KEY ("updated_by") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "bankstatements" ADD CONSTRAINT "bankstatements_account_bankaccounts_id_fk" FOREIGN KEY ("account") REFERENCES "public"."bankaccounts"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "bankstatements" ADD CONSTRAINT "bankstatements_tenant_tenants_id_fk" FOREIGN KEY ("tenant") REFERENCES "public"."tenants"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "bankstatements" ADD CONSTRAINT "bankstatements_incomingInvoice_incominginvoices_id_fk" FOREIGN KEY ("incomingInvoice") REFERENCES "public"."incominginvoices"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "bankstatements" ADD CONSTRAINT "bankstatements_contract_contracts_id_fk" FOREIGN KEY ("contract") REFERENCES "public"."contracts"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "bankstatements" ADD CONSTRAINT "bankstatements_createdDocument_createddocuments_id_fk" FOREIGN KEY ("createdDocument") REFERENCES "public"."createddocuments"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "bankstatements" ADD CONSTRAINT "bankstatements_updated_by_auth_users_id_fk" FOREIGN KEY ("updated_by") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "checkexecutions" ADD CONSTRAINT "checkexecutions_check_checks_id_fk" FOREIGN KEY ("check") REFERENCES "public"."checks"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "checks" ADD CONSTRAINT "checks_vehicle_vehicles_id_fk" FOREIGN KEY ("vehicle") REFERENCES "public"."vehicles"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "checks" ADD CONSTRAINT "checks_inventoryitem_inventoryitems_id_fk" FOREIGN KEY ("inventoryitem") REFERENCES "public"."inventoryitems"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "checks" ADD CONSTRAINT "checks_tenant_tenants_id_fk" FOREIGN KEY ("tenant") REFERENCES "public"."tenants"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "checks" ADD CONSTRAINT "checks_updated_by_auth_users_id_fk" FOREIGN KEY ("updated_by") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "contacts" ADD CONSTRAINT "contacts_customer_customers_id_fk" FOREIGN KEY ("customer") REFERENCES "public"."customers"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "contacts" ADD CONSTRAINT "contacts_updated_by_auth_users_id_fk" FOREIGN KEY ("updated_by") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "contracts" ADD CONSTRAINT "contracts_customer_customers_id_fk" FOREIGN KEY ("customer") REFERENCES "public"."customers"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "contracts" ADD CONSTRAINT "contracts_contact_contacts_id_fk" FOREIGN KEY ("contact") REFERENCES "public"."contacts"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "contracts" ADD CONSTRAINT "contracts_updated_by_auth_users_id_fk" FOREIGN KEY ("updated_by") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "costcentres" ADD CONSTRAINT "costcentres_tenant_tenants_id_fk" FOREIGN KEY ("tenant") REFERENCES "public"."tenants"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "costcentres" ADD CONSTRAINT "costcentres_vehicle_vehicles_id_fk" FOREIGN KEY ("vehicle") REFERENCES "public"."vehicles"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "costcentres" ADD CONSTRAINT "costcentres_project_projects_id_fk" FOREIGN KEY ("project") REFERENCES "public"."projects"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "costcentres" ADD CONSTRAINT "costcentres_inventoryitem_inventoryitems_id_fk" FOREIGN KEY ("inventoryitem") REFERENCES "public"."inventoryitems"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "costcentres" ADD CONSTRAINT "costcentres_updated_by_auth_users_id_fk" FOREIGN KEY ("updated_by") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "createddocuments" ADD CONSTRAINT "createddocuments_tenant_tenants_id_fk" FOREIGN KEY ("tenant") REFERENCES "public"."tenants"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "createddocuments" ADD CONSTRAINT "createddocuments_customer_customers_id_fk" FOREIGN KEY ("customer") REFERENCES "public"."customers"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "createddocuments" ADD CONSTRAINT "createddocuments_contact_contacts_id_fk" FOREIGN KEY ("contact") REFERENCES "public"."contacts"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "createddocuments" ADD CONSTRAINT "createddocuments_project_projects_id_fk" FOREIGN KEY ("project") REFERENCES "public"."projects"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "createddocuments" ADD CONSTRAINT "createddocuments_createdBy_auth_users_id_fk" FOREIGN KEY ("createdBy") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "createddocuments" ADD CONSTRAINT "createddocuments_linkedDocument_createddocuments_id_fk" FOREIGN KEY ("linkedDocument") REFERENCES "public"."createddocuments"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "createddocuments" ADD CONSTRAINT "createddocuments_letterhead_letterheads_id_fk" FOREIGN KEY ("letterhead") REFERENCES "public"."letterheads"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "createddocuments" ADD CONSTRAINT "createddocuments_plant_plants_id_fk" FOREIGN KEY ("plant") REFERENCES "public"."plants"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "createddocuments" ADD CONSTRAINT "createddocuments_updated_by_auth_users_id_fk" FOREIGN KEY ("updated_by") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "createddocuments" ADD CONSTRAINT "createddocuments_created_by_auth_users_id_fk" FOREIGN KEY ("created_by") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "createddocuments" ADD CONSTRAINT "createddocuments_contract_contracts_id_fk" FOREIGN KEY ("contract") REFERENCES "public"."contracts"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "createdletters" ADD CONSTRAINT "createdletters_tenant_tenants_id_fk" FOREIGN KEY ("tenant") REFERENCES "public"."tenants"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "createdletters" ADD CONSTRAINT "createdletters_customer_customers_id_fk" FOREIGN KEY ("customer") REFERENCES "public"."customers"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "createdletters" ADD CONSTRAINT "createdletters_vendor_vendors_id_fk" FOREIGN KEY ("vendor") REFERENCES "public"."vendors"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "createdletters" ADD CONSTRAINT "createdletters_updated_by_auth_users_id_fk" FOREIGN KEY ("updated_by") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "customers" ADD CONSTRAINT "customers_updated_by_auth_users_id_fk" FOREIGN KEY ("updated_by") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "devices" ADD CONSTRAINT "devices_tenant_tenants_id_fk" FOREIGN KEY ("tenant") REFERENCES "public"."tenants"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "documentboxes" ADD CONSTRAINT "documentboxes_space_spaces_id_fk" FOREIGN KEY ("space") REFERENCES "public"."spaces"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "documentboxes" ADD CONSTRAINT "documentboxes_tenant_tenants_id_fk" FOREIGN KEY ("tenant") REFERENCES "public"."tenants"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "documentboxes" ADD CONSTRAINT "documentboxes_updated_by_auth_users_id_fk" FOREIGN KEY ("updated_by") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "events" ADD CONSTRAINT "events_customer_customers_id_fk" FOREIGN KEY ("customer") REFERENCES "public"."customers"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "events" ADD CONSTRAINT "events_updated_by_auth_users_id_fk" FOREIGN KEY ("updated_by") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "files" ADD CONSTRAINT "files_tenant_tenants_id_fk" FOREIGN KEY ("tenant") REFERENCES "public"."tenants"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "files" ADD CONSTRAINT "files_project_projects_id_fk" FOREIGN KEY ("project") REFERENCES "public"."projects"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "files" ADD CONSTRAINT "files_customer_customers_id_fk" FOREIGN KEY ("customer") REFERENCES "public"."customers"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "files" ADD CONSTRAINT "files_contract_contracts_id_fk" FOREIGN KEY ("contract") REFERENCES "public"."contracts"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "files" ADD CONSTRAINT "files_vendor_vendors_id_fk" FOREIGN KEY ("vendor") REFERENCES "public"."vendors"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "files" ADD CONSTRAINT "files_incominginvoice_incominginvoices_id_fk" FOREIGN KEY ("incominginvoice") REFERENCES "public"."incominginvoices"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "files" ADD CONSTRAINT "files_plant_plants_id_fk" FOREIGN KEY ("plant") REFERENCES "public"."plants"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "files" ADD CONSTRAINT "files_createddocument_createddocuments_id_fk" FOREIGN KEY ("createddocument") REFERENCES "public"."createddocuments"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "files" ADD CONSTRAINT "files_vehicle_vehicles_id_fk" FOREIGN KEY ("vehicle") REFERENCES "public"."vehicles"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "files" ADD CONSTRAINT "files_product_products_id_fk" FOREIGN KEY ("product") REFERENCES "public"."products"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "files" ADD CONSTRAINT "files_check_checks_id_fk" FOREIGN KEY ("check") REFERENCES "public"."checks"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "files" ADD CONSTRAINT "files_inventoryitem_inventoryitems_id_fk" FOREIGN KEY ("inventoryitem") REFERENCES "public"."inventoryitems"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "files" ADD CONSTRAINT "files_folder_folders_id_fk" FOREIGN KEY ("folder") REFERENCES "public"."folders"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "files" ADD CONSTRAINT "files_space_spaces_id_fk" FOREIGN KEY ("space") REFERENCES "public"."spaces"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "files" ADD CONSTRAINT "files_type_filetags_id_fk" FOREIGN KEY ("type") REFERENCES "public"."filetags"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "files" ADD CONSTRAINT "files_documentbox_documentboxes_id_fk" FOREIGN KEY ("documentbox") REFERENCES "public"."documentboxes"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "files" ADD CONSTRAINT "files_updated_by_auth_users_id_fk" FOREIGN KEY ("updated_by") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "files" ADD CONSTRAINT "files_created_by_auth_users_id_fk" FOREIGN KEY ("created_by") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "files" ADD CONSTRAINT "files_auth_profile_auth_profiles_id_fk" FOREIGN KEY ("auth_profile") REFERENCES "public"."auth_profiles"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "filetags" ADD CONSTRAINT "filetags_tenant_tenants_id_fk" FOREIGN KEY ("tenant") REFERENCES "public"."tenants"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "folders" ADD CONSTRAINT "folders_tenant_tenants_id_fk" FOREIGN KEY ("tenant") REFERENCES "public"."tenants"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "folders" ADD CONSTRAINT "folders_parent_folders_id_fk" FOREIGN KEY ("parent") REFERENCES "public"."folders"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "folders" ADD CONSTRAINT "folders_standardFiletype_filetags_id_fk" FOREIGN KEY ("standardFiletype") REFERENCES "public"."filetags"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "folders" ADD CONSTRAINT "folders_updated_by_auth_users_id_fk" FOREIGN KEY ("updated_by") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "exports" ADD CONSTRAINT "exports_tenant_id_tenants_id_fk" FOREIGN KEY ("tenant_id") REFERENCES "public"."tenants"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "globalmessagesseen" ADD CONSTRAINT "globalmessagesseen_message_globalmessages_id_fk" FOREIGN KEY ("message") REFERENCES "public"."globalmessages"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "helpdesk_channel_instances" ADD CONSTRAINT "helpdesk_channel_instances_tenant_id_tenants_id_fk" FOREIGN KEY ("tenant_id") REFERENCES "public"."tenants"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "helpdesk_channel_instances" ADD CONSTRAINT "helpdesk_channel_instances_type_id_helpdesk_channel_types_id_fk" FOREIGN KEY ("type_id") REFERENCES "public"."helpdesk_channel_types"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "helpdesk_channel_instances" ADD CONSTRAINT "helpdesk_channel_instances_created_by_auth_users_id_fk" FOREIGN KEY ("created_by") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "helpdesk_contacts" ADD CONSTRAINT "helpdesk_contacts_tenant_id_tenants_id_fk" FOREIGN KEY ("tenant_id") REFERENCES "public"."tenants"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "helpdesk_contacts" ADD CONSTRAINT "helpdesk_contacts_customer_id_customers_id_fk" FOREIGN KEY ("customer_id") REFERENCES "public"."customers"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "helpdesk_contacts" ADD CONSTRAINT "helpdesk_contacts_source_channel_id_helpdesk_channel_instances_id_fk" FOREIGN KEY ("source_channel_id") REFERENCES "public"."helpdesk_channel_instances"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "helpdesk_contacts" ADD CONSTRAINT "helpdesk_contacts_contact_id_contacts_id_fk" FOREIGN KEY ("contact_id") REFERENCES "public"."contacts"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "helpdesk_conversation_participants" ADD CONSTRAINT "helpdesk_conversation_participants_conversation_id_helpdesk_conversations_id_fk" FOREIGN KEY ("conversation_id") REFERENCES "public"."helpdesk_conversations"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "helpdesk_conversation_participants" ADD CONSTRAINT "helpdesk_conversation_participants_user_id_auth_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."auth_users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "helpdesk_conversations" ADD CONSTRAINT "helpdesk_conversations_tenant_id_tenants_id_fk" FOREIGN KEY ("tenant_id") REFERENCES "public"."tenants"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "helpdesk_conversations" ADD CONSTRAINT "helpdesk_conversations_channel_instance_id_helpdesk_channel_instances_id_fk" FOREIGN KEY ("channel_instance_id") REFERENCES "public"."helpdesk_channel_instances"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "helpdesk_conversations" ADD CONSTRAINT "helpdesk_conversations_contact_id_helpdesk_contacts_id_fk" FOREIGN KEY ("contact_id") REFERENCES "public"."helpdesk_contacts"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "helpdesk_conversations" ADD CONSTRAINT "helpdesk_conversations_assignee_user_id_auth_users_id_fk" FOREIGN KEY ("assignee_user_id") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "helpdesk_conversations" ADD CONSTRAINT "helpdesk_conversations_customer_id_customers_id_fk" FOREIGN KEY ("customer_id") REFERENCES "public"."customers"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "helpdesk_conversations" ADD CONSTRAINT "helpdesk_conversations_contact_person_id_contacts_id_fk" FOREIGN KEY ("contact_person_id") REFERENCES "public"."contacts"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "helpdesk_messages" ADD CONSTRAINT "helpdesk_messages_tenant_id_tenants_id_fk" FOREIGN KEY ("tenant_id") REFERENCES "public"."tenants"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "helpdesk_messages" ADD CONSTRAINT "helpdesk_messages_conversation_id_helpdesk_conversations_id_fk" FOREIGN KEY ("conversation_id") REFERENCES "public"."helpdesk_conversations"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "helpdesk_messages" ADD CONSTRAINT "helpdesk_messages_author_user_id_auth_users_id_fk" FOREIGN KEY ("author_user_id") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "helpdesk_messages" ADD CONSTRAINT "helpdesk_messages_contact_id_helpdesk_contacts_id_fk" FOREIGN KEY ("contact_id") REFERENCES "public"."helpdesk_contacts"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "helpdesk_routing_rules" ADD CONSTRAINT "helpdesk_routing_rules_tenant_id_tenants_id_fk" FOREIGN KEY ("tenant_id") REFERENCES "public"."tenants"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "helpdesk_routing_rules" ADD CONSTRAINT "helpdesk_routing_rules_created_by_auth_users_id_fk" FOREIGN KEY ("created_by") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "historyitems" ADD CONSTRAINT "historyitems_customer_customers_id_fk" FOREIGN KEY ("customer") REFERENCES "public"."customers"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "historyitems" ADD CONSTRAINT "historyitems_tenant_tenants_id_fk" FOREIGN KEY ("tenant") REFERENCES "public"."tenants"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "historyitems" ADD CONSTRAINT "historyitems_vendor_vendors_id_fk" FOREIGN KEY ("vendor") REFERENCES "public"."vendors"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "historyitems" ADD CONSTRAINT "historyitems_project_projects_id_fk" FOREIGN KEY ("project") REFERENCES "public"."projects"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "historyitems" ADD CONSTRAINT "historyitems_plant_plants_id_fk" FOREIGN KEY ("plant") REFERENCES "public"."plants"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "historyitems" ADD CONSTRAINT "historyitems_incomingInvoice_incominginvoices_id_fk" FOREIGN KEY ("incomingInvoice") REFERENCES "public"."incominginvoices"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "historyitems" ADD CONSTRAINT "historyitems_contact_contacts_id_fk" FOREIGN KEY ("contact") REFERENCES "public"."contacts"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "historyitems" ADD CONSTRAINT "historyitems_inventoryitem_inventoryitems_id_fk" FOREIGN KEY ("inventoryitem") REFERENCES "public"."inventoryitems"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "historyitems" ADD CONSTRAINT "historyitems_product_products_id_fk" FOREIGN KEY ("product") REFERENCES "public"."products"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "historyitems" ADD CONSTRAINT "historyitems_event_events_id_fk" FOREIGN KEY ("event") REFERENCES "public"."events"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "historyitems" ADD CONSTRAINT "historyitems_task_tasks_id_fk" FOREIGN KEY ("task") REFERENCES "public"."tasks"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "historyitems" ADD CONSTRAINT "historyitems_vehicle_vehicles_id_fk" FOREIGN KEY ("vehicle") REFERENCES "public"."vehicles"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "historyitems" ADD CONSTRAINT "historyitems_bankstatement_bankstatements_id_fk" FOREIGN KEY ("bankstatement") REFERENCES "public"."bankstatements"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "historyitems" ADD CONSTRAINT "historyitems_space_spaces_id_fk" FOREIGN KEY ("space") REFERENCES "public"."spaces"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "historyitems" ADD CONSTRAINT "historyitems_projecttype_projecttypes_id_fk" FOREIGN KEY ("projecttype") REFERENCES "public"."projecttypes"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "historyitems" ADD CONSTRAINT "historyitems_check_checks_id_fk" FOREIGN KEY ("check") REFERENCES "public"."checks"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "historyitems" ADD CONSTRAINT "historyitems_service_services_id_fk" FOREIGN KEY ("service") REFERENCES "public"."services"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "historyitems" ADD CONSTRAINT "historyitems_createddocument_createddocuments_id_fk" FOREIGN KEY ("createddocument") REFERENCES "public"."createddocuments"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "historyitems" ADD CONSTRAINT "historyitems_file_files_id_fk" FOREIGN KEY ("file") REFERENCES "public"."files"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "historyitems" ADD CONSTRAINT "historyitems_inventoryitemgroup_inventoryitemgroups_id_fk" FOREIGN KEY ("inventoryitemgroup") REFERENCES "public"."inventoryitemgroups"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "historyitems" ADD CONSTRAINT "historyitems_costcentre_costcentres_id_fk" FOREIGN KEY ("costcentre") REFERENCES "public"."costcentres"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "historyitems" ADD CONSTRAINT "historyitems_ownaccount_ownaccounts_id_fk" FOREIGN KEY ("ownaccount") REFERENCES "public"."ownaccounts"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "historyitems" ADD CONSTRAINT "historyitems_documentbox_documentboxes_id_fk" FOREIGN KEY ("documentbox") REFERENCES "public"."documentboxes"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "historyitems" ADD CONSTRAINT "historyitems_hourrate_hourrates_id_fk" FOREIGN KEY ("hourrate") REFERENCES "public"."hourrates"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "historyitems" ADD CONSTRAINT "historyitems_created_by_auth_users_id_fk" FOREIGN KEY ("created_by") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "hourrates" ADD CONSTRAINT "hourrates_tenant_tenants_id_fk" FOREIGN KEY ("tenant") REFERENCES "public"."tenants"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "hourrates" ADD CONSTRAINT "hourrates_updated_by_auth_users_id_fk" FOREIGN KEY ("updated_by") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "incominginvoices" ADD CONSTRAINT "incominginvoices_tenant_tenants_id_fk" FOREIGN KEY ("tenant") REFERENCES "public"."tenants"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "incominginvoices" ADD CONSTRAINT "incominginvoices_vendor_vendors_id_fk" FOREIGN KEY ("vendor") REFERENCES "public"."vendors"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "incominginvoices" ADD CONSTRAINT "incominginvoices_updated_by_auth_users_id_fk" FOREIGN KEY ("updated_by") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "inventoryitemgroups" ADD CONSTRAINT "inventoryitemgroups_tenant_tenants_id_fk" FOREIGN KEY ("tenant") REFERENCES "public"."tenants"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "inventoryitemgroups" ADD CONSTRAINT "inventoryitemgroups_updated_by_auth_users_id_fk" FOREIGN KEY ("updated_by") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "inventoryitems" ADD CONSTRAINT "inventoryitems_tenant_tenants_id_fk" FOREIGN KEY ("tenant") REFERENCES "public"."tenants"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "inventoryitems" ADD CONSTRAINT "inventoryitems_currentSpace_spaces_id_fk" FOREIGN KEY ("currentSpace") REFERENCES "public"."spaces"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "inventoryitems" ADD CONSTRAINT "inventoryitems_vendor_vendors_id_fk" FOREIGN KEY ("vendor") REFERENCES "public"."vendors"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "inventoryitems" ADD CONSTRAINT "inventoryitems_updated_by_auth_users_id_fk" FOREIGN KEY ("updated_by") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "letterheads" ADD CONSTRAINT "letterheads_tenant_tenants_id_fk" FOREIGN KEY ("tenant") REFERENCES "public"."tenants"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "letterheads" ADD CONSTRAINT "letterheads_updated_by_auth_users_id_fk" FOREIGN KEY ("updated_by") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "movements" ADD CONSTRAINT "movements_productId_products_id_fk" FOREIGN KEY ("productId") REFERENCES "public"."products"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "movements" ADD CONSTRAINT "movements_spaceId_spaces_id_fk" FOREIGN KEY ("spaceId") REFERENCES "public"."spaces"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "movements" ADD CONSTRAINT "movements_tenant_tenants_id_fk" FOREIGN KEY ("tenant") REFERENCES "public"."tenants"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "movements" ADD CONSTRAINT "movements_projectId_projects_id_fk" FOREIGN KEY ("projectId") REFERENCES "public"."projects"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "movements" ADD CONSTRAINT "movements_updated_by_auth_users_id_fk" FOREIGN KEY ("updated_by") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "notifications_items" ADD CONSTRAINT "notifications_items_tenant_id_tenants_id_fk" FOREIGN KEY ("tenant_id") REFERENCES "public"."tenants"("id") ON DELETE cascade ON UPDATE cascade;--> statement-breakpoint +ALTER TABLE "notifications_items" ADD CONSTRAINT "notifications_items_user_id_auth_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."auth_users"("id") ON DELETE cascade ON UPDATE cascade;--> statement-breakpoint +ALTER TABLE "notifications_items" ADD CONSTRAINT "notifications_items_event_type_notifications_event_types_event_key_fk" FOREIGN KEY ("event_type") REFERENCES "public"."notifications_event_types"("event_key") ON DELETE restrict ON UPDATE cascade;--> statement-breakpoint +ALTER TABLE "notifications_preferences" ADD CONSTRAINT "notifications_preferences_tenant_id_tenants_id_fk" FOREIGN KEY ("tenant_id") REFERENCES "public"."tenants"("id") ON DELETE cascade ON UPDATE cascade;--> statement-breakpoint +ALTER TABLE "notifications_preferences" ADD CONSTRAINT "notifications_preferences_user_id_auth_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."auth_users"("id") ON DELETE cascade ON UPDATE cascade;--> statement-breakpoint +ALTER TABLE "notifications_preferences" ADD CONSTRAINT "notifications_preferences_event_type_notifications_event_types_event_key_fk" FOREIGN KEY ("event_type") REFERENCES "public"."notifications_event_types"("event_key") ON DELETE restrict ON UPDATE cascade;--> statement-breakpoint +ALTER TABLE "notifications_preferences_defaults" ADD CONSTRAINT "notifications_preferences_defaults_tenant_id_tenants_id_fk" FOREIGN KEY ("tenant_id") REFERENCES "public"."tenants"("id") ON DELETE cascade ON UPDATE cascade;--> statement-breakpoint +ALTER TABLE "notifications_preferences_defaults" ADD CONSTRAINT "notifications_preferences_defaults_event_key_notifications_event_types_event_key_fk" FOREIGN KEY ("event_key") REFERENCES "public"."notifications_event_types"("event_key") ON DELETE restrict ON UPDATE cascade;--> statement-breakpoint +ALTER TABLE "ownaccounts" ADD CONSTRAINT "ownaccounts_tenant_tenants_id_fk" FOREIGN KEY ("tenant") REFERENCES "public"."tenants"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "ownaccounts" ADD CONSTRAINT "ownaccounts_updated_by_auth_users_id_fk" FOREIGN KEY ("updated_by") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "plants" ADD CONSTRAINT "plants_tenant_tenants_id_fk" FOREIGN KEY ("tenant") REFERENCES "public"."tenants"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "plants" ADD CONSTRAINT "plants_customer_customers_id_fk" FOREIGN KEY ("customer") REFERENCES "public"."customers"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "plants" ADD CONSTRAINT "plants_contract_contracts_id_fk" FOREIGN KEY ("contract") REFERENCES "public"."contracts"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "plants" ADD CONSTRAINT "plants_updated_by_auth_users_id_fk" FOREIGN KEY ("updated_by") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "productcategories" ADD CONSTRAINT "productcategories_tenant_tenants_id_fk" FOREIGN KEY ("tenant") REFERENCES "public"."tenants"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "productcategories" ADD CONSTRAINT "productcategories_updated_by_auth_users_id_fk" FOREIGN KEY ("updated_by") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "products" ADD CONSTRAINT "products_unit_units_id_fk" FOREIGN KEY ("unit") REFERENCES "public"."units"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "products" ADD CONSTRAINT "products_tenant_tenants_id_fk" FOREIGN KEY ("tenant") REFERENCES "public"."tenants"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "products" ADD CONSTRAINT "products_updated_by_auth_users_id_fk" FOREIGN KEY ("updated_by") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "projects" ADD CONSTRAINT "projects_tenant_tenants_id_fk" FOREIGN KEY ("tenant") REFERENCES "public"."tenants"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "projects" ADD CONSTRAINT "projects_customer_customers_id_fk" FOREIGN KEY ("customer") REFERENCES "public"."customers"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "projects" ADD CONSTRAINT "projects_contract_contracts_id_fk" FOREIGN KEY ("contract") REFERENCES "public"."contracts"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "projects" ADD CONSTRAINT "projects_projecttype_projecttypes_id_fk" FOREIGN KEY ("projecttype") REFERENCES "public"."projecttypes"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "projects" ADD CONSTRAINT "projects_updated_by_auth_users_id_fk" FOREIGN KEY ("updated_by") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "projecttypes" ADD CONSTRAINT "projecttypes_tenant_tenants_id_fk" FOREIGN KEY ("tenant") REFERENCES "public"."tenants"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "projecttypes" ADD CONSTRAINT "projecttypes_updated_by_auth_users_id_fk" FOREIGN KEY ("updated_by") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "servicecategories" ADD CONSTRAINT "servicecategories_tenant_tenants_id_fk" FOREIGN KEY ("tenant") REFERENCES "public"."tenants"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "servicecategories" ADD CONSTRAINT "servicecategories_updated_by_auth_users_id_fk" FOREIGN KEY ("updated_by") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "services" ADD CONSTRAINT "services_tenant_tenants_id_fk" FOREIGN KEY ("tenant") REFERENCES "public"."tenants"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "services" ADD CONSTRAINT "services_unit_units_id_fk" FOREIGN KEY ("unit") REFERENCES "public"."units"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "services" ADD CONSTRAINT "services_updated_by_auth_users_id_fk" FOREIGN KEY ("updated_by") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "spaces" ADD CONSTRAINT "spaces_tenant_tenants_id_fk" FOREIGN KEY ("tenant") REFERENCES "public"."tenants"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "spaces" ADD CONSTRAINT "spaces_parentSpace_spaces_id_fk" FOREIGN KEY ("parentSpace") REFERENCES "public"."spaces"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "spaces" ADD CONSTRAINT "spaces_updated_by_auth_users_id_fk" FOREIGN KEY ("updated_by") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "staff_time_entries" ADD CONSTRAINT "staff_time_entries_tenant_id_tenants_id_fk" FOREIGN KEY ("tenant_id") REFERENCES "public"."tenants"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "staff_time_entries" ADD CONSTRAINT "staff_time_entries_user_id_auth_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."auth_users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "staff_time_entries" ADD CONSTRAINT "staff_time_entries_updated_by_auth_users_id_fk" FOREIGN KEY ("updated_by") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "staff_time_entries" ADD CONSTRAINT "staff_time_entries_approved_by_auth_users_id_fk" FOREIGN KEY ("approved_by") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "staff_time_entry_connects" ADD CONSTRAINT "staff_time_entry_connects_time_entry_id_staff_time_entries_id_fk" FOREIGN KEY ("time_entry_id") REFERENCES "public"."staff_time_entries"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "staff_zeitstromtimestamps" ADD CONSTRAINT "staff_zeitstromtimestamps_tenant_tenants_id_fk" FOREIGN KEY ("tenant") REFERENCES "public"."tenants"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "staff_zeitstromtimestamps" ADD CONSTRAINT "staff_zeitstromtimestamps_profile_auth_profiles_id_fk" FOREIGN KEY ("profile") REFERENCES "public"."auth_profiles"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "staff_zeitstromtimestamps" ADD CONSTRAINT "staff_zeitstromtimestamps_staff_time_entry_staff_time_entries_id_fk" FOREIGN KEY ("staff_time_entry") REFERENCES "public"."staff_time_entries"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "statementallocations" ADD CONSTRAINT "statementallocations_bs_id_bankstatements_id_fk" FOREIGN KEY ("bs_id") REFERENCES "public"."bankstatements"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "statementallocations" ADD CONSTRAINT "statementallocations_cd_id_createddocuments_id_fk" FOREIGN KEY ("cd_id") REFERENCES "public"."createddocuments"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "statementallocations" ADD CONSTRAINT "statementallocations_ii_id_incominginvoices_id_fk" FOREIGN KEY ("ii_id") REFERENCES "public"."incominginvoices"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "statementallocations" ADD CONSTRAINT "statementallocations_tenant_tenants_id_fk" FOREIGN KEY ("tenant") REFERENCES "public"."tenants"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "statementallocations" ADD CONSTRAINT "statementallocations_account_accounts_id_fk" FOREIGN KEY ("account") REFERENCES "public"."accounts"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "statementallocations" ADD CONSTRAINT "statementallocations_ownaccount_ownaccounts_id_fk" FOREIGN KEY ("ownaccount") REFERENCES "public"."ownaccounts"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "statementallocations" ADD CONSTRAINT "statementallocations_customer_customers_id_fk" FOREIGN KEY ("customer") REFERENCES "public"."customers"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "statementallocations" ADD CONSTRAINT "statementallocations_vendor_vendors_id_fk" FOREIGN KEY ("vendor") REFERENCES "public"."vendors"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "statementallocations" ADD CONSTRAINT "statementallocations_updated_by_auth_users_id_fk" FOREIGN KEY ("updated_by") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "tasks" ADD CONSTRAINT "tasks_tenant_tenants_id_fk" FOREIGN KEY ("tenant") REFERENCES "public"."tenants"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "tasks" ADD CONSTRAINT "tasks_user_id_auth_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "tasks" ADD CONSTRAINT "tasks_customer_customers_id_fk" FOREIGN KEY ("customer") REFERENCES "public"."customers"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "tasks" ADD CONSTRAINT "tasks_updated_by_auth_users_id_fk" FOREIGN KEY ("updated_by") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "taxtypes" ADD CONSTRAINT "taxtypes_updated_by_auth_users_id_fk" FOREIGN KEY ("updated_by") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "tenants" ADD CONSTRAINT "tenants_updated_by_auth_users_id_fk" FOREIGN KEY ("updated_by") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "texttemplates" ADD CONSTRAINT "texttemplates_tenant_tenants_id_fk" FOREIGN KEY ("tenant") REFERENCES "public"."tenants"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "texttemplates" ADD CONSTRAINT "texttemplates_updated_by_auth_users_id_fk" FOREIGN KEY ("updated_by") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "user_credentials" ADD CONSTRAINT "user_credentials_user_id_auth_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "user_credentials" ADD CONSTRAINT "user_credentials_tenant_id_tenants_id_fk" FOREIGN KEY ("tenant_id") REFERENCES "public"."tenants"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "vehicles" ADD CONSTRAINT "vehicles_tenant_tenants_id_fk" FOREIGN KEY ("tenant") REFERENCES "public"."tenants"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "vehicles" ADD CONSTRAINT "vehicles_driver_auth_users_id_fk" FOREIGN KEY ("driver") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "vehicles" ADD CONSTRAINT "vehicles_updated_by_auth_users_id_fk" FOREIGN KEY ("updated_by") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "vendors" ADD CONSTRAINT "vendors_tenant_tenants_id_fk" FOREIGN KEY ("tenant") REFERENCES "public"."tenants"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "vendors" ADD CONSTRAINT "vendors_updated_by_auth_users_id_fk" FOREIGN KEY ("updated_by") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +CREATE UNIQUE INDEX "notifications_preferences_tenant_id_user_id_event_type_chan_key" ON "notifications_preferences" USING btree ("tenant_id","user_id","event_type","channel");--> statement-breakpoint +CREATE UNIQUE INDEX "notifications_preferences_defau_tenant_id_event_key_channel_key" ON "notifications_preferences_defaults" USING btree ("tenant_id","event_key","channel"); \ No newline at end of file diff --git a/db/migrations/0001_medical_big_bertha.sql b/db/migrations/0001_medical_big_bertha.sql new file mode 100644 index 0000000..5ff60a1 --- /dev/null +++ b/db/migrations/0001_medical_big_bertha.sql @@ -0,0 +1,32 @@ +CREATE TABLE "time_events" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "tenant_id" bigint NOT NULL, + "user_id" uuid NOT NULL, + "actor_type" text NOT NULL, + "actor_user_id" uuid, + "event_time" timestamp with time zone NOT NULL, + "event_type" text NOT NULL, + "source" text NOT NULL, + "invalidates_event_id" uuid, + "metadata" jsonb, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT "time_events_actor_user_check" CHECK ( + (actor_type = 'system' AND actor_user_id IS NULL) + OR + (actor_type = 'user' AND actor_user_id IS NOT NULL) + ) +); +--> statement-breakpoint +ALTER TABLE "bankstatements" DROP CONSTRAINT "bankstatements_incomingInvoice_incominginvoices_id_fk"; +--> statement-breakpoint +ALTER TABLE "bankstatements" DROP CONSTRAINT "bankstatements_createdDocument_createddocuments_id_fk"; +--> statement-breakpoint +ALTER TABLE "time_events" ADD CONSTRAINT "time_events_tenant_id_tenants_id_fk" FOREIGN KEY ("tenant_id") REFERENCES "public"."tenants"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "time_events" ADD CONSTRAINT "time_events_user_id_auth_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "time_events" ADD CONSTRAINT "time_events_actor_user_id_auth_users_id_fk" FOREIGN KEY ("actor_user_id") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "time_events" ADD CONSTRAINT "time_events_invalidates_event_id_time_events_id_fk" FOREIGN KEY ("invalidates_event_id") REFERENCES "public"."time_events"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +CREATE INDEX "idx_time_events_tenant_user_time" ON "time_events" USING btree ("tenant_id","user_id","event_time");--> statement-breakpoint +CREATE INDEX "idx_time_events_created_at" ON "time_events" USING btree ("created_at");--> statement-breakpoint +CREATE INDEX "idx_time_events_invalidates" ON "time_events" USING btree ("invalidates_event_id");--> statement-breakpoint +ALTER TABLE "bankstatements" DROP COLUMN "incomingInvoice";--> statement-breakpoint +ALTER TABLE "bankstatements" DROP COLUMN "createdDocument"; \ No newline at end of file diff --git a/db/migrations/0002_silent_christian_walker.sql b/db/migrations/0002_silent_christian_walker.sql new file mode 100644 index 0000000..bd4a372 --- /dev/null +++ b/db/migrations/0002_silent_christian_walker.sql @@ -0,0 +1,13 @@ +ALTER TABLE "time_events" RENAME TO "staff_time_events";--> statement-breakpoint +ALTER TABLE "staff_time_events" DROP CONSTRAINT "time_events_tenant_id_tenants_id_fk"; +--> statement-breakpoint +ALTER TABLE "staff_time_events" DROP CONSTRAINT "time_events_user_id_auth_users_id_fk"; +--> statement-breakpoint +ALTER TABLE "staff_time_events" DROP CONSTRAINT "time_events_actor_user_id_auth_users_id_fk"; +--> statement-breakpoint +ALTER TABLE "staff_time_events" DROP CONSTRAINT "time_events_invalidates_event_id_time_events_id_fk"; +--> statement-breakpoint +ALTER TABLE "staff_time_events" ADD CONSTRAINT "staff_time_events_tenant_id_tenants_id_fk" FOREIGN KEY ("tenant_id") REFERENCES "public"."tenants"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "staff_time_events" ADD CONSTRAINT "staff_time_events_user_id_auth_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "staff_time_events" ADD CONSTRAINT "staff_time_events_actor_user_id_auth_users_id_fk" FOREIGN KEY ("actor_user_id") REFERENCES "public"."auth_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "staff_time_events" ADD CONSTRAINT "staff_time_events_invalidates_event_id_staff_time_events_id_fk" FOREIGN KEY ("invalidates_event_id") REFERENCES "public"."staff_time_events"("id") ON DELETE no action ON UPDATE no action; \ No newline at end of file diff --git a/db/migrations/meta/0000_snapshot.json b/db/migrations/meta/0000_snapshot.json new file mode 100644 index 0000000..3fff43c --- /dev/null +++ b/db/migrations/meta/0000_snapshot.json @@ -0,0 +1,9788 @@ +{ + "id": "c74cefc4-5ae7-408c-b7f3-09093efb52b5", + "prevId": "00000000-0000-0000-0000-000000000000", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.accounts": { + "name": "accounts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "accounts_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "number": { + "name": "number", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth_profiles": { + "name": "auth_profiles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "first_name": { + "name": "first_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "last_name": { + "name": "last_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "full_name": { + "name": "full_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "generated": { + "as": "((first_name || ' ') || last_name)", + "type": "stored" + } + }, + "mobile_tel": { + "name": "mobile_tel", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "fixed_tel": { + "name": "fixed_tel", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "salutation": { + "name": "salutation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "employee_number": { + "name": "employee_number", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "weekly_working_hours": { + "name": "weekly_working_hours", + "type": "double precision", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "annual_paid_leave_days": { + "name": "annual_paid_leave_days", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "weekly_regular_working_hours": { + "name": "weekly_regular_working_hours", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "clothing_size_top": { + "name": "clothing_size_top", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "clothing_size_bottom": { + "name": "clothing_size_bottom", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "clothing_size_shoe": { + "name": "clothing_size_shoe", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email_signature": { + "name": "email_signature", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'

Mit freundlichen Grüßen

'" + }, + "birthday": { + "name": "birthday", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "entry_date": { + "name": "entry_date", + "type": "date", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "automatic_hour_corrections": { + "name": "automatic_hour_corrections", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'[]'" + }, + "recreation_days_compensation": { + "name": "recreation_days_compensation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "customer_for_portal": { + "name": "customer_for_portal", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "pinned_on_navigation": { + "name": "pinned_on_navigation", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'" + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "token_id": { + "name": "token_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "weekly_working_days": { + "name": "weekly_working_days", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "old_profile_id": { + "name": "old_profile_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "temp_config": { + "name": "temp_config", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "state_code": { + "name": "state_code", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'DE-NI'" + }, + "contract_type": { + "name": "contract_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "position": { + "name": "position", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "qualification": { + "name": "qualification", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address_street": { + "name": "address_street", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address_zip": { + "name": "address_zip", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address_city": { + "name": "address_city", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + } + }, + "indexes": {}, + "foreignKeys": { + "auth_profiles_user_id_auth_users_id_fk": { + "name": "auth_profiles_user_id_auth_users_id_fk", + "tableFrom": "auth_profiles", + "tableTo": "auth_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth_role_permissions": { + "name": "auth_role_permissions", + "schema": "", + "columns": { + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "role_id": { + "name": "role_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "permission": { + "name": "permission", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "auth_role_permissions_role_id_auth_roles_id_fk": { + "name": "auth_role_permissions_role_id_auth_roles_id_fk", + "tableFrom": "auth_role_permissions", + "tableTo": "auth_roles", + "columnsFrom": [ + "role_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth_roles": { + "name": "auth_roles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "auth_roles_created_by_auth_users_id_fk": { + "name": "auth_roles_created_by_auth_users_id_fk", + "tableFrom": "auth_roles", + "tableTo": "auth_users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth_tenant_users": { + "name": "auth_tenant_users", + "schema": "", + "columns": { + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "tenant_id": { + "name": "tenant_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "auth_tenant_users_created_by_auth_users_id_fk": { + "name": "auth_tenant_users_created_by_auth_users_id_fk", + "tableFrom": "auth_tenant_users", + "tableTo": "auth_users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth_user_roles": { + "name": "auth_user_roles", + "schema": "", + "columns": { + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "role_id": { + "name": "role_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "tenant_id": { + "name": "tenant_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "auth_user_roles_user_id_auth_users_id_fk": { + "name": "auth_user_roles_user_id_auth_users_id_fk", + "tableFrom": "auth_user_roles", + "tableTo": "auth_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "auth_user_roles_role_id_auth_roles_id_fk": { + "name": "auth_user_roles_role_id_auth_roles_id_fk", + "tableFrom": "auth_user_roles", + "tableTo": "auth_roles", + "columnsFrom": [ + "role_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "auth_user_roles_created_by_auth_users_id_fk": { + "name": "auth_user_roles_created_by_auth_users_id_fk", + "tableFrom": "auth_user_roles", + "tableTo": "auth_users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth_users": { + "name": "auth_users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password_hash": { + "name": "password_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "multi_tenant": { + "name": "multi_tenant", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "must_change_password": { + "name": "must_change_password", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "ported": { + "name": "ported", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.bankaccounts": { + "name": "bankaccounts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "bankaccounts_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "iban": { + "name": "iban", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "bankId": { + "name": "bankId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "ownerName": { + "name": "ownerName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "accountId": { + "name": "accountId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "balance": { + "name": "balance", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "expired": { + "name": "expired", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "datevNumber": { + "name": "datevNumber", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "synced_at": { + "name": "synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "bankaccounts_tenant_tenants_id_fk": { + "name": "bankaccounts_tenant_tenants_id_fk", + "tableFrom": "bankaccounts", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "bankaccounts_updated_by_auth_users_id_fk": { + "name": "bankaccounts_updated_by_auth_users_id_fk", + "tableFrom": "bankaccounts", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.bankrequisitions": { + "name": "bankrequisitions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "institutionId": { + "name": "institutionId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "bankrequisitions_tenant_tenants_id_fk": { + "name": "bankrequisitions_tenant_tenants_id_fk", + "tableFrom": "bankrequisitions", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "bankrequisitions_updated_by_auth_users_id_fk": { + "name": "bankrequisitions_updated_by_auth_users_id_fk", + "tableFrom": "bankrequisitions", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.bankstatements": { + "name": "bankstatements", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "bankstatements_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "account": { + "name": "account", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "date": { + "name": "date", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "credIban": { + "name": "credIban", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "credName": { + "name": "credName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "text": { + "name": "text", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "amount": { + "name": "amount", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "debIban": { + "name": "debIban", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "debName": { + "name": "debName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gocardlessId": { + "name": "gocardlessId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "currency": { + "name": "currency", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "valueDate": { + "name": "valueDate", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "incomingInvoice": { + "name": "incomingInvoice", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "mandateId": { + "name": "mandateId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "contract": { + "name": "contract", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "createdDocument": { + "name": "createdDocument", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "bankstatements_account_bankaccounts_id_fk": { + "name": "bankstatements_account_bankaccounts_id_fk", + "tableFrom": "bankstatements", + "tableTo": "bankaccounts", + "columnsFrom": [ + "account" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "bankstatements_tenant_tenants_id_fk": { + "name": "bankstatements_tenant_tenants_id_fk", + "tableFrom": "bankstatements", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "bankstatements_incomingInvoice_incominginvoices_id_fk": { + "name": "bankstatements_incomingInvoice_incominginvoices_id_fk", + "tableFrom": "bankstatements", + "tableTo": "incominginvoices", + "columnsFrom": [ + "incomingInvoice" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "bankstatements_contract_contracts_id_fk": { + "name": "bankstatements_contract_contracts_id_fk", + "tableFrom": "bankstatements", + "tableTo": "contracts", + "columnsFrom": [ + "contract" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "bankstatements_createdDocument_createddocuments_id_fk": { + "name": "bankstatements_createdDocument_createddocuments_id_fk", + "tableFrom": "bankstatements", + "tableTo": "createddocuments", + "columnsFrom": [ + "createdDocument" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "bankstatements_updated_by_auth_users_id_fk": { + "name": "bankstatements_updated_by_auth_users_id_fk", + "tableFrom": "bankstatements", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.checkexecutions": { + "name": "checkexecutions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "check": { + "name": "check", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "executed_at": { + "name": "executed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "checkexecutions_check_checks_id_fk": { + "name": "checkexecutions_check_checks_id_fk", + "tableFrom": "checkexecutions", + "tableTo": "checks", + "columnsFrom": [ + "check" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.checks": { + "name": "checks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "vehicle": { + "name": "vehicle", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "inventoryitem": { + "name": "inventoryitem", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "distance": { + "name": "distance", + "type": "bigint", + "primaryKey": false, + "notNull": false, + "default": 1 + }, + "distanceUnit": { + "name": "distanceUnit", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'days'" + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "profiles": { + "name": "profiles", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "checks_vehicle_vehicles_id_fk": { + "name": "checks_vehicle_vehicles_id_fk", + "tableFrom": "checks", + "tableTo": "vehicles", + "columnsFrom": [ + "vehicle" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "checks_inventoryitem_inventoryitems_id_fk": { + "name": "checks_inventoryitem_inventoryitems_id_fk", + "tableFrom": "checks", + "tableTo": "inventoryitems", + "columnsFrom": [ + "inventoryitem" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "checks_tenant_tenants_id_fk": { + "name": "checks_tenant_tenants_id_fk", + "tableFrom": "checks", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "checks_updated_by_auth_users_id_fk": { + "name": "checks_updated_by_auth_users_id_fk", + "tableFrom": "checks", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.citys": { + "name": "citys", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "citys_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "short": { + "name": "short", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "long": { + "name": "long", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "geometry": { + "name": "geometry", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "zip": { + "name": "zip", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "districtCode": { + "name": "districtCode", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "countryName": { + "name": "countryName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "countryCode": { + "name": "countryCode", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "districtName": { + "name": "districtName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "geopoint": { + "name": "geopoint", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.contacts": { + "name": "contacts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "contacts_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "firstName": { + "name": "firstName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lastName": { + "name": "lastName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customer": { + "name": "customer", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "phoneMobile": { + "name": "phoneMobile", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "phoneHome": { + "name": "phoneHome", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "heroId": { + "name": "heroId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "fullName": { + "name": "fullName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "salutation": { + "name": "salutation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "vendor": { + "name": "vendor", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "birthday": { + "name": "birthday", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "profiles": { + "name": "profiles", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "contacts_customer_customers_id_fk": { + "name": "contacts_customer_customers_id_fk", + "tableFrom": "contacts", + "tableTo": "customers", + "columnsFrom": [ + "customer" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "contacts_updated_by_auth_users_id_fk": { + "name": "contacts_updated_by_auth_users_id_fk", + "tableFrom": "contacts", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.contracts": { + "name": "contracts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "contracts_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "customer": { + "name": "customer", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "recurring": { + "name": "recurring", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "rhythm": { + "name": "rhythm", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "startDate": { + "name": "startDate", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "endDate": { + "name": "endDate", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "signDate": { + "name": "signDate", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "duration": { + "name": "duration", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "contact": { + "name": "contact", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "bankingIban": { + "name": "bankingIban", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bankingBIC": { + "name": "bankingBIC", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bankingName": { + "name": "bankingName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bankingOwner": { + "name": "bankingOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sepaRef": { + "name": "sepaRef", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sepaDate": { + "name": "sepaDate", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "paymentType": { + "name": "paymentType", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "invoiceDispatch": { + "name": "invoiceDispatch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ownFields": { + "name": "ownFields", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "profiles": { + "name": "profiles", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "contractNumber": { + "name": "contractNumber", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "contracts_customer_customers_id_fk": { + "name": "contracts_customer_customers_id_fk", + "tableFrom": "contracts", + "tableTo": "customers", + "columnsFrom": [ + "customer" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "contracts_contact_contacts_id_fk": { + "name": "contracts_contact_contacts_id_fk", + "tableFrom": "contracts", + "tableTo": "contacts", + "columnsFrom": [ + "contact" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "contracts_updated_by_auth_users_id_fk": { + "name": "contracts_updated_by_auth_users_id_fk", + "tableFrom": "contracts", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.costcentres": { + "name": "costcentres", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "number": { + "name": "number", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "vehicle": { + "name": "vehicle", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "project": { + "name": "project", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "inventoryitem": { + "name": "inventoryitem", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "profiles": { + "name": "profiles", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "costcentres_tenant_tenants_id_fk": { + "name": "costcentres_tenant_tenants_id_fk", + "tableFrom": "costcentres", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "costcentres_vehicle_vehicles_id_fk": { + "name": "costcentres_vehicle_vehicles_id_fk", + "tableFrom": "costcentres", + "tableTo": "vehicles", + "columnsFrom": [ + "vehicle" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "costcentres_project_projects_id_fk": { + "name": "costcentres_project_projects_id_fk", + "tableFrom": "costcentres", + "tableTo": "projects", + "columnsFrom": [ + "project" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "costcentres_inventoryitem_inventoryitems_id_fk": { + "name": "costcentres_inventoryitem_inventoryitems_id_fk", + "tableFrom": "costcentres", + "tableTo": "inventoryitems", + "columnsFrom": [ + "inventoryitem" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "costcentres_updated_by_auth_users_id_fk": { + "name": "costcentres_updated_by_auth_users_id_fk", + "tableFrom": "costcentres", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.countrys": { + "name": "countrys", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "countrys_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.createddocuments": { + "name": "createddocuments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "createddocuments_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'INVOICE'" + }, + "customer": { + "name": "customer", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "contact": { + "name": "contact", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "address": { + "name": "address", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "project": { + "name": "project", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "documentNumber": { + "name": "documentNumber", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "documentDate": { + "name": "documentDate", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'Entwurf'" + }, + "info": { + "name": "info", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "createdBy": { + "name": "createdBy", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "startText": { + "name": "startText", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "endText": { + "name": "endText", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rows": { + "name": "rows", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'[]'::jsonb" + }, + "deliveryDateType": { + "name": "deliveryDateType", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "paymentDays": { + "name": "paymentDays", + "type": "smallint", + "primaryKey": false, + "notNull": false + }, + "deliveryDate": { + "name": "deliveryDate", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "contactPerson": { + "name": "contactPerson", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "serialConfig": { + "name": "serialConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'{}'::jsonb" + }, + "linkedDocument": { + "name": "linkedDocument", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "agriculture": { + "name": "agriculture", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "letterhead": { + "name": "letterhead", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "advanceInvoiceResolved": { + "name": "advanceInvoiceResolved", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "usedAdvanceInvoices": { + "name": "usedAdvanceInvoices", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "deliveryDateEnd": { + "name": "deliveryDateEnd", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "plant": { + "name": "plant", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "taxType": { + "name": "taxType", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customSurchargePercentage": { + "name": "customSurchargePercentage", + "type": "smallint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "report": { + "name": "report", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "availableInPortal": { + "name": "availableInPortal", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "payment_type": { + "name": "payment_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'transfer'" + }, + "contract": { + "name": "contract", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "createddocuments_tenant_tenants_id_fk": { + "name": "createddocuments_tenant_tenants_id_fk", + "tableFrom": "createddocuments", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "createddocuments_customer_customers_id_fk": { + "name": "createddocuments_customer_customers_id_fk", + "tableFrom": "createddocuments", + "tableTo": "customers", + "columnsFrom": [ + "customer" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "createddocuments_contact_contacts_id_fk": { + "name": "createddocuments_contact_contacts_id_fk", + "tableFrom": "createddocuments", + "tableTo": "contacts", + "columnsFrom": [ + "contact" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "createddocuments_project_projects_id_fk": { + "name": "createddocuments_project_projects_id_fk", + "tableFrom": "createddocuments", + "tableTo": "projects", + "columnsFrom": [ + "project" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "createddocuments_createdBy_auth_users_id_fk": { + "name": "createddocuments_createdBy_auth_users_id_fk", + "tableFrom": "createddocuments", + "tableTo": "auth_users", + "columnsFrom": [ + "createdBy" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "createddocuments_linkedDocument_createddocuments_id_fk": { + "name": "createddocuments_linkedDocument_createddocuments_id_fk", + "tableFrom": "createddocuments", + "tableTo": "createddocuments", + "columnsFrom": [ + "linkedDocument" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "createddocuments_letterhead_letterheads_id_fk": { + "name": "createddocuments_letterhead_letterheads_id_fk", + "tableFrom": "createddocuments", + "tableTo": "letterheads", + "columnsFrom": [ + "letterhead" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "createddocuments_plant_plants_id_fk": { + "name": "createddocuments_plant_plants_id_fk", + "tableFrom": "createddocuments", + "tableTo": "plants", + "columnsFrom": [ + "plant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "createddocuments_updated_by_auth_users_id_fk": { + "name": "createddocuments_updated_by_auth_users_id_fk", + "tableFrom": "createddocuments", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "createddocuments_created_by_auth_users_id_fk": { + "name": "createddocuments_created_by_auth_users_id_fk", + "tableFrom": "createddocuments", + "tableTo": "auth_users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "createddocuments_contract_contracts_id_fk": { + "name": "createddocuments_contract_contracts_id_fk", + "tableFrom": "createddocuments", + "tableTo": "contracts", + "columnsFrom": [ + "contract" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.createdletters": { + "name": "createdletters", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "customer": { + "name": "customer", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "vendor": { + "name": "vendor", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "content_json": { + "name": "content_json", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'[]'::jsonb" + }, + "content_text": { + "name": "content_text", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "createdletters_tenant_tenants_id_fk": { + "name": "createdletters_tenant_tenants_id_fk", + "tableFrom": "createdletters", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "createdletters_customer_customers_id_fk": { + "name": "createdletters_customer_customers_id_fk", + "tableFrom": "createdletters", + "tableTo": "customers", + "columnsFrom": [ + "customer" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "createdletters_vendor_vendors_id_fk": { + "name": "createdletters_vendor_vendors_id_fk", + "tableFrom": "createdletters", + "tableTo": "vendors", + "columnsFrom": [ + "vendor" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "createdletters_updated_by_auth_users_id_fk": { + "name": "createdletters_updated_by_auth_users_id_fk", + "tableFrom": "createdletters", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.customers": { + "name": "customers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "customers_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "customerNumber": { + "name": "customerNumber", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "infoData": { + "name": "infoData", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'{}'::jsonb" + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'Privat'" + }, + "heroId": { + "name": "heroId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "isCompany": { + "name": "isCompany", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "profiles": { + "name": "profiles", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "customPaymentDays": { + "name": "customPaymentDays", + "type": "smallint", + "primaryKey": false, + "notNull": false + }, + "firstname": { + "name": "firstname", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lastname": { + "name": "lastname", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "customSurchargePercentage": { + "name": "customSurchargePercentage", + "type": "smallint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "salutation": { + "name": "salutation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "nameAddition": { + "name": "nameAddition", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "availableInPortal": { + "name": "availableInPortal", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "custom_payment_type": { + "name": "custom_payment_type", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "customers_updated_by_auth_users_id_fk": { + "name": "customers_updated_by_auth_users_id_fk", + "tableFrom": "customers", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.devices": { + "name": "devices", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalId": { + "name": "externalId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "devices_tenant_tenants_id_fk": { + "name": "devices_tenant_tenants_id_fk", + "tableFrom": "devices", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.documentboxes": { + "name": "documentboxes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "space": { + "name": "space", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "documentboxes_space_spaces_id_fk": { + "name": "documentboxes_space_spaces_id_fk", + "tableFrom": "documentboxes", + "tableTo": "spaces", + "columnsFrom": [ + "space" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "documentboxes_tenant_tenants_id_fk": { + "name": "documentboxes_tenant_tenants_id_fk", + "tableFrom": "documentboxes", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "documentboxes_updated_by_auth_users_id_fk": { + "name": "documentboxes_updated_by_auth_users_id_fk", + "tableFrom": "documentboxes", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.events": { + "name": "events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "events_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "startDate": { + "name": "startDate", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "endDate": { + "name": "endDate", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "eventtype": { + "name": "eventtype", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'Umsetzung'" + }, + "project": { + "name": "project", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "resources": { + "name": "resources", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'[]'::jsonb" + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "link": { + "name": "link", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "profiles": { + "name": "profiles", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "vehicles": { + "name": "vehicles", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "inventoryitems": { + "name": "inventoryitems", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "inventoryitemgroups": { + "name": "inventoryitemgroups", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "customer": { + "name": "customer", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "vendor": { + "name": "vendor", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "events_customer_customers_id_fk": { + "name": "events_customer_customers_id_fk", + "tableFrom": "events", + "tableTo": "customers", + "columnsFrom": [ + "customer" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "events_updated_by_auth_users_id_fk": { + "name": "events_updated_by_auth_users_id_fk", + "tableFrom": "events", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.files": { + "name": "files", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "project": { + "name": "project", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "customer": { + "name": "customer", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "contract": { + "name": "contract", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "vendor": { + "name": "vendor", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "incominginvoice": { + "name": "incominginvoice", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "plant": { + "name": "plant", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "createddocument": { + "name": "createddocument", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "vehicle": { + "name": "vehicle", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "product": { + "name": "product", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "check": { + "name": "check", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "inventoryitem": { + "name": "inventoryitem", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "folder": { + "name": "folder", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "mimeType": { + "name": "mimeType", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "space": { + "name": "space", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "documentbox": { + "name": "documentbox", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "auth_profile": { + "name": "auth_profile", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "files_tenant_tenants_id_fk": { + "name": "files_tenant_tenants_id_fk", + "tableFrom": "files", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "files_project_projects_id_fk": { + "name": "files_project_projects_id_fk", + "tableFrom": "files", + "tableTo": "projects", + "columnsFrom": [ + "project" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "files_customer_customers_id_fk": { + "name": "files_customer_customers_id_fk", + "tableFrom": "files", + "tableTo": "customers", + "columnsFrom": [ + "customer" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "files_contract_contracts_id_fk": { + "name": "files_contract_contracts_id_fk", + "tableFrom": "files", + "tableTo": "contracts", + "columnsFrom": [ + "contract" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "files_vendor_vendors_id_fk": { + "name": "files_vendor_vendors_id_fk", + "tableFrom": "files", + "tableTo": "vendors", + "columnsFrom": [ + "vendor" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "files_incominginvoice_incominginvoices_id_fk": { + "name": "files_incominginvoice_incominginvoices_id_fk", + "tableFrom": "files", + "tableTo": "incominginvoices", + "columnsFrom": [ + "incominginvoice" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "files_plant_plants_id_fk": { + "name": "files_plant_plants_id_fk", + "tableFrom": "files", + "tableTo": "plants", + "columnsFrom": [ + "plant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "files_createddocument_createddocuments_id_fk": { + "name": "files_createddocument_createddocuments_id_fk", + "tableFrom": "files", + "tableTo": "createddocuments", + "columnsFrom": [ + "createddocument" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "files_vehicle_vehicles_id_fk": { + "name": "files_vehicle_vehicles_id_fk", + "tableFrom": "files", + "tableTo": "vehicles", + "columnsFrom": [ + "vehicle" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "files_product_products_id_fk": { + "name": "files_product_products_id_fk", + "tableFrom": "files", + "tableTo": "products", + "columnsFrom": [ + "product" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "files_check_checks_id_fk": { + "name": "files_check_checks_id_fk", + "tableFrom": "files", + "tableTo": "checks", + "columnsFrom": [ + "check" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "files_inventoryitem_inventoryitems_id_fk": { + "name": "files_inventoryitem_inventoryitems_id_fk", + "tableFrom": "files", + "tableTo": "inventoryitems", + "columnsFrom": [ + "inventoryitem" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "files_folder_folders_id_fk": { + "name": "files_folder_folders_id_fk", + "tableFrom": "files", + "tableTo": "folders", + "columnsFrom": [ + "folder" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "files_space_spaces_id_fk": { + "name": "files_space_spaces_id_fk", + "tableFrom": "files", + "tableTo": "spaces", + "columnsFrom": [ + "space" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "files_type_filetags_id_fk": { + "name": "files_type_filetags_id_fk", + "tableFrom": "files", + "tableTo": "filetags", + "columnsFrom": [ + "type" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "files_documentbox_documentboxes_id_fk": { + "name": "files_documentbox_documentboxes_id_fk", + "tableFrom": "files", + "tableTo": "documentboxes", + "columnsFrom": [ + "documentbox" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "files_updated_by_auth_users_id_fk": { + "name": "files_updated_by_auth_users_id_fk", + "tableFrom": "files", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "files_created_by_auth_users_id_fk": { + "name": "files_created_by_auth_users_id_fk", + "tableFrom": "files", + "tableTo": "auth_users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "files_auth_profile_auth_profiles_id_fk": { + "name": "files_auth_profile_auth_profiles_id_fk", + "tableFrom": "files", + "tableTo": "auth_profiles", + "columnsFrom": [ + "auth_profile" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.filetags": { + "name": "filetags", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "createddocumenttype": { + "name": "createddocumenttype", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "''" + }, + "incomingDocumentType": { + "name": "incomingDocumentType", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "filetags_tenant_tenants_id_fk": { + "name": "filetags_tenant_tenants_id_fk", + "tableFrom": "filetags", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.folders": { + "name": "folders", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "parent": { + "name": "parent", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "isSystemUsed": { + "name": "isSystemUsed", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "function": { + "name": "function", + "type": "folderfunctions", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "year": { + "name": "year", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "standardFiletype": { + "name": "standardFiletype", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "standardFiletypeIsOptional": { + "name": "standardFiletypeIsOptional", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "folders_tenant_tenants_id_fk": { + "name": "folders_tenant_tenants_id_fk", + "tableFrom": "folders", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "folders_parent_folders_id_fk": { + "name": "folders_parent_folders_id_fk", + "tableFrom": "folders", + "tableTo": "folders", + "columnsFrom": [ + "parent" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "folders_standardFiletype_filetags_id_fk": { + "name": "folders_standardFiletype_filetags_id_fk", + "tableFrom": "folders", + "tableTo": "filetags", + "columnsFrom": [ + "standardFiletype" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "folders_updated_by_auth_users_id_fk": { + "name": "folders_updated_by_auth_users_id_fk", + "tableFrom": "folders", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.exports": { + "name": "exports", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "exports_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "tenant_id": { + "name": "tenant_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "start_date": { + "name": "start_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "end_date": { + "name": "end_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "valid_until": { + "name": "valid_until", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'datev'" + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_path": { + "name": "file_path", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "exports_tenant_id_tenants_id_fk": { + "name": "exports_tenant_id_tenants_id_fk", + "tableFrom": "exports", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.globalmessages": { + "name": "globalmessages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "globalmessages_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.globalmessagesseen": { + "name": "globalmessagesseen", + "schema": "", + "columns": { + "message": { + "name": "message", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "seen_at": { + "name": "seen_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "globalmessagesseen_message_globalmessages_id_fk": { + "name": "globalmessagesseen_message_globalmessages_id_fk", + "tableFrom": "globalmessagesseen", + "tableTo": "globalmessages", + "columnsFrom": [ + "message" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.helpdesk_channel_instances": { + "name": "helpdesk_channel_instances", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "tenant_id": { + "name": "tenant_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "type_id": { + "name": "type_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "public_config": { + "name": "public_config", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "public_token": { + "name": "public_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret_token": { + "name": "secret_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "helpdesk_channel_instances_tenant_id_tenants_id_fk": { + "name": "helpdesk_channel_instances_tenant_id_tenants_id_fk", + "tableFrom": "helpdesk_channel_instances", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "helpdesk_channel_instances_type_id_helpdesk_channel_types_id_fk": { + "name": "helpdesk_channel_instances_type_id_helpdesk_channel_types_id_fk", + "tableFrom": "helpdesk_channel_instances", + "tableTo": "helpdesk_channel_types", + "columnsFrom": [ + "type_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "helpdesk_channel_instances_created_by_auth_users_id_fk": { + "name": "helpdesk_channel_instances_created_by_auth_users_id_fk", + "tableFrom": "helpdesk_channel_instances", + "tableTo": "auth_users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "helpdesk_channel_instances_public_token_unique": { + "name": "helpdesk_channel_instances_public_token_unique", + "nullsNotDistinct": false, + "columns": [ + "public_token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.helpdesk_channel_types": { + "name": "helpdesk_channel_types", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.helpdesk_contacts": { + "name": "helpdesk_contacts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "tenant_id": { + "name": "tenant_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "customer_id": { + "name": "customer_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "phone": { + "name": "phone", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "external_ref": { + "name": "external_ref", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "source_channel_id": { + "name": "source_channel_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "contact_id": { + "name": "contact_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "helpdesk_contacts_tenant_id_tenants_id_fk": { + "name": "helpdesk_contacts_tenant_id_tenants_id_fk", + "tableFrom": "helpdesk_contacts", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "helpdesk_contacts_customer_id_customers_id_fk": { + "name": "helpdesk_contacts_customer_id_customers_id_fk", + "tableFrom": "helpdesk_contacts", + "tableTo": "customers", + "columnsFrom": [ + "customer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "helpdesk_contacts_source_channel_id_helpdesk_channel_instances_id_fk": { + "name": "helpdesk_contacts_source_channel_id_helpdesk_channel_instances_id_fk", + "tableFrom": "helpdesk_contacts", + "tableTo": "helpdesk_channel_instances", + "columnsFrom": [ + "source_channel_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "helpdesk_contacts_contact_id_contacts_id_fk": { + "name": "helpdesk_contacts_contact_id_contacts_id_fk", + "tableFrom": "helpdesk_contacts", + "tableTo": "contacts", + "columnsFrom": [ + "contact_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.helpdesk_conversation_participants": { + "name": "helpdesk_conversation_participants", + "schema": "", + "columns": { + "conversation_id": { + "name": "conversation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "helpdesk_conversation_participants_conversation_id_helpdesk_conversations_id_fk": { + "name": "helpdesk_conversation_participants_conversation_id_helpdesk_conversations_id_fk", + "tableFrom": "helpdesk_conversation_participants", + "tableTo": "helpdesk_conversations", + "columnsFrom": [ + "conversation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "helpdesk_conversation_participants_user_id_auth_users_id_fk": { + "name": "helpdesk_conversation_participants_user_id_auth_users_id_fk", + "tableFrom": "helpdesk_conversation_participants", + "tableTo": "auth_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.helpdesk_conversations": { + "name": "helpdesk_conversations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "tenant_id": { + "name": "tenant_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "channel_instance_id": { + "name": "channel_instance_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "contact_id": { + "name": "contact_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "subject": { + "name": "subject", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'open'" + }, + "priority": { + "name": "priority", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'normal'" + }, + "assignee_user_id": { + "name": "assignee_user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "last_message_at": { + "name": "last_message_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "customer_id": { + "name": "customer_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "contact_person_id": { + "name": "contact_person_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "ticket_number": { + "name": "ticket_number", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "helpdesk_conversations_tenant_id_tenants_id_fk": { + "name": "helpdesk_conversations_tenant_id_tenants_id_fk", + "tableFrom": "helpdesk_conversations", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "helpdesk_conversations_channel_instance_id_helpdesk_channel_instances_id_fk": { + "name": "helpdesk_conversations_channel_instance_id_helpdesk_channel_instances_id_fk", + "tableFrom": "helpdesk_conversations", + "tableTo": "helpdesk_channel_instances", + "columnsFrom": [ + "channel_instance_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "helpdesk_conversations_contact_id_helpdesk_contacts_id_fk": { + "name": "helpdesk_conversations_contact_id_helpdesk_contacts_id_fk", + "tableFrom": "helpdesk_conversations", + "tableTo": "helpdesk_contacts", + "columnsFrom": [ + "contact_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "helpdesk_conversations_assignee_user_id_auth_users_id_fk": { + "name": "helpdesk_conversations_assignee_user_id_auth_users_id_fk", + "tableFrom": "helpdesk_conversations", + "tableTo": "auth_users", + "columnsFrom": [ + "assignee_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "helpdesk_conversations_customer_id_customers_id_fk": { + "name": "helpdesk_conversations_customer_id_customers_id_fk", + "tableFrom": "helpdesk_conversations", + "tableTo": "customers", + "columnsFrom": [ + "customer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "helpdesk_conversations_contact_person_id_contacts_id_fk": { + "name": "helpdesk_conversations_contact_person_id_contacts_id_fk", + "tableFrom": "helpdesk_conversations", + "tableTo": "contacts", + "columnsFrom": [ + "contact_person_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.helpdesk_messages": { + "name": "helpdesk_messages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "tenant_id": { + "name": "tenant_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "conversation_id": { + "name": "conversation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "direction": { + "name": "direction", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "author_user_id": { + "name": "author_user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "raw_meta": { + "name": "raw_meta", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "contact_id": { + "name": "contact_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "external_message_id": { + "name": "external_message_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "received_at": { + "name": "received_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "helpdesk_messages_tenant_id_tenants_id_fk": { + "name": "helpdesk_messages_tenant_id_tenants_id_fk", + "tableFrom": "helpdesk_messages", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "helpdesk_messages_conversation_id_helpdesk_conversations_id_fk": { + "name": "helpdesk_messages_conversation_id_helpdesk_conversations_id_fk", + "tableFrom": "helpdesk_messages", + "tableTo": "helpdesk_conversations", + "columnsFrom": [ + "conversation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "helpdesk_messages_author_user_id_auth_users_id_fk": { + "name": "helpdesk_messages_author_user_id_auth_users_id_fk", + "tableFrom": "helpdesk_messages", + "tableTo": "auth_users", + "columnsFrom": [ + "author_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "helpdesk_messages_contact_id_helpdesk_contacts_id_fk": { + "name": "helpdesk_messages_contact_id_helpdesk_contacts_id_fk", + "tableFrom": "helpdesk_messages", + "tableTo": "helpdesk_contacts", + "columnsFrom": [ + "contact_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "helpdesk_messages_external_message_id_unique": { + "name": "helpdesk_messages_external_message_id_unique", + "nullsNotDistinct": false, + "columns": [ + "external_message_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.helpdesk_routing_rules": { + "name": "helpdesk_routing_rules", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "tenant_id": { + "name": "tenant_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "condition": { + "name": "condition", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "action": { + "name": "action", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "helpdesk_routing_rules_tenant_id_tenants_id_fk": { + "name": "helpdesk_routing_rules_tenant_id_tenants_id_fk", + "tableFrom": "helpdesk_routing_rules", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "helpdesk_routing_rules_created_by_auth_users_id_fk": { + "name": "helpdesk_routing_rules_created_by_auth_users_id_fk", + "tableFrom": "helpdesk_routing_rules", + "tableTo": "auth_users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.historyitems": { + "name": "historyitems", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "historyitems_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "text": { + "name": "text", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "customer": { + "name": "customer", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "vendor": { + "name": "vendor", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "project": { + "name": "project", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "plant": { + "name": "plant", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "incomingInvoice": { + "name": "incomingInvoice", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "contact": { + "name": "contact", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "inventoryitem": { + "name": "inventoryitem", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "product": { + "name": "product", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "event": { + "name": "event", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "newVal": { + "name": "newVal", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "oldVal": { + "name": "oldVal", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "task": { + "name": "task", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "vehicle": { + "name": "vehicle", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "bankstatement": { + "name": "bankstatement", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "space": { + "name": "space", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "projecttype": { + "name": "projecttype", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "check": { + "name": "check", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "service": { + "name": "service", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "createddocument": { + "name": "createddocument", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "file": { + "name": "file", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "inventoryitemgroup": { + "name": "inventoryitemgroup", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'Software'" + }, + "costcentre": { + "name": "costcentre", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "ownaccount": { + "name": "ownaccount", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "documentbox": { + "name": "documentbox", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "hourrate": { + "name": "hourrate", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "historyitems_customer_customers_id_fk": { + "name": "historyitems_customer_customers_id_fk", + "tableFrom": "historyitems", + "tableTo": "customers", + "columnsFrom": [ + "customer" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "historyitems_tenant_tenants_id_fk": { + "name": "historyitems_tenant_tenants_id_fk", + "tableFrom": "historyitems", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "historyitems_vendor_vendors_id_fk": { + "name": "historyitems_vendor_vendors_id_fk", + "tableFrom": "historyitems", + "tableTo": "vendors", + "columnsFrom": [ + "vendor" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "historyitems_project_projects_id_fk": { + "name": "historyitems_project_projects_id_fk", + "tableFrom": "historyitems", + "tableTo": "projects", + "columnsFrom": [ + "project" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "historyitems_plant_plants_id_fk": { + "name": "historyitems_plant_plants_id_fk", + "tableFrom": "historyitems", + "tableTo": "plants", + "columnsFrom": [ + "plant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "historyitems_incomingInvoice_incominginvoices_id_fk": { + "name": "historyitems_incomingInvoice_incominginvoices_id_fk", + "tableFrom": "historyitems", + "tableTo": "incominginvoices", + "columnsFrom": [ + "incomingInvoice" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "historyitems_contact_contacts_id_fk": { + "name": "historyitems_contact_contacts_id_fk", + "tableFrom": "historyitems", + "tableTo": "contacts", + "columnsFrom": [ + "contact" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "historyitems_inventoryitem_inventoryitems_id_fk": { + "name": "historyitems_inventoryitem_inventoryitems_id_fk", + "tableFrom": "historyitems", + "tableTo": "inventoryitems", + "columnsFrom": [ + "inventoryitem" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "historyitems_product_products_id_fk": { + "name": "historyitems_product_products_id_fk", + "tableFrom": "historyitems", + "tableTo": "products", + "columnsFrom": [ + "product" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "historyitems_event_events_id_fk": { + "name": "historyitems_event_events_id_fk", + "tableFrom": "historyitems", + "tableTo": "events", + "columnsFrom": [ + "event" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "historyitems_task_tasks_id_fk": { + "name": "historyitems_task_tasks_id_fk", + "tableFrom": "historyitems", + "tableTo": "tasks", + "columnsFrom": [ + "task" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "historyitems_vehicle_vehicles_id_fk": { + "name": "historyitems_vehicle_vehicles_id_fk", + "tableFrom": "historyitems", + "tableTo": "vehicles", + "columnsFrom": [ + "vehicle" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "historyitems_bankstatement_bankstatements_id_fk": { + "name": "historyitems_bankstatement_bankstatements_id_fk", + "tableFrom": "historyitems", + "tableTo": "bankstatements", + "columnsFrom": [ + "bankstatement" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "historyitems_space_spaces_id_fk": { + "name": "historyitems_space_spaces_id_fk", + "tableFrom": "historyitems", + "tableTo": "spaces", + "columnsFrom": [ + "space" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "historyitems_projecttype_projecttypes_id_fk": { + "name": "historyitems_projecttype_projecttypes_id_fk", + "tableFrom": "historyitems", + "tableTo": "projecttypes", + "columnsFrom": [ + "projecttype" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "historyitems_check_checks_id_fk": { + "name": "historyitems_check_checks_id_fk", + "tableFrom": "historyitems", + "tableTo": "checks", + "columnsFrom": [ + "check" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "historyitems_service_services_id_fk": { + "name": "historyitems_service_services_id_fk", + "tableFrom": "historyitems", + "tableTo": "services", + "columnsFrom": [ + "service" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "historyitems_createddocument_createddocuments_id_fk": { + "name": "historyitems_createddocument_createddocuments_id_fk", + "tableFrom": "historyitems", + "tableTo": "createddocuments", + "columnsFrom": [ + "createddocument" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "historyitems_file_files_id_fk": { + "name": "historyitems_file_files_id_fk", + "tableFrom": "historyitems", + "tableTo": "files", + "columnsFrom": [ + "file" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "historyitems_inventoryitemgroup_inventoryitemgroups_id_fk": { + "name": "historyitems_inventoryitemgroup_inventoryitemgroups_id_fk", + "tableFrom": "historyitems", + "tableTo": "inventoryitemgroups", + "columnsFrom": [ + "inventoryitemgroup" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "historyitems_costcentre_costcentres_id_fk": { + "name": "historyitems_costcentre_costcentres_id_fk", + "tableFrom": "historyitems", + "tableTo": "costcentres", + "columnsFrom": [ + "costcentre" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "historyitems_ownaccount_ownaccounts_id_fk": { + "name": "historyitems_ownaccount_ownaccounts_id_fk", + "tableFrom": "historyitems", + "tableTo": "ownaccounts", + "columnsFrom": [ + "ownaccount" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "historyitems_documentbox_documentboxes_id_fk": { + "name": "historyitems_documentbox_documentboxes_id_fk", + "tableFrom": "historyitems", + "tableTo": "documentboxes", + "columnsFrom": [ + "documentbox" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "historyitems_hourrate_hourrates_id_fk": { + "name": "historyitems_hourrate_hourrates_id_fk", + "tableFrom": "historyitems", + "tableTo": "hourrates", + "columnsFrom": [ + "hourrate" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "historyitems_created_by_auth_users_id_fk": { + "name": "historyitems_created_by_auth_users_id_fk", + "tableFrom": "historyitems", + "tableTo": "auth_users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.holidays": { + "name": "holidays", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "holidays_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "date": { + "name": "date", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "state_code": { + "name": "state_code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.hourrates": { + "name": "hourrates", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "purchasePrice": { + "name": "purchasePrice", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "sellingPrice": { + "name": "sellingPrice", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "hourrates_tenant_tenants_id_fk": { + "name": "hourrates_tenant_tenants_id_fk", + "tableFrom": "hourrates", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "hourrates_updated_by_auth_users_id_fk": { + "name": "hourrates_updated_by_auth_users_id_fk", + "tableFrom": "hourrates", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.incominginvoices": { + "name": "incominginvoices", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "incominginvoices_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'Entwurf'" + }, + "vendor": { + "name": "vendor", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "reference": { + "name": "reference", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "date": { + "name": "date", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "document": { + "name": "document", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "dueDate": { + "name": "dueDate", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "paymentType": { + "name": "paymentType", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "accounts": { + "name": "accounts", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[{\"account\":null,\"taxType\":null,\"amountNet\":null,\"amountTax\":19,\"costCentre\":null}]'::jsonb" + }, + "paid": { + "name": "paid", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "expense": { + "name": "expense", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "incominginvoices_tenant_tenants_id_fk": { + "name": "incominginvoices_tenant_tenants_id_fk", + "tableFrom": "incominginvoices", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "incominginvoices_vendor_vendors_id_fk": { + "name": "incominginvoices_vendor_vendors_id_fk", + "tableFrom": "incominginvoices", + "tableTo": "vendors", + "columnsFrom": [ + "vendor" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "incominginvoices_updated_by_auth_users_id_fk": { + "name": "incominginvoices_updated_by_auth_users_id_fk", + "tableFrom": "incominginvoices", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.inventoryitemgroups": { + "name": "inventoryitemgroups", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "inventoryitems": { + "name": "inventoryitems", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "profiles": { + "name": "profiles", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "usePlanning": { + "name": "usePlanning", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "inventoryitemgroups_tenant_tenants_id_fk": { + "name": "inventoryitemgroups_tenant_tenants_id_fk", + "tableFrom": "inventoryitemgroups", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inventoryitemgroups_updated_by_auth_users_id_fk": { + "name": "inventoryitemgroups_updated_by_auth_users_id_fk", + "tableFrom": "inventoryitemgroups", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.inventoryitems": { + "name": "inventoryitems", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "inventoryitems_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "usePlanning": { + "name": "usePlanning", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "currentSpace": { + "name": "currentSpace", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "articleNumber": { + "name": "articleNumber", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serialNumber": { + "name": "serialNumber", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "purchaseDate": { + "name": "purchaseDate", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "vendor": { + "name": "vendor", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "quantity": { + "name": "quantity", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "purchasePrice": { + "name": "purchasePrice", + "type": "double precision", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "manufacturer": { + "name": "manufacturer", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "manufacturerNumber": { + "name": "manufacturerNumber", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "currentValue": { + "name": "currentValue", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "profiles": { + "name": "profiles", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "inventoryitems_tenant_tenants_id_fk": { + "name": "inventoryitems_tenant_tenants_id_fk", + "tableFrom": "inventoryitems", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inventoryitems_currentSpace_spaces_id_fk": { + "name": "inventoryitems_currentSpace_spaces_id_fk", + "tableFrom": "inventoryitems", + "tableTo": "spaces", + "columnsFrom": [ + "currentSpace" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inventoryitems_vendor_vendors_id_fk": { + "name": "inventoryitems_vendor_vendors_id_fk", + "tableFrom": "inventoryitems", + "tableTo": "vendors", + "columnsFrom": [ + "vendor" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inventoryitems_updated_by_auth_users_id_fk": { + "name": "inventoryitems_updated_by_auth_users_id_fk", + "tableFrom": "inventoryitems", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.letterheads": { + "name": "letterheads", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "letterheads_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'Standard'" + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "documentTypes": { + "name": "documentTypes", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "letterheads_tenant_tenants_id_fk": { + "name": "letterheads_tenant_tenants_id_fk", + "tableFrom": "letterheads", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "letterheads_updated_by_auth_users_id_fk": { + "name": "letterheads_updated_by_auth_users_id_fk", + "tableFrom": "letterheads", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.movements": { + "name": "movements", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "movements_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "quantity": { + "name": "quantity", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "productId": { + "name": "productId", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "spaceId": { + "name": "spaceId", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serials": { + "name": "serials", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "movements_productId_products_id_fk": { + "name": "movements_productId_products_id_fk", + "tableFrom": "movements", + "tableTo": "products", + "columnsFrom": [ + "productId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "movements_spaceId_spaces_id_fk": { + "name": "movements_spaceId_spaces_id_fk", + "tableFrom": "movements", + "tableTo": "spaces", + "columnsFrom": [ + "spaceId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "movements_tenant_tenants_id_fk": { + "name": "movements_tenant_tenants_id_fk", + "tableFrom": "movements", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "movements_projectId_projects_id_fk": { + "name": "movements_projectId_projects_id_fk", + "tableFrom": "movements", + "tableTo": "projects", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "movements_updated_by_auth_users_id_fk": { + "name": "movements_updated_by_auth_users_id_fk", + "tableFrom": "movements", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notifications_event_types": { + "name": "notifications_event_types", + "schema": "", + "columns": { + "event_key": { + "name": "event_key", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "severity": { + "name": "severity", + "type": "notification_severity", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'info'" + }, + "allowed_channels": { + "name": "allowed_channels", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[\"inapp\",\"email\"]'::jsonb" + }, + "payload_schema": { + "name": "payload_schema", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notifications_items": { + "name": "notifications_items", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "tenant_id": { + "name": "tenant_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "channel": { + "name": "channel", + "type": "notification_channel", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "notification_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "sent_at": { + "name": "sent_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "read_at": { + "name": "read_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "notifications_items_tenant_id_tenants_id_fk": { + "name": "notifications_items_tenant_id_tenants_id_fk", + "tableFrom": "notifications_items", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "notifications_items_user_id_auth_users_id_fk": { + "name": "notifications_items_user_id_auth_users_id_fk", + "tableFrom": "notifications_items", + "tableTo": "auth_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "notifications_items_event_type_notifications_event_types_event_key_fk": { + "name": "notifications_items_event_type_notifications_event_types_event_key_fk", + "tableFrom": "notifications_items", + "tableTo": "notifications_event_types", + "columnsFrom": [ + "event_type" + ], + "columnsTo": [ + "event_key" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notifications_preferences": { + "name": "notifications_preferences", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "tenant_id": { + "name": "tenant_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "channel": { + "name": "channel", + "type": "notification_channel", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "notifications_preferences_tenant_id_user_id_event_type_chan_key": { + "name": "notifications_preferences_tenant_id_user_id_event_type_chan_key", + "columns": [ + { + "expression": "tenant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "event_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "channel", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "notifications_preferences_tenant_id_tenants_id_fk": { + "name": "notifications_preferences_tenant_id_tenants_id_fk", + "tableFrom": "notifications_preferences", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "notifications_preferences_user_id_auth_users_id_fk": { + "name": "notifications_preferences_user_id_auth_users_id_fk", + "tableFrom": "notifications_preferences", + "tableTo": "auth_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "notifications_preferences_event_type_notifications_event_types_event_key_fk": { + "name": "notifications_preferences_event_type_notifications_event_types_event_key_fk", + "tableFrom": "notifications_preferences", + "tableTo": "notifications_event_types", + "columnsFrom": [ + "event_type" + ], + "columnsTo": [ + "event_key" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notifications_preferences_defaults": { + "name": "notifications_preferences_defaults", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "tenant_id": { + "name": "tenant_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "event_key": { + "name": "event_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "channel": { + "name": "channel", + "type": "notification_channel", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "notifications_preferences_defau_tenant_id_event_key_channel_key": { + "name": "notifications_preferences_defau_tenant_id_event_key_channel_key", + "columns": [ + { + "expression": "tenant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "event_key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "channel", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "notifications_preferences_defaults_tenant_id_tenants_id_fk": { + "name": "notifications_preferences_defaults_tenant_id_tenants_id_fk", + "tableFrom": "notifications_preferences_defaults", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "notifications_preferences_defaults_event_key_notifications_event_types_event_key_fk": { + "name": "notifications_preferences_defaults_event_key_notifications_event_types_event_key_fk", + "tableFrom": "notifications_preferences_defaults", + "tableTo": "notifications_event_types", + "columnsFrom": [ + "event_key" + ], + "columnsTo": [ + "event_key" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ownaccounts": { + "name": "ownaccounts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "number": { + "name": "number", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "profiles": { + "name": "profiles", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "ownaccounts_tenant_tenants_id_fk": { + "name": "ownaccounts_tenant_tenants_id_fk", + "tableFrom": "ownaccounts", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "ownaccounts_updated_by_auth_users_id_fk": { + "name": "ownaccounts_updated_by_auth_users_id_fk", + "tableFrom": "ownaccounts", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.plants": { + "name": "plants", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "plants_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "customer": { + "name": "customer", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "infoData": { + "name": "infoData", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "contract": { + "name": "contract", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'{\"html\":\"\",\"json\":[],\"text\":\"\"}'::jsonb" + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "profiles": { + "name": "profiles", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "plants_tenant_tenants_id_fk": { + "name": "plants_tenant_tenants_id_fk", + "tableFrom": "plants", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "plants_customer_customers_id_fk": { + "name": "plants_customer_customers_id_fk", + "tableFrom": "plants", + "tableTo": "customers", + "columnsFrom": [ + "customer" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "plants_contract_contracts_id_fk": { + "name": "plants_contract_contracts_id_fk", + "tableFrom": "plants", + "tableTo": "contracts", + "columnsFrom": [ + "contract" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "plants_updated_by_auth_users_id_fk": { + "name": "plants_updated_by_auth_users_id_fk", + "tableFrom": "plants", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.productcategories": { + "name": "productcategories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "productcategories_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "productcategories_tenant_tenants_id_fk": { + "name": "productcategories_tenant_tenants_id_fk", + "tableFrom": "productcategories", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "productcategories_updated_by_auth_users_id_fk": { + "name": "productcategories_updated_by_auth_users_id_fk", + "tableFrom": "productcategories", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.products": { + "name": "products", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "products_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "manufacturer": { + "name": "manufacturer", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "unit": { + "name": "unit", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "tags": { + "name": "tags", + "type": "json", + "primaryKey": false, + "notNull": true, + "default": "'[]'::json" + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "ean": { + "name": "ean", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "barcode": { + "name": "barcode", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "purchasePrice": { + "name": "purchasePrice", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "sellingPrice": { + "name": "sellingPrice", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "manufacturerNumber": { + "name": "manufacturerNumber", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "vendorAllocation": { + "name": "vendorAllocation", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'[]'::jsonb" + }, + "articleNumber": { + "name": "articleNumber", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "barcodes": { + "name": "barcodes", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "productcategories": { + "name": "productcategories", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'[]'::jsonb" + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "taxPercentage": { + "name": "taxPercentage", + "type": "smallint", + "primaryKey": false, + "notNull": true, + "default": 19 + }, + "markupPercentage": { + "name": "markupPercentage", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "products_unit_units_id_fk": { + "name": "products_unit_units_id_fk", + "tableFrom": "products", + "tableTo": "units", + "columnsFrom": [ + "unit" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "products_tenant_tenants_id_fk": { + "name": "products_tenant_tenants_id_fk", + "tableFrom": "products", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "products_updated_by_auth_users_id_fk": { + "name": "products_updated_by_auth_users_id_fk", + "tableFrom": "products", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.projects": { + "name": "projects", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "projects_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customer": { + "name": "customer", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "phases": { + "name": "phases", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'[]'::jsonb" + }, + "description": { + "name": "description", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "forms": { + "name": "forms", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'[]'::jsonb" + }, + "heroId": { + "name": "heroId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "measure": { + "name": "measure", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "material": { + "name": "material", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "plant": { + "name": "plant", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "profiles": { + "name": "profiles", + "type": "uuid[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "projectNumber": { + "name": "projectNumber", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "contract": { + "name": "contract", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "projectType": { + "name": "projectType", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'Projekt'" + }, + "projecttype": { + "name": "projecttype", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "customerRef": { + "name": "customerRef", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "active_phase": { + "name": "active_phase", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "projects_tenant_tenants_id_fk": { + "name": "projects_tenant_tenants_id_fk", + "tableFrom": "projects", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "projects_customer_customers_id_fk": { + "name": "projects_customer_customers_id_fk", + "tableFrom": "projects", + "tableTo": "customers", + "columnsFrom": [ + "customer" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "projects_contract_contracts_id_fk": { + "name": "projects_contract_contracts_id_fk", + "tableFrom": "projects", + "tableTo": "contracts", + "columnsFrom": [ + "contract" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "projects_projecttype_projecttypes_id_fk": { + "name": "projects_projecttype_projecttypes_id_fk", + "tableFrom": "projects", + "tableTo": "projecttypes", + "columnsFrom": [ + "projecttype" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "projects_updated_by_auth_users_id_fk": { + "name": "projects_updated_by_auth_users_id_fk", + "tableFrom": "projects", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.projecttypes": { + "name": "projecttypes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "projecttypes_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "initialPhases": { + "name": "initialPhases", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "addablePhases": { + "name": "addablePhases", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "projecttypes_tenant_tenants_id_fk": { + "name": "projecttypes_tenant_tenants_id_fk", + "tableFrom": "projecttypes", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "projecttypes_updated_by_auth_users_id_fk": { + "name": "projecttypes_updated_by_auth_users_id_fk", + "tableFrom": "projecttypes", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.servicecategories": { + "name": "servicecategories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "servicecategories_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "discount": { + "name": "discount", + "type": "double precision", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "servicecategories_tenant_tenants_id_fk": { + "name": "servicecategories_tenant_tenants_id_fk", + "tableFrom": "servicecategories", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "servicecategories_updated_by_auth_users_id_fk": { + "name": "servicecategories_updated_by_auth_users_id_fk", + "tableFrom": "servicecategories", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.services": { + "name": "services", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "services_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sellingPrice": { + "name": "sellingPrice", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "unit": { + "name": "unit", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "serviceNumber": { + "name": "serviceNumber", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "tags": { + "name": "tags", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'[]'::jsonb" + }, + "servicecategories": { + "name": "servicecategories", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "purchasePriceComposed": { + "name": "purchasePriceComposed", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"total\":0}'::jsonb" + }, + "sellingPriceComposed": { + "name": "sellingPriceComposed", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"total\":0}'::jsonb" + }, + "taxPercentage": { + "name": "taxPercentage", + "type": "smallint", + "primaryKey": false, + "notNull": true, + "default": 19 + }, + "materialComposition": { + "name": "materialComposition", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "personalComposition": { + "name": "personalComposition", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "services_tenant_tenants_id_fk": { + "name": "services_tenant_tenants_id_fk", + "tableFrom": "services", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "services_unit_units_id_fk": { + "name": "services_unit_units_id_fk", + "tableFrom": "services", + "tableTo": "units", + "columnsFrom": [ + "unit" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "services_updated_by_auth_users_id_fk": { + "name": "services_updated_by_auth_users_id_fk", + "tableFrom": "services", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.spaces": { + "name": "spaces", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "spaces_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "spaceNumber": { + "name": "spaceNumber", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "parentSpace": { + "name": "parentSpace", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "infoData": { + "name": "infoData", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"zip\":\"\",\"city\":\"\",\"streetNumber\":\"\"}'::jsonb" + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "spaces_tenant_tenants_id_fk": { + "name": "spaces_tenant_tenants_id_fk", + "tableFrom": "spaces", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "spaces_parentSpace_spaces_id_fk": { + "name": "spaces_parentSpace_spaces_id_fk", + "tableFrom": "spaces", + "tableTo": "spaces", + "columnsFrom": [ + "parentSpace" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "spaces_updated_by_auth_users_id_fk": { + "name": "spaces_updated_by_auth_users_id_fk", + "tableFrom": "spaces", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.staff_time_entries": { + "name": "staff_time_entries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "tenant_id": { + "name": "tenant_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "stopped_at": { + "name": "stopped_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "duration_minutes": { + "name": "duration_minutes", + "type": "integer", + "primaryKey": false, + "notNull": false, + "generated": { + "as": "CASE \n WHEN stopped_at IS NOT NULL \n THEN (EXTRACT(epoch FROM (stopped_at - started_at)) / 60)\n ELSE NULL\n END", + "type": "stored" + } + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'work'" + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "state": { + "name": "state", + "type": "times_state", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'draft'" + }, + "device": { + "name": "device", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "internal_note": { + "name": "internal_note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "vacation_reason": { + "name": "vacation_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "vacation_days": { + "name": "vacation_days", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": false + }, + "approved_by": { + "name": "approved_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "approved_at": { + "name": "approved_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "sick_reason": { + "name": "sick_reason", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "staff_time_entries_tenant_id_tenants_id_fk": { + "name": "staff_time_entries_tenant_id_tenants_id_fk", + "tableFrom": "staff_time_entries", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "staff_time_entries_user_id_auth_users_id_fk": { + "name": "staff_time_entries_user_id_auth_users_id_fk", + "tableFrom": "staff_time_entries", + "tableTo": "auth_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "staff_time_entries_updated_by_auth_users_id_fk": { + "name": "staff_time_entries_updated_by_auth_users_id_fk", + "tableFrom": "staff_time_entries", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "staff_time_entries_approved_by_auth_users_id_fk": { + "name": "staff_time_entries_approved_by_auth_users_id_fk", + "tableFrom": "staff_time_entries", + "tableTo": "auth_users", + "columnsFrom": [ + "approved_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.staff_time_entry_connects": { + "name": "staff_time_entry_connects", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "time_entry_id": { + "name": "time_entry_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "project_id": { + "name": "project_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "stopped_at": { + "name": "stopped_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "duration_minutes": { + "name": "duration_minutes", + "type": "integer", + "primaryKey": false, + "notNull": false, + "generated": { + "as": "(EXTRACT(epoch FROM (stopped_at - started_at)) / 60)", + "type": "stored" + } + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "staff_time_entry_connects_time_entry_id_staff_time_entries_id_fk": { + "name": "staff_time_entry_connects_time_entry_id_staff_time_entries_id_fk", + "tableFrom": "staff_time_entry_connects", + "tableTo": "staff_time_entries", + "columnsFrom": [ + "time_entry_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.staff_zeitstromtimestamps": { + "name": "staff_zeitstromtimestamps", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "profile": { + "name": "profile", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "intent": { + "name": "intent", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "time": { + "name": "time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "staff_time_entry": { + "name": "staff_time_entry", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "internal_note": { + "name": "internal_note", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "staff_zeitstromtimestamps_tenant_tenants_id_fk": { + "name": "staff_zeitstromtimestamps_tenant_tenants_id_fk", + "tableFrom": "staff_zeitstromtimestamps", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "staff_zeitstromtimestamps_profile_auth_profiles_id_fk": { + "name": "staff_zeitstromtimestamps_profile_auth_profiles_id_fk", + "tableFrom": "staff_zeitstromtimestamps", + "tableTo": "auth_profiles", + "columnsFrom": [ + "profile" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "staff_zeitstromtimestamps_staff_time_entry_staff_time_entries_id_fk": { + "name": "staff_zeitstromtimestamps_staff_time_entry_staff_time_entries_id_fk", + "tableFrom": "staff_zeitstromtimestamps", + "tableTo": "staff_time_entries", + "columnsFrom": [ + "staff_time_entry" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.statementallocations": { + "name": "statementallocations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "bs_id": { + "name": "bs_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "cd_id": { + "name": "cd_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "amount": { + "name": "amount", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "ii_id": { + "name": "ii_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "account": { + "name": "account", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "ownaccount": { + "name": "ownaccount", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customer": { + "name": "customer", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "vendor": { + "name": "vendor", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "statementallocations_bs_id_bankstatements_id_fk": { + "name": "statementallocations_bs_id_bankstatements_id_fk", + "tableFrom": "statementallocations", + "tableTo": "bankstatements", + "columnsFrom": [ + "bs_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "statementallocations_cd_id_createddocuments_id_fk": { + "name": "statementallocations_cd_id_createddocuments_id_fk", + "tableFrom": "statementallocations", + "tableTo": "createddocuments", + "columnsFrom": [ + "cd_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "statementallocations_ii_id_incominginvoices_id_fk": { + "name": "statementallocations_ii_id_incominginvoices_id_fk", + "tableFrom": "statementallocations", + "tableTo": "incominginvoices", + "columnsFrom": [ + "ii_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "statementallocations_tenant_tenants_id_fk": { + "name": "statementallocations_tenant_tenants_id_fk", + "tableFrom": "statementallocations", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "statementallocations_account_accounts_id_fk": { + "name": "statementallocations_account_accounts_id_fk", + "tableFrom": "statementallocations", + "tableTo": "accounts", + "columnsFrom": [ + "account" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "statementallocations_ownaccount_ownaccounts_id_fk": { + "name": "statementallocations_ownaccount_ownaccounts_id_fk", + "tableFrom": "statementallocations", + "tableTo": "ownaccounts", + "columnsFrom": [ + "ownaccount" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "statementallocations_customer_customers_id_fk": { + "name": "statementallocations_customer_customers_id_fk", + "tableFrom": "statementallocations", + "tableTo": "customers", + "columnsFrom": [ + "customer" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "statementallocations_vendor_vendors_id_fk": { + "name": "statementallocations_vendor_vendors_id_fk", + "tableFrom": "statementallocations", + "tableTo": "vendors", + "columnsFrom": [ + "vendor" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "statementallocations_updated_by_auth_users_id_fk": { + "name": "statementallocations_updated_by_auth_users_id_fk", + "tableFrom": "statementallocations", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tasks": { + "name": "tasks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "tasks_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "categorie": { + "name": "categorie", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "project": { + "name": "project", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "plant": { + "name": "plant", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "customer": { + "name": "customer", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "profiles": { + "name": "profiles", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "tasks_tenant_tenants_id_fk": { + "name": "tasks_tenant_tenants_id_fk", + "tableFrom": "tasks", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "tasks_user_id_auth_users_id_fk": { + "name": "tasks_user_id_auth_users_id_fk", + "tableFrom": "tasks", + "tableTo": "auth_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "tasks_customer_customers_id_fk": { + "name": "tasks_customer_customers_id_fk", + "tableFrom": "tasks", + "tableTo": "customers", + "columnsFrom": [ + "customer" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "tasks_updated_by_auth_users_id_fk": { + "name": "tasks_updated_by_auth_users_id_fk", + "tableFrom": "tasks", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.taxtypes": { + "name": "taxtypes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "taxtypes_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "percentage": { + "name": "percentage", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "taxtypes_updated_by_auth_users_id_fk": { + "name": "taxtypes_updated_by_auth_users_id_fk", + "tableFrom": "taxtypes", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tenants": { + "name": "tenants", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "tenants_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "short": { + "name": "short", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "calendarConfig": { + "name": "calendarConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'{\"eventTypes\":[{\"color\":\"blue\",\"label\":\"Büro\"},{\"color\":\"yellow\",\"label\":\"Besprechung\"},{\"color\":\"green\",\"label\":\"Umsetzung\"},{\"color\":\"red\",\"label\":\"Vor Ort Termin\"}]}'::jsonb" + }, + "timeConfig": { + "name": "timeConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "tags": { + "name": "tags", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"products\":[],\"documents\":[]}'::jsonb" + }, + "measures": { + "name": "measures", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[{\"name\":\"Netzwerktechnik\",\"short\":\"NWT\"},{\"name\":\"Elektrotechnik\",\"short\":\"ELT\"},{\"name\":\"Photovoltaik\",\"short\":\"PV\"},{\"name\":\"Videüberwachung\",\"short\":\"VÜA\"},{\"name\":\"Projekt\",\"short\":\"PRJ\"},{\"name\":\"Smart Home\",\"short\":\"SHO\"}]'::jsonb" + }, + "businessInfo": { + "name": "businessInfo", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'{\"zip\":\"\",\"city\":\"\",\"name\":\"\",\"street\":\"\"}'::jsonb" + }, + "features": { + "name": "features", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'{\"objects\":true,\"calendar\":true,\"contacts\":true,\"projects\":true,\"vehicles\":true,\"contracts\":true,\"inventory\":true,\"accounting\":true,\"timeTracking\":true,\"planningBoard\":true,\"workingTimeTracking\":true}'::jsonb" + }, + "ownFields": { + "name": "ownFields", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "numberRanges": { + "name": "numberRanges", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"vendors\":{\"prefix\":\"\",\"suffix\":\"\",\"nextNumber\":10000},\"customers\":{\"prefix\":\"\",\"suffix\":\"\",\"nextNumber\":10000},\"products\":{\"prefix\":\"AT-\",\"suffix\":\"\",\"nextNumber\":1000},\"quotes\":{\"prefix\":\"AN-\",\"suffix\":\"\",\"nextNumber\":1000},\"confirmationOrders\":{\"prefix\":\"AB-\",\"suffix\":\"\",\"nextNumber\":1000},\"invoices\":{\"prefix\":\"RE-\",\"suffix\":\"\",\"nextNumber\":1000},\"spaces\":{\"prefix\":\"LP-\",\"suffix\":\"\",\"nextNumber\":1000},\"inventoryitems\":{\"prefix\":\"IA-\",\"suffix\":\"\",\"nextNumber\":1000},\"projects\":{\"prefix\":\"PRJ-\",\"suffix\":\"\",\"nextNumber\":1000},\"costcentres\":{\"prefix\":\"KST-\",\"suffix\":\"\",\"nextNumber\":1000}}'::jsonb" + }, + "standardEmailForInvoices": { + "name": "standardEmailForInvoices", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "extraModules": { + "name": "extraModules", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "isInTrial": { + "name": "isInTrial", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "trialEndDate": { + "name": "trialEndDate", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "stripeCustomerId": { + "name": "stripeCustomerId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "hasActiveLicense": { + "name": "hasActiveLicense", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "userLicenseCount": { + "name": "userLicenseCount", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "workstationLicenseCount": { + "name": "workstationLicenseCount", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "standardPaymentDays": { + "name": "standardPaymentDays", + "type": "smallint", + "primaryKey": false, + "notNull": true, + "default": 14 + }, + "dokuboxEmailAddresses": { + "name": "dokuboxEmailAddresses", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'[]'::jsonb" + }, + "dokuboxkey": { + "name": "dokuboxkey", + "type": "uuid", + "primaryKey": false, + "notNull": true, + "default": "gen_random_uuid()" + }, + "autoPrepareIncomingInvoices": { + "name": "autoPrepareIncomingInvoices", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "portalDomain": { + "name": "portalDomain", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "portalConfig": { + "name": "portalConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"primayColor\":\"#69c350\"}'::jsonb" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "locked": { + "name": "locked", + "type": "locked_tenant", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "tenants_updated_by_auth_users_id_fk": { + "name": "tenants_updated_by_auth_users_id_fk", + "tableFrom": "tenants", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.texttemplates": { + "name": "texttemplates", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "texttemplates_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "text": { + "name": "text", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "documentType": { + "name": "documentType", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "''" + }, + "default": { + "name": "default", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "pos": { + "name": "pos", + "type": "texttemplatepositions", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "texttemplates_tenant_tenants_id_fk": { + "name": "texttemplates_tenant_tenants_id_fk", + "tableFrom": "texttemplates", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "texttemplates_updated_by_auth_users_id_fk": { + "name": "texttemplates_updated_by_auth_users_id_fk", + "tableFrom": "texttemplates", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.units": { + "name": "units", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "units_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "single": { + "name": "single", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "multiple": { + "name": "multiple", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "short": { + "name": "short", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "step": { + "name": "step", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'1'" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_credentials": { + "name": "user_credentials", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "smtp_port": { + "name": "smtp_port", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "smtp_ssl": { + "name": "smtp_ssl", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "credential_types", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "imap_port": { + "name": "imap_port", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "imap_ssl": { + "name": "imap_ssl", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "email_encrypted": { + "name": "email_encrypted", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "password_encrypted": { + "name": "password_encrypted", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "smtp_host_encrypted": { + "name": "smtp_host_encrypted", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "imap_host_encrypted": { + "name": "imap_host_encrypted", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "access_token_encrypted": { + "name": "access_token_encrypted", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "refresh_token_encrypted": { + "name": "refresh_token_encrypted", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "user_credentials_user_id_auth_users_id_fk": { + "name": "user_credentials_user_id_auth_users_id_fk", + "tableFrom": "user_credentials", + "tableTo": "auth_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "user_credentials_tenant_id_tenants_id_fk": { + "name": "user_credentials_tenant_id_tenants_id_fk", + "tableFrom": "user_credentials", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.vehicles": { + "name": "vehicles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "vehicles_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "licensePlate": { + "name": "licensePlate", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "driver": { + "name": "driver", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "vin": { + "name": "vin", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tankSize": { + "name": "tankSize", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "buildYear": { + "name": "buildYear", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "towingCapacity": { + "name": "towingCapacity", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "powerInKW": { + "name": "powerInKW", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "profiles": { + "name": "profiles", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "vehicles_tenant_tenants_id_fk": { + "name": "vehicles_tenant_tenants_id_fk", + "tableFrom": "vehicles", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "vehicles_driver_auth_users_id_fk": { + "name": "vehicles_driver_auth_users_id_fk", + "tableFrom": "vehicles", + "tableTo": "auth_users", + "columnsFrom": [ + "driver" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "vehicles_updated_by_auth_users_id_fk": { + "name": "vehicles_updated_by_auth_users_id_fk", + "tableFrom": "vehicles", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.vendors": { + "name": "vendors", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "vendors_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "vendorNumber": { + "name": "vendorNumber", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "infoData": { + "name": "infoData", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "hasSEPA": { + "name": "hasSEPA", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "profiles": { + "name": "profiles", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "defaultPaymentMethod": { + "name": "defaultPaymentMethod", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "vendors_tenant_tenants_id_fk": { + "name": "vendors_tenant_tenants_id_fk", + "tableFrom": "vendors", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "vendors_updated_by_auth_users_id_fk": { + "name": "vendors_updated_by_auth_users_id_fk", + "tableFrom": "vendors", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.credential_types": { + "name": "credential_types", + "schema": "public", + "values": [ + "mail", + "m365" + ] + }, + "public.folderfunctions": { + "name": "folderfunctions", + "schema": "public", + "values": [ + "none", + "yearSubCategory", + "incomingInvoices", + "invoices", + "quotes", + "confirmationOrders", + "deliveryNotes", + "vehicleData", + "reminders", + "taxData", + "deposit", + "timeEvaluations" + ] + }, + "public.locked_tenant": { + "name": "locked_tenant", + "schema": "public", + "values": [ + "maintenance_tenant", + "maintenance", + "general", + "no_subscription" + ] + }, + "public.notification_channel": { + "name": "notification_channel", + "schema": "public", + "values": [ + "email", + "inapp", + "sms", + "push", + "webhook" + ] + }, + "public.notification_severity": { + "name": "notification_severity", + "schema": "public", + "values": [ + "info", + "success", + "warning", + "error" + ] + }, + "public.notification_status": { + "name": "notification_status", + "schema": "public", + "values": [ + "queued", + "sent", + "failed", + "read" + ] + }, + "public.payment_types": { + "name": "payment_types", + "schema": "public", + "values": [ + "transfer", + "direct_debit" + ] + }, + "public.texttemplatepositions": { + "name": "texttemplatepositions", + "schema": "public", + "values": [ + "startText", + "endText" + ] + }, + "public.times_state": { + "name": "times_state", + "schema": "public", + "values": [ + "submitted", + "approved", + "draft" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/db/migrations/meta/0001_snapshot.json b/db/migrations/meta/0001_snapshot.json new file mode 100644 index 0000000..f5a2f32 --- /dev/null +++ b/db/migrations/meta/0001_snapshot.json @@ -0,0 +1,9947 @@ +{ + "id": "91737e8e-9323-4b3f-9c03-13793e3b160c", + "prevId": "c74cefc4-5ae7-408c-b7f3-09093efb52b5", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.accounts": { + "name": "accounts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "accounts_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "number": { + "name": "number", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth_profiles": { + "name": "auth_profiles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "first_name": { + "name": "first_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "last_name": { + "name": "last_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "full_name": { + "name": "full_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "generated": { + "as": "((first_name || ' ') || last_name)", + "type": "stored" + } + }, + "mobile_tel": { + "name": "mobile_tel", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "fixed_tel": { + "name": "fixed_tel", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "salutation": { + "name": "salutation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "employee_number": { + "name": "employee_number", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "weekly_working_hours": { + "name": "weekly_working_hours", + "type": "double precision", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "annual_paid_leave_days": { + "name": "annual_paid_leave_days", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "weekly_regular_working_hours": { + "name": "weekly_regular_working_hours", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "clothing_size_top": { + "name": "clothing_size_top", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "clothing_size_bottom": { + "name": "clothing_size_bottom", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "clothing_size_shoe": { + "name": "clothing_size_shoe", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email_signature": { + "name": "email_signature", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'

Mit freundlichen Grüßen

'" + }, + "birthday": { + "name": "birthday", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "entry_date": { + "name": "entry_date", + "type": "date", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "automatic_hour_corrections": { + "name": "automatic_hour_corrections", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'[]'" + }, + "recreation_days_compensation": { + "name": "recreation_days_compensation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "customer_for_portal": { + "name": "customer_for_portal", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "pinned_on_navigation": { + "name": "pinned_on_navigation", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'" + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "token_id": { + "name": "token_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "weekly_working_days": { + "name": "weekly_working_days", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "old_profile_id": { + "name": "old_profile_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "temp_config": { + "name": "temp_config", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "state_code": { + "name": "state_code", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'DE-NI'" + }, + "contract_type": { + "name": "contract_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "position": { + "name": "position", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "qualification": { + "name": "qualification", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address_street": { + "name": "address_street", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address_zip": { + "name": "address_zip", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address_city": { + "name": "address_city", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + } + }, + "indexes": {}, + "foreignKeys": { + "auth_profiles_user_id_auth_users_id_fk": { + "name": "auth_profiles_user_id_auth_users_id_fk", + "tableFrom": "auth_profiles", + "tableTo": "auth_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth_role_permissions": { + "name": "auth_role_permissions", + "schema": "", + "columns": { + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "role_id": { + "name": "role_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "permission": { + "name": "permission", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "auth_role_permissions_role_id_auth_roles_id_fk": { + "name": "auth_role_permissions_role_id_auth_roles_id_fk", + "tableFrom": "auth_role_permissions", + "tableTo": "auth_roles", + "columnsFrom": [ + "role_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth_roles": { + "name": "auth_roles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "auth_roles_created_by_auth_users_id_fk": { + "name": "auth_roles_created_by_auth_users_id_fk", + "tableFrom": "auth_roles", + "tableTo": "auth_users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth_tenant_users": { + "name": "auth_tenant_users", + "schema": "", + "columns": { + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "tenant_id": { + "name": "tenant_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "auth_tenant_users_created_by_auth_users_id_fk": { + "name": "auth_tenant_users_created_by_auth_users_id_fk", + "tableFrom": "auth_tenant_users", + "tableTo": "auth_users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth_user_roles": { + "name": "auth_user_roles", + "schema": "", + "columns": { + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "role_id": { + "name": "role_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "tenant_id": { + "name": "tenant_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "auth_user_roles_user_id_auth_users_id_fk": { + "name": "auth_user_roles_user_id_auth_users_id_fk", + "tableFrom": "auth_user_roles", + "tableTo": "auth_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "auth_user_roles_role_id_auth_roles_id_fk": { + "name": "auth_user_roles_role_id_auth_roles_id_fk", + "tableFrom": "auth_user_roles", + "tableTo": "auth_roles", + "columnsFrom": [ + "role_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "auth_user_roles_created_by_auth_users_id_fk": { + "name": "auth_user_roles_created_by_auth_users_id_fk", + "tableFrom": "auth_user_roles", + "tableTo": "auth_users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth_users": { + "name": "auth_users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password_hash": { + "name": "password_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "multi_tenant": { + "name": "multi_tenant", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "must_change_password": { + "name": "must_change_password", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "ported": { + "name": "ported", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.bankaccounts": { + "name": "bankaccounts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "bankaccounts_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "iban": { + "name": "iban", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "bankId": { + "name": "bankId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "ownerName": { + "name": "ownerName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "accountId": { + "name": "accountId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "balance": { + "name": "balance", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "expired": { + "name": "expired", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "datevNumber": { + "name": "datevNumber", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "synced_at": { + "name": "synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "bankaccounts_tenant_tenants_id_fk": { + "name": "bankaccounts_tenant_tenants_id_fk", + "tableFrom": "bankaccounts", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "bankaccounts_updated_by_auth_users_id_fk": { + "name": "bankaccounts_updated_by_auth_users_id_fk", + "tableFrom": "bankaccounts", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.bankrequisitions": { + "name": "bankrequisitions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "institutionId": { + "name": "institutionId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "bankrequisitions_tenant_tenants_id_fk": { + "name": "bankrequisitions_tenant_tenants_id_fk", + "tableFrom": "bankrequisitions", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "bankrequisitions_updated_by_auth_users_id_fk": { + "name": "bankrequisitions_updated_by_auth_users_id_fk", + "tableFrom": "bankrequisitions", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.bankstatements": { + "name": "bankstatements", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "bankstatements_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "account": { + "name": "account", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "date": { + "name": "date", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "credIban": { + "name": "credIban", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "credName": { + "name": "credName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "text": { + "name": "text", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "amount": { + "name": "amount", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "debIban": { + "name": "debIban", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "debName": { + "name": "debName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gocardlessId": { + "name": "gocardlessId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "currency": { + "name": "currency", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "valueDate": { + "name": "valueDate", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mandateId": { + "name": "mandateId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "contract": { + "name": "contract", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "bankstatements_account_bankaccounts_id_fk": { + "name": "bankstatements_account_bankaccounts_id_fk", + "tableFrom": "bankstatements", + "tableTo": "bankaccounts", + "columnsFrom": [ + "account" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "bankstatements_tenant_tenants_id_fk": { + "name": "bankstatements_tenant_tenants_id_fk", + "tableFrom": "bankstatements", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "bankstatements_contract_contracts_id_fk": { + "name": "bankstatements_contract_contracts_id_fk", + "tableFrom": "bankstatements", + "tableTo": "contracts", + "columnsFrom": [ + "contract" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "bankstatements_updated_by_auth_users_id_fk": { + "name": "bankstatements_updated_by_auth_users_id_fk", + "tableFrom": "bankstatements", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.checkexecutions": { + "name": "checkexecutions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "check": { + "name": "check", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "executed_at": { + "name": "executed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "checkexecutions_check_checks_id_fk": { + "name": "checkexecutions_check_checks_id_fk", + "tableFrom": "checkexecutions", + "tableTo": "checks", + "columnsFrom": [ + "check" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.checks": { + "name": "checks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "vehicle": { + "name": "vehicle", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "inventoryitem": { + "name": "inventoryitem", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "distance": { + "name": "distance", + "type": "bigint", + "primaryKey": false, + "notNull": false, + "default": 1 + }, + "distanceUnit": { + "name": "distanceUnit", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'days'" + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "profiles": { + "name": "profiles", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "checks_vehicle_vehicles_id_fk": { + "name": "checks_vehicle_vehicles_id_fk", + "tableFrom": "checks", + "tableTo": "vehicles", + "columnsFrom": [ + "vehicle" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "checks_inventoryitem_inventoryitems_id_fk": { + "name": "checks_inventoryitem_inventoryitems_id_fk", + "tableFrom": "checks", + "tableTo": "inventoryitems", + "columnsFrom": [ + "inventoryitem" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "checks_tenant_tenants_id_fk": { + "name": "checks_tenant_tenants_id_fk", + "tableFrom": "checks", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "checks_updated_by_auth_users_id_fk": { + "name": "checks_updated_by_auth_users_id_fk", + "tableFrom": "checks", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.citys": { + "name": "citys", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "citys_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "short": { + "name": "short", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "long": { + "name": "long", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "geometry": { + "name": "geometry", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "zip": { + "name": "zip", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "districtCode": { + "name": "districtCode", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "countryName": { + "name": "countryName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "countryCode": { + "name": "countryCode", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "districtName": { + "name": "districtName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "geopoint": { + "name": "geopoint", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.contacts": { + "name": "contacts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "contacts_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "firstName": { + "name": "firstName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lastName": { + "name": "lastName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customer": { + "name": "customer", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "phoneMobile": { + "name": "phoneMobile", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "phoneHome": { + "name": "phoneHome", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "heroId": { + "name": "heroId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "fullName": { + "name": "fullName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "salutation": { + "name": "salutation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "vendor": { + "name": "vendor", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "birthday": { + "name": "birthday", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "profiles": { + "name": "profiles", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "contacts_customer_customers_id_fk": { + "name": "contacts_customer_customers_id_fk", + "tableFrom": "contacts", + "tableTo": "customers", + "columnsFrom": [ + "customer" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "contacts_updated_by_auth_users_id_fk": { + "name": "contacts_updated_by_auth_users_id_fk", + "tableFrom": "contacts", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.contracts": { + "name": "contracts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "contracts_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "customer": { + "name": "customer", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "recurring": { + "name": "recurring", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "rhythm": { + "name": "rhythm", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "startDate": { + "name": "startDate", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "endDate": { + "name": "endDate", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "signDate": { + "name": "signDate", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "duration": { + "name": "duration", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "contact": { + "name": "contact", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "bankingIban": { + "name": "bankingIban", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bankingBIC": { + "name": "bankingBIC", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bankingName": { + "name": "bankingName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bankingOwner": { + "name": "bankingOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sepaRef": { + "name": "sepaRef", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sepaDate": { + "name": "sepaDate", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "paymentType": { + "name": "paymentType", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "invoiceDispatch": { + "name": "invoiceDispatch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ownFields": { + "name": "ownFields", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "profiles": { + "name": "profiles", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "contractNumber": { + "name": "contractNumber", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "contracts_customer_customers_id_fk": { + "name": "contracts_customer_customers_id_fk", + "tableFrom": "contracts", + "tableTo": "customers", + "columnsFrom": [ + "customer" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "contracts_contact_contacts_id_fk": { + "name": "contracts_contact_contacts_id_fk", + "tableFrom": "contracts", + "tableTo": "contacts", + "columnsFrom": [ + "contact" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "contracts_updated_by_auth_users_id_fk": { + "name": "contracts_updated_by_auth_users_id_fk", + "tableFrom": "contracts", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.costcentres": { + "name": "costcentres", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "number": { + "name": "number", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "vehicle": { + "name": "vehicle", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "project": { + "name": "project", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "inventoryitem": { + "name": "inventoryitem", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "profiles": { + "name": "profiles", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "costcentres_tenant_tenants_id_fk": { + "name": "costcentres_tenant_tenants_id_fk", + "tableFrom": "costcentres", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "costcentres_vehicle_vehicles_id_fk": { + "name": "costcentres_vehicle_vehicles_id_fk", + "tableFrom": "costcentres", + "tableTo": "vehicles", + "columnsFrom": [ + "vehicle" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "costcentres_project_projects_id_fk": { + "name": "costcentres_project_projects_id_fk", + "tableFrom": "costcentres", + "tableTo": "projects", + "columnsFrom": [ + "project" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "costcentres_inventoryitem_inventoryitems_id_fk": { + "name": "costcentres_inventoryitem_inventoryitems_id_fk", + "tableFrom": "costcentres", + "tableTo": "inventoryitems", + "columnsFrom": [ + "inventoryitem" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "costcentres_updated_by_auth_users_id_fk": { + "name": "costcentres_updated_by_auth_users_id_fk", + "tableFrom": "costcentres", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.countrys": { + "name": "countrys", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "countrys_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.createddocuments": { + "name": "createddocuments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "createddocuments_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'INVOICE'" + }, + "customer": { + "name": "customer", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "contact": { + "name": "contact", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "address": { + "name": "address", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "project": { + "name": "project", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "documentNumber": { + "name": "documentNumber", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "documentDate": { + "name": "documentDate", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'Entwurf'" + }, + "info": { + "name": "info", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "createdBy": { + "name": "createdBy", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "startText": { + "name": "startText", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "endText": { + "name": "endText", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rows": { + "name": "rows", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'[]'::jsonb" + }, + "deliveryDateType": { + "name": "deliveryDateType", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "paymentDays": { + "name": "paymentDays", + "type": "smallint", + "primaryKey": false, + "notNull": false + }, + "deliveryDate": { + "name": "deliveryDate", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "contactPerson": { + "name": "contactPerson", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "serialConfig": { + "name": "serialConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'{}'::jsonb" + }, + "linkedDocument": { + "name": "linkedDocument", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "agriculture": { + "name": "agriculture", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "letterhead": { + "name": "letterhead", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "advanceInvoiceResolved": { + "name": "advanceInvoiceResolved", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "usedAdvanceInvoices": { + "name": "usedAdvanceInvoices", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "deliveryDateEnd": { + "name": "deliveryDateEnd", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "plant": { + "name": "plant", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "taxType": { + "name": "taxType", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customSurchargePercentage": { + "name": "customSurchargePercentage", + "type": "smallint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "report": { + "name": "report", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "availableInPortal": { + "name": "availableInPortal", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "payment_type": { + "name": "payment_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'transfer'" + }, + "contract": { + "name": "contract", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "createddocuments_tenant_tenants_id_fk": { + "name": "createddocuments_tenant_tenants_id_fk", + "tableFrom": "createddocuments", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "createddocuments_customer_customers_id_fk": { + "name": "createddocuments_customer_customers_id_fk", + "tableFrom": "createddocuments", + "tableTo": "customers", + "columnsFrom": [ + "customer" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "createddocuments_contact_contacts_id_fk": { + "name": "createddocuments_contact_contacts_id_fk", + "tableFrom": "createddocuments", + "tableTo": "contacts", + "columnsFrom": [ + "contact" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "createddocuments_project_projects_id_fk": { + "name": "createddocuments_project_projects_id_fk", + "tableFrom": "createddocuments", + "tableTo": "projects", + "columnsFrom": [ + "project" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "createddocuments_createdBy_auth_users_id_fk": { + "name": "createddocuments_createdBy_auth_users_id_fk", + "tableFrom": "createddocuments", + "tableTo": "auth_users", + "columnsFrom": [ + "createdBy" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "createddocuments_linkedDocument_createddocuments_id_fk": { + "name": "createddocuments_linkedDocument_createddocuments_id_fk", + "tableFrom": "createddocuments", + "tableTo": "createddocuments", + "columnsFrom": [ + "linkedDocument" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "createddocuments_letterhead_letterheads_id_fk": { + "name": "createddocuments_letterhead_letterheads_id_fk", + "tableFrom": "createddocuments", + "tableTo": "letterheads", + "columnsFrom": [ + "letterhead" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "createddocuments_plant_plants_id_fk": { + "name": "createddocuments_plant_plants_id_fk", + "tableFrom": "createddocuments", + "tableTo": "plants", + "columnsFrom": [ + "plant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "createddocuments_updated_by_auth_users_id_fk": { + "name": "createddocuments_updated_by_auth_users_id_fk", + "tableFrom": "createddocuments", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "createddocuments_created_by_auth_users_id_fk": { + "name": "createddocuments_created_by_auth_users_id_fk", + "tableFrom": "createddocuments", + "tableTo": "auth_users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "createddocuments_contract_contracts_id_fk": { + "name": "createddocuments_contract_contracts_id_fk", + "tableFrom": "createddocuments", + "tableTo": "contracts", + "columnsFrom": [ + "contract" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.createdletters": { + "name": "createdletters", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "customer": { + "name": "customer", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "vendor": { + "name": "vendor", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "content_json": { + "name": "content_json", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'[]'::jsonb" + }, + "content_text": { + "name": "content_text", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "createdletters_tenant_tenants_id_fk": { + "name": "createdletters_tenant_tenants_id_fk", + "tableFrom": "createdletters", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "createdletters_customer_customers_id_fk": { + "name": "createdletters_customer_customers_id_fk", + "tableFrom": "createdletters", + "tableTo": "customers", + "columnsFrom": [ + "customer" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "createdletters_vendor_vendors_id_fk": { + "name": "createdletters_vendor_vendors_id_fk", + "tableFrom": "createdletters", + "tableTo": "vendors", + "columnsFrom": [ + "vendor" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "createdletters_updated_by_auth_users_id_fk": { + "name": "createdletters_updated_by_auth_users_id_fk", + "tableFrom": "createdletters", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.customers": { + "name": "customers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "customers_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "customerNumber": { + "name": "customerNumber", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "infoData": { + "name": "infoData", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'{}'::jsonb" + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'Privat'" + }, + "heroId": { + "name": "heroId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "isCompany": { + "name": "isCompany", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "profiles": { + "name": "profiles", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "customPaymentDays": { + "name": "customPaymentDays", + "type": "smallint", + "primaryKey": false, + "notNull": false + }, + "firstname": { + "name": "firstname", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lastname": { + "name": "lastname", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "customSurchargePercentage": { + "name": "customSurchargePercentage", + "type": "smallint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "salutation": { + "name": "salutation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "nameAddition": { + "name": "nameAddition", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "availableInPortal": { + "name": "availableInPortal", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "custom_payment_type": { + "name": "custom_payment_type", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "customers_updated_by_auth_users_id_fk": { + "name": "customers_updated_by_auth_users_id_fk", + "tableFrom": "customers", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.devices": { + "name": "devices", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalId": { + "name": "externalId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "devices_tenant_tenants_id_fk": { + "name": "devices_tenant_tenants_id_fk", + "tableFrom": "devices", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.documentboxes": { + "name": "documentboxes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "space": { + "name": "space", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "documentboxes_space_spaces_id_fk": { + "name": "documentboxes_space_spaces_id_fk", + "tableFrom": "documentboxes", + "tableTo": "spaces", + "columnsFrom": [ + "space" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "documentboxes_tenant_tenants_id_fk": { + "name": "documentboxes_tenant_tenants_id_fk", + "tableFrom": "documentboxes", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "documentboxes_updated_by_auth_users_id_fk": { + "name": "documentboxes_updated_by_auth_users_id_fk", + "tableFrom": "documentboxes", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.events": { + "name": "events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "events_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "startDate": { + "name": "startDate", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "endDate": { + "name": "endDate", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "eventtype": { + "name": "eventtype", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'Umsetzung'" + }, + "project": { + "name": "project", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "resources": { + "name": "resources", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'[]'::jsonb" + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "link": { + "name": "link", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "profiles": { + "name": "profiles", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "vehicles": { + "name": "vehicles", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "inventoryitems": { + "name": "inventoryitems", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "inventoryitemgroups": { + "name": "inventoryitemgroups", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "customer": { + "name": "customer", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "vendor": { + "name": "vendor", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "events_customer_customers_id_fk": { + "name": "events_customer_customers_id_fk", + "tableFrom": "events", + "tableTo": "customers", + "columnsFrom": [ + "customer" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "events_updated_by_auth_users_id_fk": { + "name": "events_updated_by_auth_users_id_fk", + "tableFrom": "events", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.files": { + "name": "files", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "project": { + "name": "project", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "customer": { + "name": "customer", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "contract": { + "name": "contract", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "vendor": { + "name": "vendor", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "incominginvoice": { + "name": "incominginvoice", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "plant": { + "name": "plant", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "createddocument": { + "name": "createddocument", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "vehicle": { + "name": "vehicle", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "product": { + "name": "product", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "check": { + "name": "check", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "inventoryitem": { + "name": "inventoryitem", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "folder": { + "name": "folder", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "mimeType": { + "name": "mimeType", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "space": { + "name": "space", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "documentbox": { + "name": "documentbox", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "auth_profile": { + "name": "auth_profile", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "files_tenant_tenants_id_fk": { + "name": "files_tenant_tenants_id_fk", + "tableFrom": "files", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "files_project_projects_id_fk": { + "name": "files_project_projects_id_fk", + "tableFrom": "files", + "tableTo": "projects", + "columnsFrom": [ + "project" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "files_customer_customers_id_fk": { + "name": "files_customer_customers_id_fk", + "tableFrom": "files", + "tableTo": "customers", + "columnsFrom": [ + "customer" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "files_contract_contracts_id_fk": { + "name": "files_contract_contracts_id_fk", + "tableFrom": "files", + "tableTo": "contracts", + "columnsFrom": [ + "contract" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "files_vendor_vendors_id_fk": { + "name": "files_vendor_vendors_id_fk", + "tableFrom": "files", + "tableTo": "vendors", + "columnsFrom": [ + "vendor" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "files_incominginvoice_incominginvoices_id_fk": { + "name": "files_incominginvoice_incominginvoices_id_fk", + "tableFrom": "files", + "tableTo": "incominginvoices", + "columnsFrom": [ + "incominginvoice" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "files_plant_plants_id_fk": { + "name": "files_plant_plants_id_fk", + "tableFrom": "files", + "tableTo": "plants", + "columnsFrom": [ + "plant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "files_createddocument_createddocuments_id_fk": { + "name": "files_createddocument_createddocuments_id_fk", + "tableFrom": "files", + "tableTo": "createddocuments", + "columnsFrom": [ + "createddocument" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "files_vehicle_vehicles_id_fk": { + "name": "files_vehicle_vehicles_id_fk", + "tableFrom": "files", + "tableTo": "vehicles", + "columnsFrom": [ + "vehicle" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "files_product_products_id_fk": { + "name": "files_product_products_id_fk", + "tableFrom": "files", + "tableTo": "products", + "columnsFrom": [ + "product" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "files_check_checks_id_fk": { + "name": "files_check_checks_id_fk", + "tableFrom": "files", + "tableTo": "checks", + "columnsFrom": [ + "check" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "files_inventoryitem_inventoryitems_id_fk": { + "name": "files_inventoryitem_inventoryitems_id_fk", + "tableFrom": "files", + "tableTo": "inventoryitems", + "columnsFrom": [ + "inventoryitem" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "files_folder_folders_id_fk": { + "name": "files_folder_folders_id_fk", + "tableFrom": "files", + "tableTo": "folders", + "columnsFrom": [ + "folder" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "files_space_spaces_id_fk": { + "name": "files_space_spaces_id_fk", + "tableFrom": "files", + "tableTo": "spaces", + "columnsFrom": [ + "space" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "files_type_filetags_id_fk": { + "name": "files_type_filetags_id_fk", + "tableFrom": "files", + "tableTo": "filetags", + "columnsFrom": [ + "type" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "files_documentbox_documentboxes_id_fk": { + "name": "files_documentbox_documentboxes_id_fk", + "tableFrom": "files", + "tableTo": "documentboxes", + "columnsFrom": [ + "documentbox" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "files_updated_by_auth_users_id_fk": { + "name": "files_updated_by_auth_users_id_fk", + "tableFrom": "files", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "files_created_by_auth_users_id_fk": { + "name": "files_created_by_auth_users_id_fk", + "tableFrom": "files", + "tableTo": "auth_users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "files_auth_profile_auth_profiles_id_fk": { + "name": "files_auth_profile_auth_profiles_id_fk", + "tableFrom": "files", + "tableTo": "auth_profiles", + "columnsFrom": [ + "auth_profile" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.filetags": { + "name": "filetags", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "createddocumenttype": { + "name": "createddocumenttype", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "''" + }, + "incomingDocumentType": { + "name": "incomingDocumentType", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "filetags_tenant_tenants_id_fk": { + "name": "filetags_tenant_tenants_id_fk", + "tableFrom": "filetags", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.folders": { + "name": "folders", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "parent": { + "name": "parent", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "isSystemUsed": { + "name": "isSystemUsed", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "function": { + "name": "function", + "type": "folderfunctions", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "year": { + "name": "year", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "standardFiletype": { + "name": "standardFiletype", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "standardFiletypeIsOptional": { + "name": "standardFiletypeIsOptional", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "folders_tenant_tenants_id_fk": { + "name": "folders_tenant_tenants_id_fk", + "tableFrom": "folders", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "folders_parent_folders_id_fk": { + "name": "folders_parent_folders_id_fk", + "tableFrom": "folders", + "tableTo": "folders", + "columnsFrom": [ + "parent" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "folders_standardFiletype_filetags_id_fk": { + "name": "folders_standardFiletype_filetags_id_fk", + "tableFrom": "folders", + "tableTo": "filetags", + "columnsFrom": [ + "standardFiletype" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "folders_updated_by_auth_users_id_fk": { + "name": "folders_updated_by_auth_users_id_fk", + "tableFrom": "folders", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.exports": { + "name": "exports", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "exports_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "tenant_id": { + "name": "tenant_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "start_date": { + "name": "start_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "end_date": { + "name": "end_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "valid_until": { + "name": "valid_until", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'datev'" + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_path": { + "name": "file_path", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "exports_tenant_id_tenants_id_fk": { + "name": "exports_tenant_id_tenants_id_fk", + "tableFrom": "exports", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.globalmessages": { + "name": "globalmessages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "globalmessages_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.globalmessagesseen": { + "name": "globalmessagesseen", + "schema": "", + "columns": { + "message": { + "name": "message", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "seen_at": { + "name": "seen_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "globalmessagesseen_message_globalmessages_id_fk": { + "name": "globalmessagesseen_message_globalmessages_id_fk", + "tableFrom": "globalmessagesseen", + "tableTo": "globalmessages", + "columnsFrom": [ + "message" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.helpdesk_channel_instances": { + "name": "helpdesk_channel_instances", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "tenant_id": { + "name": "tenant_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "type_id": { + "name": "type_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "public_config": { + "name": "public_config", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "public_token": { + "name": "public_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret_token": { + "name": "secret_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "helpdesk_channel_instances_tenant_id_tenants_id_fk": { + "name": "helpdesk_channel_instances_tenant_id_tenants_id_fk", + "tableFrom": "helpdesk_channel_instances", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "helpdesk_channel_instances_type_id_helpdesk_channel_types_id_fk": { + "name": "helpdesk_channel_instances_type_id_helpdesk_channel_types_id_fk", + "tableFrom": "helpdesk_channel_instances", + "tableTo": "helpdesk_channel_types", + "columnsFrom": [ + "type_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "helpdesk_channel_instances_created_by_auth_users_id_fk": { + "name": "helpdesk_channel_instances_created_by_auth_users_id_fk", + "tableFrom": "helpdesk_channel_instances", + "tableTo": "auth_users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "helpdesk_channel_instances_public_token_unique": { + "name": "helpdesk_channel_instances_public_token_unique", + "nullsNotDistinct": false, + "columns": [ + "public_token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.helpdesk_channel_types": { + "name": "helpdesk_channel_types", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.helpdesk_contacts": { + "name": "helpdesk_contacts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "tenant_id": { + "name": "tenant_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "customer_id": { + "name": "customer_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "phone": { + "name": "phone", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "external_ref": { + "name": "external_ref", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "source_channel_id": { + "name": "source_channel_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "contact_id": { + "name": "contact_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "helpdesk_contacts_tenant_id_tenants_id_fk": { + "name": "helpdesk_contacts_tenant_id_tenants_id_fk", + "tableFrom": "helpdesk_contacts", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "helpdesk_contacts_customer_id_customers_id_fk": { + "name": "helpdesk_contacts_customer_id_customers_id_fk", + "tableFrom": "helpdesk_contacts", + "tableTo": "customers", + "columnsFrom": [ + "customer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "helpdesk_contacts_source_channel_id_helpdesk_channel_instances_id_fk": { + "name": "helpdesk_contacts_source_channel_id_helpdesk_channel_instances_id_fk", + "tableFrom": "helpdesk_contacts", + "tableTo": "helpdesk_channel_instances", + "columnsFrom": [ + "source_channel_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "helpdesk_contacts_contact_id_contacts_id_fk": { + "name": "helpdesk_contacts_contact_id_contacts_id_fk", + "tableFrom": "helpdesk_contacts", + "tableTo": "contacts", + "columnsFrom": [ + "contact_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.helpdesk_conversation_participants": { + "name": "helpdesk_conversation_participants", + "schema": "", + "columns": { + "conversation_id": { + "name": "conversation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "helpdesk_conversation_participants_conversation_id_helpdesk_conversations_id_fk": { + "name": "helpdesk_conversation_participants_conversation_id_helpdesk_conversations_id_fk", + "tableFrom": "helpdesk_conversation_participants", + "tableTo": "helpdesk_conversations", + "columnsFrom": [ + "conversation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "helpdesk_conversation_participants_user_id_auth_users_id_fk": { + "name": "helpdesk_conversation_participants_user_id_auth_users_id_fk", + "tableFrom": "helpdesk_conversation_participants", + "tableTo": "auth_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.helpdesk_conversations": { + "name": "helpdesk_conversations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "tenant_id": { + "name": "tenant_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "channel_instance_id": { + "name": "channel_instance_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "contact_id": { + "name": "contact_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "subject": { + "name": "subject", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'open'" + }, + "priority": { + "name": "priority", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'normal'" + }, + "assignee_user_id": { + "name": "assignee_user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "last_message_at": { + "name": "last_message_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "customer_id": { + "name": "customer_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "contact_person_id": { + "name": "contact_person_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "ticket_number": { + "name": "ticket_number", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "helpdesk_conversations_tenant_id_tenants_id_fk": { + "name": "helpdesk_conversations_tenant_id_tenants_id_fk", + "tableFrom": "helpdesk_conversations", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "helpdesk_conversations_channel_instance_id_helpdesk_channel_instances_id_fk": { + "name": "helpdesk_conversations_channel_instance_id_helpdesk_channel_instances_id_fk", + "tableFrom": "helpdesk_conversations", + "tableTo": "helpdesk_channel_instances", + "columnsFrom": [ + "channel_instance_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "helpdesk_conversations_contact_id_helpdesk_contacts_id_fk": { + "name": "helpdesk_conversations_contact_id_helpdesk_contacts_id_fk", + "tableFrom": "helpdesk_conversations", + "tableTo": "helpdesk_contacts", + "columnsFrom": [ + "contact_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "helpdesk_conversations_assignee_user_id_auth_users_id_fk": { + "name": "helpdesk_conversations_assignee_user_id_auth_users_id_fk", + "tableFrom": "helpdesk_conversations", + "tableTo": "auth_users", + "columnsFrom": [ + "assignee_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "helpdesk_conversations_customer_id_customers_id_fk": { + "name": "helpdesk_conversations_customer_id_customers_id_fk", + "tableFrom": "helpdesk_conversations", + "tableTo": "customers", + "columnsFrom": [ + "customer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "helpdesk_conversations_contact_person_id_contacts_id_fk": { + "name": "helpdesk_conversations_contact_person_id_contacts_id_fk", + "tableFrom": "helpdesk_conversations", + "tableTo": "contacts", + "columnsFrom": [ + "contact_person_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.helpdesk_messages": { + "name": "helpdesk_messages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "tenant_id": { + "name": "tenant_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "conversation_id": { + "name": "conversation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "direction": { + "name": "direction", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "author_user_id": { + "name": "author_user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "raw_meta": { + "name": "raw_meta", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "contact_id": { + "name": "contact_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "external_message_id": { + "name": "external_message_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "received_at": { + "name": "received_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "helpdesk_messages_tenant_id_tenants_id_fk": { + "name": "helpdesk_messages_tenant_id_tenants_id_fk", + "tableFrom": "helpdesk_messages", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "helpdesk_messages_conversation_id_helpdesk_conversations_id_fk": { + "name": "helpdesk_messages_conversation_id_helpdesk_conversations_id_fk", + "tableFrom": "helpdesk_messages", + "tableTo": "helpdesk_conversations", + "columnsFrom": [ + "conversation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "helpdesk_messages_author_user_id_auth_users_id_fk": { + "name": "helpdesk_messages_author_user_id_auth_users_id_fk", + "tableFrom": "helpdesk_messages", + "tableTo": "auth_users", + "columnsFrom": [ + "author_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "helpdesk_messages_contact_id_helpdesk_contacts_id_fk": { + "name": "helpdesk_messages_contact_id_helpdesk_contacts_id_fk", + "tableFrom": "helpdesk_messages", + "tableTo": "helpdesk_contacts", + "columnsFrom": [ + "contact_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "helpdesk_messages_external_message_id_unique": { + "name": "helpdesk_messages_external_message_id_unique", + "nullsNotDistinct": false, + "columns": [ + "external_message_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.helpdesk_routing_rules": { + "name": "helpdesk_routing_rules", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "tenant_id": { + "name": "tenant_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "condition": { + "name": "condition", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "action": { + "name": "action", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "helpdesk_routing_rules_tenant_id_tenants_id_fk": { + "name": "helpdesk_routing_rules_tenant_id_tenants_id_fk", + "tableFrom": "helpdesk_routing_rules", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "helpdesk_routing_rules_created_by_auth_users_id_fk": { + "name": "helpdesk_routing_rules_created_by_auth_users_id_fk", + "tableFrom": "helpdesk_routing_rules", + "tableTo": "auth_users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.historyitems": { + "name": "historyitems", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "historyitems_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "text": { + "name": "text", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "customer": { + "name": "customer", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "vendor": { + "name": "vendor", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "project": { + "name": "project", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "plant": { + "name": "plant", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "incomingInvoice": { + "name": "incomingInvoice", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "contact": { + "name": "contact", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "inventoryitem": { + "name": "inventoryitem", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "product": { + "name": "product", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "event": { + "name": "event", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "newVal": { + "name": "newVal", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "oldVal": { + "name": "oldVal", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "task": { + "name": "task", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "vehicle": { + "name": "vehicle", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "bankstatement": { + "name": "bankstatement", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "space": { + "name": "space", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "projecttype": { + "name": "projecttype", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "check": { + "name": "check", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "service": { + "name": "service", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "createddocument": { + "name": "createddocument", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "file": { + "name": "file", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "inventoryitemgroup": { + "name": "inventoryitemgroup", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'Software'" + }, + "costcentre": { + "name": "costcentre", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "ownaccount": { + "name": "ownaccount", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "documentbox": { + "name": "documentbox", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "hourrate": { + "name": "hourrate", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "historyitems_customer_customers_id_fk": { + "name": "historyitems_customer_customers_id_fk", + "tableFrom": "historyitems", + "tableTo": "customers", + "columnsFrom": [ + "customer" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "historyitems_tenant_tenants_id_fk": { + "name": "historyitems_tenant_tenants_id_fk", + "tableFrom": "historyitems", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "historyitems_vendor_vendors_id_fk": { + "name": "historyitems_vendor_vendors_id_fk", + "tableFrom": "historyitems", + "tableTo": "vendors", + "columnsFrom": [ + "vendor" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "historyitems_project_projects_id_fk": { + "name": "historyitems_project_projects_id_fk", + "tableFrom": "historyitems", + "tableTo": "projects", + "columnsFrom": [ + "project" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "historyitems_plant_plants_id_fk": { + "name": "historyitems_plant_plants_id_fk", + "tableFrom": "historyitems", + "tableTo": "plants", + "columnsFrom": [ + "plant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "historyitems_incomingInvoice_incominginvoices_id_fk": { + "name": "historyitems_incomingInvoice_incominginvoices_id_fk", + "tableFrom": "historyitems", + "tableTo": "incominginvoices", + "columnsFrom": [ + "incomingInvoice" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "historyitems_contact_contacts_id_fk": { + "name": "historyitems_contact_contacts_id_fk", + "tableFrom": "historyitems", + "tableTo": "contacts", + "columnsFrom": [ + "contact" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "historyitems_inventoryitem_inventoryitems_id_fk": { + "name": "historyitems_inventoryitem_inventoryitems_id_fk", + "tableFrom": "historyitems", + "tableTo": "inventoryitems", + "columnsFrom": [ + "inventoryitem" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "historyitems_product_products_id_fk": { + "name": "historyitems_product_products_id_fk", + "tableFrom": "historyitems", + "tableTo": "products", + "columnsFrom": [ + "product" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "historyitems_event_events_id_fk": { + "name": "historyitems_event_events_id_fk", + "tableFrom": "historyitems", + "tableTo": "events", + "columnsFrom": [ + "event" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "historyitems_task_tasks_id_fk": { + "name": "historyitems_task_tasks_id_fk", + "tableFrom": "historyitems", + "tableTo": "tasks", + "columnsFrom": [ + "task" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "historyitems_vehicle_vehicles_id_fk": { + "name": "historyitems_vehicle_vehicles_id_fk", + "tableFrom": "historyitems", + "tableTo": "vehicles", + "columnsFrom": [ + "vehicle" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "historyitems_bankstatement_bankstatements_id_fk": { + "name": "historyitems_bankstatement_bankstatements_id_fk", + "tableFrom": "historyitems", + "tableTo": "bankstatements", + "columnsFrom": [ + "bankstatement" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "historyitems_space_spaces_id_fk": { + "name": "historyitems_space_spaces_id_fk", + "tableFrom": "historyitems", + "tableTo": "spaces", + "columnsFrom": [ + "space" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "historyitems_projecttype_projecttypes_id_fk": { + "name": "historyitems_projecttype_projecttypes_id_fk", + "tableFrom": "historyitems", + "tableTo": "projecttypes", + "columnsFrom": [ + "projecttype" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "historyitems_check_checks_id_fk": { + "name": "historyitems_check_checks_id_fk", + "tableFrom": "historyitems", + "tableTo": "checks", + "columnsFrom": [ + "check" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "historyitems_service_services_id_fk": { + "name": "historyitems_service_services_id_fk", + "tableFrom": "historyitems", + "tableTo": "services", + "columnsFrom": [ + "service" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "historyitems_createddocument_createddocuments_id_fk": { + "name": "historyitems_createddocument_createddocuments_id_fk", + "tableFrom": "historyitems", + "tableTo": "createddocuments", + "columnsFrom": [ + "createddocument" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "historyitems_file_files_id_fk": { + "name": "historyitems_file_files_id_fk", + "tableFrom": "historyitems", + "tableTo": "files", + "columnsFrom": [ + "file" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "historyitems_inventoryitemgroup_inventoryitemgroups_id_fk": { + "name": "historyitems_inventoryitemgroup_inventoryitemgroups_id_fk", + "tableFrom": "historyitems", + "tableTo": "inventoryitemgroups", + "columnsFrom": [ + "inventoryitemgroup" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "historyitems_costcentre_costcentres_id_fk": { + "name": "historyitems_costcentre_costcentres_id_fk", + "tableFrom": "historyitems", + "tableTo": "costcentres", + "columnsFrom": [ + "costcentre" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "historyitems_ownaccount_ownaccounts_id_fk": { + "name": "historyitems_ownaccount_ownaccounts_id_fk", + "tableFrom": "historyitems", + "tableTo": "ownaccounts", + "columnsFrom": [ + "ownaccount" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "historyitems_documentbox_documentboxes_id_fk": { + "name": "historyitems_documentbox_documentboxes_id_fk", + "tableFrom": "historyitems", + "tableTo": "documentboxes", + "columnsFrom": [ + "documentbox" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "historyitems_hourrate_hourrates_id_fk": { + "name": "historyitems_hourrate_hourrates_id_fk", + "tableFrom": "historyitems", + "tableTo": "hourrates", + "columnsFrom": [ + "hourrate" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "historyitems_created_by_auth_users_id_fk": { + "name": "historyitems_created_by_auth_users_id_fk", + "tableFrom": "historyitems", + "tableTo": "auth_users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.holidays": { + "name": "holidays", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "holidays_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "date": { + "name": "date", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "state_code": { + "name": "state_code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.hourrates": { + "name": "hourrates", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "purchasePrice": { + "name": "purchasePrice", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "sellingPrice": { + "name": "sellingPrice", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "hourrates_tenant_tenants_id_fk": { + "name": "hourrates_tenant_tenants_id_fk", + "tableFrom": "hourrates", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "hourrates_updated_by_auth_users_id_fk": { + "name": "hourrates_updated_by_auth_users_id_fk", + "tableFrom": "hourrates", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.incominginvoices": { + "name": "incominginvoices", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "incominginvoices_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'Entwurf'" + }, + "vendor": { + "name": "vendor", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "reference": { + "name": "reference", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "date": { + "name": "date", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "document": { + "name": "document", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "dueDate": { + "name": "dueDate", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "paymentType": { + "name": "paymentType", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "accounts": { + "name": "accounts", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[{\"account\":null,\"taxType\":null,\"amountNet\":null,\"amountTax\":19,\"costCentre\":null}]'::jsonb" + }, + "paid": { + "name": "paid", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "expense": { + "name": "expense", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "incominginvoices_tenant_tenants_id_fk": { + "name": "incominginvoices_tenant_tenants_id_fk", + "tableFrom": "incominginvoices", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "incominginvoices_vendor_vendors_id_fk": { + "name": "incominginvoices_vendor_vendors_id_fk", + "tableFrom": "incominginvoices", + "tableTo": "vendors", + "columnsFrom": [ + "vendor" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "incominginvoices_updated_by_auth_users_id_fk": { + "name": "incominginvoices_updated_by_auth_users_id_fk", + "tableFrom": "incominginvoices", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.inventoryitemgroups": { + "name": "inventoryitemgroups", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "inventoryitems": { + "name": "inventoryitems", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "profiles": { + "name": "profiles", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "usePlanning": { + "name": "usePlanning", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "inventoryitemgroups_tenant_tenants_id_fk": { + "name": "inventoryitemgroups_tenant_tenants_id_fk", + "tableFrom": "inventoryitemgroups", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inventoryitemgroups_updated_by_auth_users_id_fk": { + "name": "inventoryitemgroups_updated_by_auth_users_id_fk", + "tableFrom": "inventoryitemgroups", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.inventoryitems": { + "name": "inventoryitems", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "inventoryitems_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "usePlanning": { + "name": "usePlanning", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "currentSpace": { + "name": "currentSpace", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "articleNumber": { + "name": "articleNumber", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serialNumber": { + "name": "serialNumber", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "purchaseDate": { + "name": "purchaseDate", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "vendor": { + "name": "vendor", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "quantity": { + "name": "quantity", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "purchasePrice": { + "name": "purchasePrice", + "type": "double precision", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "manufacturer": { + "name": "manufacturer", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "manufacturerNumber": { + "name": "manufacturerNumber", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "currentValue": { + "name": "currentValue", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "profiles": { + "name": "profiles", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "inventoryitems_tenant_tenants_id_fk": { + "name": "inventoryitems_tenant_tenants_id_fk", + "tableFrom": "inventoryitems", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inventoryitems_currentSpace_spaces_id_fk": { + "name": "inventoryitems_currentSpace_spaces_id_fk", + "tableFrom": "inventoryitems", + "tableTo": "spaces", + "columnsFrom": [ + "currentSpace" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inventoryitems_vendor_vendors_id_fk": { + "name": "inventoryitems_vendor_vendors_id_fk", + "tableFrom": "inventoryitems", + "tableTo": "vendors", + "columnsFrom": [ + "vendor" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inventoryitems_updated_by_auth_users_id_fk": { + "name": "inventoryitems_updated_by_auth_users_id_fk", + "tableFrom": "inventoryitems", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.letterheads": { + "name": "letterheads", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "letterheads_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'Standard'" + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "documentTypes": { + "name": "documentTypes", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "letterheads_tenant_tenants_id_fk": { + "name": "letterheads_tenant_tenants_id_fk", + "tableFrom": "letterheads", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "letterheads_updated_by_auth_users_id_fk": { + "name": "letterheads_updated_by_auth_users_id_fk", + "tableFrom": "letterheads", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.movements": { + "name": "movements", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "movements_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "quantity": { + "name": "quantity", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "productId": { + "name": "productId", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "spaceId": { + "name": "spaceId", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serials": { + "name": "serials", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "movements_productId_products_id_fk": { + "name": "movements_productId_products_id_fk", + "tableFrom": "movements", + "tableTo": "products", + "columnsFrom": [ + "productId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "movements_spaceId_spaces_id_fk": { + "name": "movements_spaceId_spaces_id_fk", + "tableFrom": "movements", + "tableTo": "spaces", + "columnsFrom": [ + "spaceId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "movements_tenant_tenants_id_fk": { + "name": "movements_tenant_tenants_id_fk", + "tableFrom": "movements", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "movements_projectId_projects_id_fk": { + "name": "movements_projectId_projects_id_fk", + "tableFrom": "movements", + "tableTo": "projects", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "movements_updated_by_auth_users_id_fk": { + "name": "movements_updated_by_auth_users_id_fk", + "tableFrom": "movements", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notifications_event_types": { + "name": "notifications_event_types", + "schema": "", + "columns": { + "event_key": { + "name": "event_key", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "severity": { + "name": "severity", + "type": "notification_severity", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'info'" + }, + "allowed_channels": { + "name": "allowed_channels", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[\"inapp\",\"email\"]'::jsonb" + }, + "payload_schema": { + "name": "payload_schema", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notifications_items": { + "name": "notifications_items", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "tenant_id": { + "name": "tenant_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "channel": { + "name": "channel", + "type": "notification_channel", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "notification_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "sent_at": { + "name": "sent_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "read_at": { + "name": "read_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "notifications_items_tenant_id_tenants_id_fk": { + "name": "notifications_items_tenant_id_tenants_id_fk", + "tableFrom": "notifications_items", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "notifications_items_user_id_auth_users_id_fk": { + "name": "notifications_items_user_id_auth_users_id_fk", + "tableFrom": "notifications_items", + "tableTo": "auth_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "notifications_items_event_type_notifications_event_types_event_key_fk": { + "name": "notifications_items_event_type_notifications_event_types_event_key_fk", + "tableFrom": "notifications_items", + "tableTo": "notifications_event_types", + "columnsFrom": [ + "event_type" + ], + "columnsTo": [ + "event_key" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notifications_preferences": { + "name": "notifications_preferences", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "tenant_id": { + "name": "tenant_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "channel": { + "name": "channel", + "type": "notification_channel", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "notifications_preferences_tenant_id_user_id_event_type_chan_key": { + "name": "notifications_preferences_tenant_id_user_id_event_type_chan_key", + "columns": [ + { + "expression": "tenant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "event_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "channel", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "notifications_preferences_tenant_id_tenants_id_fk": { + "name": "notifications_preferences_tenant_id_tenants_id_fk", + "tableFrom": "notifications_preferences", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "notifications_preferences_user_id_auth_users_id_fk": { + "name": "notifications_preferences_user_id_auth_users_id_fk", + "tableFrom": "notifications_preferences", + "tableTo": "auth_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "notifications_preferences_event_type_notifications_event_types_event_key_fk": { + "name": "notifications_preferences_event_type_notifications_event_types_event_key_fk", + "tableFrom": "notifications_preferences", + "tableTo": "notifications_event_types", + "columnsFrom": [ + "event_type" + ], + "columnsTo": [ + "event_key" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notifications_preferences_defaults": { + "name": "notifications_preferences_defaults", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "tenant_id": { + "name": "tenant_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "event_key": { + "name": "event_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "channel": { + "name": "channel", + "type": "notification_channel", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "notifications_preferences_defau_tenant_id_event_key_channel_key": { + "name": "notifications_preferences_defau_tenant_id_event_key_channel_key", + "columns": [ + { + "expression": "tenant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "event_key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "channel", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "notifications_preferences_defaults_tenant_id_tenants_id_fk": { + "name": "notifications_preferences_defaults_tenant_id_tenants_id_fk", + "tableFrom": "notifications_preferences_defaults", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "notifications_preferences_defaults_event_key_notifications_event_types_event_key_fk": { + "name": "notifications_preferences_defaults_event_key_notifications_event_types_event_key_fk", + "tableFrom": "notifications_preferences_defaults", + "tableTo": "notifications_event_types", + "columnsFrom": [ + "event_key" + ], + "columnsTo": [ + "event_key" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ownaccounts": { + "name": "ownaccounts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "number": { + "name": "number", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "profiles": { + "name": "profiles", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "ownaccounts_tenant_tenants_id_fk": { + "name": "ownaccounts_tenant_tenants_id_fk", + "tableFrom": "ownaccounts", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "ownaccounts_updated_by_auth_users_id_fk": { + "name": "ownaccounts_updated_by_auth_users_id_fk", + "tableFrom": "ownaccounts", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.plants": { + "name": "plants", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "plants_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "customer": { + "name": "customer", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "infoData": { + "name": "infoData", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "contract": { + "name": "contract", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'{\"html\":\"\",\"json\":[],\"text\":\"\"}'::jsonb" + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "profiles": { + "name": "profiles", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "plants_tenant_tenants_id_fk": { + "name": "plants_tenant_tenants_id_fk", + "tableFrom": "plants", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "plants_customer_customers_id_fk": { + "name": "plants_customer_customers_id_fk", + "tableFrom": "plants", + "tableTo": "customers", + "columnsFrom": [ + "customer" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "plants_contract_contracts_id_fk": { + "name": "plants_contract_contracts_id_fk", + "tableFrom": "plants", + "tableTo": "contracts", + "columnsFrom": [ + "contract" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "plants_updated_by_auth_users_id_fk": { + "name": "plants_updated_by_auth_users_id_fk", + "tableFrom": "plants", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.productcategories": { + "name": "productcategories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "productcategories_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "productcategories_tenant_tenants_id_fk": { + "name": "productcategories_tenant_tenants_id_fk", + "tableFrom": "productcategories", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "productcategories_updated_by_auth_users_id_fk": { + "name": "productcategories_updated_by_auth_users_id_fk", + "tableFrom": "productcategories", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.products": { + "name": "products", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "products_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "manufacturer": { + "name": "manufacturer", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "unit": { + "name": "unit", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "tags": { + "name": "tags", + "type": "json", + "primaryKey": false, + "notNull": true, + "default": "'[]'::json" + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "ean": { + "name": "ean", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "barcode": { + "name": "barcode", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "purchasePrice": { + "name": "purchasePrice", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "sellingPrice": { + "name": "sellingPrice", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "manufacturerNumber": { + "name": "manufacturerNumber", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "vendorAllocation": { + "name": "vendorAllocation", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'[]'::jsonb" + }, + "articleNumber": { + "name": "articleNumber", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "barcodes": { + "name": "barcodes", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "productcategories": { + "name": "productcategories", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'[]'::jsonb" + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "taxPercentage": { + "name": "taxPercentage", + "type": "smallint", + "primaryKey": false, + "notNull": true, + "default": 19 + }, + "markupPercentage": { + "name": "markupPercentage", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "products_unit_units_id_fk": { + "name": "products_unit_units_id_fk", + "tableFrom": "products", + "tableTo": "units", + "columnsFrom": [ + "unit" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "products_tenant_tenants_id_fk": { + "name": "products_tenant_tenants_id_fk", + "tableFrom": "products", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "products_updated_by_auth_users_id_fk": { + "name": "products_updated_by_auth_users_id_fk", + "tableFrom": "products", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.projects": { + "name": "projects", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "projects_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customer": { + "name": "customer", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "phases": { + "name": "phases", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'[]'::jsonb" + }, + "description": { + "name": "description", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "forms": { + "name": "forms", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'[]'::jsonb" + }, + "heroId": { + "name": "heroId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "measure": { + "name": "measure", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "material": { + "name": "material", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "plant": { + "name": "plant", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "profiles": { + "name": "profiles", + "type": "uuid[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "projectNumber": { + "name": "projectNumber", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "contract": { + "name": "contract", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "projectType": { + "name": "projectType", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'Projekt'" + }, + "projecttype": { + "name": "projecttype", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "customerRef": { + "name": "customerRef", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "active_phase": { + "name": "active_phase", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "projects_tenant_tenants_id_fk": { + "name": "projects_tenant_tenants_id_fk", + "tableFrom": "projects", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "projects_customer_customers_id_fk": { + "name": "projects_customer_customers_id_fk", + "tableFrom": "projects", + "tableTo": "customers", + "columnsFrom": [ + "customer" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "projects_contract_contracts_id_fk": { + "name": "projects_contract_contracts_id_fk", + "tableFrom": "projects", + "tableTo": "contracts", + "columnsFrom": [ + "contract" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "projects_projecttype_projecttypes_id_fk": { + "name": "projects_projecttype_projecttypes_id_fk", + "tableFrom": "projects", + "tableTo": "projecttypes", + "columnsFrom": [ + "projecttype" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "projects_updated_by_auth_users_id_fk": { + "name": "projects_updated_by_auth_users_id_fk", + "tableFrom": "projects", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.projecttypes": { + "name": "projecttypes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "projecttypes_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "initialPhases": { + "name": "initialPhases", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "addablePhases": { + "name": "addablePhases", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "projecttypes_tenant_tenants_id_fk": { + "name": "projecttypes_tenant_tenants_id_fk", + "tableFrom": "projecttypes", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "projecttypes_updated_by_auth_users_id_fk": { + "name": "projecttypes_updated_by_auth_users_id_fk", + "tableFrom": "projecttypes", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.servicecategories": { + "name": "servicecategories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "servicecategories_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "discount": { + "name": "discount", + "type": "double precision", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "servicecategories_tenant_tenants_id_fk": { + "name": "servicecategories_tenant_tenants_id_fk", + "tableFrom": "servicecategories", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "servicecategories_updated_by_auth_users_id_fk": { + "name": "servicecategories_updated_by_auth_users_id_fk", + "tableFrom": "servicecategories", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.services": { + "name": "services", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "services_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sellingPrice": { + "name": "sellingPrice", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "unit": { + "name": "unit", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "serviceNumber": { + "name": "serviceNumber", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "tags": { + "name": "tags", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'[]'::jsonb" + }, + "servicecategories": { + "name": "servicecategories", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "purchasePriceComposed": { + "name": "purchasePriceComposed", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"total\":0}'::jsonb" + }, + "sellingPriceComposed": { + "name": "sellingPriceComposed", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"total\":0}'::jsonb" + }, + "taxPercentage": { + "name": "taxPercentage", + "type": "smallint", + "primaryKey": false, + "notNull": true, + "default": 19 + }, + "materialComposition": { + "name": "materialComposition", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "personalComposition": { + "name": "personalComposition", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "services_tenant_tenants_id_fk": { + "name": "services_tenant_tenants_id_fk", + "tableFrom": "services", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "services_unit_units_id_fk": { + "name": "services_unit_units_id_fk", + "tableFrom": "services", + "tableTo": "units", + "columnsFrom": [ + "unit" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "services_updated_by_auth_users_id_fk": { + "name": "services_updated_by_auth_users_id_fk", + "tableFrom": "services", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.spaces": { + "name": "spaces", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "spaces_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "spaceNumber": { + "name": "spaceNumber", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "parentSpace": { + "name": "parentSpace", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "infoData": { + "name": "infoData", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"zip\":\"\",\"city\":\"\",\"streetNumber\":\"\"}'::jsonb" + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "spaces_tenant_tenants_id_fk": { + "name": "spaces_tenant_tenants_id_fk", + "tableFrom": "spaces", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "spaces_parentSpace_spaces_id_fk": { + "name": "spaces_parentSpace_spaces_id_fk", + "tableFrom": "spaces", + "tableTo": "spaces", + "columnsFrom": [ + "parentSpace" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "spaces_updated_by_auth_users_id_fk": { + "name": "spaces_updated_by_auth_users_id_fk", + "tableFrom": "spaces", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.staff_time_entries": { + "name": "staff_time_entries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "tenant_id": { + "name": "tenant_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "stopped_at": { + "name": "stopped_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "duration_minutes": { + "name": "duration_minutes", + "type": "integer", + "primaryKey": false, + "notNull": false, + "generated": { + "as": "CASE \n WHEN stopped_at IS NOT NULL \n THEN (EXTRACT(epoch FROM (stopped_at - started_at)) / 60)\n ELSE NULL\n END", + "type": "stored" + } + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'work'" + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "state": { + "name": "state", + "type": "times_state", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'draft'" + }, + "device": { + "name": "device", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "internal_note": { + "name": "internal_note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "vacation_reason": { + "name": "vacation_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "vacation_days": { + "name": "vacation_days", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": false + }, + "approved_by": { + "name": "approved_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "approved_at": { + "name": "approved_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "sick_reason": { + "name": "sick_reason", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "staff_time_entries_tenant_id_tenants_id_fk": { + "name": "staff_time_entries_tenant_id_tenants_id_fk", + "tableFrom": "staff_time_entries", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "staff_time_entries_user_id_auth_users_id_fk": { + "name": "staff_time_entries_user_id_auth_users_id_fk", + "tableFrom": "staff_time_entries", + "tableTo": "auth_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "staff_time_entries_updated_by_auth_users_id_fk": { + "name": "staff_time_entries_updated_by_auth_users_id_fk", + "tableFrom": "staff_time_entries", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "staff_time_entries_approved_by_auth_users_id_fk": { + "name": "staff_time_entries_approved_by_auth_users_id_fk", + "tableFrom": "staff_time_entries", + "tableTo": "auth_users", + "columnsFrom": [ + "approved_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.staff_time_entry_connects": { + "name": "staff_time_entry_connects", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "time_entry_id": { + "name": "time_entry_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "project_id": { + "name": "project_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "stopped_at": { + "name": "stopped_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "duration_minutes": { + "name": "duration_minutes", + "type": "integer", + "primaryKey": false, + "notNull": false, + "generated": { + "as": "(EXTRACT(epoch FROM (stopped_at - started_at)) / 60)", + "type": "stored" + } + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "staff_time_entry_connects_time_entry_id_staff_time_entries_id_fk": { + "name": "staff_time_entry_connects_time_entry_id_staff_time_entries_id_fk", + "tableFrom": "staff_time_entry_connects", + "tableTo": "staff_time_entries", + "columnsFrom": [ + "time_entry_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.staff_zeitstromtimestamps": { + "name": "staff_zeitstromtimestamps", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "profile": { + "name": "profile", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "intent": { + "name": "intent", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "time": { + "name": "time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "staff_time_entry": { + "name": "staff_time_entry", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "internal_note": { + "name": "internal_note", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "staff_zeitstromtimestamps_tenant_tenants_id_fk": { + "name": "staff_zeitstromtimestamps_tenant_tenants_id_fk", + "tableFrom": "staff_zeitstromtimestamps", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "staff_zeitstromtimestamps_profile_auth_profiles_id_fk": { + "name": "staff_zeitstromtimestamps_profile_auth_profiles_id_fk", + "tableFrom": "staff_zeitstromtimestamps", + "tableTo": "auth_profiles", + "columnsFrom": [ + "profile" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "staff_zeitstromtimestamps_staff_time_entry_staff_time_entries_id_fk": { + "name": "staff_zeitstromtimestamps_staff_time_entry_staff_time_entries_id_fk", + "tableFrom": "staff_zeitstromtimestamps", + "tableTo": "staff_time_entries", + "columnsFrom": [ + "staff_time_entry" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.statementallocations": { + "name": "statementallocations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "bs_id": { + "name": "bs_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "cd_id": { + "name": "cd_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "amount": { + "name": "amount", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "ii_id": { + "name": "ii_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "account": { + "name": "account", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "ownaccount": { + "name": "ownaccount", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customer": { + "name": "customer", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "vendor": { + "name": "vendor", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "statementallocations_bs_id_bankstatements_id_fk": { + "name": "statementallocations_bs_id_bankstatements_id_fk", + "tableFrom": "statementallocations", + "tableTo": "bankstatements", + "columnsFrom": [ + "bs_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "statementallocations_cd_id_createddocuments_id_fk": { + "name": "statementallocations_cd_id_createddocuments_id_fk", + "tableFrom": "statementallocations", + "tableTo": "createddocuments", + "columnsFrom": [ + "cd_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "statementallocations_ii_id_incominginvoices_id_fk": { + "name": "statementallocations_ii_id_incominginvoices_id_fk", + "tableFrom": "statementallocations", + "tableTo": "incominginvoices", + "columnsFrom": [ + "ii_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "statementallocations_tenant_tenants_id_fk": { + "name": "statementallocations_tenant_tenants_id_fk", + "tableFrom": "statementallocations", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "statementallocations_account_accounts_id_fk": { + "name": "statementallocations_account_accounts_id_fk", + "tableFrom": "statementallocations", + "tableTo": "accounts", + "columnsFrom": [ + "account" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "statementallocations_ownaccount_ownaccounts_id_fk": { + "name": "statementallocations_ownaccount_ownaccounts_id_fk", + "tableFrom": "statementallocations", + "tableTo": "ownaccounts", + "columnsFrom": [ + "ownaccount" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "statementallocations_customer_customers_id_fk": { + "name": "statementallocations_customer_customers_id_fk", + "tableFrom": "statementallocations", + "tableTo": "customers", + "columnsFrom": [ + "customer" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "statementallocations_vendor_vendors_id_fk": { + "name": "statementallocations_vendor_vendors_id_fk", + "tableFrom": "statementallocations", + "tableTo": "vendors", + "columnsFrom": [ + "vendor" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "statementallocations_updated_by_auth_users_id_fk": { + "name": "statementallocations_updated_by_auth_users_id_fk", + "tableFrom": "statementallocations", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tasks": { + "name": "tasks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "tasks_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "categorie": { + "name": "categorie", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "project": { + "name": "project", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "plant": { + "name": "plant", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "customer": { + "name": "customer", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "profiles": { + "name": "profiles", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "tasks_tenant_tenants_id_fk": { + "name": "tasks_tenant_tenants_id_fk", + "tableFrom": "tasks", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "tasks_user_id_auth_users_id_fk": { + "name": "tasks_user_id_auth_users_id_fk", + "tableFrom": "tasks", + "tableTo": "auth_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "tasks_customer_customers_id_fk": { + "name": "tasks_customer_customers_id_fk", + "tableFrom": "tasks", + "tableTo": "customers", + "columnsFrom": [ + "customer" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "tasks_updated_by_auth_users_id_fk": { + "name": "tasks_updated_by_auth_users_id_fk", + "tableFrom": "tasks", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.taxtypes": { + "name": "taxtypes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "taxtypes_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "percentage": { + "name": "percentage", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "taxtypes_updated_by_auth_users_id_fk": { + "name": "taxtypes_updated_by_auth_users_id_fk", + "tableFrom": "taxtypes", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tenants": { + "name": "tenants", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "tenants_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "short": { + "name": "short", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "calendarConfig": { + "name": "calendarConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'{\"eventTypes\":[{\"color\":\"blue\",\"label\":\"Büro\"},{\"color\":\"yellow\",\"label\":\"Besprechung\"},{\"color\":\"green\",\"label\":\"Umsetzung\"},{\"color\":\"red\",\"label\":\"Vor Ort Termin\"}]}'::jsonb" + }, + "timeConfig": { + "name": "timeConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "tags": { + "name": "tags", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"products\":[],\"documents\":[]}'::jsonb" + }, + "measures": { + "name": "measures", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[{\"name\":\"Netzwerktechnik\",\"short\":\"NWT\"},{\"name\":\"Elektrotechnik\",\"short\":\"ELT\"},{\"name\":\"Photovoltaik\",\"short\":\"PV\"},{\"name\":\"Videüberwachung\",\"short\":\"VÜA\"},{\"name\":\"Projekt\",\"short\":\"PRJ\"},{\"name\":\"Smart Home\",\"short\":\"SHO\"}]'::jsonb" + }, + "businessInfo": { + "name": "businessInfo", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'{\"zip\":\"\",\"city\":\"\",\"name\":\"\",\"street\":\"\"}'::jsonb" + }, + "features": { + "name": "features", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'{\"objects\":true,\"calendar\":true,\"contacts\":true,\"projects\":true,\"vehicles\":true,\"contracts\":true,\"inventory\":true,\"accounting\":true,\"timeTracking\":true,\"planningBoard\":true,\"workingTimeTracking\":true}'::jsonb" + }, + "ownFields": { + "name": "ownFields", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "numberRanges": { + "name": "numberRanges", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"vendors\":{\"prefix\":\"\",\"suffix\":\"\",\"nextNumber\":10000},\"customers\":{\"prefix\":\"\",\"suffix\":\"\",\"nextNumber\":10000},\"products\":{\"prefix\":\"AT-\",\"suffix\":\"\",\"nextNumber\":1000},\"quotes\":{\"prefix\":\"AN-\",\"suffix\":\"\",\"nextNumber\":1000},\"confirmationOrders\":{\"prefix\":\"AB-\",\"suffix\":\"\",\"nextNumber\":1000},\"invoices\":{\"prefix\":\"RE-\",\"suffix\":\"\",\"nextNumber\":1000},\"spaces\":{\"prefix\":\"LP-\",\"suffix\":\"\",\"nextNumber\":1000},\"inventoryitems\":{\"prefix\":\"IA-\",\"suffix\":\"\",\"nextNumber\":1000},\"projects\":{\"prefix\":\"PRJ-\",\"suffix\":\"\",\"nextNumber\":1000},\"costcentres\":{\"prefix\":\"KST-\",\"suffix\":\"\",\"nextNumber\":1000}}'::jsonb" + }, + "standardEmailForInvoices": { + "name": "standardEmailForInvoices", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "extraModules": { + "name": "extraModules", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "isInTrial": { + "name": "isInTrial", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "trialEndDate": { + "name": "trialEndDate", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "stripeCustomerId": { + "name": "stripeCustomerId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "hasActiveLicense": { + "name": "hasActiveLicense", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "userLicenseCount": { + "name": "userLicenseCount", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "workstationLicenseCount": { + "name": "workstationLicenseCount", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "standardPaymentDays": { + "name": "standardPaymentDays", + "type": "smallint", + "primaryKey": false, + "notNull": true, + "default": 14 + }, + "dokuboxEmailAddresses": { + "name": "dokuboxEmailAddresses", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'[]'::jsonb" + }, + "dokuboxkey": { + "name": "dokuboxkey", + "type": "uuid", + "primaryKey": false, + "notNull": true, + "default": "gen_random_uuid()" + }, + "autoPrepareIncomingInvoices": { + "name": "autoPrepareIncomingInvoices", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "portalDomain": { + "name": "portalDomain", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "portalConfig": { + "name": "portalConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"primayColor\":\"#69c350\"}'::jsonb" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "locked": { + "name": "locked", + "type": "locked_tenant", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "tenants_updated_by_auth_users_id_fk": { + "name": "tenants_updated_by_auth_users_id_fk", + "tableFrom": "tenants", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.texttemplates": { + "name": "texttemplates", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "texttemplates_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "text": { + "name": "text", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "documentType": { + "name": "documentType", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "''" + }, + "default": { + "name": "default", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "pos": { + "name": "pos", + "type": "texttemplatepositions", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "texttemplates_tenant_tenants_id_fk": { + "name": "texttemplates_tenant_tenants_id_fk", + "tableFrom": "texttemplates", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "texttemplates_updated_by_auth_users_id_fk": { + "name": "texttemplates_updated_by_auth_users_id_fk", + "tableFrom": "texttemplates", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.units": { + "name": "units", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "units_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "single": { + "name": "single", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "multiple": { + "name": "multiple", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "short": { + "name": "short", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "step": { + "name": "step", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'1'" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_credentials": { + "name": "user_credentials", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "smtp_port": { + "name": "smtp_port", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "smtp_ssl": { + "name": "smtp_ssl", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "credential_types", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "imap_port": { + "name": "imap_port", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "imap_ssl": { + "name": "imap_ssl", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "email_encrypted": { + "name": "email_encrypted", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "password_encrypted": { + "name": "password_encrypted", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "smtp_host_encrypted": { + "name": "smtp_host_encrypted", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "imap_host_encrypted": { + "name": "imap_host_encrypted", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "access_token_encrypted": { + "name": "access_token_encrypted", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "refresh_token_encrypted": { + "name": "refresh_token_encrypted", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "user_credentials_user_id_auth_users_id_fk": { + "name": "user_credentials_user_id_auth_users_id_fk", + "tableFrom": "user_credentials", + "tableTo": "auth_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "user_credentials_tenant_id_tenants_id_fk": { + "name": "user_credentials_tenant_id_tenants_id_fk", + "tableFrom": "user_credentials", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.vehicles": { + "name": "vehicles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "vehicles_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "licensePlate": { + "name": "licensePlate", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "driver": { + "name": "driver", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "vin": { + "name": "vin", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tankSize": { + "name": "tankSize", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "buildYear": { + "name": "buildYear", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "towingCapacity": { + "name": "towingCapacity", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "powerInKW": { + "name": "powerInKW", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "profiles": { + "name": "profiles", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "vehicles_tenant_tenants_id_fk": { + "name": "vehicles_tenant_tenants_id_fk", + "tableFrom": "vehicles", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "vehicles_driver_auth_users_id_fk": { + "name": "vehicles_driver_auth_users_id_fk", + "tableFrom": "vehicles", + "tableTo": "auth_users", + "columnsFrom": [ + "driver" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "vehicles_updated_by_auth_users_id_fk": { + "name": "vehicles_updated_by_auth_users_id_fk", + "tableFrom": "vehicles", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.vendors": { + "name": "vendors", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "vendors_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "vendorNumber": { + "name": "vendorNumber", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tenant": { + "name": "tenant", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "infoData": { + "name": "infoData", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "hasSEPA": { + "name": "hasSEPA", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "profiles": { + "name": "profiles", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "archived": { + "name": "archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "defaultPaymentMethod": { + "name": "defaultPaymentMethod", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "vendors_tenant_tenants_id_fk": { + "name": "vendors_tenant_tenants_id_fk", + "tableFrom": "vendors", + "tableTo": "tenants", + "columnsFrom": [ + "tenant" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "vendors_updated_by_auth_users_id_fk": { + "name": "vendors_updated_by_auth_users_id_fk", + "tableFrom": "vendors", + "tableTo": "auth_users", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.time_events": { + "name": "time_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "tenant_id": { + "name": "tenant_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "actor_type": { + "name": "actor_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "actor_user_id": { + "name": "actor_user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "event_time": { + "name": "event_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "invalidates_event_id": { + "name": "invalidates_event_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_time_events_tenant_user_time": { + "name": "idx_time_events_tenant_user_time", + "columns": [ + { + "expression": "tenant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "event_time", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_time_events_created_at": { + "name": "idx_time_events_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_time_events_invalidates": { + "name": "idx_time_events_invalidates", + "columns": [ + { + "expression": "invalidates_event_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "time_events_tenant_id_tenants_id_fk": { + "name": "time_events_tenant_id_tenants_id_fk", + "tableFrom": "time_events", + "tableTo": "tenants", + "columnsFrom": [ + "tenant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "time_events_user_id_auth_users_id_fk": { + "name": "time_events_user_id_auth_users_id_fk", + "tableFrom": "time_events", + "tableTo": "auth_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "time_events_actor_user_id_auth_users_id_fk": { + "name": "time_events_actor_user_id_auth_users_id_fk", + "tableFrom": "time_events", + "tableTo": "auth_users", + "columnsFrom": [ + "actor_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "time_events_invalidates_event_id_time_events_id_fk": { + "name": "time_events_invalidates_event_id_time_events_id_fk", + "tableFrom": "time_events", + "tableTo": "time_events", + "columnsFrom": [ + "invalidates_event_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "time_events_actor_user_check": { + "name": "time_events_actor_user_check", + "value": "\n (actor_type = 'system' AND actor_user_id IS NULL)\n OR\n (actor_type = 'user' AND actor_user_id IS NOT NULL)\n " + } + }, + "isRLSEnabled": false + } + }, + "enums": { + "public.credential_types": { + "name": "credential_types", + "schema": "public", + "values": [ + "mail", + "m365" + ] + }, + "public.folderfunctions": { + "name": "folderfunctions", + "schema": "public", + "values": [ + "none", + "yearSubCategory", + "incomingInvoices", + "invoices", + "quotes", + "confirmationOrders", + "deliveryNotes", + "vehicleData", + "reminders", + "taxData", + "deposit", + "timeEvaluations" + ] + }, + "public.locked_tenant": { + "name": "locked_tenant", + "schema": "public", + "values": [ + "maintenance_tenant", + "maintenance", + "general", + "no_subscription" + ] + }, + "public.notification_channel": { + "name": "notification_channel", + "schema": "public", + "values": [ + "email", + "inapp", + "sms", + "push", + "webhook" + ] + }, + "public.notification_severity": { + "name": "notification_severity", + "schema": "public", + "values": [ + "info", + "success", + "warning", + "error" + ] + }, + "public.notification_status": { + "name": "notification_status", + "schema": "public", + "values": [ + "queued", + "sent", + "failed", + "read" + ] + }, + "public.payment_types": { + "name": "payment_types", + "schema": "public", + "values": [ + "transfer", + "direct_debit" + ] + }, + "public.texttemplatepositions": { + "name": "texttemplatepositions", + "schema": "public", + "values": [ + "startText", + "endText" + ] + }, + "public.times_state": { + "name": "times_state", + "schema": "public", + "values": [ + "submitted", + "approved", + "draft" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/db/migrations/meta/_journal.json b/db/migrations/meta/_journal.json new file mode 100644 index 0000000..25a83ac --- /dev/null +++ b/db/migrations/meta/_journal.json @@ -0,0 +1,41 @@ +{ + "version": "7", + "dialect": "postgresql", + "entries": [ + { + "idx": 0, + "version": "7", + "when": 1764947303113, + "tag": "0000_brief_dark_beast", + "breakpoints": true + }, + { + "idx": 1, + "version": "7", + "when": 1765641431341, + "tag": "0001_medical_big_bertha", + "breakpoints": true + }, + { + "idx": 2, + "version": "7", + "when": 1765642446738, + "tag": "0002_silent_christian_walker", + "breakpoints": true + }, + { + "idx": 3, + "version": "7", + "when": 1765716484200, + "tag": "0003_woozy_adam_destine", + "breakpoints": true + }, + { + "idx": 4, + "version": "7", + "when": 1765716877146, + "tag": "0004_stormy_onslaught", + "breakpoints": true + } + ] +} \ No newline at end of file From 267a57c4ea17978c08cfe86f7cb7bab8ab495c34 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sun, 14 Dec 2025 16:29:00 +0100 Subject: [PATCH 125/149] Time Migration --- db/schema/staff_time_events.ts | 85 +++++ .../time/buildtimeevaluation.service.ts | 229 ++++++++++++ src/modules/time/derivetimespans.service.ts | 165 +++++++++ .../time/enrichtimespanswithstatus.service.ts | 91 +++++ src/modules/time/loadvalidevents.service.ts | 105 ++++++ src/routes/staff/time.ts | 328 +++++++++++++++++- 6 files changed, 1001 insertions(+), 2 deletions(-) create mode 100644 db/schema/staff_time_events.ts create mode 100644 src/modules/time/buildtimeevaluation.service.ts create mode 100644 src/modules/time/derivetimespans.service.ts create mode 100644 src/modules/time/enrichtimespanswithstatus.service.ts create mode 100644 src/modules/time/loadvalidevents.service.ts diff --git a/db/schema/staff_time_events.ts b/db/schema/staff_time_events.ts new file mode 100644 index 0000000..d58f77c --- /dev/null +++ b/db/schema/staff_time_events.ts @@ -0,0 +1,85 @@ +import { + pgTable, + uuid, + bigint, + text, + timestamp, + jsonb, + index, + check, +} from "drizzle-orm/pg-core"; +import { sql } from "drizzle-orm"; +import {tenants} from "./tenants"; +import {authUsers} from "./auth_users"; + +export const stafftimeevents = pgTable( + "staff_time_events", + { + id: uuid("id").primaryKey().defaultRandom(), + + tenant_id: bigint("tenant_id", { mode: "number" }) + .notNull() + .references(() => tenants.id), + + user_id: uuid("user_id") + .notNull() + .references(() => authUsers.id), + + // Akteur + actortype: text("actor_type").notNull(), // 'user' | 'system' + actoruser_id: uuid("actor_user_id").references(() => authUsers.id), + + // Zeit + eventtime: timestamp("event_time", { + withTimezone: true, + }).notNull(), + + // Fachliche Bedeutung + eventtype: text("event_type").notNull(), + + // Quelle + source: text("source").notNull(), // web | mobile | terminal | system + + // Entkräftung + invalidates_event_id: uuid("invalidates_event_id") + .references(() => stafftimeevents.id), + + //Beziehung Approval etc + related_event_id: uuid("related_event_id") + .references(() => stafftimeevents.id), + + // Zusatzdaten + metadata: jsonb("metadata"), + + // Technisch + created_at: timestamp("created_at", { + withTimezone: true, + }) + .defaultNow() + .notNull(), + }, + (table) => ({ + // Indizes + tenantUserTimeIdx: index("idx_time_events_tenant_user_time").on( + table.tenant_id, + table.user_id, + table.eventtime + ), + + createdAtIdx: index("idx_time_events_created_at").on(table.created_at), + + invalidatesIdx: index("idx_time_events_invalidates").on( + table.invalidates_event_id + ), + + // Constraints + actorUserCheck: check( + "time_events_actor_user_check", + sql` + (actor_type = 'system' AND actor_user_id IS NULL) + OR + (actor_type = 'user' AND actor_user_id IS NOT NULL) + ` + ), + }) +); diff --git a/src/modules/time/buildtimeevaluation.service.ts b/src/modules/time/buildtimeevaluation.service.ts new file mode 100644 index 0000000..e420e7c --- /dev/null +++ b/src/modules/time/buildtimeevaluation.service.ts @@ -0,0 +1,229 @@ +// src/services/buildTimeEvaluationFromSpans.ts + +import { FastifyInstance } from "fastify"; +import { and, eq, gte, lte, inArray } from "drizzle-orm"; +import { authProfiles, holidays } from "../../../db/schema"; +import { DerivedSpan } from "./derivetimespans.service"; // Importiert den angereicherten Span-Typ + +// Definiert das erwartete Rückgabeformat +export type TimeEvaluationResult = { + user_id: string; + tenant_id: number; + from: string; + to: string; + + // Sollzeit + timeSpanWorkingMinutes: number; + + // Arbeitszeit Salden + sumWorkingMinutesSubmitted: number; + sumWorkingMinutesApproved: number; + + // Abwesenheiten (minuten und Tage) + sumWorkingMinutesRecreationDays: number; + sumRecreationDays: number; + sumWorkingMinutesVacationDays: number; + sumVacationDays: number; + sumWorkingMinutesSickDays: number; + sumSickDays: number; + + // Endsalden + saldoApproved: number; // Saldo basierend auf genehmigter Zeit + saldoSubmitted: number; // Saldo basierend auf eingereichter/genehmigter Zeit + + spans: DerivedSpan[]; +}; + +// Hilfsfunktion zur Berechnung der Minuten (nur für geschlossene Spannen) +const calcMinutes = (start: Date, end: Date | null): number => { + if (!end) return 0; + return (end.getTime() - start.getTime()) / 60000; +}; + + +export async function buildTimeEvaluationFromSpans( + server: FastifyInstance, + user_id: string, + tenant_id: number, + startDateInput: string, + endDateInput: string, + // Der wichtigste Unterschied: Wir nehmen die angereicherten Spannen als Input + spans: DerivedSpan[] +): Promise { + + const startDate = server.dayjs(startDateInput); + const endDate = server.dayjs(endDateInput); + + // ------------------------------------------------------------- + // 1️⃣ Profil und Feiertage laden (WIE IM ALTEN SERVICE) + // ------------------------------------------------------------- + + const profileRows = await server.db + .select() + .from(authProfiles) + .where( + and( + eq(authProfiles.user_id, user_id), + eq(authProfiles.tenant_id, tenant_id) + ) + ) + .limit(1); + + const profile = profileRows[0]; + if (!profile) throw new Error("Profil konnte nicht geladen werden."); + + const holidaysRows = await server.db + .select({ + date: holidays.date, + }) + .from(holidays) + .where( + and( + inArray(holidays.state_code, [profile.state_code, "DE"]), + gte(holidays.date, startDate.format("YYYY-MM-DD")), + lte(holidays.date, endDate.add(1, "day").format("YYYY-MM-DD")) + ) + ); + + // ------------------------------------------------------------- + // 2️⃣ Sollzeit berechnen (WIE IM ALTEN SERVICE) + // ------------------------------------------------------------- + let timeSpanWorkingMinutes = 0; + const totalDays = endDate.add(1, "day").diff(startDate, "days"); + + for (let i = 0; i < totalDays; i++) { + const date = startDate.add(i, "days"); + const weekday = date.day(); + timeSpanWorkingMinutes += + (profile.weekly_regular_working_hours?.[weekday] || 0) * 60; + } + + + // ------------------------------------------------------------- + // 3️⃣ Arbeits- und Abwesenheitszeiten berechnen (NEUE LOGIK) + // ------------------------------------------------------------- + + let sumWorkingMinutesSubmitted = 0; + let sumWorkingMinutesApproved = 0; + + let sumWorkingMinutesVacationDays = 0; + let sumVacationDays = 0; + let sumWorkingMinutesSickDays = 0; + let sumSickDays = 0; + + // Akkumulieren der Zeiten basierend auf dem abgeleiteten Typ und Status + for (const span of spans) { + + // **A. Arbeitszeiten (WORK)** + if (span.type === "work") { + const minutes = calcMinutes(span.startedAt, span.endedAt); + + // Zähle zur eingereichten Summe, wenn der Status submitted oder approved ist + if (span.status === "submitted" || span.status === "approved") { + sumWorkingMinutesSubmitted += minutes; + } + + // Zähle zur genehmigten Summe, wenn der Status approved ist + if (span.status === "approved") { + sumWorkingMinutesApproved += minutes; + } + } + + // **B. Abwesenheiten (VACATION, SICK)** + // Wir verwenden die Logik aus dem alten Service: Berechnung der Sollzeit + // basierend auf den Tagen der Span (Voraussetzung: Spannen sind Volltages-Spannen) + if (span.type === "vacation" || span.type === "sick") { + + // Behandle nur genehmigte Abwesenheiten für die Saldenberechnung + if (span.status !== "approved") { + continue; + } + + const startDay = server.dayjs(span.startedAt).startOf('day'); + // Wenn endedAt null ist (offene Span), nehmen wir das Ende des Zeitraums + const endDay = span.endedAt ? server.dayjs(span.endedAt).startOf('day') : endDate.startOf('day'); + + // Berechnung der Tage der Span + const days = endDay.diff(startDay, "day") + 1; + + for (let i = 0; i < days; i++) { + const day = startDay.add(i, "day"); + const weekday = day.day(); + const hours = profile.weekly_regular_working_hours?.[weekday] || 0; + + if (span.type === "vacation") { + sumWorkingMinutesVacationDays += hours * 60; + } else if (span.type === "sick") { + sumWorkingMinutesSickDays += hours * 60; + } + } + + if (span.type === "vacation") { + sumVacationDays += days; + } else if (span.type === "sick") { + sumSickDays += days; + } + } + + // PAUSE Spannen werden ignoriert, da sie in der faktischen Ableitung bereits von WORK abgezogen wurden. + } + + + // ------------------------------------------------------------- + // 4️⃣ Feiertagsausgleich (WIE IM ALTEN SERVICE) + // ------------------------------------------------------------- + let sumWorkingMinutesRecreationDays = 0; + let sumRecreationDays = 0; + + if (profile.recreation_days_compensation && holidaysRows?.length) { + holidaysRows.forEach(({ date }) => { + const weekday = server.dayjs(date).day(); + const hours = profile.weekly_regular_working_hours?.[weekday] || 0; + sumWorkingMinutesRecreationDays += hours * 60; + sumRecreationDays++; + }); + } + + // ------------------------------------------------------------- + // 5️⃣ Salden berechnen (NEUE LOGIK) + // ------------------------------------------------------------- + + const totalCompensatedMinutes = + sumWorkingMinutesRecreationDays + + sumWorkingMinutesVacationDays + + sumWorkingMinutesSickDays; + + // Saldo basierend auf GENEHMIGTER Arbeitszeit + const totalApprovedMinutes = sumWorkingMinutesApproved + totalCompensatedMinutes; + const saldoApproved = totalApprovedMinutes - timeSpanWorkingMinutes; + + // Saldo basierend auf EINGEREICHTER und GENEHMIGTER Arbeitszeit + const totalSubmittedMinutes = sumWorkingMinutesSubmitted + totalCompensatedMinutes; + const saldoSubmitted = totalSubmittedMinutes - timeSpanWorkingMinutes; + + + // ------------------------------------------------------------- + // 6️⃣ Rückgabe + // ------------------------------------------------------------- + return { + user_id, + tenant_id, + from: startDate.format("YYYY-MM-DD"), + to: endDate.format("YYYY-MM-DD"), + timeSpanWorkingMinutes, + + sumWorkingMinutesSubmitted, + sumWorkingMinutesApproved, + + sumWorkingMinutesRecreationDays, + sumRecreationDays, + sumWorkingMinutesVacationDays, + sumVacationDays, + sumWorkingMinutesSickDays, + sumSickDays, + + saldoApproved, + saldoSubmitted, + spans, + }; +} \ No newline at end of file diff --git a/src/modules/time/derivetimespans.service.ts b/src/modules/time/derivetimespans.service.ts new file mode 100644 index 0000000..2c18f25 --- /dev/null +++ b/src/modules/time/derivetimespans.service.ts @@ -0,0 +1,165 @@ +type State = "IDLE" | "WORKING" | "PAUSED" | "ABSENT"; + +export type SpanStatus = "factual" | "submitted" | "approved" | "rejected"; + +export type DerivedSpan = { + type: "work" | "pause" | "vacation" | "sick" | "overtime_compensation"; + startedAt: Date; + endedAt: Date | null; + sourceEventIds: string[]; + status: SpanStatus; + statusActorId?: string; +}; + +type TimeEvent = { + id: string; + eventtype: string; + eventtime: Date; +}; + +// Liste aller faktischen Event-Typen, die die Zustandsmaschine beeinflussen +const FACTUAL_EVENT_TYPES = new Set([ + "work_start", + "pause_start", + "pause_end", + "work_end", + "auto_stop", // Wird als work_end behandelt + "vacation_start", + "vacation_end", + "sick_start", + "sick_end", + "overtime_compensation_start", + "overtime_compensation_end", +]); + +export function deriveTimeSpans(allValidEvents: TimeEvent[]): DerivedSpan[] { + + // 1. FILTERN: Nur faktische Events verarbeiten + const events = allValidEvents.filter(event => + FACTUAL_EVENT_TYPES.has(event.eventtype) + ); + + const spans: DerivedSpan[] = []; + + let state: State = "IDLE"; + let currentStart: Date | null = null; + let currentType: DerivedSpan["type"] | null = null; + let sourceEventIds: string[] = []; + + const closeSpan = (end: Date) => { + if (!currentStart || !currentType) return; + + spans.push({ + type: currentType, + startedAt: currentStart, + endedAt: end, + sourceEventIds: [...sourceEventIds], + // Standardstatus ist "factual", wird später angereichert + status: "factual" + }); + + currentStart = null; + currentType = null; + sourceEventIds = []; + }; + + const closeOpenSpanAsRunning = () => { + if (!currentStart || !currentType) return; + + spans.push({ + type: currentType, + startedAt: currentStart, + endedAt: null, + sourceEventIds: [...sourceEventIds], + // Standardstatus ist "factual", wird später angereichert + status: "factual" + }); + + currentStart = null; + currentType = null; + sourceEventIds = []; + }; + + for (const event of events) { + sourceEventIds.push(event.id); + + switch (event.eventtype) { + /* ========================= + ARBEITSZEIT + ========================= */ + + case "work_start": + if (state === "WORKING" || state === "PAUSED" || state === "ABSENT") { + // Schließt die vorherige Spanne (falls z.B. work_start nach sick_start kommt) + closeSpan(event.eventtime); + } + state = "WORKING"; + currentStart = event.eventtime; + currentType = "work"; + break; + + case "pause_start": + if (state === "WORKING") { + closeSpan(event.eventtime); + state = "PAUSED"; + currentStart = event.eventtime; + currentType = "pause"; + } + break; + + case "pause_end": + if (state === "PAUSED") { + closeSpan(event.eventtime); + state = "WORKING"; + currentStart = event.eventtime; + currentType = "work"; + } + break; + + case "work_end": + case "auto_stop": + if (state === "WORKING" || state === "PAUSED") { + closeSpan(event.eventtime); + } + state = "IDLE"; + break; + + /* ========================= + ABWESENHEITEN + ========================= */ + + case "vacation_start": + case "sick_start": + case "overtime_compensation_start": + // Mappt den Event-Typ direkt auf den Span-Typ + const newType = event.eventtype.split('_')[0] as DerivedSpan["type"]; + + if (state !== "IDLE") { + closeSpan(event.eventtime); + } + state = "ABSENT"; + currentStart = event.eventtime; + currentType = newType; + break; + + case "vacation_end": + case "sick_end": + case "overtime_compensation_end": + // Extrahiert den Typ der zu beendenden Spanne + const endedType = event.eventtype.split('_')[0] as DerivedSpan["type"]; + + if (state === "ABSENT" && currentType === endedType) { + closeSpan(event.eventtime); + } + state = "IDLE"; + break; + } + } + + // 🔴 WICHTIG: Offene Spannen als laufend zurückgeben + if (state !== "IDLE") { + closeOpenSpanAsRunning(); + } + + return spans; +} \ No newline at end of file diff --git a/src/modules/time/enrichtimespanswithstatus.service.ts b/src/modules/time/enrichtimespanswithstatus.service.ts new file mode 100644 index 0000000..b82f2eb --- /dev/null +++ b/src/modules/time/enrichtimespanswithstatus.service.ts @@ -0,0 +1,91 @@ +// src/services/enrichSpansWithStatus.ts (Korrigierte Version) + +import { DerivedSpan, SpanStatus } from "./derivetimespans.service"; +import { TimeEvent } from "./loadvalidevents.service"; // Jetzt mit related_event_id und actoruser_id + +// ... (Rest der Imports) + +export function enrichSpansWithStatus( + factualSpans: DerivedSpan[], + allValidEvents: TimeEvent[] +): DerivedSpan[] { + + // 1. Map der administrativen Aktionen erstellen + const eventStatusMap = new Map(); + + const administrativeEvents = allValidEvents.filter(e => + e.eventtype === 'submitted' || e.eventtype === 'approved' || e.eventtype === 'rejected' + ); + + // allValidEvents ist nach Zeit sortiert + for (const event of administrativeEvents) { + + // **Verwendung des expliziten Feldes** + const relatedId = event.related_event_id; + const actorId = event.actoruser_id; + + if (relatedId) { // Nur fortfahren, wenn ein Bezugs-Event existiert + + let status: SpanStatus = "factual"; + // Wir überschreiben den Status des relatedId basierend auf der Event-Historie + if (event.eventtype === 'submitted') status = 'submitted'; + else if (event.eventtype === 'approved') status = 'approved'; + else if (event.eventtype === 'rejected') status = 'rejected'; + + eventStatusMap.set(relatedId, { status, actorId }); + } + } + + // 2. Status der Spannen bestimmen und anreichern + return factualSpans.map(span => { + + let approvedCount = 0; + let rejectedCount = 0; + let submittedCount = 0; + let isFactualCount = 0; + + for (const sourceId of span.sourceEventIds) { + const statusInfo = eventStatusMap.get(sourceId); + + if (statusInfo) { + // Ein faktisches Event kann durch mehrere administrative Events betroffen sein + // Wir speichern im Map nur den letzten Status (z.B. approved überschreibt submitted) + if (statusInfo.status === 'approved') approvedCount++; + else if (statusInfo.status === 'rejected') rejectedCount++; + else if (statusInfo.status === 'submitted') submittedCount++; + } else { + // Wenn kein administratives Event existiert + isFactualCount++; + } + } + + // Regel zur Bestimmung des Span-Status: + const totalSourceEvents = span.sourceEventIds.length; + let finalStatus: SpanStatus = "factual"; + + if (totalSourceEvents > 0) { + + // Priorität 1: Rejection + if (rejectedCount > 0) { + finalStatus = "rejected"; + } + // Priorität 2: Full Approval + else if (approvedCount === totalSourceEvents) { + finalStatus = "approved"; + } + // Priorität 3: Submitted (wenn nicht fully approved oder rejected, aber mindestens eines submitted ist) + else if (submittedCount > 0 || approvedCount > 0) { + finalStatus = "submitted"; + // Ein Span ist submitted, wenn es zumindest teilweise eingereicht (oder genehmigt) ist, + // aber nicht alle Events den finalen Status "approved" haben. + } + // Ansonsten bleibt es "factual" (wenn z.B. nur work_start aber nicht work_end eingereicht wurde, oder nichts) + } + + // Rückgabe der angereicherten Span + return { + ...span, + status: finalStatus, + }; + }); +} \ No newline at end of file diff --git a/src/modules/time/loadvalidevents.service.ts b/src/modules/time/loadvalidevents.service.ts new file mode 100644 index 0000000..134e363 --- /dev/null +++ b/src/modules/time/loadvalidevents.service.ts @@ -0,0 +1,105 @@ +// src/services/loadValidEvents.ts + +import { stafftimeevents } from "../../../db/schema"; +import {sql, and, eq, gte, lte, inArray} from "drizzle-orm"; +import { FastifyInstance } from "fastify"; + +export type TimeType = "work_start" | "work_end" | "pause_start" | "pause_end" | "sick_start" | "sick_end" | "vacation_start" | "vacation_end" | "ovetime_compensation_start" | "overtime_compensation_end"; + + +// Die Definition des TimeEvent Typs, der zurückgegeben wird (muss mit dem tatsächlichen Typ übereinstimmen) +export type TimeEvent = { + id: string; + eventtype: string; + eventtime: Date; + actoruser_id: string; + related_event_id: string | null; + // Fügen Sie hier alle weiteren Felder hinzu, die Sie benötigen +}; + +export async function loadValidEvents( + server: FastifyInstance, + tenantId: number, + userId: string, + from: Date, + to: Date +): Promise { + // Definieren Sie einen Alias für die stafftimeevents Tabelle in der äußeren Abfrage + const baseEvents = stafftimeevents; + + // Die Subquery, um alle IDs zu finden, die ungültig gemacht wurden + // Wir nennen die innere Tabelle 'invalidatingEvents' + const invalidatingEvents = server.db + .select({ + invalidatedId: baseEvents.invalidates_event_id + }) + .from(baseEvents) + .as('invalidating_events'); + + // Die Hauptabfrage + const result = await server.db + .select() + .from(baseEvents) + .where( + and( + // 1. Tenant und User filtern + eq(baseEvents.tenant_id, tenantId), + eq(baseEvents.user_id, userId), + + // 2. Zeitbereich filtern (Typensicher) + gte(baseEvents.eventtime, from), + lte(baseEvents.eventtime, to), + + // 3. WICHTIG: Korrekturen ausschließen (NOT EXISTS) + // Schließe jedes Event aus, dessen ID in der Liste der invalidates_event_id erscheint. + sql` + not exists ( + select 1 + from ${stafftimeevents} i + where i.invalidates_event_id = ${baseEvents.id} + ) + ` + ) + ) + .orderBy( + // Wichtig für die deriveTimeSpans Logik: Event-Zeit muss primär sein + baseEvents.eventtime, + baseEvents.created_at, // Wenn Eventtime identisch ist, das später erstellte zuerst + baseEvents.id + ); + + // Mapping auf den sauberen TimeEvent Typ + return result.map(e => ({ + id: e.id, + eventtype: e.eventtype, + eventtime: e.eventtime, + // Fügen Sie hier alle weiteren benötigten Felder hinzu (z.B. metadata, actoruser_id) + // ... + })) as TimeEvent[]; +} + +export async function loadRelatedAdminEvents(server, eventIds) { + if (eventIds.length === 0) return []; + + // Lädt alle administrativen Events, die sich auf die faktischen Event-IDs beziehen + const adminEvents = await server.db + .select() + .from(stafftimeevents) + .where( + and( + inArray(stafftimeevents.related_event_id, eventIds), + // Wir müssen hier die Entkräftung prüfen, um z.B. einen abgelehnten submitted-Event auszuschließen + sql` + not exists ( + select 1 + from ${stafftimeevents} i + where i.invalidates_event_id = ${stafftimeevents}.id + ) + `, + ) + ) + // Muss nach Zeit sortiert sein, um den Status korrekt zu bestimmen! + .orderBy(stafftimeevents.eventtime); + + return adminEvents; +} \ No newline at end of file diff --git a/src/routes/staff/time.ts b/src/routes/staff/time.ts index 5b4956c..33f57c1 100644 --- a/src/routes/staff/time.ts +++ b/src/routes/staff/time.ts @@ -10,10 +10,334 @@ import { lte, desc } from "drizzle-orm" +import {stafftimeevents} from "../../../db/schema/staff_time_events"; +import {loadRelatedAdminEvents, loadValidEvents} from "../../modules/time/loadvalidevents.service"; +import {deriveTimeSpans} from "../../modules/time/derivetimespans.service"; +import {buildTimeEvaluationFromSpans} from "../../modules/time/buildtimeevaluation.service"; +import {z} from "zod"; +import {enrichSpansWithStatus} from "../../modules/time/enrichtimespanswithstatus.service"; export default async function staffTimeRoutes(server: FastifyInstance) { - // ------------------------------------------------------------- + + server.post("/staff/time/event", async (req, reply) => { + try { + const userId = req.user.user_id + const tenantId = req.user.tenant_id + + const body = req.body as any + + const normalizeDate = (val: any) => { + if (!val) return null + const d = new Date(val) + return isNaN(d.getTime()) ? null : d + } + + const dataToInsert = { + tenant_id: tenantId, + user_id: userId, + actortype: "user", + actoruser_id: userId, + eventtime: normalizeDate(body.eventtime), + eventtype: body.eventtype, + source: "WEB" + } + + console.log(dataToInsert) + + const [created] = await server.db + .insert(stafftimeevents) + //@ts-ignore + .values(dataToInsert) + .returning() + + return created + } catch (err: any) { + console.error(err) + return reply.code(400).send({ error: err.message }) + } + }) + + // POST /staff/time/submit + server.post("/staff/time/submit", async (req, reply) => { + try { + const userId = req.user.user_id; // Mitarbeiter, der einreicht + const tenantId = req.user.tenant_id; + + // Erwartet eine Liste von IDs der faktischen Events (work_start, work_end, etc.) + const { eventIds } = req.body as { eventIds: string[] }; + + if (eventIds.length === 0) { + return reply.code(400).send({ error: "Keine Events zum Einreichen angegeben." }); + } + + const inserts = eventIds.map((eventId) => ({ + tenant_id: tenantId, + user_id: userId, // Event gehört zum Mitarbeiter + actortype: "user", + actoruser_id: userId, // Mitarbeiter ist der Akteur + eventtime: new Date(), + eventtype: "submitted", // NEU: Event-Typ für Einreichung + source: "WEB", + related_event_id: eventId, // Verweis auf das faktische Event + })); + + const createdEvents = await server.db + .insert(stafftimeevents) + //@ts-ignore + .values(inserts) + .returning(); + + return { submittedCount: createdEvents.length }; + } catch (err: any) { + console.error(err); + return reply.code(500).send({ error: err.message }); + } + }); + + // POST /staff/time/approve + server.post("/staff/time/approve", async (req, reply) => { + try { + // 🚨 Berechtigungsprüfung (Voraussetzung: req.user enthält Manager-Status) + /*if (!req.user.isManager) { + return reply.code(403).send({ error: "Keine Genehmigungsberechtigung." }); + }*/ + + const actorId = req.user.user_id; // Manager ist der Akteur + const tenantId = req.user.tenant_id; + + const { eventIds, employeeUserId } = req.body as { + eventIds: string[]; + employeeUserId: string; // Die ID des Mitarbeiters, dessen Zeit genehmigt wird + }; + + if (eventIds.length === 0) { + return reply.code(400).send({ error: "Keine Events zur Genehmigung angegeben." }); + } + + const inserts = eventIds.map((eventId) => ({ + tenant_id: tenantId, + user_id: employeeUserId, // Event gehört zum Mitarbeiter + actortype: "user", + actoruser_id: actorId, // Manager ist der Akteur + eventtime: new Date(), + eventtype: "approved", // NEU: Event-Typ für Genehmigung + source: "WEB", + related_event_id: eventId, // Verweis auf das faktische Event + metadata: { + // Optional: Genehmigungskommentar + approvedBy: req.user.email + } + })); + + const createdEvents = await server.db + .insert(stafftimeevents) + //@ts-ignore + .values(inserts) + .returning(); + + return { approvedCount: createdEvents.length }; + } catch (err: any) { + console.error(err); + return reply.code(500).send({ error: err.message }); + } + }); + + // POST /staff/time/reject + server.post("/staff/time/reject", async (req, reply) => { + try { + // 🚨 Berechtigungsprüfung + /*if (!req.user.isManager) { + return reply.code(403).send({ error: "Keine Zurückweisungsberechtigung." }); + }*/ + + const actorId = req.user.user_id; // Manager ist der Akteur + const tenantId = req.user.tenant_id; + + const { eventIds, employeeUserId, reason } = req.body as { + eventIds: string[]; + employeeUserId: string; + reason?: string; // Optionaler Grund für die Ablehnung + }; + + if (eventIds.length === 0) { + return reply.code(400).send({ error: "Keine Events zur Ablehnung angegeben." }); + } + + const inserts = eventIds.map((eventId) => ({ + tenant_id: tenantId, + user_id: employeeUserId, // Event gehört zum Mitarbeiter + actortype: "user", + actoruser_id: actorId, // Manager ist der Akteur + eventtime: new Date(), + eventtype: "rejected", // NEU: Event-Typ für Ablehnung + source: "WEB", + related_event_id: eventId, // Verweis auf das faktische Event + metadata: { + reason: reason || "Ohne Angabe" + } + })); + + const createdEvents = await server.db + .insert(stafftimeevents) + //@ts-ignore + .values(inserts) + .returning(); + + return { rejectedCount: createdEvents.length }; + } catch (err: any) { + console.error(err); + return reply.code(500).send({ error: err.message }); + } + }); + + // GET /api/staff/time/spans + server.get("/staff/time/spans", async (req, reply) => { + try { + // Der eingeloggte User (Anfragesteller) + const actingUserId = req.user.user_id; + const tenantId = req.user.tenant_id; + + // Query-Parameter: targetUserId ist optional + const { targetUserId } = req.query as { targetUserId?: string }; + + // Falls eine targetUserId übergeben wurde, nutzen wir diese, sonst die eigene ID + const evaluatedUserId = targetUserId || actingUserId; + + // 💡 "Unendlicher" Zeitraum, wie gewünscht + const startDate = new Date(0); // 1970 + const endDate = new Date("2100-12-31"); + + // SCHRITT 1: Lade ALLE Events für den ZIEL-USER (evaluatedUserId) + const allEventsInTimeFrame = await loadValidEvents( + server, + tenantId, + evaluatedUserId, // <--- Hier wird jetzt die variable ID genutzt + startDate, + endDate + ); + + // SCHRITT 2: Filtere faktische Events + const FACTUAL_EVENT_TYPES = new Set([ + "work_start", "work_end", "pause_start", "pause_end", + "sick_start", "sick_end", "vacation_start", "vacation_end", + "overtime_compensation_start", "overtime_compensation_end", + "auto_stop" + ]); + const factualEvents = allEventsInTimeFrame.filter(e => FACTUAL_EVENT_TYPES.has(e.eventtype)); + + // SCHRITT 3: Hole administrative Events + const factualEventIds = factualEvents.map(e => e.id); + + if (factualEventIds.length === 0) { + return []; + } + + const relatedAdminEvents = await loadRelatedAdminEvents(server, factualEventIds); + + // SCHRITT 4: Kombinieren und Sortieren + const combinedEvents = [ + ...factualEvents, + ...relatedAdminEvents, + ].sort((a, b) => a.eventtime.getTime() - b.eventtime.getTime()); + + // SCHRITT 5: Spans ableiten + const derivedSpans = deriveTimeSpans(combinedEvents); + + // SCHRITT 6: Spans anreichern + const enrichedSpans = enrichSpansWithStatus(derivedSpans, combinedEvents); + + return enrichedSpans; + + } catch (error) { + console.error("Fehler beim Laden der Spans:", error); + return reply.code(500).send({ error: "Interner Fehler beim Laden der Zeitspannen." }); + } + }); + + server.get("/staff/time/evaluation", async (req, reply) => { + try { + // --- 1. Eingangsdaten und Validierung des aktuellen Nutzers --- + + // Daten des aktuell eingeloggten (anfragenden) Benutzers + const actingUserId = req.user.user_id; + const tenantId = req.user.tenant_id; + + // Query-Parameter extrahieren + const { from, to, targetUserId } = req.query as { + from: string, + to: string, + targetUserId?: string // Optionale ID des Benutzers, dessen Daten abgerufen werden sollen + }; + + // Die ID, für die die Auswertung tatsächlich durchgeführt wird + const evaluatedUserId = targetUserId || actingUserId; + + const startDate = new Date(from); + const endDate = new Date(to); + + if (isNaN(startDate.getTime()) || isNaN(endDate.getTime())) { + return reply.code(400).send({ error: "Ungültiges Datumsformat." }); + } + + // --- 3. Ausführung der Logik für den ermittelten Benutzer --- + + // SCHRITT 1: Lade ALLE gültigen Events im Zeitraum + const allEventsInTimeFrame = await loadValidEvents( + server, tenantId, evaluatedUserId, startDate, endDate // Verwendung der evaluatedUserId + ); + + // 1b: Trenne Faktische und Administrative Events + const FACTUAL_EVENT_TYPES = new Set([ + "work_start", "work_end", "pause_start", "pause_end", + "sick_start", "sick_end", "vacation_start", "vacation_end", + "overtime_compensation_start", "overtime_compensation_end", + "auto_stop" + ]); + const factualEvents = allEventsInTimeFrame.filter(e => FACTUAL_EVENT_TYPES.has(e.eventtype)); + + // 1c: Sammle alle IDs der faktischen Events im Zeitraum + const factualEventIds = factualEvents.map(e => e.id); + + // SCHRITT 2: Lade die administrativen Events, die sich auf diese IDs beziehen (auch NACH dem Zeitraum) + const relatedAdminEvents = await loadRelatedAdminEvents(server, factualEventIds); + + // SCHRITT 3: Kombiniere alle Events für die Weiterverarbeitung + const combinedEvents = [ + ...factualEvents, + ...relatedAdminEvents, + ].sort((a, b) => a.eventtime.getTime() - b.eventtime.getTime()); + + // SCHRITT 4: Ableiten und Anreichern + const derivedSpans = deriveTimeSpans(combinedEvents); + const enrichedSpans = enrichSpansWithStatus(derivedSpans, combinedEvents); + + // SCHRITT 5: Erstellung der finalen Auswertung (Summen und Salden) + const evaluationSummary = await buildTimeEvaluationFromSpans( + server, + evaluatedUserId, // Verwendung der evaluatedUserId + tenantId, + from, + to, + enrichedSpans + ); + + return { + userId: evaluatedUserId, // Rückgabe der ID, für die ausgewertet wurde + spans: enrichedSpans, + summary: evaluationSummary + }; + + + } catch (error) { + console.error("Fehler in /staff/time/evaluation:", error); + return reply.code(500).send({ error: "Interner Serverfehler bei der Zeitauswertung." }); + } + }); + + + + /*// ------------------------------------------------------------- // ▶ Neue Zeit starten // ------------------------------------------------------------- server.post("/staff/time", async (req, reply) => { @@ -232,5 +556,5 @@ export default async function staffTimeRoutes(server: FastifyInstance) { } catch (err) { return reply.code(400).send({ error: (err as Error).message }) } - }) + })*/ } From 1281976ec353f596bb714a6ee58a501fd1059634 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sun, 14 Dec 2025 16:29:08 +0100 Subject: [PATCH 126/149] Time Migration --- src/utils/pdf.ts | 198 ++++++++++++++++++++++++++++------------------- 1 file changed, 120 insertions(+), 78 deletions(-) diff --git a/src/utils/pdf.ts b/src/utils/pdf.ts index 03e8aa5..a231a40 100644 --- a/src/utils/pdf.ts +++ b/src/utils/pdf.ts @@ -878,11 +878,10 @@ export const createTimeSheetPDF = async (server: FastifyInstance, returnMode, da let pages = [] let pageCounter = 1 - - const backgroudPdf = await PDFDocument.load(backgroundSourceBuffer) const firstPageBackground = await pdfDoc.embedPage(backgroudPdf.getPages()[0]) + // Fallback für einseitige Hintergründe const secondPageBackground = await pdfDoc.embedPage(backgroudPdf.getPages()[backgroudPdf.getPages().length > 1 ? 1 : 0]) const page1 = pdfDoc.addPage() @@ -894,121 +893,145 @@ export const createTimeSheetPDF = async (server: FastifyInstance, returnMode, da pages.push(page1) + console.log("PDF Input Data:", input) - //Falzmarke 1 - /*pages[pageCounter - 1].drawLine({ - start: getCoordinatesForPDFLib(0,105,page1), - end: getCoordinatesForPDFLib(7,105,page1), - thickness: 0.25, - color: rgb(0,0,0), - opacity: 1 - })*/ + // --------------------------------------------------------- + // DATEN-EXTRAKTION MIT FALLBACKS (Calculated vs. Standard) + // --------------------------------------------------------- - //Lochmarke - /*pages[pageCounter - 1].drawLine({ - start: getCoordinatesForPDFLib(0,148.5,page1), - end: getCoordinatesForPDFLib(7,148.5,page1), - thickness: 0.25, - color: rgb(0,0,0), - opacity: 1 - })*/ + // Summen: Bevorzuge calculated..., falls vorhanden + const sumSubmitted = input.calculatedSumWorkingMinutesSubmitted ?? input.sumWorkingMinutesSubmitted ?? 0; + const sumApproved = input.calculatedSumWorkingMinutesApproved ?? input.sumWorkingMinutesApproved ?? 0; + + // Saldi: Bevorzuge calculated... + const saldoSubmitted = input.calculatedSaldoSubmitted ?? input.saldoSubmitted ?? input.saldoInOfficial ?? 0; + const saldoApproved = input.calculatedSaldoApproved ?? input.saldoApproved ?? input.saldo ?? 0; + + // Andere Summen (diese sind meist korrekt vom Backend) + const sumRecreation = input.sumWorkingMinutesRecreationDays ?? 0; + const sumVacation = input.sumWorkingMinutesVacationDays ?? 0; + const sumSick = input.sumWorkingMinutesSickDays ?? 0; + const sumTarget = input.timeSpanWorkingMinutes ?? 0; + + // Hilfsfunktion zur Formatierung von Minuten -> HH:MM + const fmtTime = (mins) => { + const m = Math.floor(Math.abs(mins)); + return `${Math.floor(m / 60)}:${String(m % 60).padStart(2, "0")}`; + }; + + const fmtSaldo = (mins) => { + const sign = Math.sign(mins) >= 0 ? "+" : "-"; + return `${sign} ${fmtTime(mins)}`; + } + + + // --------------------------------------------------------- + // HEADER TEXTE ZEICHNEN + // --------------------------------------------------------- - //Falzmarke 2 - /*pages[pageCounter - 1].drawLine({ - start: getCoordinatesForPDFLib(0,210,page1), - end: getCoordinatesForPDFLib(7,210,page1), - thickness: 0.25, - color: rgb(0,0,0), - opacity: 1 - })*/ - console.log(input) pages[pageCounter - 1].drawText(`Anwesenheitsauswertung`,{ - x: getCoordinatesForPDFLib(20,40,pages[pageCounter -1]).x, - y: getCoordinatesForPDFLib(20,40,pages[pageCounter -1]).y, + x: getCoordinatesForPDFLib(20,60,pages[pageCounter -1]).x, + y: getCoordinatesForPDFLib(20,60,pages[pageCounter -1]).y, size: 15, font: fontBold }) - pages[pageCounter - 1].drawText(`Mitarbeiter: ${input.full_name}`,{ - x: getCoordinatesForPDFLib(20,50,pages[pageCounter -1]).x, - y: getCoordinatesForPDFLib(20,50,pages[pageCounter -1]).y, - size: 10, - }) - pages[pageCounter - 1].drawText(`Nummer: ${input.employee_number}`,{ - x: getCoordinatesForPDFLib(20,55,pages[pageCounter -1]).x, - y: getCoordinatesForPDFLib(20,55,pages[pageCounter -1]).y, - size: 10, - }) - pages[pageCounter - 1].drawText(`Eingereicht: ${Math.floor(input.sumWorkingMinutesEingereicht/60)}:${String(input.sumWorkingMinutesEingereicht % 60).padStart(2,"0")} Std`,{ - x: getCoordinatesForPDFLib(20,60,pages[pageCounter -1]).x, - y: getCoordinatesForPDFLib(20,60,pages[pageCounter -1]).y, - size: 10, - }) - pages[pageCounter - 1].drawText(`Genehmigt: ${Math.floor(input.sumWorkingMinutesApproved/60)}:${String(input.sumWorkingMinutesApproved % 60).padStart(2,"0")} Std`,{ - x: getCoordinatesForPDFLib(20,65,pages[pageCounter -1]).x, - y: getCoordinatesForPDFLib(20,65,pages[pageCounter -1]).y, - size: 10, - }) - - pages[pageCounter - 1].drawText(`Feiertagsausgleich: ${Math.floor(input.sumWorkingMinutesRecreationDays/60)}:${String(input.sumWorkingMinutesRecreationDays % 60).padStart(2,"0")} Std`,{ + pages[pageCounter - 1].drawText(`Mitarbeiter: ${input.full_name || ''}`,{ x: getCoordinatesForPDFLib(20,70,pages[pageCounter -1]).x, y: getCoordinatesForPDFLib(20,70,pages[pageCounter -1]).y, size: 10, }) - pages[pageCounter - 1].drawText(`Urlaubsausgleich: ${Math.floor(input.sumWorkingMinutesVacationDays/60)}:${String(input.sumWorkingMinutesVacationDays % 60).padStart(2,"0")} Std`,{ + pages[pageCounter - 1].drawText(`Nummer: ${input.employee_number || '-'}`,{ x: getCoordinatesForPDFLib(20,75,pages[pageCounter -1]).x, y: getCoordinatesForPDFLib(20,75,pages[pageCounter -1]).y, size: 10, }) - pages[pageCounter - 1].drawText(`Krankheitsausgleich: ${Math.floor(input.sumWorkingMinutesSickDays/60)}:${String(input.sumWorkingMinutesSickDays % 60).padStart(2,"0")} Std`,{ + + // Zeile 1: Eingereicht & Genehmigt + pages[pageCounter - 1].drawText(`Eingereicht: ${fmtTime(sumSubmitted)} Std`,{ x: getCoordinatesForPDFLib(20,80,pages[pageCounter -1]).x, y: getCoordinatesForPDFLib(20,80,pages[pageCounter -1]).y, size: 10, }) - pages[pageCounter - 1].drawText(`Soll Stunden: ${Math.floor(input.timeSpanWorkingMinutes/60)}:${Math.floor(Number(String(input.timeSpanWorkingMinutes % 60).padStart(2,"0")))} Std`,{ + pages[pageCounter - 1].drawText(`Genehmigt: ${fmtTime(sumApproved)} Std`,{ x: getCoordinatesForPDFLib(20,85,pages[pageCounter -1]).x, y: getCoordinatesForPDFLib(20,85,pages[pageCounter -1]).y, size: 10, }) - pages[pageCounter - 1].drawText(`Inoffizielles Saldo: ${Math.sign(input.saldoInOfficial) === 1 ? "+" : "-"} ${Math.floor(Math.abs(input.saldoInOfficial/60))}:${Math.floor(Number(String(Math.abs(input.saldoInOfficial) % 60).padStart(2,"0")))} Std`,{ + + // Zeile 2: Ausgleichstage + pages[pageCounter - 1].drawText(`Feiertagsausgleich: ${fmtTime(sumRecreation)} Std`,{ x: getCoordinatesForPDFLib(20,90,pages[pageCounter -1]).x, y: getCoordinatesForPDFLib(20,90,pages[pageCounter -1]).y, size: 10, }) - pages[pageCounter - 1].drawText(`Saldo: ${Math.sign(input.saldo) === 1 ? "+" : "-"} ${Math.floor(Math.abs(input.saldo/60))}:${Math.floor(Number(String(Math.abs(input.saldo) % 60).padStart(2,"0")))} Std`,{ + pages[pageCounter - 1].drawText(`Urlaubsausgleich: ${fmtTime(sumVacation)} Std`,{ x: getCoordinatesForPDFLib(20,95,pages[pageCounter -1]).x, y: getCoordinatesForPDFLib(20,95,pages[pageCounter -1]).y, size: 10, }) - - pages[pageCounter - 1].drawText(`Start:`,{ + pages[pageCounter - 1].drawText(`Krankheitsausgleich: ${fmtTime(sumSick)} Std`,{ x: getCoordinatesForPDFLib(20,100,pages[pageCounter -1]).x, y: getCoordinatesForPDFLib(20,100,pages[pageCounter -1]).y, size: 10, }) + // Zeile 3: Soll & Saldo + pages[pageCounter - 1].drawText(`Soll Stunden: ${fmtTime(sumTarget)} Std`,{ + x: getCoordinatesForPDFLib(20,105,pages[pageCounter -1]).x, + y: getCoordinatesForPDFLib(20,105,pages[pageCounter -1]).y, + size: 10, + }) + + // Wir nutzen hier die Begriffe "Inoffiziell" (Submitted Saldo) und "Saldo" (Approved Saldo) + pages[pageCounter - 1].drawText(`Inoffizielles Saldo: ${fmtSaldo(saldoSubmitted)} Std`,{ + x: getCoordinatesForPDFLib(20,110,pages[pageCounter -1]).x, + y: getCoordinatesForPDFLib(20,110,pages[pageCounter -1]).y, + size: 10, + }) + pages[pageCounter - 1].drawText(`Saldo: ${fmtSaldo(saldoApproved)} Std`,{ + x: getCoordinatesForPDFLib(20,115,pages[pageCounter -1]).x, + y: getCoordinatesForPDFLib(20,115,pages[pageCounter -1]).y, + size: 10, + }) + + // Tabellen-Header + pages[pageCounter - 1].drawText(`Start:`,{ + x: getCoordinatesForPDFLib(20,125,pages[pageCounter -1]).x, + y: getCoordinatesForPDFLib(20,125,pages[pageCounter -1]).y, + size: 10, + }) + pages[pageCounter - 1].drawText(`Ende:`,{ - x: getCoordinatesForPDFLib(60,100,pages[pageCounter -1]).x, - y: getCoordinatesForPDFLib(60,100,pages[pageCounter -1]).y, + x: getCoordinatesForPDFLib(60,125,pages[pageCounter -1]).x, + y: getCoordinatesForPDFLib(60,125,pages[pageCounter -1]).y, size: 10, }) pages[pageCounter - 1].drawText(`Dauer:`,{ - x: getCoordinatesForPDFLib(100,100,pages[pageCounter -1]).x, - y: getCoordinatesForPDFLib(100,100,pages[pageCounter -1]).y, + x: getCoordinatesForPDFLib(100,125,pages[pageCounter -1]).x, + y: getCoordinatesForPDFLib(100,125,pages[pageCounter -1]).y, size: 10, }) + // --------------------------------------------------------- + // TABELLE GENERIEREN (Spans verarbeiten) + // --------------------------------------------------------- - let rowHeight = 115 + let rowHeight = 130 + // WICHTIG: input.spans verwenden, fallback auf input.times (altes Format) + // Wir filtern leere Einträge raus + const rawItems = (input.spans || input.times || []).filter(t => t); + + // Sortierung umkehren (neueste zuletzt für den Druck? Oder wie gewünscht) + // Im Original war es .reverse(). + let reversedInput = rawItems.slice().reverse(); let splitted = [] - - let reversedInput = input.times.slice().reverse() - const splittedLength = Math.floor((reversedInput.length - 25) / 40) + // Erste Seite hat weniger Platz wegen Header (25 Zeilen) splitted.push(reversedInput.slice(0,25)) let lastIndex = 25 @@ -1017,10 +1040,11 @@ export const createTimeSheetPDF = async (server: FastifyInstance, returnMode, da lastIndex = lastIndex + (i + 1) * 40 + 1 } - if(reversedInput.slice(lastIndex, reversedInput.length).length > 0) splitted.push(reversedInput.slice(lastIndex, reversedInput.length)) - - console.log(splitted ) + if(reversedInput.slice(lastIndex, reversedInput.length).length > 0) { + splitted.push(reversedInput.slice(lastIndex, reversedInput.length)) + } + console.log("PDF Pages Chunks:", splitted.length) splitted.forEach((chunk,index) => { if(index > 0) { @@ -1034,30 +1058,50 @@ export const createTimeSheetPDF = async (server: FastifyInstance, returnMode, da pages.push(page) pageCounter++ rowHeight = 20 - } - chunk.forEach(time => { - pages[pageCounter - 1].drawText(`${dayjs(time.started_at).format("HH:mm DD.MM.YY")}`,{ + chunk.forEach(span => { + // Mapping für Felder: spans nutzen 'startedAt', times nutzten 'started_at' + const startStr = span.startedAt || span.started_at; + const endStr = span.endedAt || span.stopped_at; // endedAt oder stopped_at + + // Dauer berechnen (da Spans keine duration_minutes haben) + let durationStr = ""; + if (startStr && endStr) { + const diffMins = dayjs(endStr).diff(dayjs(startStr), 'minute'); + durationStr = fmtTime(diffMins); + } else if (span.duration_minutes) { + durationStr = fmtTime(span.duration_minutes); + } else if (span.duration) { // Falls schon formatiert übergeben + durationStr = span.duration; + } + + pages[pageCounter - 1].drawText(`${dayjs(startStr).format("HH:mm DD.MM.YY")}`,{ x: getCoordinatesForPDFLib(20,rowHeight,pages[pageCounter -1]).x, y: getCoordinatesForPDFLib(20,rowHeight,pages[pageCounter -1]).y, size: 10, }) - pages[pageCounter - 1].drawText(`${dayjs(time.stopped_at).format("HH:mm DD.MM.YY")}`,{ + pages[pageCounter - 1].drawText(`${endStr ? dayjs(endStr).format("HH:mm DD.MM.YY") : 'läuft...'}`,{ x: getCoordinatesForPDFLib(60,rowHeight,pages[pageCounter -1]).x, y: getCoordinatesForPDFLib(60,rowHeight,pages[pageCounter -1]).y, size: 10, }) - pages[pageCounter - 1].drawText(`${getDuration(time).composed}`,{ + pages[pageCounter - 1].drawText(`${durationStr}`,{ x: getCoordinatesForPDFLib(100,rowHeight,pages[pageCounter -1]).x, y: getCoordinatesForPDFLib(100,rowHeight,pages[pageCounter -1]).y, size: 10, }) - rowHeight += 6 + // Optional: Status anzeigen? + /*pages[pageCounter - 1].drawText(`${span.status || span.state || ''}`,{ + x: getCoordinatesForPDFLib(130,rowHeight,pages[pageCounter -1]).x, + y: getCoordinatesForPDFLib(130,rowHeight,pages[pageCounter -1]).y, + size: 8, + })*/ + rowHeight += 6 }) }) @@ -1077,8 +1121,6 @@ export const createTimeSheetPDF = async (server: FastifyInstance, returnMode, da } } catch(error) { console.log(error) + throw error; // Fehler weiterwerfen, damit er oben ankommt } - - - } \ No newline at end of file From 5270313b3d2eeabbfbcf3a42b854fb10f1692b33 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sun, 14 Dec 2025 17:14:50 +0100 Subject: [PATCH 127/149] Time Migration --- db/schema/index.ts | 3 +- src/index.ts | 6 ++ src/routes/internal/devices.ts | 41 +++++++++++ src/routes/internal/tenant.ts | 107 +++++++++++++++++++++++++++++ src/routes/internal/time.ts | 122 +++++++++++++++++++++++++++++++++ 5 files changed, 278 insertions(+), 1 deletion(-) create mode 100644 src/routes/internal/devices.ts create mode 100644 src/routes/internal/tenant.ts create mode 100644 src/routes/internal/time.ts diff --git a/db/schema/index.ts b/db/schema/index.ts index 4208507..360363d 100644 --- a/db/schema/index.ts +++ b/db/schema/index.ts @@ -67,4 +67,5 @@ export * from "./texttemplates" export * from "./units" export * from "./user_credentials" export * from "./vehicles" -export * from "./vendors" \ No newline at end of file +export * from "./vendors" +export * from "./staff_time_events" \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index a057ed7..ca5223f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -35,6 +35,9 @@ import resourceRoutes from "./routes/resources/main"; //M2M import authM2m from "./plugins/auth.m2m"; import helpdeskInboundEmailRoutes from "./routes/helpdesk.inbound.email"; +import deviceRoutes from "./routes/internal/devices"; +import tenantRoutesInternal from "./routes/internal/tenant"; +import staffTimeRoutesInternal from "./routes/internal/time"; import {sendMail} from "./utils/mailer"; import {loadSecrets, secrets} from "./utils/secrets"; @@ -91,6 +94,9 @@ async function main() { await app.register(async (m2mApp) => { await m2mApp.register(authM2m) await m2mApp.register(helpdeskInboundEmailRoutes) + await m2mApp.register(deviceRoutes) + await m2mApp.register(tenantRoutesInternal) + await m2mApp.register(staffTimeRoutesInternal) },{prefix: "/internal"}) diff --git a/src/routes/internal/devices.ts b/src/routes/internal/devices.ts new file mode 100644 index 0000000..640304b --- /dev/null +++ b/src/routes/internal/devices.ts @@ -0,0 +1,41 @@ +import { FastifyInstance } from "fastify"; +import { eq } from "drizzle-orm"; +import { devices } from "../../../db/schema"; + +export default async function deviceRoutes(fastify: FastifyInstance) { + fastify.get<{ + Params: { + externalId: string; + }; + }>( + "/devices/by-external-id/:externalId", + async (request, reply) => { + const { externalId } = request.params; + + const device = await fastify.db + .select({ + id: devices.id, + name: devices.name, + type: devices.type, + tenant: devices.tenant, + externalId: devices.externalId, + created_at: devices.createdAt, + }) + .from(devices) + .where( + eq(devices.externalId, externalId) + + ) + .limit(1) + .then(rows => rows[0]); + + if (!device) { + return reply.status(404).send({ + message: "Device not found", + }); + } + + return reply.send(device); + } + ); +} diff --git a/src/routes/internal/tenant.ts b/src/routes/internal/tenant.ts new file mode 100644 index 0000000..ace0dc4 --- /dev/null +++ b/src/routes/internal/tenant.ts @@ -0,0 +1,107 @@ +import { FastifyInstance } from "fastify" + +import { + authTenantUsers, + authUsers, + authProfiles, + tenants +} from "../../../db/schema" + +import {and, eq, inArray} from "drizzle-orm" + + +export default async function tenantRoutesInternal(server: FastifyInstance) { + + + // ------------------------------------------------------------- + // GET CURRENT TENANT + // ------------------------------------------------------------- + server.get("/tenant/:id", async (req) => { + //@ts-ignore + const tenant = (await server.db.select().from(tenants).where(eq(tenants.id,req.params.id)).limit(1))[0] + + return tenant + }) + + + // ------------------------------------------------------------- + // TENANT USERS (auth_users + auth_profiles) + // ------------------------------------------------------------- + server.get("/tenant/users", async (req, reply) => { + try { + const authUser = req.user + if (!authUser) return reply.code(401).send({ error: "Unauthorized" }) + + const tenantId = authUser.tenant_id + + // 1) auth_tenant_users → user_ids + const tenantUsers = await server.db + .select() + .from(authTenantUsers) + .where(eq(authTenantUsers.tenant_id, tenantId)) + + const userIds = tenantUsers.map(u => u.user_id) + + if (!userIds.length) { + return { tenant_id: tenantId, users: [] } + } + + // 2) auth_users laden + const users = await server.db + .select() + .from(authUsers) + .where(inArray(authUsers.id, userIds)) + + // 3) auth_profiles pro Tenant laden + const profiles = await server.db + .select() + .from(authProfiles) + .where( + and( + eq(authProfiles.tenant_id, tenantId), + inArray(authProfiles.user_id, userIds) + )) + + const combined = users.map(u => { + const profile = profiles.find(p => p.user_id === u.id) + return { + id: u.id, + email: u.email, + profile, + full_name: profile?.full_name ?? null + } + }) + + return { tenant_id: tenantId, users: combined } + + } catch (err) { + console.error("/tenant/users ERROR:", err) + return reply.code(500).send({ error: "Internal Server Error" }) + } + }) + + + + // ------------------------------------------------------------- + // TENANT PROFILES + // ------------------------------------------------------------- + server.get("/tenant/:id/profiles", async (req, reply) => { + try { + // @ts-ignore + const tenantId = req.params.id + if (!tenantId) return reply.code(401).send({ error: "Unauthorized" }) + + const data = await server.db + .select() + .from(authProfiles) + .where(eq(authProfiles.tenant_id, tenantId)) + + return data + + } catch (err) { + console.error("/tenant/profiles ERROR:", err) + return reply.code(500).send({ error: "Internal Server Error" }) + } + }) + +} diff --git a/src/routes/internal/time.ts b/src/routes/internal/time.ts new file mode 100644 index 0000000..89b0544 --- /dev/null +++ b/src/routes/internal/time.ts @@ -0,0 +1,122 @@ +import { FastifyInstance } from "fastify" +import { + stafftimeentries, + stafftimenetryconnects +} from "../../../db/schema" +import { + eq, + and, + gte, + lte, + desc +} from "drizzle-orm" +import {stafftimeevents} from "../../../db/schema/staff_time_events"; +import {loadRelatedAdminEvents, loadValidEvents} from "../../modules/time/loadvalidevents.service"; +import {deriveTimeSpans} from "../../modules/time/derivetimespans.service"; +import {buildTimeEvaluationFromSpans} from "../../modules/time/buildtimeevaluation.service"; +import {z} from "zod"; +import {enrichSpansWithStatus} from "../../modules/time/enrichtimespanswithstatus.service"; + +export default async function staffTimeRoutesInternal(server: FastifyInstance) { + + + server.post("/staff/time/event", async (req, reply) => { + try { + + const body = req.body as {user_id:string,tenant_id:number,eventtime:string,eventtype:string} + + const normalizeDate = (val: any) => { + if (!val) return null + const d = new Date(val) + return isNaN(d.getTime()) ? null : d + } + + const dataToInsert = { + tenant_id: body.tenant_id, + user_id: body.user_id, + actortype: "user", + actoruser_id: body.user_id, + eventtime: normalizeDate(body.eventtime), + eventtype: body.eventtype, + source: "WEB" + } + + console.log(dataToInsert) + + const [created] = await server.db + .insert(stafftimeevents) + //@ts-ignore + .values(dataToInsert) + .returning() + + return created + } catch (err: any) { + console.error(err) + return reply.code(400).send({ error: err.message }) + } + }) + + + // GET /api/staff/time/spans + server.get("/staff/time/spans", async (req, reply) => { + try { + + // Query-Parameter: targetUserId ist optional + const { targetUserId, tenantId} = req.query as { targetUserId: string, tenantId:number }; + + // Falls eine targetUserId übergeben wurde, nutzen wir diese, sonst die eigene ID + const evaluatedUserId = targetUserId; + + // 💡 "Unendlicher" Zeitraum, wie gewünscht + const startDate = new Date(0); // 1970 + const endDate = new Date("2100-12-31"); + + // SCHRITT 1: Lade ALLE Events für den ZIEL-USER (evaluatedUserId) + const allEventsInTimeFrame = await loadValidEvents( + server, + tenantId, + evaluatedUserId, // <--- Hier wird jetzt die variable ID genutzt + startDate, + endDate + ); + + // SCHRITT 2: Filtere faktische Events + const FACTUAL_EVENT_TYPES = new Set([ + "work_start", "work_end", "pause_start", "pause_end", + "sick_start", "sick_end", "vacation_start", "vacation_end", + "overtime_compensation_start", "overtime_compensation_end", + "auto_stop" + ]); + const factualEvents = allEventsInTimeFrame.filter(e => FACTUAL_EVENT_TYPES.has(e.eventtype)); + + // SCHRITT 3: Hole administrative Events + const factualEventIds = factualEvents.map(e => e.id); + + if (factualEventIds.length === 0) { + return []; + } + + const relatedAdminEvents = await loadRelatedAdminEvents(server, factualEventIds); + + // SCHRITT 4: Kombinieren und Sortieren + const combinedEvents = [ + ...factualEvents, + ...relatedAdminEvents, + ].sort((a, b) => a.eventtime.getTime() - b.eventtime.getTime()); + + // SCHRITT 5: Spans ableiten + const derivedSpans = deriveTimeSpans(combinedEvents); + + // SCHRITT 6: Spans anreichern + const enrichedSpans = enrichSpansWithStatus(derivedSpans, combinedEvents); + + return enrichedSpans; + + } catch (error) { + console.error("Fehler beim Laden der Spans:", error); + return reply.code(500).send({ error: "Interner Fehler beim Laden der Zeitspannen." }); + } + }); + + +} From 94c252ec6cf6570363f80913ce63dc91b45b4b8c Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Tue, 16 Dec 2025 14:13:48 +0100 Subject: [PATCH 128/149] rfid --- src/index.ts | 8 ++++ src/routes/devices/rfid.ts | 98 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 src/routes/devices/rfid.ts diff --git a/src/index.ts b/src/index.ts index ca5223f..61b2ee1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -39,6 +39,10 @@ import deviceRoutes from "./routes/internal/devices"; import tenantRoutesInternal from "./routes/internal/tenant"; import staffTimeRoutesInternal from "./routes/internal/time"; +//Devices +import devicesRFIDRoutes from "./routes/devices/rfid"; + + import {sendMail} from "./utils/mailer"; import {loadSecrets, secrets} from "./utils/secrets"; import {initMailer} from "./utils/mailer" @@ -99,6 +103,10 @@ async function main() { await m2mApp.register(staffTimeRoutesInternal) },{prefix: "/internal"}) + await app.register(async (devicesApp) => { + await devicesApp.register(devicesRFIDRoutes) + },{prefix: "/devices"}) + //Geschützte Routes diff --git a/src/routes/devices/rfid.ts b/src/routes/devices/rfid.ts new file mode 100644 index 0000000..2931c07 --- /dev/null +++ b/src/routes/devices/rfid.ts @@ -0,0 +1,98 @@ +import { FastifyInstance } from "fastify"; +import {and, desc, eq} from "drizzle-orm"; +import {authProfiles, devices, stafftimeevents} from "../../../db/schema"; + +export default async function devicesRFIDRoutes(server: FastifyInstance) { + server.post( + "/rfid/createevent/:terminal_id", + async (req, reply) => { + try { + + const {rfid_id} = req.body as {rfid_id: string}; + const {terminal_id} = req.params as {terminal_id: string}; + + if(!rfid_id ||!terminal_id) { + console.log(`Missing Params`); + return reply.code(400).send(`Missing Params`) + } + + const device = await server.db + .select() + .from(devices) + .where( + eq(devices.externalId, terminal_id) + + ) + .limit(1) + .then(rows => rows[0]); + + if(!device) { + console.log(`Device ${terminal_id} not found`); + return reply.code(400).send(`Device ${terminal_id} not found`) + + } + + const profile = await server.db + .select() + .from(authProfiles) + .where( + and( + eq(authProfiles.tenant_id, device.tenant), + eq(authProfiles.token_id, rfid_id) + ) + ) + .limit(1) + .then(rows => rows[0]); + + if(!profile) { + console.log(`Profile for Token ${rfid_id} not found`); + return reply.code(400).send(`Profile for Token ${rfid_id} not found`) + + } + + const lastEvent = await server.db + .select() + .from(stafftimeevents) + .where( + eq(stafftimeevents.user_id, profile.user_id) + ) + .orderBy(desc(stafftimeevents.eventtime)) // <-- Sortierung: Neuestes zuerst + .limit(1) + .then(rows => rows[0]); + + console.log(lastEvent) + + + const dataToInsert = { + tenant_id: device.tenant, + user_id: profile.user_id, + actortype: "system", + eventtime: new Date(), + eventtype: lastEvent.eventtype === "work_start" ? "work_end" : "work_start", + source: "WEB" + } + + console.log(dataToInsert) + + const [created] = await server.db + .insert(stafftimeevents) + //@ts-ignore + .values(dataToInsert) + .returning() + + return created + } catch (err: any) { + console.error(err) + return reply.code(400).send({ error: err.message }) + } + + + + console.log(req.body) + + return + + + } + ); +} From e6bc123481b2390543e5e76a01dc1f6438b351b9 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Tue, 16 Dec 2025 16:23:25 +0100 Subject: [PATCH 129/149] Time --- src/routes/staff/time.ts | 318 ++++++++++++--------------------------- 1 file changed, 94 insertions(+), 224 deletions(-) diff --git a/src/routes/staff/time.ts b/src/routes/staff/time.ts index 33f57c1..d1c3a58 100644 --- a/src/routes/staff/time.ts +++ b/src/routes/staff/time.ts @@ -40,7 +40,8 @@ export default async function staffTimeRoutes(server: FastifyInstance) { actoruser_id: userId, eventtime: normalizeDate(body.eventtime), eventtype: body.eventtype, - source: "WEB" + source: "WEB", + payload: body.payload // Payload (z.B. Description) mit speichern } console.log(dataToInsert) @@ -58,6 +59,95 @@ export default async function staffTimeRoutes(server: FastifyInstance) { } }) + // 🆕 POST /staff/time/edit (Bearbeiten durch Invalidieren + Neu erstellen) + server.post("/staff/time/edit", async (req, reply) => { + try { + const userId = req.user.user_id; + const tenantId = req.user.tenant_id; + + // Wir erwarten das komplette Paket für die Änderung + const { + originalEventIds, // Array der IDs, die "gelöscht" werden sollen (Start ID, End ID) + newStart, // ISO String + newEnd, // ISO String + newType, // z.B. 'work', 'vacation' + description, + reason // Warum wurde geändert? (Audit) + } = req.body as { + originalEventIds: string[], + newStart: string, + newEnd: string | null, + newType: string, + description?: string, + reason?: string + }; + + if (!originalEventIds || originalEventIds.length === 0) { + return reply.code(400).send({ error: "Keine Events zum Bearbeiten angegeben." }); + } + + // 1. Transaction starten (damit alles oder nichts passiert) + await server.db.transaction(async (tx) => { + + // A. INVALIDIEREN (Die alten Events "löschen") + // Wir erstellen für jedes alte Event ein 'invalidated' Event + const invalidations = originalEventIds.map(id => ({ + tenant_id: tenantId, + user_id: userId, // Gehört dem Mitarbeiter + actortype: "user", + actoruser_id: userId, // Wer hat geändert? + eventtime: new Date(), + eventtype: "invalidated", // <--- NEUER TYP: Muss in loadValidEvents gefiltert werden! + source: "WEB", + related_event_id: id, // Zeigt auf das alte Event + metadata: { + reason: reason || "Bearbeitung", + replaced_by_edit: true + } + })); + + // Batch Insert + // @ts-ignore + await tx.insert(stafftimeevents).values(invalidations); + + // B. NEU ERSTELLEN (Die korrigierten Events anlegen) + + // Start Event + // @ts-ignore + await tx.insert(stafftimeevents).values({ + tenant_id: tenantId, + user_id: userId, + actortype: "user", + actoruser_id: userId, + eventtime: new Date(newStart), + eventtype: `${newType}_start`, // z.B. work_start + source: "WEB", + payload: { description: description || "" } + }); + + // End Event (nur wenn vorhanden) + if (newEnd) { + // @ts-ignore + await tx.insert(stafftimeevents).values({ + tenant_id: tenantId, + user_id: userId, + actortype: "user", + actoruser_id: userId, + eventtime: new Date(newEnd), + eventtype: `${newType}_end`, // z.B. work_end + source: "WEB" + }); + } + }); + + return { success: true }; + + } catch (err: any) { + console.error("Fehler beim Bearbeiten:", err); + return reply.code(500).send({ error: err.message }); + } + }); + // POST /staff/time/submit server.post("/staff/time/submit", async (req, reply) => { try { @@ -209,6 +299,7 @@ export default async function staffTimeRoutes(server: FastifyInstance) { const endDate = new Date("2100-12-31"); // SCHRITT 1: Lade ALLE Events für den ZIEL-USER (evaluatedUserId) + // WICHTIG: loadValidEvents muss "invalidated" Events und deren related_ids ausfiltern! const allEventsInTimeFrame = await loadValidEvents( server, tenantId, @@ -283,6 +374,7 @@ export default async function staffTimeRoutes(server: FastifyInstance) { // --- 3. Ausführung der Logik für den ermittelten Benutzer --- // SCHRITT 1: Lade ALLE gültigen Events im Zeitraum + // WICHTIG: loadValidEvents muss "invalidated" Events und deren related_ids ausfiltern! const allEventsInTimeFrame = await loadValidEvents( server, tenantId, evaluatedUserId, startDate, endDate // Verwendung der evaluatedUserId ); @@ -335,226 +427,4 @@ export default async function staffTimeRoutes(server: FastifyInstance) { } }); - - - /*// ------------------------------------------------------------- - // ▶ Neue Zeit starten - // ------------------------------------------------------------- - server.post("/staff/time", async (req, reply) => { - try { - const userId = req.user.user_id - const tenantId = req.user.tenant_id - - const body = req.body as any - - const normalizeDate = (val: any) => { - if (!val) return null - const d = new Date(val) - return isNaN(d.getTime()) ? null : d - } - - const dataToInsert = { - tenant_id: tenantId, - user_id: body.user_id || userId, - type: body.type || "work", - description: body.description || null, - started_at: normalizeDate(body.started_at), - stopped_at: normalizeDate(body.stopped_at), - } - - const [created] = await server.db - .insert(stafftimeentries) - .values(dataToInsert) - .returning() - - return created - } catch (err: any) { - console.error(err) - return reply.code(400).send({ error: err.message }) - } - }) - - // ------------------------------------------------------------- - // ▶ Zeit stoppen - // ------------------------------------------------------------- - server.put<{ - Params: { id: string }, - Body: { stopped_at: string } - }>("/staff/time/:id/stop", async (req, reply) => { - try { - const { id } = req.params - const { stopped_at } = req.body - - // Normalize timestamp - const normalizeDate = (val: any) => { - const d = new Date(val) - return isNaN(d.getTime()) ? null : d - } - - const stopTime = normalizeDate(stopped_at) - if (!stopTime) { - return reply.code(400).send({ error: "Invalid stopped_at timestamp" }) - } - - const [updated] = await server.db - .update(stafftimeentries) - .set({ - stopped_at: stopTime, - updated_at: new Date(), - }) - .where(eq(stafftimeentries.id, id)) - .returning() - - if (!updated) { - return reply.code(404).send({ error: "Time entry not found" }) - } - - return reply.send(updated) - } catch (err: any) { - console.error("STOP ERROR:", err) - return reply.code(500).send({ error: err.message || "Internal server error" }) - } - }) - - // ------------------------------------------------------------- - // ▶ Liste aller Zeiten - // ------------------------------------------------------------- - server.get("/staff/time", async (req, reply) => { - try { - const { from, to, type, user_id } = req.query as any - const { tenant_id, user_id: currentUserId } = req.user - - let where = and(eq(stafftimeentries.tenant_id, tenant_id)) - - // Zugriffsbeschränkung - if (!req.hasPermission("staff.time.read_all")) { - where = and(where, eq(stafftimeentries.user_id, currentUserId)) - } else if (user_id) { - where = and(where, eq(stafftimeentries.user_id, user_id)) - } - - if (from) where = and(where, gte(stafftimeentries.started_at, from)) - if (to) where = and(where, lte(stafftimeentries.started_at, to)) - if (type) where = and(where, eq(stafftimeentries.type, type)) - - const rows = await server.db - .select() - .from(stafftimeentries) - .where(where) - .orderBy(desc(stafftimeentries.started_at)) - - return rows - } catch (err) { - console.error(err) - return reply.code(400).send({ error: (err as Error).message }) - } - }) - - // ------------------------------------------------------------- - // ▶ Einzelne Zeit (inkl. Connects) - // ------------------------------------------------------------- - server.get("/staff/time/:id", async (req, reply) => { - try { - const { id } = req.params as any - - const rows = await server.db - .select() - .from(stafftimeentries) - .where(eq(stafftimeentries.id, id)) - .limit(1) - - if (!rows.length) return reply.code(404).send({ error: "Not found" }) - - const entry = rows[0] - - const connects = await server.db - .select() - .from(stafftimenetryconnects) - .where(eq(stafftimenetryconnects.stafftimeentry, id)) - - return { - ...entry, - staff_time_entry_connects: connects - } - } catch (err) { - return reply.code(400).send({ error: (err as Error).message }) - } - }) - - // ------------------------------------------------------------- - // ▶ Zeit bearbeiten - // ------------------------------------------------------------- - // ▶ Zeit bearbeiten - server.put<{ - Params: { id: string }, - }>("/staff/time/:id", async (req, reply) => { - try { - const { id } = req.params - const body = req.body - - // Normalize all timestamp fields - const normalizeDate = (val: any) => { - if (!val) return null - const d = new Date(val) - return isNaN(d.getTime()) ? null : d - } - - - const updateData: any = { - // @ts-ignore - ...body, - updated_at: new Date(), - } - - // Only convert if present — avoid overriding with null unless sent - // @ts-ignore - if (body.started_at !== undefined) { - // @ts-ignore - updateData.started_at = normalizeDate(body.started_at) - } - // @ts-ignore - if (body.stopped_at !== undefined) { - // @ts-ignore - updateData.stopped_at = normalizeDate(body.stopped_at) - } - // @ts-ignore - if (body.approved_at !== undefined) { - // @ts-ignore - updateData.approved_at = normalizeDate(body.approved_at) - } - - const [updated] = await server.db - .update(stafftimeentries) - .set(updateData) - .where(eq(stafftimeentries.id, id)) - .returning() - - if (!updated) { - return reply.code(404).send({ error: "Time entry not found" }) - } - - return reply.send(updated) - } catch (err: any) { - console.error("UPDATE ERROR:", err) - return reply.code(500).send({ error: err.message || "Internal server error" }) - } - }) - - - // ------------------------------------------------------------- - // ▶ Zeit löschen - // ------------------------------------------------------------- - server.delete("/staff/time/:id", async (req, reply) => { - try { - const { id } = req.params as any - - await server.db - .delete(stafftimeentries) - .where(eq(stafftimeentries.id, id)) - - return { success: true } - } catch (err) { - return reply.code(400).send({ error: (err as Error).message }) - } - })*/ -} +} \ No newline at end of file From 9d6f2de4ab659e3690879b59b8bb8ddc5a67ea7e Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Wed, 17 Dec 2025 09:23:25 +0100 Subject: [PATCH 130/149] Fix E-Mail --- src/routes/emailAsUser.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/routes/emailAsUser.ts b/src/routes/emailAsUser.ts index 0017688..c2d147a 100644 --- a/src/routes/emailAsUser.ts +++ b/src/routes/emailAsUser.ts @@ -140,10 +140,12 @@ export default async function emailAsUserRoutes(server: FastifyInstance) { const accounts = rows.map(row => { const temp: any = {} + console.log(row) Object.entries(row).forEach(([key, val]) => { - if (key.endsWith("Encrypted")) { + console.log(key,val) + if (key.endsWith("Encrypted") && val) { // @ts-ignore - temp[key.replace("Encrypted", "")] = decrypt(val as string) + temp[key.replace("Encrypted", "")] = decrypt(val) } else { temp[key] = val } @@ -189,7 +191,7 @@ export default async function emailAsUserRoutes(server: FastifyInstance) { const accountData: any = {} Object.entries(row).forEach(([key, val]) => { - if (key.endsWith("Encrypted")) { + if (key.endsWith("Encrypted") && val) { // @ts-ignore accountData[key.replace("Encrypted", "")] = decrypt(val as string) } else { From 8e9b9dfc98a9f1716f3c6785d6aeba113eb20df5 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Thu, 18 Dec 2025 12:19:09 +0100 Subject: [PATCH 131/149] Changed Resource Config --- src/utils/resource.config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils/resource.config.ts b/src/utils/resource.config.ts index a0f26a3..68c4649 100644 --- a/src/utils/resource.config.ts +++ b/src/utils/resource.config.ts @@ -125,6 +125,7 @@ export const resourceConfig = { table: incominginvoices, mtmLoad: ["statementallocations"], mtmListLoad: ["statementallocations"], + mtoLoad: ["vendor"], }, statementallocations: { table: statementallocations, From 97f66f808a9cf8aa7f3e0f2c3099bbd4f128da18 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sat, 27 Dec 2025 13:05:01 +0100 Subject: [PATCH 132/149] Change in Resource --- src/utils/resource.config.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/resource.config.ts b/src/utils/resource.config.ts index 68c4649..aacdfa2 100644 --- a/src/utils/resource.config.ts +++ b/src/utils/resource.config.ts @@ -21,7 +21,7 @@ export const resourceConfig = { }, customers: { searchColumns: ["name", "customerNumber", "firstname", "lastname", "notes"], - mtmLoad: ["contacts","projects","plants"], + mtmLoad: ["contacts","projects","plants","createddocuments","contracts"], table: customers, numberRangeHolder: "customerNumber", }, @@ -123,7 +123,7 @@ export const resourceConfig = { }, incominginvoices: { table: incominginvoices, - mtmLoad: ["statementallocations"], + mtmLoad: ["statementallocations","files"], mtmListLoad: ["statementallocations"], mtoLoad: ["vendor"], }, From a3df87caee41f6e4c04b58ca03f6a6e9659ac91a Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Thu, 1 Jan 2026 15:33:48 +0100 Subject: [PATCH 133/149] Serial and Other --- db/schema/createddocuments.ts | 3 + db/schema/index.ts | 4 +- db/schema/serialexecutions.ts | 21 + db/schema/serialtypes.ts | 40 ++ package.json | 1 + src/modules/serialexecution.service.ts | 702 +++++++++++++++++++++++++ src/routes/functions.ts | 13 + src/utils/files.ts | 95 ++++ src/utils/resource.config.ts | 38 +- 9 files changed, 910 insertions(+), 7 deletions(-) create mode 100644 db/schema/serialexecutions.ts create mode 100644 db/schema/serialtypes.ts create mode 100644 src/modules/serialexecution.service.ts create mode 100644 src/utils/files.ts diff --git a/db/schema/createddocuments.ts b/db/schema/createddocuments.ts index 9f16acf..9fd881a 100644 --- a/db/schema/createddocuments.ts +++ b/db/schema/createddocuments.ts @@ -17,6 +17,7 @@ import { letterheads } from "./letterheads" import { projects } from "./projects" import { plants } from "./plants" import { authUsers } from "./auth_users" +import {serialExecutions} from "./serialexecutions"; export const createddocuments = pgTable("createddocuments", { id: bigint("id", { mode: "number" }) @@ -115,6 +116,8 @@ export const createddocuments = pgTable("createddocuments", { contract: bigint("contract", { mode: "number" }).references( () => contracts.id ), + + serialexecution: uuid("serialexecution").references(() => serialExecutions.id) }) export type CreatedDocument = typeof createddocuments.$inferSelect diff --git a/db/schema/index.ts b/db/schema/index.ts index 360363d..195de05 100644 --- a/db/schema/index.ts +++ b/db/schema/index.ts @@ -68,4 +68,6 @@ export * from "./units" export * from "./user_credentials" export * from "./vehicles" export * from "./vendors" -export * from "./staff_time_events" \ No newline at end of file +export * from "./staff_time_events" +export * from "./serialtypes" +export * from "./serialexecutions" \ No newline at end of file diff --git a/db/schema/serialexecutions.ts b/db/schema/serialexecutions.ts new file mode 100644 index 0000000..c63cfb2 --- /dev/null +++ b/db/schema/serialexecutions.ts @@ -0,0 +1,21 @@ +import { + pgTable, + bigint, + timestamp, + text, + jsonb, + boolean, + uuid, +} from "drizzle-orm/pg-core" +import {tenants} from "./tenants"; + +export const serialExecutions = pgTable("serial_executions", { + id: uuid("id").primaryKey().defaultRandom(), + tenant: bigint("tenant", { mode: "number" }) + .notNull() + .references(() => tenants.id), executionDate: timestamp("execution_date").notNull(), + status: text("status").default("draft"), // 'draft', 'completed' + createdBy: text("created_by"), // oder UUID, je nach Auth-System + createdAt: timestamp("created_at").defaultNow(), + summary: text("summary"), // z.B. "25 Rechnungen erstellt" +}); \ No newline at end of file diff --git a/db/schema/serialtypes.ts b/db/schema/serialtypes.ts new file mode 100644 index 0000000..6a32d3b --- /dev/null +++ b/db/schema/serialtypes.ts @@ -0,0 +1,40 @@ +import { + pgTable, + bigint, + timestamp, + text, + jsonb, + boolean, + uuid, +} from "drizzle-orm/pg-core" + +import { tenants } from "./tenants" +import { authUsers } from "./auth_users" + +export const serialtypes = pgTable("serialtypes", { + id: bigint("id", { mode: "number" }) + .primaryKey() + .generatedByDefaultAsIdentity(), + + createdAt: timestamp("created_at", { withTimezone: true }) + .notNull() + .defaultNow(), + + name: text("name").notNull(), + + intervall: text("intervall"), + + icon: text("icon"), + + tenant: bigint("tenant", { mode: "number" }) + .notNull() + .references(() => tenants.id), + + archived: boolean("archived").notNull().default(false), + + updatedAt: timestamp("updated_at", { withTimezone: true }), + updatedBy: uuid("updated_by").references(() => authUsers.id), +}) + +export type SerialType = typeof serialtypes.$inferSelect +export type NewSerialType = typeof serialtypes.$inferInsert diff --git a/package.json b/package.json index 984f697..f188720 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "drizzle-orm": "^0.45.0", "fastify": "^5.5.0", "fastify-plugin": "^5.0.1", + "handlebars": "^4.7.8", "imapflow": "^1.1.1", "jsonwebtoken": "^9.0.2", "nodemailer": "^7.0.6", diff --git a/src/modules/serialexecution.service.ts b/src/modules/serialexecution.service.ts new file mode 100644 index 0000000..b5b960f --- /dev/null +++ b/src/modules/serialexecution.service.ts @@ -0,0 +1,702 @@ +import dayjs from "dayjs"; +import quarterOfYear from "dayjs/plugin/quarterOfYear"; +import Handlebars from "handlebars"; +import axios from "axios"; +import { eq, inArray, and } from "drizzle-orm"; // Drizzle Operatoren + +// DEINE IMPORTS +import * as schema from "../../db/schema"; // Importiere dein Drizzle Schema +import { saveFile } from "../utils/files"; +import {FastifyInstance} from "fastify"; +import {useNextNumberRangeNumber} from "../utils/functions"; +import {createInvoicePDF} from "../utils/pdf"; // Achtung: Muss Node.js Buffer unterstützen! + +dayjs.extend(quarterOfYear); + + +export const executeManualGeneration = async (server:FastifyInstance,executionDate,templateIds,tenantId,executedBy) => { + try { + console.log(executedBy) + + const executionDayjs = dayjs(executionDate); + + console.log(`Starte manuelle Generierung für Tenant ${tenantId} am ${executionDate}`); + + // 1. Tenant laden (Drizzle) + // Wir nehmen an, dass 'tenants' im Schema definiert ist + const [tenant] = await server.db + .select() + .from(schema.tenants) + .where(eq(schema.tenants.id, tenantId)) + .limit(1); + + if (!tenant) throw new Error(`Tenant mit ID ${tenantId} nicht gefunden.`); + + // 2. Templates laden + const templates = await server.db + .select() + .from(schema.createddocuments) + .where( + and( + eq(schema.createddocuments.tenant, tenantId), + eq(schema.createddocuments.type, "serialInvoices"), + inArray(schema.createddocuments.id, templateIds) + ) + ); + + if (templates.length === 0) { + console.warn("Keine passenden Vorlagen gefunden."); + return []; + } + + // 3. Folder & FileType IDs holen (Hilfsfunktionen unten) + const folderId = await getFolderId(server,tenantId); + const fileTypeId = await getFileTypeId(server,tenantId); + + const results = []; + + const [executionRecord] = await server.db + .insert(schema.serialExecutions) + .values({ + tenant: tenantId, + executionDate: executionDayjs.toDate(), + status: "draft", + createdBy: executedBy, + summary: `${templateIds.length} Vorlagen verarbeitet` + }) + .returning(); + + console.log(executionRecord); + + // 4. Loop durch die Templates + for (const template of templates) { + try { + const resultId = await processSingleTemplate( + server, + template, + tenant, + executionDayjs, + folderId, + fileTypeId, + executedBy, + executionRecord.id + ); + results.push({ id: template.id, status: "success", newDocumentId: resultId }); + } catch (e: any) { + console.error(`Fehler bei Template ${template.id}:`, e); + results.push({ id: template.id, status: "error", error: e.message }); + } + } + + return results; + } catch (error) { + console.log(error); + } +} + +export const finishManualGeneration = async (server: FastifyInstance, executionId: number) => { + try { + console.log(`Beende Ausführung ${executionId}...`); + + // 1. Execution und Tenant laden + + const [executionRecord] = await server.db + .select() + .from(schema.serialExecutions)// @ts-ignore + .where(eq(schema.serialExecutions.id, executionId)) + .limit(1); + + if (!executionRecord) throw new Error("Execution nicht gefunden"); + + console.log(executionRecord); + + const tenantId = executionRecord.tenant; + + console.log(tenantId) + + // Tenant laden (für Settings etc.) + const [tenant] = await server.db + .select() + .from(schema.tenants) + .where(eq(schema.tenants.id, tenantId)) + .limit(1); + + // 2. Status auf "processing" setzen (optional, damit UI feedback hat) + + /*await server.db + .update(schema.serialExecutions) + .set({ status: "processing" })// @ts-ignore + .where(eq(schema.serialExecutions.id, executionId));*/ + + // 3. Alle erstellten Dokumente dieser Execution laden + const documents = await server.db + .select() + .from(schema.createddocuments) + .where(eq(schema.createddocuments.serialexecution, executionId)); + + console.log(`${documents.length} Dokumente werden finalisiert...`); + + // 4. IDs für File-System laden (nur einmalig nötig) + const folderId = await getFolderId(server, tenantId); + const fileTypeId = await getFileTypeId(server, tenantId); + + // Globale Daten laden, die für alle gleich sind (Optimierung) + const [units, products, services] = await Promise.all([ + server.db.select().from(schema.units), + server.db.select().from(schema.products).where(eq(schema.products.tenant, tenantId)), + server.db.select().from(schema.services).where(eq(schema.services.tenant, tenantId)), + ]); + + let successCount = 0; + let errorCount = 0; + + // 5. Loop durch Dokumente + for (const doc of documents) { + try { + + const [letterhead] = await Promise.all([ + /*fetchById(server, schema.contacts, doc.contact), + fetchById(server, schema.customers, doc.customer), + fetchById(server, schema.authProfiles, doc.contactPerson), // oder createdBy, je nach Logik + fetchById(server, schema.projects, doc.project), + fetchById(server, schema.contracts, doc.contract),*/ + doc.letterhead ? fetchById(server, schema.letterheads, doc.letterhead) : null + ]); + + const pdfData = await getCloseData( + server, + doc, + tenant, + units, + products, + services, + ); + + console.log(pdfData); + + // D. PDF Generieren + const pdfBase64 = await createInvoicePDF(server,"base64",pdfData, letterhead?.path); + + console.log(pdfBase64); + + // E. Datei speichern + // @ts-ignore + const fileBuffer = Buffer.from(pdfBase64.base64, "base64"); + const filename = `${pdfData.documentNumber}.pdf`; + + await saveFile( + server, + tenantId, + null, // User ID (hier ggf. executionRecord.createdBy nutzen wenn verfügbar) + fileBuffer, + folderId, + fileTypeId, + { + createddocument: doc.id, + filename: filename, + filesize: fileBuffer.length // Falls saveFile das braucht + } + ); + + // F. Dokument in DB final updaten + await server.db + .update(schema.createddocuments) + .set({ + state: "Gebucht", + documentNumber: pdfData.documentNumber, + title: pdfData.title, + pdf_path: filename // Optional, falls du den Pfad direkt am Doc speicherst + }) + .where(eq(schema.createddocuments.id, doc.id)); + + successCount++; + + } catch (innerErr) { + console.error(`Fehler beim Finalisieren von Doc ID ${doc.id}:`, innerErr); + errorCount++; + // Optional: Status des einzelnen Dokuments auf Error setzen + } + } + + // 6. Execution abschließen + const finalStatus = errorCount > 0 ? "error" : "completed"; // Oder 'completed' auch wenn Teilerfolge + + + await server.db + .update(schema.serialExecutions) + .set({ + status: finalStatus, + summary: `Abgeschlossen: ${successCount} erfolgreich, ${errorCount} Fehler.` + })// @ts-ignore + .where(eq(schema.serialExecutions.id, executionId)); + + return { success: true, processed: successCount, errors: errorCount }; + + } catch (error) { + console.error("Critical Error in finishManualGeneration:", error); + // Execution auf Error setzen + // @ts-ignore + await server.db + .update(schema.serialExecutions) + .set({ status: "error", summary: "Kritischer Fehler beim Finalisieren." }) + .where(eq(schema.serialExecutions.id, executionId)); + throw error; + } +} + + +/** + * Verarbeitet eine einzelne Vorlage + */ +async function processSingleTemplate(server: FastifyInstance, template: any, tenant: any,executionDate: dayjs.Dayjs,folderId: string,fileTypeId: string,executedBy: string,executionId: string) { + // A. Zugehörige Daten parallel laden + const [contact, customer, profile, project, contract, units, products, services, letterhead] = await Promise.all([ + fetchById(server, schema.contacts, template.contact), + fetchById(server, schema.customers, template.customer), + fetchById(server, schema.authProfiles, template.contactPerson), + fetchById(server, schema.projects, template.project), + fetchById(server, schema.contracts, template.contract), + server.db.select().from(schema.units), + server.db.select().from(schema.products).where(eq(schema.products.tenant, tenant.id)), + server.db.select().from(schema.services).where(eq(schema.services.tenant, tenant.id)), + template.letterhead ? fetchById(server, schema.letterheads, template.letterhead) : null + ]); + + // B. Datumsberechnung (Logik aus dem Original) + const { firstDate, lastDate } = calculateDateRange(template.serialConfig, executionDate); + + // C. Rechnungsnummer & Save Data + const savePayload = await getSaveData( + template, + tenant, + firstDate, + lastDate, + executionDate.toISOString(), + executedBy + ); + + const payloadWithRelation = { + ...savePayload, + serialexecution: executionId + }; + + // D. Dokument in DB anlegen (Drizzle Insert) + const [createdDoc] = await server.db + .insert(schema.createddocuments) + .values(payloadWithRelation) + .returning(); // Wichtig für Postgres: returning() gibt das erstellte Objekt zurück + + return createdDoc.id; +} + +// --- Drizzle Helper --- + +async function fetchById(server: FastifyInstance, table: any, id: number | null) { + if (!id) return null; + const [result] = await server.db.select().from(table).where(eq(table.id, id)).limit(1); + return result || null; +} + +async function getFolderId(server:FastifyInstance, tenantId: number) { + const [folder] = await server.db + .select({ id: schema.folders.id }) + .from(schema.folders) + .where( + and( + eq(schema.folders.tenant, tenantId), + eq(schema.folders.function, "invoices"), // oder 'invoices', check deine DB + eq(schema.folders.year, dayjs().format("YYYY")) + ) + ) + .limit(1); + return folder?.id; +} + +async function getFileTypeId(server: FastifyInstance,tenantId: number) { + const [tag] = await server.db + .select({ id: schema.filetags.id }) + .from(schema.filetags) + .where( + and( + eq(schema.filetags.tenant, tenantId), + eq(schema.filetags.createdDocumentType, "invoices") + ) + ) + .limit(1); + return tag?.id; +} + + + +// --- Logik Helper (Unverändert zur Business Logik) --- + +function calculateDateRange(config: any, executionDate: dayjs.Dayjs) { + let firstDate = executionDate; + let lastDate = executionDate; + // Logik 1:1 übernommen + if (config.intervall === "monatlich" && config.dateDirection === "Rückwirkend") { + firstDate = executionDate.subtract(1, "month").date(1); + lastDate = executionDate.subtract(1, "month").endOf("month"); + } else if (config.intervall === "vierteljährlich" && config.dateDirection === "Rückwirkend") { + firstDate = executionDate.subtract(1, "quarter").startOf("quarter"); + lastDate = executionDate.subtract(1, "quarter").endOf("quarter"); + } + return { firstDate: firstDate.toISOString(), lastDate: lastDate.toISOString() }; +} + +async function getSaveData(item: any, tenant: any, firstDate: string, lastDate: string, executionDate: string, executedBy: string) { + const cleanRows = item.rows.map((row: any) => ({ + ...row, + descriptionText: row.description || null, + })); + + //const documentNumber = await this.useNextInvoicesNumber(item.tenant); + + return { + tenant: item.tenant, + type: "invoices", + state: "Entwurf", + customer: item.customer, + contact: item.contact, + contract: item.contract, + address: item.address, + project: item.project, + documentDate: executionDate, + deliveryDate: firstDate, + deliveryDateEnd: lastDate, + paymentDays: item.paymentDays, + payment_type: item.payment_type, + deliveryDateType: item.deliveryDateType, + info: {}, // Achtung: Postgres erwartet hier valides JSON Objekt + createdBy: item.createdBy, + created_by: executedBy, + title: `Rechnung-Nr. XXX`, + description: item.description, + startText: item.startText, + endText: item.endText, + rows: cleanRows, // JSON Array + contactPerson: item.contactPerson, + linkedDocument: item.linkedDocument, + letterhead: item.letterhead, + taxType: item.taxType, + }; +} + +async function getCloseData(server:FastifyInstance,item: any, tenant: any, units, products,services) { + const documentNumber = await useNextNumberRangeNumber(server,tenant.id,"invoices"); + + console.log(item); + + const [contact, customer, profile, project, contract] = await Promise.all([ + fetchById(server, schema.contacts, item.contact), + fetchById(server, schema.customers, item.customer), + fetchById(server, schema.authProfiles, item.contactPerson), // oder createdBy, je nach Logik + fetchById(server, schema.projects, item.project), + fetchById(server, schema.contracts, item.contract), + item.letterhead ? fetchById(server, schema.letterheads, item.letterhead) : null + ]); + + const pdfData = getDocumentDataBackend( + { + ...item, + state: "Gebucht", + documentNumber: documentNumber.usedNumber, + title: `Rechnung-Nr. ${documentNumber.usedNumber}`, + }, // Das Dokument (mit neuer Nummer) + tenant, // Tenant Object + customer, // Customer Object + contact, // Contact Object (kann null sein) + profile, // User Profile (Contact Person) + project, // Project Object + contract, // Contract Object + units, // Units Array + products, // Products Array + services // Services Array + ); + + + return pdfData; +} + + + + + +// Formatiert Zahlen zu deutscher Währung +function renderCurrency(value: any, currency = "€") { + if (value === undefined || value === null) return "0,00 " + currency; + return Number(value).toFixed(2).replace(".", ",") + " " + currency; +} + +// Berechnet den Zeilenpreis (Menge * Preis * Rabatt) +function getRowAmount(row: any) { + const price = Number(row.price || 0); + const quantity = Number(row.quantity || 0); + const discount = Number(row.discountPercent || 0); + return quantity * price * (1 - discount / 100); +} + +// Berechnet alle Summen (Netto, Brutto, Steuern, Titel-Summen) +// Dies ersetzt 'documentTotal.value' aus dem Frontend +function calculateDocumentTotals(rows: any[], taxType: string) { + console.log(rows); + + let totalNet = 0; + let totalNet19 = 0; + let totalNet7 = 0; + let totalNet0 = 0; + let titleSums: Record = {}; + + // Aktueller Titel für Gruppierung + let currentTitle = "Ohne Titel"; + + rows.forEach(row => { + if (row.mode === 'title') { + currentTitle = row.text || row.description || "Titel"; + if (!titleSums[currentTitle]) titleSums[currentTitle] = 0; + return; + } + + if (['normal', 'service', 'free'].includes(row.mode)) { + const amount = getRowAmount(row); + totalNet += amount; + + // Summen pro Titel addieren + if (!titleSums[currentTitle]) titleSums[currentTitle] = 0; + titleSums[currentTitle] += amount; + + // Steuer-Logik + const tax = taxType === "19 UStG" || taxType === "13b UStG" ? 0 : Number(row.taxPercent); + + if (tax === 19) totalNet19 += amount; + else if (tax === 7) totalNet7 += amount; + else totalNet0 += amount; + } + }); + + const isTaxFree = ["13b UStG", "19 UStG"].includes(taxType); + + const tax19 = isTaxFree ? 0 : totalNet19 * 0.19; + const tax7 = isTaxFree ? 0 : totalNet7 * 0.07; + const totalGross = totalNet + tax19 + tax7; + + return { + totalNet, + totalNet19, + totalNet7, + totalNet0, + total19: tax19, + total7: tax7, + total0: 0, + totalGross, + titleSums // Gibt ein Objekt zurück: { "Titel A": 150.00, "Titel B": 200.00 } + }; +} + +export function getDocumentDataBackend( + itemInfo: any, // Das Dokument objekt (createddocument) + tenant: any, // Tenant Infos (auth.activeTenantData) + customerData: any, // Geladener Kunde + contactData: any, // Geladener Kontakt (optional) + contactPerson: any, // Geladenes User-Profil (ersetzt den API Call) + projectData: any, // Projekt + contractData: any, // Vertrag + units: any[], // Array aller Einheiten + products: any[], // Array aller Produkte + services: any[] // Array aller Services +) { + const businessInfo = tenant.businessInfo || {}; // Fallback falls leer + + // --- 1. Agriculture Logic --- + // Prüfen ob 'extraModules' existiert, sonst leeres Array annehmen + const modules = tenant.extraModules || []; + if (modules.includes("agriculture")) { + itemInfo.rows.forEach((row: any) => { + if (row.agriculture && row.agriculture.dieselUsage) { + row.agriculture.description = `${row.agriculture.dieselUsage} L Diesel zu ${renderCurrency(row.agriculture.dieselPrice)}/L verbraucht ${row.description ? "\n" + row.description : ""}`; + } + }); + } + + // --- 2. Tax Override Logic --- + let rows = JSON.parse(JSON.stringify(itemInfo.rows)); // Deep Clone um Original nicht zu mutieren + if (itemInfo.taxType === "13b UStG" || itemInfo.taxType === "19 UStG") { + rows = rows.map((row: any) => ({ ...row, taxPercent: 0 })); + } + + // --- 4. Berechnungen (Ersetzt Vue computed props) --- + const totals = calculateDocumentTotals(rows, itemInfo.taxType); + + console.log(totals); + + // --- 3. Rows Mapping & Processing --- + rows = rows.map((row: any) => { + const unit = units.find(i => i.id === row.unit) || { short: "" }; + + // Description Text Logic + if (!['pagebreak', 'title'].includes(row.mode)) { + if (row.agriculture && row.agriculture.description) { + row.descriptionText = row.agriculture.description; + } else if (row.description) { + row.descriptionText = row.description; + } else { + delete row.descriptionText; + } + } + + // Product/Service Name Resolution + if (!['pagebreak', 'title', 'text'].includes(row.mode)) { + if (row.mode === 'normal') { + const prod = products.find(i => i.id === row.product); + if (prod) row.text = prod.name; + } + if (row.mode === 'service') { + const serv = services.find(i => i.id === row.service); + if (serv) row.text = serv.name; + } + + const rowAmount = getRowAmount(row); + + return { + ...row, + rowAmount: renderCurrency(rowAmount), + quantity: String(row.quantity).replace(".", ","), + unit: unit.short, + pos: String(row.pos), + price: renderCurrency(row.price), + discountText: row.discountPercent > 0 ? `(Rabatt: ${row.discountPercent} %)` : "" + }; + } else { + return row; + } + }); + + + + // --- 5. Handlebars Context --- + const generateContext = () => { + return { + // lohnkosten: null, // Backend hat diesen Wert oft nicht direkt, ggf. aus itemInfo holen + anrede: (contactData && contactData.salutation) || (customerData && customerData.salutation), + titel: (contactData && contactData.title) || (customerData && customerData.title), + vorname: (contactData && contactData.firstName) || (customerData && customerData.firstname), // Achte auf casing (firstName vs firstname) in deiner DB + nachname: (contactData && contactData.lastName) || (customerData && customerData.lastname), + kundenname: customerData && customerData.name, + zahlungsziel_in_tagen: itemInfo.paymentDays, + zahlungsart: itemInfo.payment_type === "transfer" ? "Überweisung" : "Lastschrift", + diesel_gesamtverbrauch: (itemInfo.agriculture && itemInfo.agriculture.dieselUsageTotal) || null + }; + }; + + const templateStartText = Handlebars.compile(itemInfo.startText || ""); + const templateEndText = Handlebars.compile(itemInfo.endText || ""); + + // --- 6. Title Sums Formatting --- + let returnTitleSums: Record = {}; + Object.keys(totals.titleSums).forEach(key => { + returnTitleSums[key] = renderCurrency(totals.titleSums[key]); + }); + + // Transfer logic (Falls nötig, hier vereinfacht) + let returnTitleSumsTransfer = { ...returnTitleSums }; + + // --- 7. Construct Final Object --- + + // Adresse aufbereiten + const recipientArray = [ + customerData.name, + ...(customerData.nameAddition ? [customerData.nameAddition] : []), + ...(contactData ? [`${contactData.firstName} ${contactData.lastName}`] : []), + itemInfo.address?.street || customerData.street || "", + ...(itemInfo.address?.special ? [itemInfo.address.special] : []), + `${itemInfo.address?.zip || customerData.zip} ${itemInfo.address?.city || customerData.city}`, + ].filter(Boolean); // Leere Einträge entfernen + + // Info Block aufbereiten + const infoBlock = [ + { + label: itemInfo.documentNumberTitle || "Rechnungsnummer", + content: itemInfo.documentNumber || "ENTWURF", + }, { + label: "Kundennummer", + content: customerData.customerNumber, + }, { + label: "Belegdatum", + content: itemInfo.documentDate ? dayjs(itemInfo.documentDate).format("DD.MM.YYYY") : dayjs().format("DD.MM.YYYY"), + }, + // Lieferdatum Logik + ...(itemInfo.deliveryDateType !== "Kein Lieferdatum anzeigen" ? [{ + label: itemInfo.deliveryDateType || "Lieferdatum", + content: !['Lieferzeitraum', 'Leistungszeitraum'].includes(itemInfo.deliveryDateType) + ? (itemInfo.deliveryDate ? dayjs(itemInfo.deliveryDate).format("DD.MM.YYYY") : "") + : `${itemInfo.deliveryDate ? dayjs(itemInfo.deliveryDate).format("DD.MM.YYYY") : ""} - ${itemInfo.deliveryDateEnd ? dayjs(itemInfo.deliveryDateEnd).format("DD.MM.YYYY") : ""}`, + }] : []), + { + label: "Ansprechpartner", + content: contactPerson ? (contactPerson.name || contactPerson.fullName || contactPerson.email) : "-", + }, + // Kontakt Infos + ...((itemInfo.contactTel || contactPerson?.fixedTel || contactPerson?.mobileTel) ? [{ + label: "Telefon", + content: itemInfo.contactTel || contactPerson?.fixedTel || contactPerson?.mobileTel, + }] : []), + ...(contactPerson?.email ? [{ + label: "E-Mail", + content: contactPerson.email, + }] : []), + // Objekt / Projekt / Vertrag + ...(itemInfo.plant ? [{ label: "Objekt", content: "Objekt Name" }] : []), // Hier müsstest du Plant Data übergeben wenn nötig + ...(projectData ? [{ label: "Projekt", content: projectData.name }] : []), + ...(contractData ? [{ label: "Vertrag", content: contractData.contractNumber }] : []) + ]; + + // Total Array für PDF Footer + const totalArray = [ + { + label: "Nettobetrag", + content: renderCurrency(totals.totalNet), + }, + ...(totals.totalNet19 > 0 && !["13b UStG"].includes(itemInfo.taxType) ? [{ + label: `zzgl. 19% USt auf ${renderCurrency(totals.totalNet19)}`, + content: renderCurrency(totals.total19), + }] : []), + ...(totals.totalNet7 > 0 && !["13b UStG"].includes(itemInfo.taxType) ? [{ + label: `zzgl. 7% USt auf ${renderCurrency(totals.totalNet7)}`, + content: renderCurrency(totals.total7), + }] : []), + { + label: "Gesamtbetrag", + content: renderCurrency(totals.totalGross), + }, + ]; + + return { + ...itemInfo, + type: itemInfo.type, + taxType: itemInfo.taxType, + adressLine: `${businessInfo.name || ''}, ${businessInfo.street || ''}, ${businessInfo.zip || ''} ${businessInfo.city || ''}`, + recipient: recipientArray, + info: infoBlock, + title: itemInfo.title, + description: itemInfo.description, + // Handlebars Compilation ausführen + endText: templateEndText(generateContext()), + startText: templateStartText(generateContext()), + rows: rows, + totalArray: totalArray, + total: { + totalNet: renderCurrency(totals.totalNet), + total19: renderCurrency(totals.total19), + total0: renderCurrency(totals.total0), // 0% USt Zeilen + totalGross: renderCurrency(totals.totalGross), + // Diese Werte existieren im einfachen Backend-Kontext oft nicht (Zahlungen checken), daher 0 oder Logik bauen + totalGrossAlreadyPaid: renderCurrency(0), + totalSumToPay: renderCurrency(totals.totalGross), + titleSums: returnTitleSums, + titleSumsTransfer: returnTitleSumsTransfer + }, + agriculture: itemInfo.agriculture, + // Falls du AdvanceInvoices brauchst, musst du die Objekte hier übergeben oder leer lassen + usedAdvanceInvoices: [] + }; +} diff --git a/src/routes/functions.ts b/src/routes/functions.ts index 471ea1d..c372d32 100644 --- a/src/routes/functions.ts +++ b/src/routes/functions.ts @@ -16,6 +16,7 @@ import {generateTimesEvaluation} from "../modules/time/evaluation.service"; import {citys} from "../../db/schema"; import {eq} from "drizzle-orm"; import {useNextNumberRangeNumber} from "../utils/functions"; +import {executeManualGeneration, finishManualGeneration} from "../modules/serialexecution.service"; dayjs.extend(customParseFormat) dayjs.extend(isoWeek) dayjs.extend(isBetween) @@ -157,6 +158,18 @@ export default async function functionRoutes(server: FastifyInstance) { } }) + server.post('/functions/serial/start', async (req, reply) => { + console.log(req.body) + const {executionDate,templateIds,tenantId} = req.body as {executionDate:string,templateIds:Number[],tenantId:Number} + await executeManualGeneration(server,executionDate,templateIds,tenantId,req.user.user_id) + }) + + server.post('/functions/serial/finish/:execution_id', async (req, reply) => { + const {execution_id} = req.params as { execution_id: string } + await finishManualGeneration(server,execution_id) + }) + + /*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} diff --git a/src/utils/files.ts b/src/utils/files.ts new file mode 100644 index 0000000..0abe4cd --- /dev/null +++ b/src/utils/files.ts @@ -0,0 +1,95 @@ +import { PutObjectCommand } from "@aws-sdk/client-s3" +import { s3 } from "./s3" +import { secrets } from "./secrets" + +// Drizzle schema +import { files } from "../../db/schema" +import { eq } from "drizzle-orm" +import { FastifyInstance } from "fastify" + +export const saveFile = async ( + server: FastifyInstance, + tenant: number, + messageId: string | number | null, // Typ angepasst (oft null bei manueller Gen) + attachment: any, // Kann File, Buffer oder Mailparser-Objekt sein + folder: string | null, + type: string | null, + other: Record = {} +) => { + try { + // --------------------------------------------------- + // 1️⃣ FILE ENTRY ANLEGEN + // --------------------------------------------------- + const insertRes = await server.db + .insert(files) + .values({ + tenant, + folder, + type, + ...other + }) + .returning() + + const created = insertRes?.[0] + if (!created) { + console.error("File creation failed (no row returned)") + return null + } + + // Name ermitteln (Fallback Logik) + // Wenn attachment ein Buffer ist, muss der Name in 'other' stehen oder generiert werden + const filename = attachment.filename || other.filename || `${created.id}.pdf` + + // --------------------------------------------------- + // 2️⃣ BODY & CONTENT TYPE ERMITTELN + // --------------------------------------------------- + let body: Buffer | Uint8Array | string + let contentType = type || "application/octet-stream" + + if (Buffer.isBuffer(attachment)) { + // FALL 1: RAW BUFFER (von finishManualGeneration) + body = attachment + // ContentType wurde oben schon über 'type' Parameter gesetzt (z.B. application/pdf) + } else if (typeof File !== "undefined" && attachment instanceof File) { + // FALL 2: BROWSER FILE + body = Buffer.from(await attachment.arrayBuffer()) + contentType = attachment.type || contentType + } else if (attachment.content) { + // FALL 3: MAILPARSER OBJECT + body = attachment.content + contentType = attachment.contentType || contentType + } else { + console.error("saveFile: Unknown attachment format") + return null + } + + // --------------------------------------------------- + // 3️⃣ S3 UPLOAD + // --------------------------------------------------- + const key = `${tenant}/filesbyid/${created.id}/${filename}` + + await s3.send( + new PutObjectCommand({ + Bucket: secrets.S3_BUCKET, + Key: key, + Body: body, + ContentType: contentType, + ContentLength: body.length // <--- WICHTIG: Behebt den AWS Fehler + }) + ) + + // --------------------------------------------------- + // 4️⃣ PATH IN DB SETZEN + // --------------------------------------------------- + await server.db + .update(files) + .set({ path: key }) + .where(eq(files.id, created.id)) + + console.log(`File saved: ${key}`) + return { id: created.id, key } + } catch (err) { + console.error("saveFile error:", err) + return null + } +} \ No newline at end of file diff --git a/src/utils/resource.config.ts b/src/utils/resource.config.ts index aacdfa2..99c9713 100644 --- a/src/utils/resource.config.ts +++ b/src/utils/resource.config.ts @@ -1,13 +1,36 @@ import { - accounts, bankaccounts, bankrequisitions, bankstatements, + accounts, + bankaccounts, + bankrequisitions, + bankstatements, contacts, - contracts, costcentres, createddocuments, + contracts, + costcentres, + createddocuments, customers, - files, filetags, folders, hourrates, incominginvoices, inventoryitemgroups, - inventoryitems, letterheads, ownaccounts, - plants, productcategories, products, + files, + filetags, + folders, + hourrates, + incominginvoices, + inventoryitemgroups, + inventoryitems, + letterheads, + ownaccounts, + plants, + productcategories, + products, projects, - projecttypes, servicecategories, services, spaces, statementallocations, tasks, texttemplates, units, vehicles, + projecttypes, + serialExecutions, + servicecategories, + services, + spaces, + statementallocations, + tasks, + texttemplates, + units, + vehicles, vendors } from "../../db/schema"; @@ -144,5 +167,8 @@ export const resourceConfig = { }, bankrequisitions: { table: bankrequisitions, + }, + serialexecutions: { + table: serialExecutions } } \ No newline at end of file From 4121666c708ef8a2fbca1606f15058cbdf179cda Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Thu, 1 Jan 2026 15:34:35 +0100 Subject: [PATCH 134/149] Serial and Other --- src/modules/serialexecution.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/serialexecution.service.ts b/src/modules/serialexecution.service.ts index b5b960f..2578093 100644 --- a/src/modules/serialexecution.service.ts +++ b/src/modules/serialexecution.service.ts @@ -369,7 +369,7 @@ async function getSaveData(item: any, tenant: any, firstDate: string, lastDate: deliveryDateType: item.deliveryDateType, info: {}, // Achtung: Postgres erwartet hier valides JSON Objekt createdBy: item.createdBy, - created_by: executedBy, + created_by: item.created_by, title: `Rechnung-Nr. XXX`, description: item.description, startText: item.startText, From b3fd5eafbc3590c2bbec7dfeeb20dc9cec278368 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Thu, 1 Jan 2026 15:51:47 +0100 Subject: [PATCH 135/149] Bankstatementsync --- src/index.ts | 5 + src/modules/cron/bankstatementsync.service.ts | 246 ++++++++++++++++++ src/plugins/services.ts | 24 ++ src/routes/functions.ts | 4 + 4 files changed, 279 insertions(+) create mode 100644 src/modules/cron/bankstatementsync.service.ts create mode 100644 src/plugins/services.ts diff --git a/src/index.ts b/src/index.ts index 61b2ee1..a154b01 100644 --- a/src/index.ts +++ b/src/index.ts @@ -48,6 +48,10 @@ import {loadSecrets, secrets} from "./utils/secrets"; import {initMailer} from "./utils/mailer" import {initS3} from "./utils/s3"; +//Services +import servicesPlugin from "./plugins/services"; +import {prepareIncomingInvoices} from "./modules/cron/prepareIncomingInvoices"; + async function main() { const app = Fastify({ logger: false }); await loadSecrets(); @@ -68,6 +72,7 @@ async function main() { await app.register(tenantPlugin); await app.register(dayjsPlugin); await app.register(dbPlugin); + await app.register(servicesPlugin); app.addHook('preHandler', (req, reply, done) => { console.log(req.method) diff --git a/src/modules/cron/bankstatementsync.service.ts b/src/modules/cron/bankstatementsync.service.ts new file mode 100644 index 0000000..23714fb --- /dev/null +++ b/src/modules/cron/bankstatementsync.service.ts @@ -0,0 +1,246 @@ +// /services/bankStatementService.ts +import axios from "axios" +import dayjs from "dayjs" +import utc from "dayjs/plugin/utc.js" +import {secrets} from "../../utils/secrets" +import {FastifyInstance} from "fastify" + +// Drizzle imports +import { + bankaccounts, + bankstatements, +} from "../../../db/schema" + +import { + eq, + and, + isNull, +} from "drizzle-orm" + +dayjs.extend(utc) + +interface BalanceAmount { + amount: string + currency: string +} + +interface BookedTransaction { + bookingDate: string + valueDate: string + internalTransactionId: string + transactionAmount: { amount: string; currency: string } + + creditorAccount?: { iban?: string } + creditorName?: string + + debtorAccount?: { iban?: string } + debtorName?: string + + remittanceInformationUnstructured?: string + remittanceInformationStructured?: string + remittanceInformationStructuredArray?: string[] + additionalInformation?: string +} + +interface TransactionsResponse { + transactions: { + booked: BookedTransaction[] + } +} + +const normalizeDate = (val: any) => { + if (!val) return null + const d = new Date(val) + return isNaN(d.getTime()) ? null : d +} + +export function bankStatementService(server: FastifyInstance) { + + let accessToken: string | null = null + + // ----------------------------------------------- + // ✔ TOKEN LADEN + // ----------------------------------------------- + const getToken = async () => { + console.log("Fetching GoCardless token…") + + const response = await axios.post( + `${secrets.GOCARDLESS_BASE_URL}/token/new/`, + { + secret_id: secrets.GOCARDLESS_SECRET_ID, + secret_key: secrets.GOCARDLESS_SECRET_KEY, + } + ) + + accessToken = response.data.access + } + + // ----------------------------------------------- + // ✔ Salden laden + // ----------------------------------------------- + const getBalanceData = async (accountId: string): Promise => { + try { + const {data} = await axios.get( + `${secrets.GOCARDLESS_BASE_URL}/accounts/${accountId}/balances`, + { + headers: { + Authorization: `Bearer ${accessToken}`, + Accept: "application/json", + }, + } + ) + + return data + } catch (err: any) { + server.log.error(err.response?.data ?? err) + + const expired = + err.response?.data?.summary?.includes("expired") || + err.response?.data?.detail?.includes("expired") + + if (expired) { + await server.db + .update(bankaccounts) + .set({expired: true}) + .where(eq(bankaccounts.accountId, accountId)) + } + + return false + } + } + + // ----------------------------------------------- + // ✔ Transaktionen laden + // ----------------------------------------------- + const getTransactionData = async (accountId: string) => { + try { + const {data} = await axios.get( + `${secrets.GOCARDLESS_BASE_URL}/accounts/${accountId}/transactions`, + { + headers: { + Authorization: `Bearer ${accessToken}`, + Accept: "application/json", + }, + } + ) + + return data.transactions.booked + } catch (err: any) { + server.log.error(err.response?.data ?? err) + return null + } + } + + // ----------------------------------------------- + // ✔ Haupt-Sync-Prozess + // ----------------------------------------------- + const syncAccounts = async (tenantId:number) => { + try { + console.log("Starting account sync…") + + // 🟦 DB: Aktive Accounts + const accounts = await server.db + .select() + .from(bankaccounts) + .where(and(eq(bankaccounts.expired, false),eq(bankaccounts.tenant, tenantId))) + + if (!accounts.length) return + + const allNewTransactions: any[] = [] + + for (const account of accounts) { + + // --------------------------- + // 1. BALANCE SYNC + // --------------------------- + const balData = await getBalanceData(account.accountId) + + if (balData === false) break + + if (balData) { + const closing = balData.balances.find( + (i: any) => i.balanceType === "closingBooked" + ) + + const bookedBal = Number(closing.balanceAmount.amount) + + await server.db + .update(bankaccounts) + .set({balance: bookedBal}) + .where(eq(bankaccounts.id, account.id)) + } + + // --------------------------- + // 2. TRANSACTIONS + // --------------------------- + let transactions = await getTransactionData(account.accountId) + if (!transactions) continue + + //@ts-ignore + transactions = transactions.map((item) => ({ + account: account.id, + date: normalizeDate(item.bookingDate), + credIban: item.creditorAccount?.iban ?? null, + credName: item.creditorName ?? null, + text: ` + ${item.remittanceInformationUnstructured ?? ""} + ${item.remittanceInformationStructured ?? ""} + ${item.additionalInformation ?? ""} + ${item.remittanceInformationStructuredArray?.join("") ?? ""} + `.trim(), + amount: Number(item.transactionAmount.amount), + tenant: account.tenant, + debIban: item.debtorAccount?.iban ?? null, + debName: item.debtorName ?? null, + gocardlessId: item.internalTransactionId, + currency: item.transactionAmount.currency, + valueDate: normalizeDate(item.valueDate), + })) + + // Existierende Statements laden + const existing = await server.db + .select({gocardlessId: bankstatements.gocardlessId}) + .from(bankstatements) + .where(eq(bankstatements.tenant, account.tenant)) + + const filtered = transactions.filter( + //@ts-ignore + (tx) => !existing.some((x) => x.gocardlessId === tx.gocardlessId) + ) + + allNewTransactions.push(...filtered) + } + + // --------------------------- + // 3. NEW TRANSACTIONS → DB + // --------------------------- + if (allNewTransactions.length > 0) { + await server.db.insert(bankstatements).values(allNewTransactions) + + const affectedAccounts = [ + ...new Set(allNewTransactions.map((t) => t.account)), + ] + + for (const accId of affectedAccounts) { + await server.db + .update(bankaccounts) + .set({syncedAt: dayjs().utc().toISOString()}) + .where(eq(bankaccounts.id, accId)) + } + } + + console.log("Bank statement sync completed.") + } catch (error) { + console.error(error) + } + + } + + return { + run: async (tenant) => { + await getToken() + await syncAccounts(tenant) + console.log("Service: Bankstatement sync finished") + } + } +} diff --git a/src/plugins/services.ts b/src/plugins/services.ts new file mode 100644 index 0000000..d5f32ed --- /dev/null +++ b/src/plugins/services.ts @@ -0,0 +1,24 @@ +// /plugins/services.ts +import fp from "fastify-plugin"; +import { bankStatementService } from "../modules/cron/bankstatementsync.service"; +//import {initDokuboxClient, syncDokubox} from "../modules/cron/dokuboximport.service"; +import { FastifyInstance } from "fastify"; +import {prepareIncomingInvoices} from "../modules/cron/prepareIncomingInvoices"; + +declare module "fastify" { + interface FastifyInstance { + services: { + bankStatements: ReturnType; + //dokuboxSync: ReturnType; + //prepareIncomingInvoices: ReturnType; + }; + } +} + +export default fp(async function servicePlugin(server: FastifyInstance) { + server.decorate("services", { + bankStatements: bankStatementService(server), + //dokuboxSync: syncDokubox(server), + //prepareIncomingInvoices: prepareIncomingInvoices(server), + }); +}); diff --git a/src/routes/functions.ts b/src/routes/functions.ts index c372d32..0258c8c 100644 --- a/src/routes/functions.ts +++ b/src/routes/functions.ts @@ -169,6 +169,10 @@ export default async function functionRoutes(server: FastifyInstance) { await finishManualGeneration(server,execution_id) }) + server.post('/functions/services/bankstatementsync', async (req, reply) => { + await server.services.bankStatements.run(req.user.tenant_id); + }) + /*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} From 3da3aee50d6036a7cfb04d15f995ec1b5061c1bb Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Thu, 1 Jan 2026 16:24:26 +0100 Subject: [PATCH 136/149] Fixed Dates in create resource --- src/routes/resources/main.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/routes/resources/main.ts b/src/routes/resources/main.ts index 9fc41dc..1c65d3d 100644 --- a/src/routes/resources/main.ts +++ b/src/routes/resources/main.ts @@ -455,6 +455,16 @@ export default async function resourceRoutes(server: FastifyInstance) { createData[resourceConfig[resource].numberRangeHolder] = result.usedNumber } + const normalizeDate = (val: any) => { + const d = new Date(val) + return isNaN(d.getTime()) ? null : d + } + + Object.keys(createData).forEach((key) => { + if(key.toLowerCase().includes("date")) createData[key] = normalizeDate(createData[key]) + }) + + const [created] = await server.db .insert(table) .values(createData) From 78ae8989ba40c4c40ea67bdfa5bfa3b2c822e62d Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Thu, 1 Jan 2026 16:35:43 +0100 Subject: [PATCH 137/149] Fix TS --- src/modules/serialexecution.service.ts | 1 + src/plugins/services.ts | 2 +- src/routes/functions.ts | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/modules/serialexecution.service.ts b/src/modules/serialexecution.service.ts index 2578093..8f78b5d 100644 --- a/src/modules/serialexecution.service.ts +++ b/src/modules/serialexecution.service.ts @@ -239,6 +239,7 @@ export const finishManualGeneration = async (server: FastifyInstance, executionI await server.db .update(schema.serialExecutions) .set({ status: "error", summary: "Kritischer Fehler beim Finalisieren." }) + //@ts-ignore .where(eq(schema.serialExecutions.id, executionId)); throw error; } diff --git a/src/plugins/services.ts b/src/plugins/services.ts index d5f32ed..1c9429c 100644 --- a/src/plugins/services.ts +++ b/src/plugins/services.ts @@ -3,7 +3,7 @@ import fp from "fastify-plugin"; import { bankStatementService } from "../modules/cron/bankstatementsync.service"; //import {initDokuboxClient, syncDokubox} from "../modules/cron/dokuboximport.service"; import { FastifyInstance } from "fastify"; -import {prepareIncomingInvoices} from "../modules/cron/prepareIncomingInvoices"; +//import {prepareIncomingInvoices} from "../modules/cron/prepareIncomingInvoices"; declare module "fastify" { interface FastifyInstance { diff --git a/src/routes/functions.ts b/src/routes/functions.ts index 0258c8c..67a6582 100644 --- a/src/routes/functions.ts +++ b/src/routes/functions.ts @@ -166,6 +166,7 @@ export default async function functionRoutes(server: FastifyInstance) { server.post('/functions/serial/finish/:execution_id', async (req, reply) => { const {execution_id} = req.params as { execution_id: string } + //@ts-ignore await finishManualGeneration(server,execution_id) }) From a5f82c3ef332b2b73ab4a97a66e629d2be1cd917 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Thu, 1 Jan 2026 16:37:40 +0100 Subject: [PATCH 138/149] Fix TS --- src/index.ts | 1 - src/modules/cron/bankstatementsync.service.ts | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index a154b01..a5b846f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -50,7 +50,6 @@ import {initS3} from "./utils/s3"; //Services import servicesPlugin from "./plugins/services"; -import {prepareIncomingInvoices} from "./modules/cron/prepareIncomingInvoices"; async function main() { const app = Fastify({ logger: false }); diff --git a/src/modules/cron/bankstatementsync.service.ts b/src/modules/cron/bankstatementsync.service.ts index 23714fb..9773631 100644 --- a/src/modules/cron/bankstatementsync.service.ts +++ b/src/modules/cron/bankstatementsync.service.ts @@ -224,6 +224,7 @@ export function bankStatementService(server: FastifyInstance) { for (const accId of affectedAccounts) { await server.db .update(bankaccounts) + //@ts-ignore .set({syncedAt: dayjs().utc().toISOString()}) .where(eq(bankaccounts.id, accId)) } From 848c9e4b2f8161d5a8d4dfc5f2d9a81a5725914d Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Fri, 2 Jan 2026 12:39:20 +0100 Subject: [PATCH 139/149] Added Prepare Service --- src/modules/serialexecution.service.ts | 16 +++++++++++----- src/plugins/services.ts | 6 +++--- src/routes/functions.ts | 5 +++++ 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/modules/serialexecution.service.ts b/src/modules/serialexecution.service.ts index 8f78b5d..82a98ff 100644 --- a/src/modules/serialexecution.service.ts +++ b/src/modules/serialexecution.service.ts @@ -388,15 +388,19 @@ async function getCloseData(server:FastifyInstance,item: any, tenant: any, units console.log(item); - const [contact, customer, profile, project, contract] = await Promise.all([ + const [contact, customer, project, contract] = await Promise.all([ fetchById(server, schema.contacts, item.contact), fetchById(server, schema.customers, item.customer), - fetchById(server, schema.authProfiles, item.contactPerson), // oder createdBy, je nach Logik fetchById(server, schema.projects, item.project), fetchById(server, schema.contracts, item.contract), item.letterhead ? fetchById(server, schema.letterheads, item.letterhead) : null + ]); + const profile = (await server.db.select().from(schema.authProfiles).where(and(eq(schema.authProfiles.user_id, item.created_by),eq(schema.authProfiles.tenant_id,tenant.id))).limit(1))[0]; + + console.log(profile) + const pdfData = getDocumentDataBackend( { ...item, @@ -613,6 +617,8 @@ export function getDocumentDataBackend( `${itemInfo.address?.zip || customerData.zip} ${itemInfo.address?.city || customerData.city}`, ].filter(Boolean); // Leere Einträge entfernen + console.log(contactPerson) + // Info Block aufbereiten const infoBlock = [ { @@ -634,12 +640,12 @@ export function getDocumentDataBackend( }] : []), { label: "Ansprechpartner", - content: contactPerson ? (contactPerson.name || contactPerson.fullName || contactPerson.email) : "-", + content: contactPerson ? (contactPerson.name || contactPerson.full_name || contactPerson.email) : "-", }, // Kontakt Infos - ...((itemInfo.contactTel || contactPerson?.fixedTel || contactPerson?.mobileTel) ? [{ + ...((itemInfo.contactTel || contactPerson?.fixed_tel || contactPerson?.mobile_tel) ? [{ label: "Telefon", - content: itemInfo.contactTel || contactPerson?.fixedTel || contactPerson?.mobileTel, + content: itemInfo.contactTel || contactPerson?.fixed_tel || contactPerson?.mobile_tel, }] : []), ...(contactPerson?.email ? [{ label: "E-Mail", diff --git a/src/plugins/services.ts b/src/plugins/services.ts index 1c9429c..c5788b7 100644 --- a/src/plugins/services.ts +++ b/src/plugins/services.ts @@ -3,14 +3,14 @@ import fp from "fastify-plugin"; import { bankStatementService } from "../modules/cron/bankstatementsync.service"; //import {initDokuboxClient, syncDokubox} from "../modules/cron/dokuboximport.service"; import { FastifyInstance } from "fastify"; -//import {prepareIncomingInvoices} from "../modules/cron/prepareIncomingInvoices"; +import {prepareIncomingInvoices} from "../modules/cron/prepareIncomingInvoices"; declare module "fastify" { interface FastifyInstance { services: { bankStatements: ReturnType; //dokuboxSync: ReturnType; - //prepareIncomingInvoices: ReturnType; + prepareIncomingInvoices: ReturnType; }; } } @@ -19,6 +19,6 @@ export default fp(async function servicePlugin(server: FastifyInstance) { server.decorate("services", { bankStatements: bankStatementService(server), //dokuboxSync: syncDokubox(server), - //prepareIncomingInvoices: prepareIncomingInvoices(server), + prepareIncomingInvoices: prepareIncomingInvoices(server), }); }); diff --git a/src/routes/functions.ts b/src/routes/functions.ts index 67a6582..d37963b 100644 --- a/src/routes/functions.ts +++ b/src/routes/functions.ts @@ -174,6 +174,11 @@ export default async function functionRoutes(server: FastifyInstance) { await server.services.bankStatements.run(req.user.tenant_id); }) + server.post('/functions/services/prepareincominginvoices', async (req, reply) => { + + await server.services.prepareIncomingInvoices.run(req.user.tenant_id) + }) + /*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} From 8a8711327595a8ea05064b1810e15fcaa3f89cad Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Fri, 2 Jan 2026 12:44:26 +0100 Subject: [PATCH 140/149] Added Prepare Service --- src/modules/cron/prepareIncomingInvoices.ts | 175 ++++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 src/modules/cron/prepareIncomingInvoices.ts diff --git a/src/modules/cron/prepareIncomingInvoices.ts b/src/modules/cron/prepareIncomingInvoices.ts new file mode 100644 index 0000000..011c1a3 --- /dev/null +++ b/src/modules/cron/prepareIncomingInvoices.ts @@ -0,0 +1,175 @@ +import { FastifyInstance } from "fastify" +import dayjs from "dayjs" +import { getInvoiceDataFromGPT } from "../../utils/gpt" + +// Drizzle schema +import { + tenants, + files, + filetags, + incominginvoices, +} from "../../../db/schema" + +import { eq, and, isNull, not } from "drizzle-orm" + +export function prepareIncomingInvoices(server: FastifyInstance) { + const processInvoices = async (tenantId:number) => { + console.log("▶ Starting Incoming Invoice Preparation") + + const tenantsRes = await server.db + .select() + .from(tenants) + .where(eq(tenants.id, tenantId)) + .orderBy(tenants.id) + + if (!tenantsRes.length) { + console.log("No tenants with autoPrepareIncomingInvoices = true") + return + } + + console.log(`Processing tenants: ${tenantsRes.map(t => t.id).join(", ")}`) + + // ------------------------------------------------------------- + // 2️⃣ Jeden Tenant einzeln verarbeiten + // ------------------------------------------------------------- + for (const tenant of tenantsRes) { + const tenantId = tenant.id + + // 2.1 Datei-Tags holen für incoming invoices + const tagRes = await server.db + .select() + .from(filetags) + .where( + and( + eq(filetags.tenant, tenantId), + eq(filetags.incomingDocumentType, "invoices") + ) + ) + .limit(1) + + const invoiceFileTag = tagRes?.[0]?.id + if (!invoiceFileTag) { + server.log.error(`❌ Missing filetag 'invoices' for tenant ${tenantId}`) + continue + } + + // 2.2 Alle Dateien laden, die als Invoice markiert sind aber NOCH keine incominginvoice haben + const filesRes = await server.db + .select() + .from(files) + .where( + and( + eq(files.tenant, tenantId), + eq(files.type, invoiceFileTag), + isNull(files.incominginvoice), + eq(files.archived, false), + not(isNull(files.path)) + ) + ) + + if (!filesRes.length) { + console.log(`No invoice files for tenant ${tenantId}`) + continue + } + + // ------------------------------------------------------------- + // 3️⃣ Jede Datei einzeln durch GPT jagen & IncomingInvoice erzeugen + // ------------------------------------------------------------- + for (const file of filesRes) { + console.log(`Processing file ${file.id} for tenant ${tenantId}`) + + const data = await getInvoiceDataFromGPT(server,file, tenantId) + + if (!data) { + server.log.warn(`GPT returned no data for file ${file.id}`) + continue + } + + // --------------------------------------------------------- + // 3.1 IncomingInvoice-Objekt vorbereiten + // --------------------------------------------------------- + let itemInfo: any = { + tenant: tenantId, + state: "Vorbereitet" + } + + if (data.invoice_number) itemInfo.reference = data.invoice_number + if (data.invoice_date) itemInfo.date = dayjs(data.invoice_date).toISOString() + if (data.issuer?.id) itemInfo.vendor = data.issuer.id + if (data.invoice_duedate) itemInfo.dueDate = dayjs(data.invoice_duedate).toISOString() + + // Payment terms mapping + const mapPayment: any = { + "Direct Debit": "Einzug", + "Transfer": "Überweisung", + "Credit Card": "Kreditkarte", + "Other": "Sonstiges", + } + if (data.terms) itemInfo.paymentType = mapPayment[data.terms] ?? data.terms + + // 3.2 Positionszeilen konvertieren + if (data.invoice_items?.length > 0) { + itemInfo.accounts = data.invoice_items.map(item => ({ + account: item.account_id, + description: item.description, + amountNet: item.total_without_tax, + amountTax: Number((item.total - item.total_without_tax).toFixed(2)), + taxType: String(item.tax_rate), + amountGross: item.total, + costCentre: null, + quantity: item.quantity, + })) + } + + // 3.3 Beschreibung generieren + let description = "" + if (data.delivery_note_number) description += `Lieferschein: ${data.delivery_note_number}\n` + if (data.reference) description += `Referenz: ${data.reference}\n` + if (data.invoice_items) { + for (const item of data.invoice_items) { + description += `${item.description} - ${item.quantity} ${item.unit} - ${item.total}\n` + } + } + itemInfo.description = description.trim() + + // --------------------------------------------------------- + // 4️⃣ IncomingInvoice erstellen + // --------------------------------------------------------- + const inserted = await server.db + .insert(incominginvoices) + .values(itemInfo) + .returning() + + const newInvoice = inserted?.[0] + + if (!newInvoice) { + server.log.error(`Failed to insert incoming invoice for file ${file.id}`) + continue + } + + // --------------------------------------------------------- + // 5️⃣ Datei mit incominginvoice-ID verbinden + // --------------------------------------------------------- + await server.db + .update(files) + .set({ incominginvoice: newInvoice.id }) + .where(eq(files.id, file.id)) + + console.log(`IncomingInvoice ${newInvoice.id} created for file ${file.id}`) + } + + } + + + return + } + + return { + run: async (tenant:number) => { + await processInvoices(tenant) + console.log("Incoming Invoice Preparation Completed.") + + } + } + +} From e8fe6940c28a0d88d4feefc464408c0c38564acb Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Fri, 2 Jan 2026 12:45:14 +0100 Subject: [PATCH 141/149] Added Prepare Service --- src/utils/gpt.ts | 204 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100644 src/utils/gpt.ts diff --git a/src/utils/gpt.ts b/src/utils/gpt.ts new file mode 100644 index 0000000..d46a4f1 --- /dev/null +++ b/src/utils/gpt.ts @@ -0,0 +1,204 @@ +import dayjs from "dayjs"; +import axios from "axios"; +import OpenAI from "openai"; +import { z } from "zod"; +import { zodResponseFormat } from "openai/helpers/zod"; +import { GetObjectCommand } from "@aws-sdk/client-s3"; +import { Blob } from "buffer"; +import { FastifyInstance } from "fastify"; + +import { s3 } from "./s3"; +import { secrets } from "./secrets"; + +// Drizzle schema +import { vendors, accounts } from "../../db/schema"; +import {eq} from "drizzle-orm"; + +let openai: OpenAI | null = null; + +// --------------------------------------------------------- +// INITIALIZE OPENAI +// --------------------------------------------------------- +export const initOpenAi = async () => { + openai = new OpenAI({ + apiKey: secrets.OPENAI_API_KEY, + }); +}; + +// --------------------------------------------------------- +// STREAM → BUFFER +// --------------------------------------------------------- +async function streamToBuffer(stream: any): Promise { + return new Promise((resolve, reject) => { + const chunks: Buffer[] = []; + stream.on("data", (chunk: Buffer) => chunks.push(chunk)); + stream.on("error", reject); + stream.on("end", () => resolve(Buffer.concat(chunks))); + }); +} + +// --------------------------------------------------------- +// GPT RESPONSE FORMAT (Zod Schema) +// --------------------------------------------------------- +const InstructionFormat = z.object({ + invoice_number: z.string(), + invoice_date: z.string(), + invoice_duedate: z.string(), + invoice_type: z.string(), + delivery_type: z.string(), + delivery_note_number: z.string(), + reference: z.string(), + issuer: z.object({ + id: z.number().nullable().optional(), + name: z.string(), + address: z.string(), + phone: z.string(), + email: z.string(), + bank: z.string(), + bic: z.string(), + iban: z.string(), + }), + recipient: z.object({ + name: z.string(), + address: z.string(), + phone: z.string(), + email: z.string(), + }), + invoice_items: z.array( + z.object({ + description: z.string(), + unit: z.string(), + quantity: z.number(), + total: z.number(), + total_without_tax: z.number(), + tax_rate: z.number(), + ean: z.number().nullable().optional(), + article_number: z.number().nullable().optional(), + account_number: z.number().nullable().optional(), + account_id: z.number().nullable().optional(), + }) + ), + subtotal: z.number(), + tax_rate: z.number(), + tax: z.number(), + total: z.number(), + terms: z.string(), +}); + +// --------------------------------------------------------- +// MAIN FUNCTION – REPLACES SUPABASE VERSION +// --------------------------------------------------------- +export const getInvoiceDataFromGPT = async function ( + server: FastifyInstance, + file: any, + tenantId: number +) { + await initOpenAi(); + + if (!openai) { + throw new Error("OpenAI not initialized. Call initOpenAi() first."); + } + + console.log(`📄 Reading invoice file ${file.id}`); + + // --------------------------------------------------------- + // 1) DOWNLOAD PDF FROM S3 + // --------------------------------------------------------- + let fileData: Buffer; + + try { + const command = new GetObjectCommand({ + Bucket: secrets.S3_BUCKET, + Key: file.path, + }); + + const response: any = await s3.send(command); + fileData = await streamToBuffer(response.Body); + } catch (err) { + console.log(`❌ S3 Download failed for file ${file.id}`, err); + return null; + } + + // Only process PDFs + if (!file.path.toLowerCase().endsWith(".pdf")) { + server.log.warn(`Skipping non-PDF file ${file.id}`); + return null; + } + + const fileBlob = new Blob([fileData], { type: "application/pdf" }); + + // --------------------------------------------------------- + // 2) SEND FILE TO PDF → TEXT API + // --------------------------------------------------------- + const form = new FormData(); + form.append("fileInput", fileBlob, file.path.split("/").pop()); + form.append("outputFormat", "txt"); + + let extractedText: string; + + try { + const res = await axios.post( + "http://23.88.52.85:8080/api/v1/convert/pdf/text", + form, + { + headers: { + "Content-Type": "multipart/form-data", + Authorization: `Bearer ${secrets.STIRLING_API_KEY}`, + }, + } + ); + + extractedText = res.data; + } catch (err) { + console.log("❌ PDF OCR API failed", err); + return null; + } + + // --------------------------------------------------------- + // 3) LOAD VENDORS + ACCOUNTS (DRIZZLE) + // --------------------------------------------------------- + const vendorList = await server.db + .select({ id: vendors.id, name: vendors.name }) + .from(vendors) + .where(eq(vendors.tenant,tenantId)); + + const accountList = await server.db + .select({ + id: accounts.id, + label: accounts.label, + number: accounts.number, + }) + .from(accounts); + + // --------------------------------------------------------- + // 4) GPT ANALYSIS + // --------------------------------------------------------- + + + + const completion = await openai.chat.completions.parse({ + model: "gpt-4o", + store: true, + response_format: zodResponseFormat(InstructionFormat, "instruction"), + messages: [ + { role: "user", content: extractedText }, + { + role: "user", + content: + "You extract structured invoice data.\n\n" + + `VENDORS: ${JSON.stringify(vendorList)}\n` + + `ACCOUNTS: ${JSON.stringify(accountList)}\n\n` + + "Match issuer by name to vendor.id.\n" + + "Match invoice items to account id based on label/number.\n" + + "Convert dates to YYYY-MM-DD.\n" + + "Keep invoice items in original order.\n", + }, + ], + }); + + const parsed = completion.choices[0].message.parsed; + + console.log(`🧾 Extracted invoice data for file ${file.id}`); + + return parsed; +}; From bc3b944f5d4daa3e4ab084adc1bde61593d3c60f Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Fri, 2 Jan 2026 12:45:24 +0100 Subject: [PATCH 142/149] Added Prepare Service --- src/utils/secrets.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/utils/secrets.ts b/src/utils/secrets.ts index b67a1e7..c21e2d7 100644 --- a/src/utils/secrets.ts +++ b/src/utils/secrets.ts @@ -33,6 +33,13 @@ export let secrets = { GOCARDLESS_BASE_URL: string GOCARDLESS_SECRET_ID: string GOCARDLESS_SECRET_KEY: string + DOKUBOX_IMAP_HOST: string + DOKUBOX_IMAP_PORT: number + DOKUBOX_IMAP_SECURE: boolean + DOKUBOX_IMAP_USER: string + DOKUBOX_IMAP_PASSWORD: string + OPENAI_API_KEY: string + STIRLING_API_KEY: string } export async function loadSecrets () { From 65acad1c0d685e3c68df2d84bad5a6e6c54a7cec Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Fri, 2 Jan 2026 12:45:47 +0100 Subject: [PATCH 143/149] Added Prepare Service --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index f188720..c657240 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "imapflow": "^1.1.1", "jsonwebtoken": "^9.0.2", "nodemailer": "^7.0.6", + "openai": "^6.10.0", "pdf-lib": "^1.17.1", "pg": "^8.16.3", "pngjs": "^7.0.0", From 8a2e91e7028100594234994b2513a3ee71d1a727 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Fri, 2 Jan 2026 14:34:53 +0100 Subject: [PATCH 144/149] TS Fix --- src/utils/gpt.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/gpt.ts b/src/utils/gpt.ts index d46a4f1..ec804ce 100644 --- a/src/utils/gpt.ts +++ b/src/utils/gpt.ts @@ -179,7 +179,7 @@ export const getInvoiceDataFromGPT = async function ( const completion = await openai.chat.completions.parse({ model: "gpt-4o", store: true, - response_format: zodResponseFormat(InstructionFormat, "instruction"), + response_format: zodResponseFormat(InstructionFormat as any, "instruction"), messages: [ { role: "user", content: extractedText }, { From 5485d2c0cccdb51b52d355a323e670847445447f Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Tue, 6 Jan 2026 11:41:13 +0100 Subject: [PATCH 145/149] Fix Synced At --- src/modules/cron/bankstatementsync.service.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/modules/cron/bankstatementsync.service.ts b/src/modules/cron/bankstatementsync.service.ts index 9773631..66863b0 100644 --- a/src/modules/cron/bankstatementsync.service.ts +++ b/src/modules/cron/bankstatementsync.service.ts @@ -221,11 +221,17 @@ export function bankStatementService(server: FastifyInstance) { ...new Set(allNewTransactions.map((t) => t.account)), ] + const normalizeDate = (val: any) => { + if (!val) return null + const d = new Date(val) + return isNaN(d.getTime()) ? null : d + } + for (const accId of affectedAccounts) { await server.db .update(bankaccounts) //@ts-ignore - .set({syncedAt: dayjs().utc().toISOString()}) + .set({syncedAt: normalizeDate(dayjs())}) .where(eq(bankaccounts.id, accId)) } } From fa369f7b818a655ea3ff7f765b23406d859ff60e Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Tue, 6 Jan 2026 11:41:34 +0100 Subject: [PATCH 146/149] Added Public Pin Header to Cors --- src/plugins/cors.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/cors.ts b/src/plugins/cors.ts index a140d1e..e62e9c3 100644 --- a/src/plugins/cors.ts +++ b/src/plugins/cors.ts @@ -15,7 +15,7 @@ export default fp(async (server: FastifyInstance) => { "capacitor://localhost", // dein Nuxt-Frontend ], methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"], - allowedHeaders: ["Content-Type", "Authorization", "Context"], + allowedHeaders: ["Content-Type", "Authorization", "Context", "X-Public-Pin"], exposedHeaders: ["Authorization", "Content-Disposition", "Content-Type", "Content-Length"], // optional, falls du ihn auch auslesen willst credentials: true, // wichtig, falls du Cookies nutzt }); From 8b9b5744bfb6007176f1dded50672abf46130e2f Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Tue, 6 Jan 2026 11:43:19 +0100 Subject: [PATCH 147/149] Fix Time Issue --- src/modules/serialexecution.service.ts | 38 ++++++++++++++++++-------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/src/modules/serialexecution.service.ts b/src/modules/serialexecution.service.ts index 82a98ff..e7166b4 100644 --- a/src/modules/serialexecution.service.ts +++ b/src/modules/serialexecution.service.ts @@ -332,17 +332,33 @@ async function getFileTypeId(server: FastifyInstance,tenantId: number) { // --- Logik Helper (Unverändert zur Business Logik) --- function calculateDateRange(config: any, executionDate: dayjs.Dayjs) { - let firstDate = executionDate; - let lastDate = executionDate; - // Logik 1:1 übernommen + // Basis nehmen + let baseDate = executionDate; + + let firstDate = baseDate; + let lastDate = baseDate; + if (config.intervall === "monatlich" && config.dateDirection === "Rückwirkend") { - firstDate = executionDate.subtract(1, "month").date(1); - lastDate = executionDate.subtract(1, "month").endOf("month"); + // 1. Monat abziehen + // 2. Start/Ende des Monats berechnen + // 3. WICHTIG: Zeit hart auf 12:00:00 setzen, damit Zeitzonen das Datum nicht kippen + firstDate = baseDate.subtract(1, "month").startOf("month").hour(12).minute(0).second(0).millisecond(0); + lastDate = baseDate.subtract(1, "month").endOf("month").hour(12).minute(0).second(0).millisecond(0); + } else if (config.intervall === "vierteljährlich" && config.dateDirection === "Rückwirkend") { - firstDate = executionDate.subtract(1, "quarter").startOf("quarter"); - lastDate = executionDate.subtract(1, "quarter").endOf("quarter"); + + firstDate = baseDate.subtract(1, "quarter").startOf("quarter").hour(12).minute(0).second(0).millisecond(0); + lastDate = baseDate.subtract(1, "quarter").endOf("quarter").hour(12).minute(0).second(0).millisecond(0); } - return { firstDate: firstDate.toISOString(), lastDate: lastDate.toISOString() }; + + // Das Ergebnis ist nun z.B.: + // firstDate: '2025-12-01T12:00:00.000Z' (Eindeutig der 1. Dezember) + // lastDate: '2025-12-31T12:00:00.000Z' (Eindeutig der 31. Dezember) + + return { + firstDate: firstDate.toISOString(), + lastDate: lastDate.toISOString() + }; } async function getSaveData(item: any, tenant: any, firstDate: string, lastDate: string, executionDate: string, executedBy: string) { @@ -453,7 +469,7 @@ function calculateDocumentTotals(rows: any[], taxType: string) { let titleSums: Record = {}; // Aktueller Titel für Gruppierung - let currentTitle = "Ohne Titel"; + let currentTitle = ""; rows.forEach(row => { if (row.mode === 'title') { @@ -467,8 +483,8 @@ function calculateDocumentTotals(rows: any[], taxType: string) { totalNet += amount; // Summen pro Titel addieren - if (!titleSums[currentTitle]) titleSums[currentTitle] = 0; - titleSums[currentTitle] += amount; + //if (!titleSums[currentTitle]) titleSums[currentTitle] = 0; + if(currentTitle.length > 0) titleSums[currentTitle] += amount; // Steuer-Logik const tax = taxType === "19 UStG" || taxType === "13b UStG" ? 0 : Number(row.taxPercent); From 7f09ef2911476cf3e36e25d20aec7e8d04ddb020 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Tue, 6 Jan 2026 11:43:43 +0100 Subject: [PATCH 148/149] Added Dokubox Service & Mailparser --- package.json | 1 + src/modules/cron/dokuboximport.service.ts | 259 ++++++++++++++++++++++ 2 files changed, 260 insertions(+) create mode 100644 src/modules/cron/dokuboximport.service.ts diff --git a/package.json b/package.json index c657240..6b2b772 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "handlebars": "^4.7.8", "imapflow": "^1.1.1", "jsonwebtoken": "^9.0.2", + "mailparser": "^3.9.0", "nodemailer": "^7.0.6", "openai": "^6.10.0", "pdf-lib": "^1.17.1", diff --git a/src/modules/cron/dokuboximport.service.ts b/src/modules/cron/dokuboximport.service.ts new file mode 100644 index 0000000..dd1aaec --- /dev/null +++ b/src/modules/cron/dokuboximport.service.ts @@ -0,0 +1,259 @@ +import axios from "axios" +import dayjs from "dayjs" +import { ImapFlow } from "imapflow" +import { simpleParser } from "mailparser" +import { FastifyInstance } from "fastify" + +import {saveFile} from "../../utils/files"; +import { secrets } from "../../utils/secrets" + +// Drizzle Imports +import { + tenants, + folders, + filetags, +} from "../../../db/schema" + +import { + eq, + and, +} from "drizzle-orm" + +let badMessageDetected = false +let badMessageMessageSent = false + +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") + + 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() + } + } + + + +// ------------------------------------------------------------- +// 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 + } +} From bf3f0cc7842b818c811a9b06697c0f3de7a6d6c6 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Tue, 6 Jan 2026 11:43:51 +0100 Subject: [PATCH 149/149] Added Public Links --- db/schema/index.ts | 3 +- db/schema/public_links.ts | 30 ++ src/index.ts | 9 +- src/modules/publiclinks.service.ts | 406 ++++++++++++++++++ .../publiclinks/publiclinks-authenticated.ts | 41 ++ .../publiclinks-non-authenticated.ts | 91 ++++ 6 files changed, 577 insertions(+), 3 deletions(-) create mode 100644 db/schema/public_links.ts create mode 100644 src/modules/publiclinks.service.ts create mode 100644 src/routes/publiclinks/publiclinks-authenticated.ts create mode 100644 src/routes/publiclinks/publiclinks-non-authenticated.ts diff --git a/db/schema/index.ts b/db/schema/index.ts index 195de05..47d8bee 100644 --- a/db/schema/index.ts +++ b/db/schema/index.ts @@ -70,4 +70,5 @@ export * from "./vehicles" export * from "./vendors" export * from "./staff_time_events" export * from "./serialtypes" -export * from "./serialexecutions" \ No newline at end of file +export * from "./serialexecutions" +export * from "./public_links" \ No newline at end of file diff --git a/db/schema/public_links.ts b/db/schema/public_links.ts new file mode 100644 index 0000000..283f96b --- /dev/null +++ b/db/schema/public_links.ts @@ -0,0 +1,30 @@ +import { pgTable, text, integer, boolean, jsonb, timestamp, uuid } from 'drizzle-orm/pg-core'; +import { tenants } from './tenants'; +import { authProfiles } from './auth_profiles'; + +export const publicLinks = pgTable('public_links', { + id: uuid("id").primaryKey().defaultRandom(), + + // Der öffentliche Token (z.B. "werkstatt-tablet-01") + token: text('token').notNull().unique(), + + // Zuordnung zum Tenant (WICHTIG für die Datentrennung) + tenant: integer('tenant').references(() => tenants.id).notNull(), + + defaultProfile: uuid('default_profile').references(() => authProfiles.id), + + // Sicherheit + isProtected: boolean('is_protected').default(false).notNull(), + pinHash: text('pin_hash'), + + // Konfiguration (JSON) + config: jsonb('config').default({}), + + // Metadaten + name: text('name').notNull(), + description: text('description'), + + active: boolean('active').default(true).notNull(), + createdAt: timestamp('created_at').defaultNow(), + updatedAt: timestamp('updated_at').defaultNow(), +}); \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index a5b846f..d4155be 100644 --- a/src/index.ts +++ b/src/index.ts @@ -28,6 +28,10 @@ import notificationsRoutes from "./routes/notifications"; import staffTimeRoutes from "./routes/staff/time"; import staffTimeConnectRoutes from "./routes/staff/timeconnects"; import userRoutes from "./routes/auth/user"; +import publiclinksAuthenticatedRoutes from "./routes/publiclinks/publiclinks-authenticated"; + +//Public Links +import publiclinksNonAuthenticatedRoutes from "./routes/publiclinks/publiclinks-non-authenticated"; //Resources import resourceRoutes from "./routes/resources/main"; @@ -98,6 +102,8 @@ async function main() { await app.register(helpdeskInboundRoutes); + await app.register(publiclinksNonAuthenticatedRoutes) + await app.register(async (m2mApp) => { await m2mApp.register(authM2m) @@ -133,8 +139,7 @@ async function main() { await subApp.register(staffTimeRoutes); await subApp.register(staffTimeConnectRoutes); await subApp.register(userRoutes); - - + await subApp.register(publiclinksAuthenticatedRoutes); await subApp.register(resourceRoutes); },{prefix: "/api"}) diff --git a/src/modules/publiclinks.service.ts b/src/modules/publiclinks.service.ts new file mode 100644 index 0000000..e9b450a --- /dev/null +++ b/src/modules/publiclinks.service.ts @@ -0,0 +1,406 @@ +import { FastifyInstance } from 'fastify'; +import bcrypt from 'bcrypt'; +import crypto from 'crypto'; +import {and, eq, inArray, not} from 'drizzle-orm'; +import * as schema from '../../db/schema'; +import {useNextNumberRangeNumber} from "../utils/functions"; // Pfad anpassen + + +export const publicLinkService = { + + /** + * Erstellt einen neuen Public Link + */ + async createLink(server: FastifyInstance, tenantId: number, + name: string, + isProtected?: boolean, + pin?: string, + customToken?: string, + config?: Record, + defaultProfileId?: string) { + let pinHash: string | null = null; + + // 1. PIN Hashen, falls Schutz aktiviert ist + if (isProtected && pin) { + pinHash = await bcrypt.hash(pin, 10); + } else if (isProtected && !pin) { + throw new Error("Für geschützte Links muss eine PIN angegeben werden."); + } + + // 2. Token generieren oder Custom Token verwenden + let token = customToken; + + if (!token) { + // Generiere einen zufälligen Token (z.B. hex string) + // Alternativ: nanoid nutzen, falls installiert + token = crypto.randomBytes(12).toString('hex'); + } + + // Prüfen, ob Token schon existiert (nur bei Custom Token wichtig) + if (customToken) { + const existing = await server.db.query.publicLinks.findFirst({ + where: eq(schema.publicLinks.token, token) + }); + if (existing) { + throw new Error(`Der Token '${token}' ist bereits vergeben.`); + } + } + + // 3. DB Insert + const [newLink] = await server.db.insert(schema.publicLinks).values({ + tenant: tenantId, + name: name, + token: token, + isProtected: isProtected || false, + pinHash: pinHash, + config: config || {}, + defaultProfile: defaultProfileId || null, + active: true + }).returning(); + + return newLink; + }, + + /** + * Listet alle Links für einen Tenant auf (für die Verwaltungs-UI später) + */ + async getLinksByTenant(server: FastifyInstance, tenantId: number) { + return server.db.select() + .from(schema.publicLinks) + .where(eq(schema.publicLinks.tenant, tenantId)); + }, + + + async getLinkContext(server: FastifyInstance, token: string, providedPin?: string) { + // 1. Link laden & Checks + const linkConfig = await server.db.query.publicLinks.findFirst({ + where: eq(schema.publicLinks.token, token) + }); + + if (!linkConfig || !linkConfig.active) throw new Error("Link_NotFound"); + + // 2. PIN Check + if (linkConfig.isProtected) { + if (!providedPin) throw new Error("Pin_Required"); + const isValid = await bcrypt.compare(providedPin, linkConfig.pinHash || ""); + if (!isValid) throw new Error("Pin_Invalid"); + } + + const tenantId = linkConfig.tenant; + const config = linkConfig.config as any; + + // --- RESSOURCEN & FILTER --- + + // Standardmäßig alles laden, wenn 'resources' nicht definiert ist + const requestedResources: string[] = config.resources || ['profiles', 'projects', 'services', 'units']; + const filters = config.filters || {}; // Erwartet jetzt: { projects: [1,2], services: [3,4] } + + const queryPromises: Record> = {}; + + // --------------------------------------------------------- + // 1. PROFILES (Mitarbeiter) + // --------------------------------------------------------- + if (requestedResources.includes('profiles')) { + let profileCondition = and( + eq(schema.authProfiles.tenant_id, tenantId), + eq(schema.authProfiles.active, true) + ); + + // Sicherheits-Feature: Default Profil erzwingen + if (linkConfig.defaultProfile) { + profileCondition = and(profileCondition, eq(schema.authProfiles.id, linkConfig.defaultProfile)); + } + + // Optional: Auch hier Filter ermöglichen (falls man z.B. nur 3 bestimmte MA zur Auswahl geben will) + if (filters.profiles && Array.isArray(filters.profiles) && filters.profiles.length > 0) { + profileCondition = and(profileCondition, inArray(schema.authProfiles.id, filters.profiles)); + } + + queryPromises.profiles = server.db.select({ + id: schema.authProfiles.id, + fullName: schema.authProfiles.full_name + }) + .from(schema.authProfiles) + .where(profileCondition); + } + + // --------------------------------------------------------- + // 2. PROJECTS (Aufträge) + // --------------------------------------------------------- + if (requestedResources.includes('projects')) { + let projectCondition = and( + eq(schema.projects.tenant, tenantId), + not(eq(schema.projects.active_phase, 'Abgeschlossen')) + ); + + // NEU: Zugriff direkt auf filters.projects + if (filters.projects && Array.isArray(filters.projects) && filters.projects.length > 0) { + projectCondition = and(projectCondition, inArray(schema.projects.id, filters.projects)); + } + + queryPromises.projects = server.db.select({ + id: schema.projects.id, + name: schema.projects.name + }) + .from(schema.projects) + .where(projectCondition); + } + + // --------------------------------------------------------- + // 3. SERVICES (Tätigkeiten) + // --------------------------------------------------------- + if (requestedResources.includes('services')) { + let serviceCondition = eq(schema.services.tenant, tenantId); + + // NEU: Zugriff direkt auf filters.services + if (filters.services && Array.isArray(filters.services) && filters.services.length > 0) { + serviceCondition = and(serviceCondition, inArray(schema.services.id, filters.services)); + } + + queryPromises.services = server.db.select({ + id: schema.services.id, + name: schema.services.name, + unit: schema.services.unit + }) + .from(schema.services) + .where(serviceCondition); + } + + // --------------------------------------------------------- + // 4. UNITS (Einheiten) + // --------------------------------------------------------- + if (requestedResources.includes('units')) { + // Units werden meist global geladen, könnten aber auch gefiltert werden + queryPromises.units = server.db.select().from(schema.units); + } + + // --- QUERY AUSFÜHRUNG --- + const results = await Promise.all(Object.values(queryPromises)); + const keys = Object.keys(queryPromises); + + const dataResponse: Record = { + profiles: [], + projects: [], + services: [], + units: [] + }; + + keys.forEach((key, index) => { + dataResponse[key] = results[index]; + }); + + return { + config: linkConfig.config, + meta: { + formName: linkConfig.name, + defaultProfileId: linkConfig.defaultProfile + }, + data: dataResponse + }; + }, + + async submitFormData(server: FastifyInstance, token: string, payload: any, providedPin?: string) { + // 1. Validierung (Token & PIN) + const linkConfig = await server.db.query.publicLinks.findFirst({ + where: eq(schema.publicLinks.token, token) + }); + + if (!linkConfig || !linkConfig.active) throw new Error("Link_NotFound"); + + if (linkConfig.isProtected) { + if (!providedPin) throw new Error("Pin_Required"); + const isValid = await bcrypt.compare(providedPin, linkConfig.pinHash || ""); + if (!isValid) throw new Error("Pin_Invalid"); + } + + const tenantId = linkConfig.tenant; + const config = linkConfig.config as any; + + // 2. USER ID AUFLÖSEN + // Wir holen die profileId aus dem Link (Default) oder dem Payload (User-Auswahl) + const rawProfileId = linkConfig.defaultProfile || payload.profile; + if (!rawProfileId) throw new Error("Profile_Missing"); + + // Profil laden, um die user_id zu bekommen + const authProfile = await server.db.query.authProfiles.findFirst({ + where: eq(schema.authProfiles.id, rawProfileId) + }); + + if (!authProfile) throw new Error("Profile_Not_Found"); + + // Da du sagtest, es gibt immer einen User, verlassen wir uns darauf. + // Falls null, werfen wir einen Fehler, da die DB sonst beim Insert knallt. + const userId = authProfile.user_id; + if (!userId) throw new Error("Profile_Has_No_User_Assigned"); + + + // Helper für Datum + const normalizeDate = (val: any) => { + if (!val) return null + const d = new Date(val) + return isNaN(d.getTime()) ? null : d + } + + // ================================================================= + // SCHRITT A: Stammdaten laden + // ================================================================= + const project = await server.db.query.projects.findFirst({ + where: eq(schema.projects.id, payload.project) + }); + if (!project) throw new Error("Project not found"); + + const customer = await server.db.query.customers.findFirst({ + where: eq(schema.customers.id, project.customer) + }); + + const service = await server.db.query.services.findFirst({ + where: eq(schema.services.id, payload.service) + }); + if (!service) throw new Error("Service not found"); + + // Texttemplates & Letterhead laden + const texttemplates = await server.db.query.texttemplates.findMany({ + where: (t, {and, eq}) => and( + eq(t.tenant, tenantId), + eq(t.documentType, 'deliveryNotes') + ) + }); + const letterhead = await server.db.query.letterheads.findFirst({ + where: eq(schema.letterheads.tenant, tenantId) + }); + + // ================================================================= + // SCHRITT B: Nummernkreis generieren + // ================================================================= + const {usedNumber} = await useNextNumberRangeNumber(server, tenantId, "deliveryNotes"); + + // ================================================================= + // SCHRITT C: Berechnungen + // ================================================================= + const startDate = normalizeDate(payload.startDate) || new Date(); + let endDate = normalizeDate(payload.endDate); + + // Fallback Endzeit (+1h) + if (!endDate) { + endDate = server.dayjs(startDate).add(1, 'hour').toDate(); + } + + // Menge berechnen + let quantity = payload.quantity; + if (!quantity && payload.totalHours) quantity = payload.totalHours; + if (!quantity) { + const diffMin = server.dayjs(endDate).diff(server.dayjs(startDate), 'minute'); + quantity = Number((diffMin / 60).toFixed(2)); + } + + // ================================================================= + // SCHRITT D: Lieferschein erstellen + // ================================================================= + const createDocData = { + tenant: tenantId, + type: "deliveryNotes", + state: "Entwurf", + customer: project.customer, + //@ts-ignore + address: customer ? {zip: customer.infoData.zip, city: customer.infoData.city, street: customer.infoData.street,} : {}, + project: project.id, + documentNumber: usedNumber, + documentDate: String(new Date()), // Schema sagt 'text', evtl toISOString() besser? + deliveryDate: String(startDate), // Schema sagt 'text' + deliveryDateType: "Leistungsdatum", + createdBy: userId, // WICHTIG: Hier die User ID + created_by: userId, // WICHTIG: Hier die User ID + title: "Lieferschein", + description: "", + startText: texttemplates.find((i: any) => i.default && i.pos === "startText")?.text || "", + endText: texttemplates.find((i: any) => i.default && i.pos === "endText")?.text || "", + rows: [ + { + pos: "1", + mode: "service", + service: service.id, + text: service.name, + unit: service.unit, + quantity: quantity, + description: service.description || null, + descriptionText: service.description || null, + agriculture: { + dieselUsage: payload.dieselUsage || null, + } + } + ], + contactPerson: userId, // WICHTIG: Hier die User ID + letterhead: letterhead?.id, + }; + + const [createdDoc] = await server.db.insert(schema.createddocuments) + //@ts-ignore + .values(createDocData) + .returning(); + + // ================================================================= + // SCHRITT E: Zeiterfassung Events + // ================================================================= + if (config.features?.timeTracking) { + + // Metadaten für das Event + const eventMetadata = { + project: project.id, + service: service.id, + description: payload.description || "", + generatedDocumentId: createdDoc.id + }; + + // 1. START EVENT + await server.db.insert(schema.stafftimeevents).values({ + tenant_id: tenantId, + user_id: userId, // WICHTIG: User ID + actortype: "user", + actoruser_id: userId, // WICHTIG: User ID + eventtime: startDate, + eventtype: "START", + source: "PUBLIC_LINK", + metadata: eventMetadata // WICHTIG: Schema heißt 'metadata', nicht 'payload' + }); + + // 2. STOP EVENT + await server.db.insert(schema.stafftimeevents).values({ + tenant_id: tenantId, + user_id: userId, + actortype: "user", + actoruser_id: userId, + eventtime: endDate, + eventtype: "STOP", + source: "PUBLIC_LINK", + metadata: eventMetadata + }); + } + + // ================================================================= + // SCHRITT F: History Items + // ================================================================= + const historyItemsToCreate = []; + + if (payload.description) { + historyItemsToCreate.push({ + tenant: tenantId, + createdBy: userId, // WICHTIG: User ID + text: `Notiz aus Webformular Lieferschein ${createdDoc.documentNumber}: ${payload.description}`, + project: project.id, + createdDocument: createdDoc.id + }); + } + + historyItemsToCreate.push({ + tenant: tenantId, + createdBy: userId, // WICHTIG: User ID + text: `Webformular abgeschickt. Lieferschein ${createdDoc.documentNumber} erstellt. Zeit gebucht (Start/Stop).`, + project: project.id, + createdDocument: createdDoc.id + }); + + await server.db.insert(schema.historyitems).values(historyItemsToCreate); + + return {success: true, documentNumber: createdDoc.documentNumber}; + } +} \ No newline at end of file diff --git a/src/routes/publiclinks/publiclinks-authenticated.ts b/src/routes/publiclinks/publiclinks-authenticated.ts new file mode 100644 index 0000000..33e8fa5 --- /dev/null +++ b/src/routes/publiclinks/publiclinks-authenticated.ts @@ -0,0 +1,41 @@ +import { FastifyRequest, FastifyReply, FastifyInstance } from 'fastify'; +import { publicLinkService } from '../../modules/publiclinks.service'; + + +export default async function publiclinksAuthenticatedRoutes(server: FastifyInstance) { + server.post("/publiclinks", async (req, reply) => { + try { + const tenantId = 21; // Hardcoded für Test, später: req.user.tenantId + + const { name, isProtected, pin, customToken, config, defaultProfileId } = req.body as { name:string, isProtected:boolean, pin:string, customToken:string, config:Object, defaultProfileId:string}; + + const newLink = await publicLinkService.createLink(server, tenantId, + name, + isProtected, + pin, + customToken, + config, + defaultProfileId); + + return reply.code(201).send({ + success: true, + data: { + id: newLink.id, + token: newLink.token, + fullUrl: `/public/${newLink.token}`, // Helper für Frontend + isProtected: newLink.isProtected + } + }); + + } catch (error: any) { + server.log.error(error); + + // Einfache Fehlerbehandlung + if (error.message.includes("bereits vergeben")) { + return reply.code(409).send({ error: error.message }); + } + + return reply.code(500).send({ error: "Fehler beim Erstellen des Links", details: error.message }); + } + }) +} diff --git a/src/routes/publiclinks/publiclinks-non-authenticated.ts b/src/routes/publiclinks/publiclinks-non-authenticated.ts new file mode 100644 index 0000000..ded3b64 --- /dev/null +++ b/src/routes/publiclinks/publiclinks-non-authenticated.ts @@ -0,0 +1,91 @@ +import { FastifyRequest, FastifyReply, FastifyInstance } from 'fastify'; +import { publicLinkService } from '../../modules/publiclinks.service'; + + +export default async function publiclinksNonAuthenticatedRoutes(server: FastifyInstance) { + server.get("/workflows/context/:token", async (req, reply) => { + const { token } = req.params as { token: string }; + + // Wir lesen die PIN aus dem Header (Best Practice für Security) + const pin = req.headers['x-public-pin'] as string | undefined; + + try { + const context = await publicLinkService.getLinkContext(server, token, pin); + + return reply.send(context); + + } catch (error: any) { + // Spezifische Fehlercodes für das Frontend + if (error.message === "Link_NotFound") { + return reply.code(404).send({ error: "Link nicht gefunden oder abgelaufen" }); + } + + if (error.message === "Pin_Required") { + return reply.code(401).send({ + error: "PIN erforderlich", + code: "PIN_REQUIRED", + requirePin: true + }); + } + + if (error.message === "Pin_Invalid") { + return reply.code(403).send({ + error: "PIN falsch", + code: "PIN_INVALID", + requirePin: true + }); + } + + server.log.error(error); + return reply.code(500).send({ error: "Interner Server Fehler" }); + } + }); + + server.post("/workflows/submit/:token", async (req, reply) => { + const { token } = req.params as { token: string }; + // PIN sicher aus dem Header lesen + const pin = req.headers['x-public-pin'] as string | undefined; + // Der Body enthält { profile, project, service, ... } + const payload = req.body; + + console.log(payload) + + try { + // Service aufrufen (führt die 3 Schritte aus: Lieferschein -> Zeit -> History) + const result = await publicLinkService.submitFormData(server, token, payload, pin); + + // 201 Created zurückgeben + return reply.code(201).send(result); + + } catch (error: any) { + console.log(error); + + // Fehler-Mapping für saubere HTTP Codes + if (error.message === "Link_NotFound") { + return reply.code(404).send({ error: "Link ungültig oder nicht aktiv" }); + } + + if (error.message === "Pin_Required") { + return reply.code(401).send({ error: "PIN erforderlich" }); + } + + if (error.message === "Pin_Invalid") { + return reply.code(403).send({ error: "PIN ist falsch" }); + } + + if (error.message === "Profile_Missing") { + return reply.code(400).send({ error: "Kein Mitarbeiter-Profil gefunden (weder im Link noch in der Eingabe)" }); + } + + if (error.message === "Project not found" || error.message === "Service not found") { + return reply.code(400).send({ error: "Ausgewähltes Projekt oder Leistung existiert nicht mehr." }); + } + + // Fallback für alle anderen Fehler (z.B. DB Constraints) + return reply.code(500).send({ + error: "Interner Fehler beim Speichern", + details: error.message + }); + } + }); +}