removed Routes, Introduced Single Route with Cecking

This commit is contained in:
2025-12-06 22:50:15 +01:00
parent dff2b05401
commit 0f3c8c862f
11 changed files with 1279 additions and 2094 deletions

View File

@@ -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<string, any[]> = {};
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" })
}
})*/
}