133 lines
4.6 KiB
TypeScript
133 lines
4.6 KiB
TypeScript
import { FastifyInstance } from "fastify"
|
||
import { eq, and, isNull } 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<string, any> }
|
||
|
||
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),
|
||
isNull(authProfiles.archived_at)
|
||
)
|
||
)
|
||
|
||
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 }
|
||
for (const key of ["archived_at", "user_id", "tenant_id", "id"]) delete updateData[key]
|
||
|
||
// 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(and(eq(authProfiles.id, profile.id), isNull(authProfiles.archived_at)))
|
||
.returning()
|
||
|
||
if (!updatedProfile) return reply.code(404).send({ error: "Profile not found in tenant" })
|
||
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" })
|
||
}
|
||
})
|
||
}
|