commit
This commit is contained in:
@@ -1,10 +1,123 @@
|
||||
const SELECTION_SET_KEY = "selection:ids";
|
||||
const SELECTION_SEQ_KEY = "selection:seq";
|
||||
const ROLE_SET_KEY = "role:names";
|
||||
|
||||
export const DEFAULT_ROLE_DEFINITIONS = [
|
||||
{
|
||||
name: "viewer",
|
||||
label: "Viewer",
|
||||
permissions: ["view_dashboard", "view_trends"],
|
||||
isSystem: true,
|
||||
},
|
||||
{
|
||||
name: "technician",
|
||||
label: "Techniker",
|
||||
permissions: ["view_dashboard", "view_trends", "edit_aliases", "manage_sources", "view_debug"],
|
||||
isSystem: true,
|
||||
},
|
||||
{
|
||||
name: "admin",
|
||||
label: "Admin",
|
||||
permissions: ["view_dashboard", "view_trends", "edit_aliases", "manage_sources", "manage_users", "manage_roles", "view_debug"],
|
||||
isSystem: true,
|
||||
},
|
||||
];
|
||||
|
||||
function safeParse(jsonValue) {
|
||||
return jsonValue ? JSON.parse(jsonValue) : null;
|
||||
}
|
||||
|
||||
function normalizeRoleName(value) {
|
||||
const normalized = String(value || "")
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9_-]+/g, "-")
|
||||
.replace(/^-+|-+$/g, "")
|
||||
.slice(0, 40);
|
||||
|
||||
return normalized || "viewer";
|
||||
}
|
||||
|
||||
function normalizePermissions(input) {
|
||||
return [...new Set((Array.isArray(input) ? input : [])
|
||||
.map((entry) => String(entry || "").trim())
|
||||
.filter(Boolean))].sort();
|
||||
}
|
||||
|
||||
function normalizeRoleRecord(role) {
|
||||
const systemRole = DEFAULT_ROLE_DEFINITIONS.find((entry) => entry.name === normalizeRoleName(role?.name));
|
||||
return {
|
||||
name: normalizeRoleName(role?.name),
|
||||
label: String(role?.label || systemRole?.label || role?.name || "Rolle").trim() || systemRole?.label || "Rolle",
|
||||
permissions: normalizePermissions(role?.permissions || systemRole?.permissions || []),
|
||||
isSystem: Boolean(role?.isSystem ?? systemRole?.isSystem),
|
||||
updatedAt: role?.updatedAt || new Date().toISOString(),
|
||||
};
|
||||
}
|
||||
|
||||
async function ensureDefaultRoles(redis) {
|
||||
for (const definition of DEFAULT_ROLE_DEFINITIONS) {
|
||||
const key = `role:${definition.name}`;
|
||||
const existing = safeParse(await redis.get(key));
|
||||
const payload = normalizeRoleRecord({
|
||||
...definition,
|
||||
...(existing || {}),
|
||||
name: definition.name,
|
||||
label: existing?.label || definition.label,
|
||||
permissions: Array.isArray(existing?.permissions) && existing.permissions.length ? existing.permissions : definition.permissions,
|
||||
isSystem: true,
|
||||
});
|
||||
await redis.set(key, JSON.stringify(payload));
|
||||
await redis.sadd(ROLE_SET_KEY, definition.name);
|
||||
}
|
||||
}
|
||||
|
||||
export async function listRoles(redis) {
|
||||
await ensureDefaultRoles(redis);
|
||||
const names = await redis.smembers(ROLE_SET_KEY);
|
||||
const items = await Promise.all(names.map((name) => redis.get(`role:${name}`)));
|
||||
|
||||
return items
|
||||
.map((item) => safeParse(item))
|
||||
.filter(Boolean)
|
||||
.map((role) => normalizeRoleRecord(role))
|
||||
.sort((left, right) => left.label.localeCompare(right.label, "de-DE"));
|
||||
}
|
||||
|
||||
export async function getRoleMap(redis) {
|
||||
const roles = await listRoles(redis);
|
||||
return new Map(roles.map((role) => [role.name, role]));
|
||||
}
|
||||
|
||||
export async function saveRole(redis, role, previousName = "") {
|
||||
await ensureDefaultRoles(redis);
|
||||
|
||||
const existingName = normalizeRoleName(previousName || role?.name);
|
||||
const payload = normalizeRoleRecord(role);
|
||||
const current = safeParse(await redis.get(`role:${existingName}`));
|
||||
|
||||
if (current?.isSystem && existingName !== payload.name) {
|
||||
throw new Error("Systemrollen können nicht umbenannt werden.");
|
||||
}
|
||||
|
||||
const merged = normalizeRoleRecord({
|
||||
...(current || {}),
|
||||
...payload,
|
||||
name: current?.isSystem ? existingName : payload.name,
|
||||
isSystem: Boolean(current?.isSystem || payload.isSystem),
|
||||
updatedAt: new Date().toISOString(),
|
||||
});
|
||||
|
||||
if (existingName && existingName !== merged.name) {
|
||||
await redis.del(`role:${existingName}`);
|
||||
await redis.srem(ROLE_SET_KEY, existingName);
|
||||
}
|
||||
|
||||
await redis.set(`role:${merged.name}`, JSON.stringify(merged));
|
||||
await redis.sadd(ROLE_SET_KEY, merged.name);
|
||||
return merged;
|
||||
}
|
||||
|
||||
export async function listSelections(redis, user) {
|
||||
const ids = await redis.smembers(SELECTION_SET_KEY);
|
||||
const items = await Promise.all(ids.map((id) => redis.get(`selection:${id}`)));
|
||||
|
||||
Reference in New Issue
Block a user