first commit
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
import { and, eq } from 'drizzle-orm';
|
||||
import {
|
||||
PERMISSIONS,
|
||||
ROLE_PERMISSIONS,
|
||||
type Department,
|
||||
type Permission,
|
||||
type Role,
|
||||
type Rank,
|
||||
type MdtUser,
|
||||
} from '@d4rk-tablet/shared';
|
||||
import { db } from '../../db/client';
|
||||
import { mdtRanks, mdtUsers } from '../../db/schema';
|
||||
import { DEFAULT_RANKS, defaultPermissionsFor } from '../../config/rankDefaults';
|
||||
|
||||
const OVERRIDE_ROLES: Role[] = ['admin', 'dispatch'];
|
||||
|
||||
function asRoles(v: unknown): Role[] {
|
||||
if (!Array.isArray(v)) return [];
|
||||
const set = new Set<string>(['admin', 'dispatch', 'police', 'ems', 'fire']);
|
||||
return v.filter((r): r is Role => typeof r === 'string' && set.has(r));
|
||||
}
|
||||
|
||||
/**
|
||||
* Effektive Rechte eines Users: Rang-Matrix(Department, Grade) ∪ Override-Rollen.
|
||||
* admin → alle Rechte. DB-Rang überschreibt Code-Default.
|
||||
*/
|
||||
export async function computePermissions(
|
||||
department: Department | null,
|
||||
grade: number | null,
|
||||
overrideRoles: Role[],
|
||||
): Promise<Permission[]> {
|
||||
if (overrideRoles.includes('admin')) return [...PERMISSIONS];
|
||||
|
||||
const perms = new Set<Permission>();
|
||||
|
||||
if (department && grade != null) {
|
||||
const [row] = await db
|
||||
.select()
|
||||
.from(mdtRanks)
|
||||
.where(and(eq(mdtRanks.department, department), eq(mdtRanks.grade, grade)))
|
||||
.limit(1);
|
||||
const rankPerms = row ? (row.permissions as Permission[]) : defaultPermissionsFor(department, grade);
|
||||
for (const p of rankPerms) perms.add(p);
|
||||
}
|
||||
|
||||
for (const role of overrideRoles) {
|
||||
if (role === 'dispatch') for (const p of ROLE_PERMISSIONS.dispatch) perms.add(p);
|
||||
}
|
||||
|
||||
return [...perms];
|
||||
}
|
||||
|
||||
/** Rechte-Matrix einer Behörde: DB-Ränge über Defaults gemerged. */
|
||||
export async function getRankMatrix(department: Department): Promise<Rank[]> {
|
||||
const rows = await db.select().from(mdtRanks).where(eq(mdtRanks.department, department));
|
||||
const byGrade = new Map(rows.map((r) => [r.grade, r]));
|
||||
|
||||
const grades = new Set<number>([
|
||||
...DEFAULT_RANKS[department].map((r) => r.grade),
|
||||
...rows.map((r) => r.grade),
|
||||
]);
|
||||
|
||||
return [...grades]
|
||||
.sort((a, b) => a - b)
|
||||
.map((grade) => {
|
||||
const dbRow = byGrade.get(grade);
|
||||
if (dbRow) {
|
||||
return {
|
||||
department,
|
||||
grade,
|
||||
label: dbRow.label,
|
||||
permissions: dbRow.permissions as Permission[],
|
||||
};
|
||||
}
|
||||
const def = DEFAULT_RANKS[department].find((r) => r.grade === grade)!;
|
||||
return { department, grade, label: def.label, permissions: def.permissions };
|
||||
});
|
||||
}
|
||||
|
||||
export async function updateRank(
|
||||
department: Department,
|
||||
grade: number,
|
||||
input: { label?: string; permissions: Permission[] },
|
||||
): Promise<Rank> {
|
||||
const label =
|
||||
input.label ?? DEFAULT_RANKS[department].find((r) => r.grade === grade)?.label ?? `Grade ${grade}`;
|
||||
|
||||
await db
|
||||
.insert(mdtRanks)
|
||||
.values({ department, grade, label, permissions: input.permissions })
|
||||
.onDuplicateKeyUpdate({ set: { label, permissions: input.permissions } });
|
||||
|
||||
return { department, grade, label, permissions: input.permissions };
|
||||
}
|
||||
|
||||
// ── Mitarbeiter ──
|
||||
function mapUser(row: typeof mdtUsers.$inferSelect): MdtUser {
|
||||
return {
|
||||
id: row.id,
|
||||
citizenid: row.citizenid,
|
||||
discordId: row.discordId,
|
||||
name: row.name,
|
||||
callsign: row.callsign,
|
||||
department: row.department,
|
||||
grade: row.grade,
|
||||
roles: asRoles(row.roles),
|
||||
};
|
||||
}
|
||||
|
||||
export async function listUsers(): Promise<MdtUser[]> {
|
||||
const rows = await db.select().from(mdtUsers);
|
||||
return rows.map(mapUser);
|
||||
}
|
||||
|
||||
export async function updateUserRoles(id: number, roles: string[]): Promise<MdtUser | null> {
|
||||
const filtered = asRoles(roles).filter((r) => OVERRIDE_ROLES.includes(r));
|
||||
await db.update(mdtUsers).set({ roles: filtered }).where(eq(mdtUsers.id, id));
|
||||
const [row] = await db.select().from(mdtUsers).where(eq(mdtUsers.id, id)).limit(1);
|
||||
return row ? mapUser(row) : null;
|
||||
}
|
||||
Reference in New Issue
Block a user