Kein fertiger Stand, sondern ein Zwischenstand: Hier wurde im September 2026 angefangen, die App auf „Kurbeler" umzubenennen — Wortmarke, Icons, brand.ts, Benachrichtigungen, das Benutzerverzeichnis. Weitergegangen ist es dann im eigenen Repo `~/Dokumente/Projekte/kurbeler`, das die vollständige Historie dieses Repos mitgenommen hat. Committet wird das hier nicht, weil es gebraucht würde, sondern damit es nicht als Haufen unversionierter Dateien im Arbeitsverzeichnis verrottet. Wer in zwei Jahren nachsieht, findet so eine Geschichte statt eines Rätsels — und das Stammtisch-Wappen, das nur hier je existiert hat. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LTw6xVYgjMy9AQtfs1Gdyu
74 lines
2.7 KiB
JavaScript
74 lines
2.7 KiB
JavaScript
/// <reference path="../pb_data/types.d.ts" />
|
|
|
|
// Das Benutzerverzeichnis des Superadmins.
|
|
//
|
|
// GET /api/directory/users — alle Konten der App, nur Superadmin
|
|
//
|
|
// Warum eine eigene Route und keine Collection-Regel? Wegen der E-Mail.
|
|
// Konten entstehen mit `emailVisibility: false`, PocketBase gibt die Adresse
|
|
// damit nur ihrem Inhaber heraus — und ein Verzeichnis ohne Adressen wäre
|
|
// eine Liste von Namen, in der niemand ein Konto wiedererkennt. Das Flag
|
|
// umzulegen wäre eine Zeile und zugleich ein Loch: `users.listRule` gilt
|
|
// appweit (`@request.auth.id != ""`), jede angemeldete Person läse danach
|
|
// jede Adresse, quer über alle Teams.
|
|
//
|
|
// Der Hook liest sie stattdessen serverseitig und gibt sie nur dem einen
|
|
// heraus, der sie sehen darf.
|
|
//
|
|
// Jeder Handler holt sich seine Helfer selbst per require(): PocketBase führt
|
|
// Route-Handler in einer eigenen JS-Laufzeit aus, der Dateikopf hier ist dort
|
|
// nicht sichtbar.
|
|
|
|
// --- Alle Konten der App ----------------------------------------------------
|
|
//
|
|
// Das Verzeichnis des Superadmins. Es führt Konten, nicht Fahrer: Ein Fahrer
|
|
// gehört einem Team und wird dort verwaltet, ein Konto gehört niemandem außer
|
|
// sich selbst — und genau deshalb kann es auch nur hier fallen.
|
|
//
|
|
// `owns` sagt, welche Teams das Konto besitzt. Die Oberfläche braucht das,
|
|
// bevor sie den Löschknopf anbietet: `teams.owner` ist eine Pflichtrelation
|
|
// ohne cascadeDelete, ein Löschen liefe sonst in einen Datenbankfehler statt
|
|
// in einen Satz, den jemand lesen kann.
|
|
|
|
routerAdd('GET', '/api/directory/users', (e) => {
|
|
const lib = require(`${__hooks}/directory_lib.js`)
|
|
|
|
const user = e.auth
|
|
if (!user || !lib.isSuperadmin(e.app, user)) {
|
|
throw new ForbiddenError('Nur für Superadmins.')
|
|
}
|
|
|
|
const teams = lib.allTeams(e.app)
|
|
const records = e.app.findAllRecords(lib.USERS)
|
|
const out = []
|
|
|
|
for (let i = 0; i < records.length; i++) {
|
|
const u = records[i]
|
|
const memberOf = []
|
|
const owns = []
|
|
|
|
for (let t = 0; t < teams.length; t++) {
|
|
const team = teams[t]
|
|
if (lib.ownerId(team) === u.id) {
|
|
owns.push(team.getString('name'))
|
|
}
|
|
if (lib.isMember(team, u.id)) {
|
|
memberOf.push(team.getString('name'))
|
|
}
|
|
}
|
|
|
|
out.push({
|
|
id: u.id,
|
|
name: u.getString('name'),
|
|
email: u.getString('email'),
|
|
verified: u.getBool('verified'),
|
|
superadmin: u.getBool('superadmin'),
|
|
created: u.getString('created'),
|
|
avatar: u.getString('avatar'),
|
|
teams: memberOf,
|
|
owns: owns,
|
|
})
|
|
}
|
|
|
|
return e.json(200, { users: out })
|
|
}, $apis.requireAuth())
|