163 lines
5.5 KiB
TypeScript
163 lines
5.5 KiB
TypeScript
import { FastifyInstance } from "fastify";
|
|
import { eq, and } from "drizzle-orm";
|
|
|
|
import {
|
|
authProfiles,
|
|
} from "../../db/schema";
|
|
import {
|
|
loadProfileWithBranches,
|
|
resolveTenantBranchIds,
|
|
syncProfileBranches,
|
|
} from "../utils/profileBranches";
|
|
import {
|
|
enrichProfilesWithTeams,
|
|
resolveTenantTeamIds,
|
|
syncProfileTeams,
|
|
} from "../utils/profileTeams";
|
|
|
|
export default async function authProfilesRoutes(server: FastifyInstance) {
|
|
|
|
// -------------------------------------------------------------
|
|
// GET SINGLE PROFILE
|
|
// -------------------------------------------------------------
|
|
server.get("/profiles/:id", async (req, reply) => {
|
|
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" });
|
|
}
|
|
|
|
const profileWithBranches = await loadProfileWithBranches(server, id, tenantId)
|
|
const [profile] = profileWithBranches
|
|
? await enrichProfilesWithTeams(server, [profileWithBranches])
|
|
: [null]
|
|
|
|
if (!profile) {
|
|
return reply.code(404).send({ error: "User not found or not in tenant" });
|
|
}
|
|
|
|
return profile;
|
|
|
|
} catch (error) {
|
|
console.error("GET /profiles/:id ERROR:", error);
|
|
return reply.code(500).send({ error: "Internal Server Error" });
|
|
}
|
|
});
|
|
|
|
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",
|
|
"branch"
|
|
]
|
|
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
|
|
}
|
|
|
|
// ✅ Date-Felder sauber konvertieren, falls vorhanden
|
|
const dateFields = ["birthday", "entry_date"]
|
|
|
|
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
|
|
}
|
|
|
|
// -------------------------------------------------------------
|
|
// UPDATE PROFILE
|
|
// -------------------------------------------------------------
|
|
server.put("/profiles/:id", async (req, reply) => {
|
|
try {
|
|
const tenantId = req.user?.tenant_id
|
|
const userId = req.user?.user_id
|
|
|
|
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 { primaryBranchId, branchIds } = await resolveTenantBranchIds(
|
|
server,
|
|
tenantId,
|
|
[
|
|
...(Array.isArray(body.branch_ids) ? body.branch_ids : []),
|
|
...(Array.isArray(body.branches) ? body.branches : []),
|
|
],
|
|
body.branch_id ?? body.branch?.id ?? null
|
|
)
|
|
const teamIds = await resolveTenantTeamIds(
|
|
server,
|
|
tenantId,
|
|
[
|
|
...(Array.isArray(body.team_ids) ? body.team_ids : []),
|
|
...(Array.isArray(body.teams) ? body.teams : []),
|
|
],
|
|
)
|
|
|
|
delete body.branch_ids
|
|
delete body.branches
|
|
delete body.team_ids
|
|
delete body.teams
|
|
|
|
const updateData = {
|
|
...body,
|
|
branch_id: primaryBranchId,
|
|
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" })
|
|
}
|
|
|
|
await syncProfileBranches(server, id, branchIds, userId)
|
|
await syncProfileTeams(server, id, teamIds, userId)
|
|
|
|
const profileWithBranches = await loadProfileWithBranches(server, id, tenantId)
|
|
const [profile] = profileWithBranches
|
|
? await enrichProfilesWithTeams(server, [profileWithBranches])
|
|
: [null]
|
|
return profile || updated[0]
|
|
|
|
} catch (err) {
|
|
console.error("PUT /profiles/:id ERROR:", err)
|
|
if (err instanceof Error && ["INVALID_BRANCH_SELECTION", "INVALID_PRIMARY_BRANCH"].includes(err.message)) {
|
|
return reply.code(400).send({ error: "Ungültige Niederlassungsauswahl" })
|
|
}
|
|
if (err instanceof Error && err.message === "INVALID_TEAM_SELECTION") {
|
|
return reply.code(400).send({ error: "Ungültige Teamauswahl" })
|
|
}
|
|
return reply.code(500).send({ error: "Internal Server Error" })
|
|
}
|
|
})
|
|
}
|