Members
(constant) ACCOUNTING_TARGETS
Accounting target objects (normalized transition, flag-gated).
- Source:
(constant) ACCOUNT_FIELD
Konten gehoeren dazu wie Adresse/E-Mail/Telefon: ein geteiltes Konto, das die
Quelle nicht mehr fuehrt, verschwindet bei den Geschwistern. Nur der Vergleich
laeuft anders — IBANs liegen verschluesselt vor, siehe `accountsEqual`.
- Source:
(constant) AGE_UPDATE_STATS_PREFIX
Key-Präfix für die ageUpdate-Stats (gemeinsam von Bot + members-back genutzt).
Per Env überschreibbar (`AGE_UPDATE_STATS_PREFIX`), damit parallele Umgebungen
(z.B. Test-members-back neben Produktion) in SEPARATE Redis-Keys lesen und die
Produktions-Statistik nicht mit Test-Läufen vermischen. Produktion nutzt den
Default 'ageUpdate:stats:' (= Key-Aufbau des ageUpdate-SystemBots in basic-bots).
- Source:
(constant) ALLOWED_ICON_MIME
Apps und Domains laufen über `registryService`; `service.js` bleibt als
**Vault-Adapter** im Spiel (Lesefallback während der Umstellung) und für
**Mail**.
Die Validierung liegt bewusst auch im `registryService`: sie muss gegen
denselben Bestand prüfen, in den sie schreibt. Eine Fassung, die noch den
Vault-Bestand las, sah in der Datenbank längst aufgelöste Domains nicht — eine
Domain ließ sich damit zweimal vergeben.
`registryService` entscheidet selbst über `REGISTRY_READ_MODE`, ob er die DB
oder (bei `vault`) den Vault bedient. Die HTTP-Oberfläche bleibt in beiden
Fällen identisch — genau das macht `admin` zum Abnahmewerkzeug.
- Source:
(constant) APP_BASE_DOMAIN_DEFAULT
Basis-Domain, aus der interne Hosts gebildet werden (`APP_BASE_DOMAIN`).
(constant) BYTEA_COLUMNS
Known binary/bytea columns.
- Source:
(constant) CAST
UID-Spalten als `UUID-…`, `Data` als Objekt (§ registryTypes: Cast-Regeln)
(constant) CREATE_APP_RELEASE
`AppKey` ist die App-Kennung aus dem Registry (`Data.appId` des `app`-Objekts),
nicht eine UID: die Registry vergibt `appId` als stabilen Schlüssel
(§ registryTypes), und genau er adressiert ein Release über Organisationen
hinweg. Die App-Objekte selbst bleiben in `ObjectBase`.
(constant) CREATE_ORG_RELEASE_OVERRIDE
`(AppKey, OrgUID)` als Primärschlüssel: eine Organisation kann pro App nur auf
**ein** Canary-Release zeigen. Genau eine Zeile ist damit der Canary, und ein
Rollback ist ein `DELETE`.
`OrgUID` folgt dem Haus-Format `BINARY(16)` und wird über `U_UUID2BIN(?)`
adressiert (41-stellige `UUID-…`-Form, § registryTypes: Cast-Regeln).
`AddedAt` steht ohne `ON UPDATE` — es ist der Zeitpunkt, ab dem der Canary
gilt, und soll sich beim Lesen nicht verändern.
(constant) DATA_BUCKET
Bucket for org data/manifest assets (PWA icons, manifests, etc.)
- Source:
(constant) DOMAIN_TYPE_INTERNAL
Eine Domain trägt **zwei** unabhängige Aussagen. Sie lagen früher in einem
einzigen Feld: in Vault stand `{"kpe.de":"verified","ct":"internal"}` — eine
Domänenart neben einem Prüfstand. Das ging nicht auf, denn es sind zwei
Achsen:
| Achse | Werte | Frage |
|---|---|---|
| `type` | `internal` \| `external` | Wo liegt der Host? |
| `status` | `pending` \| `verified` | Ist er nachgewiesen? |
In einem Feld führte das zu einem stillen Widerspruch: die Oberfläche bot
`internal`/`external` an, der Bestand enthielt `verified`, und
`validateDomains` wies damit **den eigenen Bestand** als ungültig zurück.
Getrennt ist jede Achse für sich prüfbar — und der DNS-Nachweis hat einen
Platz, ohne ein dritter „Typ" zu werden.
(constant) DRY_RUN
Set to false to actually write changes
(constant) DRY_RUN
Set to false to actually write changes
(constant) ENTRY_SPLIT_REASON
`memberALinkDecision` flags a multi-list `entry` with this prefix. Those are the
objects the link repair must not decide — the entry has to be split, not trimmed.
(constant) EXTERNAL_RE
extern: mindestens zwei Labels, gültige TLD
(constant) FILTER_LINK_TYPE
Filter-Objekte tragen ihre Zielbindung generisch als `list`-Link.
(constant) FILTER_TYPES_SQL
Die Filter-Typen, die Rechte vergeben.
(constant) GENERIC_VISIBLE_TARGETS
The full set of target types handled by the generic visible/changeable path.
- Source:
(constant) ICON_EXT
MIME type → extension für akzeptierte Icon-Formate
(constant) ICON_EXT
MIME type → file extension map for accepted icon formats
- Source:
(constant) INTERNAL_RE
intern: Kleinbuchstaben, Ziffern, Bindestriche — kein führender/letzter Bindestrich
(constant) JSON_COLUMNS
Known longtext JSON columns.
- Source:
(constant) KNOWN_ACTIONS
Actions that function templates may grant (extend as new rights are added).
- Source:
(constant) LEGACY_GENERIC_TARGETS
Legacy generic list-type targets that already used the visibilityList path.
- Source:
(constant) LINK_TYPES
Accepted `linkType` values in requests (legacy alias for the share mode).
(constant) LINK_TYPES
Link-Typen für das App-Registry (Links.Type).
`app` selbst ist kein Link-Typ — eine App wird nicht verlinkt, sie ist das
Ziel. Domains und Assets hängen über Links an der App.
(constant) LINK_TYPE_APP_ASSET
Verbindet eine **App** (als Link-`UID`) mit einem **Asset** (`UIDTarget`).
(constant) LINK_TYPE_APP_DOMAIN
Verbindet eine **App** (als Link-`UID`) mit einer **Domain** (`UIDTarget`).
Richtung ist bewusst „von der App weg" — und damit **gleich** wie bei
`appAsset`. Ein gemischtes Modell (App→Asset, Domain→App) hätte jede Abfrage
zu einer Frage der Erinnerung gemacht. Der Preis ist eine Umkehrung beim
Auflösen: „welche App hängt an diesem Host" muss über den `UIDTarget` der
Domain suchen, nicht über ihren `UID`.
(constant) MAIL_THROTTLE_MS
Mindestabstand zwischen zwei Fehler-Mails.
Ein Fehlersturm darf kein Mailsturm werden: `errorLogger` wird pro
fehlgeschlagenem Request gerufen, und ein einziger kaputter Aufrufpfad kann
das im Sekundentakt ausloesen.
- Source:
(constant) MAX_PENDING_MESSAGES
Obergrenze pro Client, damit eine sehr lange Trennung nicht unbegrenzt waechst.
- Source:
(constant) MIGRATION_API
API passed into each migration's migrate() function.
Migrations can use SQL queries, transactions, or pure JS logic.
(constant) OBJECT_IN_ORG_PARAMS
The number of `?` placeholders `objectInOrgSql` expects.
- Source:
(constant) OBJECT_TYPES
Objekttypen für das App-Registry (ObjectBase.Type)
(constant) OBJ_TYPE_APP
Eine App (Auslieferungseinheit). `UIDBelongsTo` → Organisation.
(constant) OBJ_TYPE_APP_ASSET
Ein App-Artefakt (Icon, Favicon, …). `UIDBelongsTo` → App.
(constant) OBJ_TYPE_APP_DOMAIN
Eine Domain/Host-Zuordnung. `UIDBelongsTo` → Organisation.
(constant) PROJECT_LINK_TYPES
Link types that connect a share with its project. The target model has exactly
one (`memberA`); `member` is tolerated while old rows exist. The owner link
(share -> person) uses the same type but a different target type (person), so
it is never matched by these predicates.
(constant) PROJECT_LINK_TYPES_SQL :const
Type:
- const
(constant) PROJECT_TARGETS
Project scope (new feature, always active).
- Source:
(constant) PUBLIC_BUCKET
Bucket for publicly readable assets (app icons, etc.)
- Source:
(constant) PWA_ICON_SIZES
PWA-Icon-Varianten im data bucket — Pfade, die der Static-Server erwartet
(constant) PWA_ICON_SIZES
Standard PWA icon variants written to the data bucket under manifests path
- Source:
(constant) READ_ONLY_MODES
Stored `metadata.mode` values that mean "read only". `reference` is the legacy spelling.
(constant) REBUILD_CLEANUP_EXCLUDE
Was der Personen-Rebuild **loeschen** darf.
Muss genau die Menge sein, die `personListRebuildAccess` danach wieder
aufbaut — sonst loescht der eine Lauf, was der andere nicht zurueckholt.
Genau das war der Fehler: die Loesch-Menge stand auf den Legacy-Typen fest,
waehrend die Wiederaufbau-Menge um `project` und die Share-Typen gewachsen
ist. Ein Projekt/ein Share verlor damit seine Personen-Zeile, ohne dass sie
neu entstand.
Accounting-Ziele sind ausgenommen, solange die normalisierte Ueberfuehrung
laeuft — sie werden getrennt gerechnet.
- Source:
(constant) RECONNECT_RETENTION_MS :number
Schonfrist fuer getrennte Clients.
Beim `disconnect` wird der Client nicht mehr sofort geloescht, sondern als
getrennt markiert und fuer diese Dauer behalten. Meldet er sich in der Zeit
mit derselben `?id=` neu an (Socket.IO behaelt die Query bei), bekommt er die
waehrend der Trennung aufgelaufenen Nachrichten in Reihenfolge nachgeliefert.
Das deckt den Normalfall ab (Tab kurz offline, Reconnect innerhalb von
Sekunden). Nach Ablauf raeumt sweepDisconnectedClients() auf.
Type:
- number
- Source:
(constant) RESERVED_PREFIXES
Präfixe, die eine Organisation nicht belegen darf.
Sie liegen im System-Namensraum: `admin`, `api`, `auth` … würden mit
Infrastruktur-Hosts verwechselt, die von der Plattform selbst vergeben
werden. Eine Organisation, die `admin` beansprucht, bekäme
`admin.{app}.commtool.org` — und damit einen Host, der wie die
Administrationsumgebung aussieht.
(constant) RETENTION_YEARS
MariaDB temporal history guarantee (contract: 13 years).
- Source:
(constant) RUNNER_MODES :const
Type:
- const
(constant) RUNNER_MODES :const
Type:
- const
- Source:
(constant) SCHEMA_VERSION
Common HTTP envelope for the new Members service endpoints (Projects, Shares,
Snapshots, Event replay). See 080-Workspaces/018-Cross-Service-Contracts.mdx.
Success: { success: true, result, request_id, schema_version }
Error: { success: false, error: { code, message, details }, request_id, schema_version }
Legacy routes keep their own envelope; only the new endpoints use this one.
- Source:
(constant) SENSITIVE_PATTERNS
Regex patterns for detecting sensitive information in strings
- Source:
(constant) SHARED_FIELDS
Kontaktfelder, deren Eintraege mit der Familie geteilt werden.
- Source:
(constant) SHARE_IS_BASE_SQL
SQL-Ausdruck: ist die Zeile selbst die Basis (kein Ableger)?
(constant) SHARE_PROJECT_MAP
Zuordnung Share -> Projekt, tolerant gegen **beide** Link-Richtungen
(alt: Projekt -> Share, neu: Share -> Projekt).
(constant) SHARE_ROOT_SQL
SQL-Ausdruck: der **Root** (Basis-Share) einer Share-Zeile — die
Index-Identitaet des Repos (`UIDBelongsTo`-Kette bis zur Selbstreferenz).
Nur **eine** Vererbungsstufe ist vorgesehen: Basis ist die Zeile, wenn
`UIDBelongsTo` leer ist, auf die eigene UID zeigt **oder** auf kein
Share-Objekt zeigt (Altbestand: dort stand die Organisation). Sonst ist
`UIDBelongsTo` die Basis.
Bewusst in SQL: `binary(16)`-Werte sollen nicht als Buffer/Hex-String durch
JS-Vergleiche laufen.
(constant) SHARE_TARGETS
Share scope. Shares tragen keine eigenen Filter: sie **spiegeln** den
Filter-Satz ihres Projekts (./../../RouterProject/projectShare/access.js) und
werden ueber denselben Rebuild materialisiert. Sie muessen deshalb im
generischen Ziel-Set stehen — sonst wuerde ein Personen-Rebuild die
Share-Zeilen der Person nicht nachziehen.
- Source:
(constant) SHARE_TYPES
Enum-Werte der Share-Objekte in `ObjectBase.Type`.
(constant) SHARE_TYPES_SQL
SQL-Liste der Share-Typen.
(constant) SHARE_TYPES_SQL
Enum-Werte der Share-Objekte in `ObjectBase.Type`.
(constant) SHARE_TYPES_SQL
Enum-Werte der Share-Objekte in `ObjectBase.Type`.
(constant) SHARE_TYPES_SQL
Enum-Werte der Share-Objekte in `ObjectBase.Type`.
(constant) TABLES
Tables to compress with their entity keys (columns that define a unique entity)
(constant) TIMESTAMP_COLUMNS
Known timestamp columns that need timestamptz handling.
- Source:
(constant) TRIGGER_DDL
Die Trigger-DDL selbst — **eine** Quelle fuer Migration und Test-Fixtures,
damit beide nie auseinanderlaufen.
Zwei Fallstricke stecken hier drin:
- `SIGNAL ... SET MESSAGE_TEXT = CONCAT(...)` ist **nicht** erlaubt
("Undeclared variable: CONCAT"): die SET-Klausel von SIGNAL akzeptiert nur
Literale oder Variablen, keine Ausdruecke. Deshalb erst die Meldung per
`SET` (dort sind Funktionen erlaubt) in eine Variable schreiben und diese
an SIGNAL uebergeben. So bleibt die Objekt-UID in der Fehlermeldung und
der Verursacher ist ohne Nachfrage im Log identifizierbar.
Die Meldung ist 85 Zeichen — unter dem Limit von 128 fuer MESSAGE_TEXT.
- `UIDTarget <> NEW.UIDTarget` ist Pflicht. Der Trigger feuert *vor* der
Duplikatspruefung, ein zweiter `INSERT IGNORE` **desselben** Links (heute
ein stiller No-Op) wuerde sonst 1644 werfen und idempotente Re-Inserts an
~20 Stellen brechen.
(constant) TRIGGER_NAME
Name des Triggers — auch fuer die Idempotenz-Pruefung.
Exportiert, damit Test-Fixtures die Invariante **befristet** ausschalten
koennen, wenn sie Altbestand simulieren muessen. Siehe
`src/__tests__/.helpers/memberAInvariant.js`.
(constant) TRUTHY
Query parameter values that switch from report-only to repair mode.
(constant) TRUTHY
Query parameter values that switch from report-only to repair mode.
(constant) UUID_COLUMNS
Known binary(16) columns that need uuid conversion.
- Source:
(constant) UUID_STRING_RE
`UUID-`-präfixierte Form, wie sie `U_UUID2BIN()` und die JS-Casts erwarten
(constant) addFamilyAction
Handles the `family` and `familyB` add actions.
Fired when a family member is added to a family and the organisation uses
family-based fees. Recalculates membership fees and updates family indices
for the target family.
(constant) addFamilySyncAction
Handles the `familySync` add action.
Syncs addresses, phone numbers, email addresses, and accounts between
family members when a family-shared contact detail is created or modified.
(constant) addFamilyUpdate
Adds family updates to a new member when they join a family
Synchronizes family data (address, email, phone, accounts) from existing family members
to the newly added family member.
- Source:
(constant) addFilter
Add a filter to a list
- Source:
(constant) addFilter
Adds a filter to a target and executes the filtering logic
(constant) addFilterAction
Handles filter-type add actions: visible, changeable, include, exclude, intersect.
A filter object (`action.UIDObjectID`) is attached to a source (`action.UIDBelongsTo`).
The filter targets either a dlist (include/exclude/intersect) or a user (visible/changeable).
After applying the filter, the target list is flagged for WebSocket update.
(constant) addFunctionAccountingVAction
Handles the `functionAccountingV` add action.
Fired when a function template's accounting rules (accountingAccess,
accountingWriteAccess, financialMaster) are modified. Deletes and rebuilds
only the accounting filter entries for every job linked to this function template.
(constant) addFunctionAction
Handles the `function` add action.
Fired when a function template is created or modified with respect to its
qualification requirements. Re-qualifies all jobs based on this template.
(constant) addFunctionVAction
Handles the `functionV` add action.
Fired when a function template is modified with respect to its visibility or
changeability requirements. Deletes and rebuilds all visibility/changeability
filter entries for every job linked to this function template, then refreshes
the holding person's access.
(constant) addGroupGuest
Erstellt eine Gastgruppe für eine Zielgruppe und übernimmt Mitglieder/Jobs als Gäste.
- Source:
(constant) addGroupToEventController
Add group to event controller
Handles PUT /:UIDevent
(constant) addGroupVisibility
Add group visibility to an event
(constant) addGuests
Fügt mehrere Gäste oder Gastgruppen zu einer Gruppe hinzu.
- Source:
(constant) addListAbo
Adds a list UID to the monitoring set of a connected client.
The UID is authorisation-checked via isObjectVisible before being
added. An unauthorised UID is silently skipped.
Passing `'quit'` or `null` clears the entire `updateAbo` Set, which is the
same effect as the frontend sending `monitor { UID: null }` during a
subscription re-sync.
Once registered, the backend will push `{ update: true, UID, timestamp }`
messages via addUpdateList / broadcastUpdate whenever the
list changes.
- Source:
(constant) addListEntryAction
Handles the `list` add action.
Fired when an entry is added to a list. Matches the entry object against
every filter defined on the list and adds it to the corresponding dlists.
(constant) addListMemberAction
Handles the `listMember` add action.
Evaluates to which dlists an object should belong and which users can
see/modify it when the object is newly created or its list membership changes.
Checks all filters and updates visibility/changeability for the relevant users.
(constant) addListVisibilityAction
Handles the `listVisibility` add action.
Fired when a visibility or changeability filter defining access for a
list/dlist/email/event/eventT has been modified or added.
Rebuilds the full access table for the affected list.
(constant) addMemberAction
Handles tree-structural add actions: adding an object to a group hierarchy.
Supported types: `group`, `person`, `extern`, `job`, `guest`, `ggroup`, `eventJob`.
The function:
1. Resolves the transitive group memberships of the target group (delta).
2. Applies type-specific logic (guest deduplication, group cascade, person guest-group
handling, job visibility filter creation).
3. Inserts missing `member` Links for non-person types.
4. Adds the object to any guest groups (ggroups) defined on the target.
5. Fires WebSocket and event-bus notifications.
(constant) addObjectAbo
Adds one or more object UIDs to the monitoring set of a connected client.
Each UID is authorisation-checked via isObjectVisible before being
added. Unauthorised UIDs are silently skipped.
Passing `'quit'` or `null` clears the entire `objectAbo` Set, which is the
same effect as the frontend sending `monitorObject { UID: null }` during a
subscription re-sync.
Once registered, the backend will push `{ object: true, value }` messages
via addUpdateEntry whenever the object changes.
- Source:
(constant) addPersonFilterAction
Handles the `personFilter` action type: re-evaluates dynamic list memberships
and filter entries for a person/extern whose data-relevant fields changed.
Unlike `addMemberAction` for `person`/`extern`, this does NOT insert new
member Links and does NOT publish `/add/group/…` events — the person is
already in the tree; only their list/filter placement needs to be refreshed.
(constant) addPersons
Helper function to add persons to an email
- Source:
(constant) addPersonsToEmail
Add persons to email
- Source:
(constant) addShare
Add a share to a project. Requires **changeable** on the project. Creates the
share as a base share (`UIDBelongsTo` = own UID), links it to the project
(`memberA`) and records the creator as owner (`member` link + `Visible.admin`).
Legacy `linkType` in the body (`member`/`read`) is translated into
`metadata.mode = readOnly`.
(constant) addSingleGuest
Adds a single person as guest to a group
- Source:
(constant) addTailnetRunner
Assign a runner to a tailnet network (explicit link).
- Source:
(constant) addTailnetRunner
PUT /api/tailnets/:UID/runners/:runnerUID
(constant) addUpdateEntry
Pushes an object-change payload to all clients that have registered the
object via `monitorObject` / addObjectAbo.
Iterates `clientData.objectAbo` (a `Set`) for each connected client
and emits `{ object: true, value }` to matching sockets.
Called by API routes whenever an object's data changes (e.g. after a PATCH).
- Source:
(constant) addUpdateList
Notifies clients that one or more lists have changed, with leading-edge
debouncing to prevent update storms.
**Debounce strategy (per list UID):**
- First change: sends `{ update: true, UID, timestamp }` immediately to all
subscribed clients and records the UID in `delayedUpdateTimestamps`.
- Subsequent changes within the debounce window: refreshes the timestamp
but does NOT send again immediately.
- monitorDelayedUpdates (called by a periodic interval) flushes any
entry that has been quiet for the configured debounce period.
Clients subscribed via `monitor` / addListAbo have the matching
UID in their `updateAbo` Set and receive the notification.
- Source:
(constant) addVisibility
Setzt Sichtbarkeit fuer die Filter, die an `sources` haengen, und **meldet**
die Zeilen, die dabei entstehen oder verschwinden.
- Source:
(constant) addVisibility
Add visibility filters for a location
(constant) addVisibilityFilter
Add visibility filters for event template
(constant) adminDlistCheck
Controller for GET /dlist/admin/:UID
- Source:
(constant) adminListCheck
Controller for GET /list/admin/:UID
- Source:
(constant) ageUpdate
Updates the age (`dindex` field) of persons associated with a given root UID
- Source:
(constant) ageUpdatePersons
Recalculate `dindex` (numeric age) for every person in the organisation whose
stored age differs from today's computed value.
- Source:
(constant) ageUpdateStats
Reads the ageUpdate statistics stored by the ageUpdate SystemBot (basic-bots)
in the shared Redis (last 7 nightly runs per organisation, 7-day TTL).
GET /person/ageUpdate/stats
- Source:
(constant) alternativeRoot
Alternative root finding logic when no specific organization is provided
- Source:
(constant) api :express.Express
Type:
- express.Express
- Source:
(constant) api :express.Express
Type:
- express.Express
- Source:
(constant) api :express.Express
Type:
- express.Express
- Source:
(constant) api :express.Express
Type:
- express.Express
- Source:
(constant) api :express.Express
Type:
- express.Express
- Source:
(constant) api :express.Express
Type:
- express.Express
- Source:
(constant) api :express.Router
Type:
- express.Router
- Source:
(constant) api :express.Express
Type:
- express.Express
- Source:
(constant) api :express.Express
Type:
- express.Express
- Source:
(constant) api :express.Express
Type:
- express.Express
- Source:
(constant) api :express.Express
Type:
- express.Express
- Source:
(constant) api :express.Express
Type:
- express.Express
- Source:
(constant) api :express.Express
Type:
- express.Express
- Source:
(constant) api :express.Express
Type:
- express.Express
- Source:
(constant) api :express.Express
Type:
- express.Express
- Source:
(constant) api :express.Express
Type:
- express.Express
(constant) apiError
Create an ApiError carrying an HTTP status and a machine-readable code.
- Source:
(constant) appHostFor
Baut den Host, unter dem eine App erreichbar ist.
Der **Punkt entscheidet**, und das ist keine eigene Erfindung: dieselbe Regel
steht in `shared-auth` (`organizationDomains.js`) und im Portal-Bot
(`portal.controller.js` → `buildAppUrl`). Sie wird hier nur nachgebildet,
damit Routing und Anmeldung nicht auseinanderlaufen.
| `domain` der App | Ergebnis |
|---|---|
| `db.app.kpe.de` (mit Punkt) | `db.app.kpe.de` — eine Kunden-Domain gilt, wie sie steht |
| `sjm` (ohne Punkt) | `sjm.admin.app.commtool.org` — Präfix, App-Kennung, Basis |
(constant) appIdFromData
Liest die `appId` aus dem `Data`-Feld eines App-Objekts.
Die `appId` (z.B. `member.app`) ist **keine** UID: sie ist der logische,
menschenlesbare Schlüssel aus Vault (`orgas/data/{orgId}/apps` → Map-Key) und
wird auch in der API (`admin`) als Map-Key verwendet. Sie liegt deshalb als
Feld in `Data` und nicht im Primärschlüssel (§6.1 der Planung).
(constant) appTitles
Baut die Objekt-Titel-Felder aus `appId` und Anzeigetitel.
`Title`/`Display` tragen den **Anzeigetitel**, `SortName` dessen
Kleinschreibung — die App-ID liegt in `Data.appId`. Den Schlüssel in `Title`
zu legen wäre verlockend (er wäre dann indexiert), würde aber Anzeigename und
Identität vermischen: ein umbenannter Titel hätte den Lookup zerbrochen.
(constant) applyFilterToEmail
Apply filter to email
- Source:
(constant) applyVisibilityAndMatching
Applies visibility records and object-list matching for supported types.
(constant) approveRegistration
Approve registration: bind pending code to org (already tenant-bound at create).
Optional publicKey from connector may be attached before exchange.
(constant) approveRegistration
POST /api/runners/registrations/:code/approve
- Source:
(constant) authenticateRunner
Verify runner request: credentialId + signature over timestamp+method+path+bodyHash.
Credential liegt im Companion-Slot (`Member.lookup_key`), der
publicKey in `ObjectBase.Data`; revoked Runner werden über den
ObjectBase-Status abgewiesen.
(async, constant) authorizeUser
The function retrieves all jobs associated with the user from the database,
applies the provided filter to these jobs, and determines if the user has
the necessary permissions to manage achievements for a person. This is used
to control who can add, modify, or delete achievement records based on their
job roles and permissions.
- Source:
(constant) broadcastUpdate
Immediately emits a list-update notification to all clients monitoring `UID`,
bypassing the debounce logic of addUpdateList.
Use this when the update must arrive without delay (e.g. after a server-side
transaction completes and the client needs to refresh right away).
Emits `{ update: true, UID, timestamp }` via the `update` event.
- Source:
(constant) buildDeltaVisual
Merges `delta` and `secondLevel` into a deduplicated `deltaVisual` array.
(constant) buildEventPayload
Build the wire payload for an event. The payload keeps the existing members
event contract: `{ data, UIDorga, backDate, timestamp }`.
- Source:
(constant) buildMemberAConsistencyReport
Builds the report and, with `fix`, removes the surplus links.
Exported separately from the HTTP handler so the same logic can be run against a
database directly (dry run on a production copy) without going through the API.
(constant) buildTranslateObject
Recursively builds a translation object by extracting translatable properties
(content, label, placeholder, html) from a serialized input object and its nested
children. Ensures that existing entries in the translation object are not overridden.
- Source:
Example
const serialized = {
component1: {
paras: { content: "Hello", label: "Greeting", html: "<p>Welcome</p>" },
children: [
{ component2: { paras: { content: "World", label: "Planet" } } }
]
}
};
const translateObject = {};
buildTranslateObject(serialized, translateObject);
console.log(translateObject);
// { Hello: "Hello", Greeting: "Greeting", "<p>Welcome</p>": "<p>Welcome</p>", World: "World", Planet: "Planet" }
(constant) bulkAddGuests
Bulk add guests to a group
- Source:
(constant) checkEmailAdmin
Check if user is email admin
- Source:
(constant) checkExpire
Middleware to check file expiration parameters
- Source:
(constant) checkFamilies
Checks family consistency
- Source:
(constant) checkFileVisible
Middleware to check file visibility permissions
- Source:
(constant) checkGroupAdmin
GET /admin/:UID — check whether the current user has admin rights for a group.
- Source:
(constant) checkGroupAdminStatus
Checks whether the current user has admin rights for the specified group.
- Source:
(constant) checkIsAdmin
Check if user is admin
(constant) checkIsMember
Check whether a specific person is a (current) member of a group.
- Source:
(constant) checkIsMemberController
Check whether a person is currently a member of a group.
- Source:
(constant) checkRoot
Check and set the organization root for the current request
Uses Keycloak organization as single source of truth
Answers the request itself (does not call `next`) when the organization context is unusable:
- `400` with `missingOrga: true` - `x-organization`/`x-orga` missing or not a UUID
- `403` - `x-user-uid` was sent, but that user is not valid for the organization
- `500` - the organization config could not be loaded
- Source:
(constant) cleanFilters
Cleans unused filters
- Source:
(constant) cleanupSocketData
Clears all in-memory state managed by this module.
Empties both clientDataStore and delayedUpdateTimestamps.
Intended for use during a graceful server shutdown or in test teardown to
prevent state leaking between test cases.
Does NOT close existing socket connections — call `io.close()` separately
if a full shutdown is required.
- Source:
client
- Source:
(constant) clientDataStore :Map.<string, any>
Type:
- Map.<string, any>
- Source:
(constant) compressHistory
Compress history for all configured system-versioned tables.
GET /maintenance/compressHistory
Query params:
?apply=true — actually create _new tables and compress (default: dry-run, no writes)
?swap=true — also perform the RENAME TABLE swap (requires apply=true)
?chunkSize=N — rows per chunk (default: 50000)
configLoadPromise
Gemeinsames Init-Promise: `loadAllConfigs()` darf nur einmal gleichzeitig laufen.
Vorher guardete `getConfig()` nur mit `if(!configs.admin)` (Check-then-Act). Da ein
Durchlauf Dateien aus S3 liest und DB-Queries fahrt, sahen parallele Requests denselben
unfertigen Zustand, starteten jeweils einen eigenen Durchlauf, und der zweite Lauf
leerte `configs[app] = {}` mitten im ersten. Ergebnis waren sporadische "Table 'Member'
doesn't exist" bzw. unvollstaendige `mergedConfigs`.
Das Promise wird memoisiert: Boot (server.js) und Lazy-Init aus `getConfig()` teilen
sich genau einen Durchlauf. Nach Erfolg bleibt es bestehen — ein erneuter Voll-Durchlauf
ist nicht noetig, Config-Aenderungen laufen ueber `remergeConfig()` (siehe
`triggerConfigUpdates`). Nur nach einem Fehler wird zurueckgesetzt, damit ein Retry
moeglich bleibt.
- Source:
(constant) configLoggers
Initialize all rotating file streams using env configuration.
- Source:
connecting :Promise.<boolean>
Type:
- Promise.<boolean>
- Source:
corroborated
The object also holds a `member`-family link to this target — the tie
breaker when the `ValidFrom` comparison cannot decide (see
`memberALinkDecision.js`).
corroborated
The object also holds a `member`-family link to this target. Surfaced so a reader
of the report can see WHY a `ValidFrom` tie was decided the way it was.
(constant) createAction
Create or update an action from a template and trigger
- Source:
(constant) createActionFromTemplate
Create an action instance from a template and trigger, without Express req/res.
Mirrors the core logic of createAction in controller.js but takes plain
parameters instead of Express objects. No template rendering, no events.
- Source:
(constant) createBirthdayFilter
re-assesses birthday filter
- Source:
(constant) createCredential
Create a credential: generates an ed25519 key pair and persists it entirely
in Vault. `ref` (= credentialsRef) is `{orgId}/{name}` for org scope and
`{userUID}/{name}` for user scope.
(constant) createDB
Create a new database
Endpoint to initialize a new database schema.
Protected by employee rights.
(constant) createEventController
Create event controller
Handles PUT /:group/:template
- Source:
(constant) createFreeJobController
Create free job without template
Handles PUT /:member/:event
(constant) createGuestGroup
Create guest group from existing group
- Source:
(constant) createJobWithTemplateController
Create job with template
Handles PUT /:member/:event/:function
(constant) createNewExtern
Insert a brand-new extern member into ObjectBase, Member, Links, and Visible.
Fires tree queue and starts a non-blocking AI-embedding job.
- Source:
(constant) createNewPerson
Insert a brand-new person into ObjectBase, Member, Links, Visible,
create the initial family record, and publish the join event.
Note: session values are passed explicitly so this function has no HTTP dependency.
- Source:
(constant) createOrChangeFamily
Create or change family membership (PUT /:famMember/:member)
Handles the creation or assignment of a family for a member.
Can create new families or assign members to existing families.
Supports rebate mode for family fee calculations.
- Source:
(constant) createOrUpdateAchievementTemplate
Creates or updates an achievement template
(constant) createOrUpdateEmail
Create or update an email
- Source:
(constant) createOrUpdateEventTemplate
Create or update an event template
(constant) createOrUpdateEventTemplateController
Controller for creating or updating an event template (PUT)
(constant) createOrUpdateExtern
PUT /:group — Create or update an extern in a group.
- Source:
(constant) createOrUpdateFilter
Create or update filter (PUT /:source/:target/:type)
* @param {ExpressRequestAuthorized} req - Express request object
* @param {ExpressResponse} res - Express response object
- Source:
(constant) createOrUpdateGroup
Creates a new group under a parent, or updates an existing group by UID.
When the group UID from the request body does not yet exist in the DB a new
group is inserted as a child of UIDparent. When it already exists (update
path) its Member data is refreshed and sister-group links are re-evaluated.
- Source:
(constant) createOrUpdateJobTemplateController
Create or update job template
Handles PUT /:UIDeventTemplate
(constant) createOrUpdateLocation
Create or update a location
(constant) createOrUpdateLocationController
Controller for creating or updating a location
(constant) createOrUpdatePerson
Creates or updates a person entry in the database
- Source:
(constant) createProject
Create a new project.
Mirrors the list/dlist pattern (`Router/list/service.js`):
- `memberA` link points to the project group (owner group), not the organization
- `member` link points to the creating user
- creator gets `admin` in Visible
- a default visibility filter rule is created via `addVisibility`, so all
job holders of the project group (and super admins) see the project
- Source:
(constant) createRegistration
Create a one-time registration code for the current tenant. The admin may
already decide the connector's mode (`personal`|`team`|`shared`), its owner
group (`groupUID`) and a tailnet network (`tailnetUID`) — the connector
cannot know these during the credential exchange, so they are bound to the
code (Redis, transient, TTL 30 min).
(constant) createRegistration
GET /api/runners/registrations
- Source:
(constant) createRunner
Create a runner object after registration approval / exchange.
Mirrors the project/list creation pattern:
- `team`/`shared` get a `memberA` link to their owner group (team = given
group, shared = org root group)
- the creator always gets `Visible admin`
- `personal` keeps the existing `member` link to the owning user
- for `team`/`shared` a default visibility rule is created via `addVisibility`
so job holders of the group see the runner
- Source:
(constant) createTailnet
Create a tailnet network under an owner group.
Mirrors the project/list creation pattern:
- `memberA` link to the owner group
- creator gets `Visible admin`
- default visibility rule via `addVisibility` (group job holders see it)
- Source:
(constant) createdTimestamp
GET /createdTimestamp/:UID — return the creation allowed backdate timestamp (ms)
for a group object, constrained by its latest history row.
- Source:
(constant) createdTimestampCheck
Checks created timestamp to avoid something is linked to before the creation of the target
- Source:
(constant) dbErrorLogger
Logs database errors using the DB logger stream.
- Source:
(constant) dbLogger
Custom database logger
- Source:
(constant) debounceDelay :number
Type:
- number
- Source:
(constant) decideMemberALinks
(constant) delayedUpdateTimestamps :Map.<string, number>
Type:
- Map.<string, number>
- Source:
(constant) deleteAchievement
This function deletes an achievement from the database. It performs the following steps:
1. Retrieves the achievement data using the provided UID
2. Checks if the user is authorized to delete the achievement
3. Deletes the achievement and related links from the database
4. Updates the achievement queue and list membership
5. Updates the client with the changes
Authorization is based on either template-defined filters or admin status.
- Source:
(constant) deleteAchievement
Deletes an achievement from the database
- Source:
(constant) deleteAchievementTemplate
Deletes an achievement template
(constant) deleteAction
Delete an action
- Source:
(constant) deleteActionTemplate
Delete action template (DELETE /:UID)
(constant) deleteActionTemplatesForBotNotInOrgs
Delete all action Templates for a specific bot, where the organizations are not in the provided list (DELETE /bot/:botUID)
(constant) deleteCredential
Delete a credential: removes the Vault secret.
(constant) deleteCredential
DELETE /project/credential/:ref
(constant) deleteDirectory
Delete directory (DELETE /dir/:UID/:prefix)
- Source:
(constant) deleteDlistUID
Controller for DELETE /dlist/:UID
- Source:
(constant) deleteEmail
Delete an email
- Source:
(constant) deleteEmailShare
Delete email share (delegate to list share function)
- Source:
(constant) deleteEntries
Delete entries function
(constant) deleteEntry
Remove one or more persons from a static list
- Source:
(constant) deleteEntryController
Handle DELETE /list/person/:UIDlist — remove persons from a static list
- Source:
(constant) deleteEventController
Delete event controller
Handles DELETE /:UID
- Source:
(constant) deleteEventTemplate
Delete an event template
(constant) deleteEventTemplateController
Controller for deleting an event template
(constant) deleteEvents
Deletes all events
- Source:
(constant) deleteExtern
DELETE /:UID — Delete an extern and all associated data.
(constant) deleteExternById
Delete an extern and all its child objects (guest, job, entry) plus associated links.
- Source:
(constant) deleteFilter
Delete filters by source, target and type
- Source:
(constant) deleteFiltersByType
Delete filters by source, target and type (DELETE /:source/:target/:type)
- Source:
(constant) deleteGroup
DELETE /:UID — delete a group if it has no remaining members.
- Source:
(constant) deleteGroupById
Deletes a group if it has no remaining members.
Responds with the list of remaining members if deletion is blocked.
- Source:
(constant) deleteGroupFromEventController
Delete group from event controller
Handles DELETE /:UIDevent/:UIDgroup
(constant) deleteGuest
Delete a guest or guest group
- Source:
(constant) deleteJob
Deletes a job from the database.
- Source:
(constant) deleteJobController
Delete job
Handles DELETE /:UID
(constant) deleteJobTemplateController
Delete job template
Handles DELETE /:UID
(constant) deleteLanguageFileController
Delete a language file from the object store.
Handles DELETE /:app/:filename and DELETE /:app/:UIDroot/:filename
(constant) deleteLeaderJobsController
Delete all jobs for person in event
Handles DELETE /leader/:UIDevent/:UIDperson
(constant) deleteLinks
Delete links for filters
(constant) deleteList
Delete a list/dlist
- Source:
(constant) deleteListUID
Controller for DELETE /list/:UID
- Source:
(constant) deleteLocation
Delete a location
(constant) deleteLocationController
Controller for deleting a location
(constant) deleteMultipleFiles
Delete multiple files (DELETE /:UID)
- Source:
(constant) deletePortalPending
Delete pending portal link and token payload.
- Source:
(constant) deletePortalPendingController
- Source:
(constant) deletePrivateRailFiles
Delete private rail files (DELETE /rail/:UID/:prefix/:filename)
- Source:
(constant) deleteProject
Delete a project (requires admin on the project).
**Nur bei leeren Root-Shares.** Haengt an einer Basis dieses Projekts ein
Ableger in einem anderen Projekt, bricht der Aufruf mit `409
PROJECT_HAS_DEPENDENT_SHARES` ab und nennt die betroffenen Repositories samt
ihrer abhaengigen Projekte. Der Aufrufer muss sie vorher aufloesen: die
Zuordnung mit `successor` entfernen (der Root wandert dann in eines der
abhaengigen Projekte) oder den Ableger loesen. Danach ist der Root leer, und
das Projekt kann weg. Die Alternative waere, die Basis still stehen zu lassen
— dann haengt sie projektlos im Raum, und niemand sieht, dass sein Repository
woanders verwurzelt ist.
- Source:
(constant) deleteProject
DELETE /project/project/:UID
(constant) deletePublicFiles
Delete public files (DELETE /public/:expire/:prefix/:filename)
- Source:
(constant) deletePublicRailFiles
Delete public rail files (DELETE /rail/:app/:prefix/:filename)
- Source:
(constant) deleteRunner
Hard-delete a runner: the topology object, its `Member` companion row,
all links and the visibility grants/filters.
`revokeRunner` is the graceful path (soft-delete, keeps the object for
audit); this is the cleanup path — e.g. for runners that are already
`revoked` and would otherwise accumulate forever.
Publishes `/remove/runner/{uid}` so runnerSync drops the registry entry
(contract 090-Runner-Events §2.3) — without it the ide-server would keep
accepting a credential that no longer exists in members.
- Source:
(constant) deleteRunner
DELETE /api/runners/:UID
Hard-delete a runner (cleanup; `revoke` is the soft path).
- Source:
(constant) deleteShare
Eine Zuordnung aus einem Projekt entfernen.
Ist das Objekt die **Basis** („Root") und haengen Ableger daran, wird **nicht
stillschweigend** die Basis behalten: der Aufrufer bestimmt mit
`successor` (`body.successor` oder `?successor=`), **welcher Ableger die neue
Basis wird** — also in welches Projekt der Root wandert. Ohne diese Angabe
antwortet der Endpunkt mit `409 SHARE_ROOT_NEEDS_SUCCESSOR` und der Liste der
moeglichen Nachfolger, damit die Oberflaeche waehlen kann.
Der Aufruf bleibt idempotent-tolerant: zeigt der `successor` nicht auf einen
Ableger dieses Roots, ist das ein `422 INVALID_SUCCESSOR`.
(constant) deleteShare
DELETE /project/project/:projectUid/shares/:shareUid
(constant) deleteSpecificFilter
Delete specific filter by UID (DELETE /:UID)
- Source:
(constant) deleteTailnet
Delete a tailnet network. Assigned runners are unlinked (no cascade to
the runners themselves — they just lose this network assignment).
- Source:
(constant) deleteTailnet
DELETE /api/tailnets/:UID
(constant) deleteUserByPerson
Remove identifyer link for one person.
- Source:
(constant) deleteUserController
- Source:
(constant) deleteUsers
Remove identifyer links for selected users.
- Source:
(constant) deleteUsersController
- Source:
(constant) deleteVisibilityFilter
Delete visibility filters for event template
(constant) dependentsOfRoots
Die **Ableger** mehrerer Basis-Shares (mit ihrem Projekt) — die Gegenfrage zu
„wo kommt der Ableger her": „welche Projekte haengen an dieser Basis?"
(constant) diffMaps
Vergleicht zwei flache Maps (appId bzw. domain → Wert) und beschreibt die
Unterschiede. Bewusst auf Schlüssel-Ebene: „Zahl stimmt, Inhalt nicht" ist
der Fall, der bei einem reinen Zähler-Vergleich durchrutscht.
(constant) disabled
Set to false (or delete this line) when ready to apply
(constant) disabled
Set to false (or delete this line) when ready to apply
(constant) dispatchRecreate
Dispatches recreate logic to the appropriate handler based on object type.
(constant) doCheckVisible
Checks the visibility of a person and retrieves related job and member data
- Source:
(constant) drainRunner
Mark runner draining — der Zusteller (ide-server) weist einem drainenden
Runner keine neuen Sessions mehr zu. Reine Topologie-Markierung.
- Source:
(constant) drainRunner
POST /api/runners/:UID/drain
- Source:
(constant) emailValues
Extract all email addresses from a Data.email array into a lowercased string list.
- Source:
(constant) ensureConfigsLoaded
Wartet, bis der Config-Bestand vollstaendig geladen ist. Race-frei und idempotent:
parallele Aufrufe haengen sich an denselben Durchlauf, spaetere Aufrufe sind ein No-Op.
- Source:
(constant) errorLogger
Logs generic application errors.
- Source:
(constant) errorLoggerRead
Logs read-specific errors (legacy entry point).
- Source:
(constant) errorLoggerUpdate
Logs update-specific errors (legacy entry point).
- Source:
(constant) eventList :express.Express
Type:
- express.Express
- Source:
(constant) exchange
POST /api/runner/v1/register/exchange
(constant) exchangeRegistration
Exchange approved/pending code + publicKey for runner credential.
Creates ObjectBase runner + `Member` companion row (lookup_key).
(constant) execDListFilter
Executes a dynamic list filter (include/exclude/intersect) on a target list
(async, constant) expireAchievements
Expires achievements based on their renewal period
This function checks all achievements linked to a membership organization (root entity)
and expires those whose renewal period has elapsed. For each expired achievement,
it updates the dindex to mark it as expired and then updates the achievement lists
for affected persons.
- Source:
(constant) extractUIDefaults
Recursively extract defaultValue entries from UIaction form field definitions.
Returns a flat object mapping field name → defaultValue.
- Source:
(constant) familyAddress
Synchronizes family-shared data (address, email, phone, accounts) across all family members
When a person/extern/family object is updated with family data, this function propagates
those changes to all other members of the same family. Handles:
- Family addresses
- Family email addresses
- Family phone numbers
- Family accounts (including familyFees accounts)
Only the fields listed in `object.Touched` are reconciled (i.e. shared entries the
source no longer carries are removed there). Without `Touched` the sync is purely
additive, as it always was.
- Source:
(constant) fetchGroup
Fetch a group record (ObjectBase + Member) by its binary UID.
Shared entry point for person and extern create/update flows.
- Source:
(constant) fetchLatestObjectValidFrom
Return the UNIX timestamp (seconds) of the most recent ObjectBase row for a UID
across all system-time history. Used to prevent backdated type-change operations
(extern → person or person → extern) from landing before existing history rows,
which would create impossible temporal ordering in the versioned table.
- Source:
(constant) fetchMemberExists
Check whether a person or extern record already exists for the given binary UID.
Shared by both person and extern create/update flows.
- Source:
(constant) filterGuestDelta
Filters out delta groups where the person/guest is already a member.
Used for `guest` and `ggroup` action types.
(constant) findDomainConflict
Sucht einen Konflikt zwischen einer Domain und dem Bestand **anderer**
Organisationen.
Verglichen wird nur die Achse `type`. Ob eine Domain verifiziert ist, ändert
nichts daran, wem sie gehört — würde der Status mitgezählt, ließe sich
dieselbe Domain zweimal vergeben, solange eine Seite den Nachweis noch nicht
erbracht hat.
Zwei Regeln:
- gleiche Art + gleicher Name → direkter Konflikt
- intern `foo` ↔ extern `foo.*` → Präfix-Konflikt (dieselbe Wurzel)
(constant) findPersonalRunnerForUser
Persönlicher Runner einer Person (mode=personal, `member`-Link → Person,
nicht revoked) in derselben Orga. Für die Session-Auflösung „mein
Personal-Runner" (Soll: Projekt-Runner → Personal-Runner des Users).
- Source:
(constant) generateMultipleUIDs
Generate multiple unique identifiers (UIDs)
(constant) generateUID
Generate a single unique identifier (UID)
(constant) geocodePlace
Forward geocode a place name to coordinates
- Source:
(constant) geocodePlaceController
Forward geocode: place name → coordinates
(constant) getAchievement
Retrieves a specific achievement by UID
- Source:
(constant) getAchievementTemplates
Gets all achievement templates for the organization
(constant) getAchievements
Gets achievements for the organization
- Source:
(constant) getAchievementsDuplicates
Gets duplicate achievements
- Source:
(constant) getAchievementsTree
Gets achievements tree structure
- Source:
(constant) getAction
Get a specific action by UID
- Source:
(constant) getActionCatalog
Katalog der von Funktionsvorlagen gewährbaren Aktionsrechte mit fachlicher
Beschreibung. Wird über `GET /api/kpe20/function/actions` an die Admin-App
ausgeliefert (Phase 3: "Die Auswahl wird aus einem Backend-Katalog geladen").
- Source:
(constant) getActionTemplate
Get specific action template (GET /:UID)
(constant) getActionTemplateForBotInOrg
Get action template for a specific bot in the current organization (GET /bot/:botUID/org)
Returns the single template linked to the bot that belongs to the requesting org.
(constant) getActionTemplates
Get all action templates for organization (GET /)
(constant) getActionTemplatesForBot
Get all action templates for a specific bot (GET /bot/:botUID)
(constant) getActionsByTemplate
Get actions by template
- Source:
(constant) getActionsByTrigger
Get actions by trigger
- Source:
(constant) getAddedListing
Retrieve entries that have been added to a list since the given timestamp.
Works for both static and dynamic lists.
The result is deduplicated and entries with `member0` link type are excluded.
Where a member appears multiple times (multiple links), their Data objects are merged.
- Source:
(constant) getAddedListingController
GET /list/added/:UID/:timestamp — entries added to a static list since timestamp
GET /dlist/added/:UID/:timestamp — entries added to a dynamic list since timestamp
- Source:
(constant) getAddedPersons
Return persons that were **added** to a group after `timestamp`.
- Source:
(constant) getAddedPersonsController
Return persons added to a group after the given timestamp.
- Source:
(constant) getAdminEventTemplates
Get all templates the user can administer
(constant) getAdminEventTemplatesController
Controller for getting administrable event templates
(constant) getAllEmailShares
Get all email shares (delegate to list share function)
- Source:
(constant) getAllEventTemplates
Get all templates for the organization
(constant) getAllEventTemplatesController
Controller for getting all event templates
(constant) getAllEventTemplatesWithData
Get all templates with data for the organization
(constant) getAllEventTemplatesWithDataController
Controller for getting all event templates with data
(constant) getAllFamilies
Get all families (GET /)
Administrative endpoint to retrieve all families.
Requires admin privileges.
- Source:
(constant) getAllFilters
Get all filters (GET /)
- Source:
(constant) getAllIdentifyers
Get all identifyer links for the current organisation.
- Source:
(constant) getAllIdentifyersController
- Source:
(constant) getAppsController
GET /kpe20/orgaSettings/apps
Returns the app registry for the current organisation.
(constant) getBulkFamilies
Bulk family retrieval (POST /families)
Retrieves family information for multiple UIDs at once.
Supports both current and historical data retrieval.
- Source:
(constant) getCachedUserData
Get user data from cache or members API
- Source:
(constant) getCollectionCounts
Get membership counts for collections.
- Source:
(constant) getCollectionsController
Controller for GET /
- Source:
(constant) getCollectionsListing
Get collections listing based on filter/query input.
- Source:
(constant) getCollectionsUIDController
Controller for GET /:UID
- Source:
(constant) getCredential
Get a single credential.
(constant) getCredential
GET /project/credential/:ref
(constant) getDlistPersonsController
Handle GET /dlist/persons/:UID — retrieve all persons in a dynamic list
- Source:
(constant) getDlistPersonsPaginatedController
Handle GET /dlist/persons/:UID (paginated) — pass through to next if no __page param
- Source:
(constant) getDlistUID
Controller for GET /dlist/:UID
- Source:
(constant) getDomainsController
GET /kpe20/orgaSettings/domains
Returns the domain settings for the current organisation.
- Source:
(constant) getEmail
Get a specific email
- Source:
(constant) getEmailPersons
Get persons in email (with pagination support)
- Source:
(constant) getEmailShare
Get email share details (delegate to list share function)
- Source:
(constant) getEmailShares
Get email shares for specific list (delegate to list share function)
- Source:
(constant) getEmailsForPerson
Get emails for a person
- Source:
(constant) getEnteredPersons
Return persons who **entered** a group (became member from extern) after `timestamp`.
- Source:
(constant) getEnteredPersonsController
Return persons who transitioned from extern to person (entered) after the timestamp.
- Source:
(constant) getEventController
Get event controller
Handles GET /:UID
- Source:
(constant) getEventForList
Get the event that a list belongs to
(constant) getEventJobsController
Get all jobs for a function template
Handles GET /eventJobs/:UID
(constant) getEventLists
Get all lists for an event with participants
(constant) getEventListsController
Controller for getting event lists with participants
(constant) getEventLog
Gets event logs
- Source:
(constant) getEventParticipantLists
Manages event participant lists
- Source:
(constant) getEventTemplateByEventUID
Get the template for an event with that UID
(constant) getEventTemplateByEventUIDController
Controller for getting the template for an event
(constant) getEventTemplateByUID
Get a specific event template by UID
(constant) getEventTemplateByUIDController
Controller for getting a specific event template by UID
(constant) getEventVisibility
Rebuilds event visibility
- Source:
(constant) getEvents
GET /events?organization=&after=&limit=
- Source:
(constant) getExcludeObjects
Gets objects that match exclude/intersect filter criteria
(constant) getExitedPersons
Return persons who **exited** (changed from person to extern) after `timestamp`.
- Source:
(constant) getExitedPersonsController
Return persons who transitioned from person to extern (exited) after the timestamp.
- Source:
(constant) getExtern
GET /:UID — Delegates to person controller (same data structure)
(constant) getExternAdmin
GET /admin/:UID — Delegates to person controller (same admin check)
(constant) getExternDuplicates
GET /duplicates/:firstName/:lastName — Delegates to person controller
(constant) getExternHistory
GET /history/:UID — Delegates to person controller (same history logic)
(constant) getFakeUser
Get fake user data for impersonation (admin only)
Uses Redis caching to avoid database queries on every API call
- Source:
(constant) getFamily
Get family information (GET /:UID)
Retrieves family data including current information or historical data for a specific year.
Supports fee calculation and historical data retrieval.
- Source:
(constant) getFamilyData
Gets family data or processes family corrections
- Source:
(constant) getFamilyListForGroup
Get family listing for a group (GET /list/:group)
Retrieves families associated with a specific group.
Supports pagination and various filtering options.
- Source:
(constant) getFamilyListing
Helper function to get family listing
Core function that builds family listings with various filtering and data options.
Supports historical data, person data inclusion, fee information, and visibility filtering.
- Source:
(constant) getFileWithPrefix
Get files with prefix (GET /:UID/:prefix/:filename)
- Source:
(constant) getFilter
Get specific filter by UID (GET /:UID)
- Source:
(constant) getFilterData
Gets filter data and source information for a filter
(constant) getFilters
Retrieves all filters for the supplied sources
(constant) getFunctionActions
GET /function/actions — Katalog der gewährbaren Aktionsrechte.
Basis für die Auswahl im ActionPermissionsSection der Admin-App (Phase 3).
(constant) getGroup
GET /:UID — fetch group details, optionally with parent and/or sibling data.
- Source:
(constant) getGroupById
Retrieves a group by UID with optional parent and sibling data.
- Source:
(constant) getGroupGuests
Get all guests from a group
- Source:
(constant) getGroupTreeGraph
Gets a Kroki graph representation of groups connected via memberA/memberS links.
- Source:
(constant) getIncludeObjects
Gets objects that match include filter criteria
(constant) getJobController
Get single job
Handles GET /:UID
(constant) getJobTemplatesController
Get all function template names and UIDs
Handles GET /:UID
(constant) getJobTemplatesWithDataController
Get all function templates with full data
Handles GET /Data/:UID
(constant) getLanguageFileController
Stream a language file to the client.
Handles GET /:app/:filename and GET /:app/:UIDroot/:filename
The organization layer is only readable for the organization of the session.
The portal routes in `http-server.js` run without `checkRoot`, so the check
has to happen here.
(constant) getList
Get list/dlist details
- Source:
(constant) getListEntry
Get entry data for a specific person in a list
- Source:
(constant) getListEntryController
Handle GET /list/entry/:UIDlist/:UIDperson — get entry data for a specific person in a list
- Source:
(constant) getListPersonsByUIDsController
Handle POST /list/persons/:UID — retrieve specific persons in a list by UID list
- Source:
(constant) getListPersonsController
Handle GET /list/persons/:UID — retrieve all persons in a static list
- Source:
(constant) getListPersonsPaginatedController
Handle GET /list/persons/:UID (paginated) — pass through to next if no __page param
- Source:
(constant) getListUID
Controller for GET /list/:UID
- Source:
(constant) getListVisibleSql
Build the SQL snippet that restricts the `Visible` table join to the
correct user / admin context.
- Super-admins: restricted to the super-admin visible user
- Org-admins: `changeable` visibility only for non-group targets
- Normal users: full visibility of their own objects
- Source:
(constant) getListVisibleSql
Get SQL for visible list filtering
(constant) getListing
List projects the user can see (visible | changeable | admin). Org admins
and bots see all projects of the organization.
Optional `group_uid` query filter: only projects whose owner group matches
(used by the project group tab).
- Source:
(constant) getListing
Get email listing (persons in email)
- Source:
(constant) getListing
Get all persons in a list with optional field selection, filtering, and temporal queries
- Source:
(constant) getListing
Retrieve members/objects belonging to a group or object identified by `UID`.
Supports siblings query, temporal snapshots (`timestamp`, `since`), data field
projections, optional grouping and filtering.
- Source:
(constant) getListing
Get listing of locations with optional filters
(constant) getListingByUIDs
Get specific persons (by UID) from a list with optional field selection and temporal queries
Behaves like getListing but additionally filters the result to only those
entries whose person UID (`UIDBelongsTo`) or entry UID (`ObjectBase.UID`) appears
in the array of UUIDs supplied in `req.body`.
- Source:
(constant) getListingController
Return the full list of persons belonging to a group / object.
- Source:
(constant) getListingPaginatedController
Return a paginated list of persons belonging to a group.
Only invoked when `__page` query parameter is present.
- Source:
(constant) getLocation
Get a single location by UID
(constant) getLocationController
Controller for getting a single location
(constant) getLocationListingController
Controller for getting location listing (with pagination support)
(constant) getLocationTemplates
Get location criteria templates
(constant) getLocationTemplatesController
Controller for getting location templates
(constant) getLogFiles
Get log files (GET /:api/logs)
- Source:
(constant) getMailController
GET /kpe20/orgaSettings/mail
Returns the SMTP settings for the current organisation (password masked).
(constant) getMissedEventsByKey
Get missed events by key and timestamp
- Source:
(constant) getMissedEventsByKeys
Get missed events by multiple keys and timestamp
- Source:
(constant) getMissedEventsByPattern
Get missed events by pattern and timestamp
- Source:
(constant) getMissedEventsByTemplate
Get missed events by template and timestamp
- Source:
(constant) getMultipleActions
Get multiple actions by UIDs
- Source:
(async, constant) getObjects
Retrieves objects based on the provided ObjectUID(s).
This function queries the database to fetch objects and their associated data.
It supports both single ObjectUID and an array of ObjectUIDs. The returned data
includes details such as UID, type, associated member data, extra data, main base data,
title, and validity timestamps.
(constant) getObjectsByGroupOwner
Resolve objects of a specific type for an organization through owner group links.
Traversal: object(memberA)->group(member)->organization.
- Source:
(constant) getOrganizationForGroup
Get the organization UID for a given group UID
- Deprecated:
- Use getOrganizationForObject instead (works for all object types due to link propagation)
- Source:
(constant) getOrganizationForList
Get the organization UID for a given list UID
- Deprecated:
- Delegates to getOrganizationForObject, which covers the list/project case (object → group → organization) as its second step.
- Source:
(constant) getOrganizationForObject
Get the organization UID for any PROPAGATED object UID (group, person, extern, job, guest)
Works because these object links are propagated to their organization via member links.
Link Propagation Flow:
Propagated Object → member link → ... → Organization (Data.root=true)
For NOT propagated objects (list, project) the single hop is not enough: they
only link to their group. The second query below covers that case
(object → group → organization), so this function answers for all object
types except shares — a share's organization is the one of its project.
- Source:
(constant) getOrganizationForShare
Get the organization UID for a share (repositoryShare / directoryShare).
A share carries **no** organization link: the organization is derived over its
project (`Share → memberA → Projekt → Owner-Gruppe → Organisation`). While the
old model is still in the database (project → share link, `UIDBelongsTo` = org),
both link directions and the direct `UIDBelongsTo` fallback are tolerated, so the
function answers before, during and after the migration.
- Source:
(constant) getPaginatedFamilyList
Get paginated family listing (GET /list/:group with __page parameter)
Returns paginated family data for a group.
- Source:
(constant) getPendingEmails
Get pending emails (admin only)
- Source:
(constant) getPerson
Retrieves detailed information about a person, job, guest, or extern object
- Source:
(constant) getPersonActions
Load the action names granted to a person through her jobs in an organization.
- Source:
(constant) getPersonByEmail
Find persons/externs by exact email match using the SearchIndex table.
Returns all matches within the organisation.
- Source:
(constant) getPersonByEmailController
- Source:
(constant) getPersonIdentifyer
Get identifyer UID for a person.
- Source:
(constant) getPersonIdentifyerController
- Source:
(constant) getPersonLists
Get all lists containing a specific person
- Source:
(constant) getPersonListsController
Handle GET /list/lists/:UIDperson — get all lists containing a specific person
- Source:
(constant) getPersonMinBackdate
Return the minimum allowed backdate timestamp (seconds) for adding a person/extern
to a target group.
addToTree inserts Links rows from the person to the target group AND all its
ancestor groups (discovered via the group's own member/memberA links). If the
person was previously a member in any of those same groups, historical link rows
already exist in the system-versioned Links table. Backdating a new insertion
before the latest existing validFrom of those overlapping links would create an
impossible temporal ordering.
The floor is therefore MAX(validFrom) across all historical Links rows where
UID = personUID AND UIDTarget IN (targetGroup + all its ancestor groups).
- Source:
(constant) getPersonsByGroups
Fetch persons belonging to one or more groups supplied in `req.body` as UUID array.
- Source:
(constant) getPersonsByGroupsController
Return persons belonging to one or more groups supplied as UUID array in body.
- Source:
(constant) getPersonsByUIDs
Fetch multiple person objects by an array of UIDs supplied in `req.body`.
Used when the caller already knows specific UIDs instead of querying by group.
- Source:
(constant) getPersonsByUIDsController
Return persons matching an array of UIDs supplied in the request body.
- Source:
(constant) getPortalPendingByKcUID
Get pending portal link details by KC UID and root orga.
- Source:
(constant) getPortalPendingByKcUIDController
- Source:
(constant) getPrivateRailFiles
Get private rail files (GET /rail/:UID/:side/:filename)
- Source:
(constant) getProject
Get a single project (requires at least visible).
- Source:
(constant) getProject
GET /project/project/:UID
(constant) getProjectCapabilities
GET /project/project/capabilities
(constant) getPublicFiles
Get public files with expiration (GET /public/:expire/:UID/:filename)
- Source:
(constant) getPublicLanguageFiles
Get public language/config files (GET /:api/prefix1:(languages|config)/:filename/)
- Source:
(constant) getPublicRailFiles
Get public rail files (GET /rail/:app/:side/:filename)
- Source:
(constant) getQueueCount
Get the count of pending items in the processing queue
(constant) getRemovedListing
Retrieve entries that have been removed from a list since the given timestamp.
Works for both static and dynamic lists.
The result is deduplicated and entries with `member0` link type are excluded.
Where a member appears multiple times (multiple links), their Data objects are merged.
- Source:
(constant) getRemovedListingController
GET /list/removed/:UID/:timestamp — entries removed from a static list since timestamp
GET /dlist/removed/:UID/:timestamp — entries removed from a dynamic list since timestamp
- Source:
(constant) getRemovedPersons
Return persons that were **removed** from a group after `timestamp`.
- Source:
(constant) getRemovedPersonsController
Return persons removed from a group after the given timestamp.
- Source:
(constant) getRepoLanguageController
Ein Bot-Repo mit getrennten Ebenen (Editor).
Handles GET /:repo?lang=de
- Source:
(constant) getRunner
- Source:
(constant) getRunner
GET /api/runners/:UID
- Source:
(constant) getSearchData
Search users for UI selector.
- Source:
(constant) getSearchDataController
- Source:
(constant) getSharedEmailData
Get shared email data (delegate to list shared function)
- Source:
(constant) getSignedUrl
Generate a pre-signed S3 URL (GET /:UID/:prefix/:filename/signed)
Returns a short-lived URL that the browser can use to download
the file directly from S3 without needing auth headers.
- Source:
(constant) getSingleFile
Get single file (GET /:UID/:filename)
- Source:
(constant) getSnapshotAsOf
GET /projects/:projectUid/snapshot?asOf=...
(constant) getTailnet
- Source:
(constant) getTailnet
GET /api/tailnets/:UID
(constant) getTimestamp
Get current server timestamp
(constant) getTree
Rebuilds the membership tree for the given type.
Delegates to rebuildMembershipTree() for the core logic (virtual transaction diff).
- Source:
(constant) getType
Parse and validate the `type` query parameter.
Falls back to `['person']` when the parameter is absent or invalid.
Only allowed types are returned to prevent injection via illegal type values.
- Source:
(constant) getTypes
Determines what types of objects to select based on source type and filter
(constant) getUser
Get user object for the logged-in user for their organizations
Uses Redis caching to avoid database queries on every API call with Bearer auth
- Source:
(constant) getUserByUID
Get user object by identifyer UID in current orga.
- Source:
(constant) getUserByUIDController
- Source:
(constant) getUsersWithVisibility
Get users who have specific visibility permissions for an entity
(constant) getUsersWithVisibilityData
Get users and their data who have specific visibility permissions for an entity
(constant) getVisibility
Rebuilds visibility settings for objects
- Source:
(constant) getVisibleFilter
Build the Visible JOIN fragment based on the current user's permission level.
Admins get no filter; changeable/admin visibility users see all entries;
read-only users only see entries where they have changeable rights.
- Source:
(constant) handleGroupMembershipMigration
Handles group membership migration for both person and extern types
- Source:
(constant) handleGroupType
Handles the `group` type cascade: queues member actions for all current members
of the added group, and pre-inserts delta member Links for persons/externs.
(constant) handleJobType
Handles the `job` type: creates `visible` and `changeable` filter objects
based on the function template attached to the job.
(constant) handlePersonType
Handles the `person` type: removes duplicate guest memberships and creates
guests for `memberGA` groups within a transaction.
(constant) hasActionPermission
Whether the current user has a given action right (e.g. `project.create`)
in the current organization context.
- Source:
(constant) hasDerivedShares
Haengt ein **anderes** Share-Objekt an dieser UID (ist sie eine Basis, die
noch gebraucht wird)? Letzte Absicherung beim Loeschen: lieber den Root
behalten als einem Ableger den Boden wegziehen.
(constant) heartbeat
POST /api/runner/v1/heartbeat — Topologie-Heartbeat in die Companion-Zeile.
(constant) id
AP 30 — Projekt-/Share-Zielmodell.
Alt (Ist):
- `ObjectBase.UIDBelongsTo` trug die **Organisation** (Org-Gruppe)
- der Projekt-Link zeigte vom Projekt zum Share
(`Links.UID` = Projekt, `UIDTarget` = Share, Type `memberA`/`member`/`projectShare`)
- die Lesestufe steckte im Link-Typ (`memberA` = schreiben, `member` = lesen)
Neu (Ziel, normativ seit 2026-09-24):
- `UIDBelongsTo` = eigene UID. Es transportiert **keine** Organisation mehr;
die Org wird ueber die `memberA`-Kette abgeleitet
(`getOrganizationForObject`, siehe 080-Workspaces/051-Migrationsplan-Komponenten).
Ein Ableger zeigt auf seinen Basis-Share (bleibt unveraendert).
- der Projekt-Link zeigt vom Share zum Projekt (`UID` = Share, `UIDTarget` = Projekt)
- die Lesestufe ist eine Eigenschaft des Shares: `metadata.mode`
(`readOnly` sperrt, alles andere ist editierbar)
Die Migration ist idempotent und laeuft ueber den normalen Migrationslauf
(`dbVersion`). Sie aendert ausschliesslich Basisobjekte: ein Share/Projekt,
dessen Elternobjekt selbst ein Share/Projekt ist, bleibt ein Ableger.
(constant) id
AP 30 — Owner-Backfill fuer Bestands-Shares.
Neue Shares bekommen ihren Owner beim Anlegen (`addShare`: `member`-Link
Share -> Person + `Visible.admin`) — der Anleger ist der Owner
([Shares eines Projekts](https://members.app.commtool.org/-/001-Backend/Datenstruktur/dProjectShares)).
Der **Bestand** hat weder Link noch `Visible`-Zeile. Ohne Owner greift die
dokumentierte Voreinstellung "Shares erben die Rechte des Projekts" — das ist
richtig, laesst aber den Besitz-Anspruch des Anlegers verfallen (er koennte den
Share nicht mehr administrieren, sobald er das Projekt-Recht verliert).
Dieses Modul zieht den Owner nach. Es ist bewusst **regelbasiert** und enthaelt
keine UID: Owner werden die **Personen**-Konten mit `Visible` am Projekt. Das
sind echte Nutzer; die Projekt-`admin`-Zeile traegt haeufig ein **Bot-/Systemkonto**
(`Type='extern'`), das kein Besitzer eines Shares sein soll.
Idempotent (`INSERT IGNORE`), akzeptiert beide Link-Richtungen — die Reihenfolge
zum Modul `20260924-project-share-target-model` ist damit gleichgueltig.
(constant) id
Stable unique identifier — never change this after the migration has been applied
(constant) id
Migration: Add EmailIndex column to Member table
Adds a dedicated VARCHAR(512) column for email lookups, populated with all
lowercased emails from Member.Data.email[].email, space-separated.
Same pattern as FullTextIndex / PhonetikIndex — a denormalized search column
maintained on person save.
Backfills existing rows from the JSON Data field.
(constant) id
Migration: Convert TreeQueue.Type from ENUM to VARCHAR(64)
The ENUM constraint required a schema change every time a new queue type was
added. Converting to VARCHAR(64) removes that friction and allows new action
types (e.g. 'personFilter') to be introduced in application code alone.
Existing values are preserved; the column is made NOT NULL with no default,
matching the previous ENUM behaviour.
(constant) id
Migration: Replace EmailIndex column with a generic SearchIndex table
The previous approach stored a space-separated string of all emails in a
VARCHAR(512) column on Member and queried it with LIKE '%email%'. That
cannot use a B-tree index (leading wildcard) and cannot be extended to
other object types or index categories without adding more columns.
This migration:
1. Drops the EmailIndex column if it was added by the dev-only migration
20260419-member-emailindex (safe no-op in production where it never ran).
2. Creates a generic SearchIndex table with one row per indexed value,
scoped by object type and index category.
3. Backfills all person/extern email entries from Member.Data.
Table design:
SearchIndex (UID, ObjType, IndexType, Value)
UID – Member.UID (= ObjectBase.UID for person/extern)
ObjType – object type: 'person', 'extern', 'group', …
IndexType – index category: 'email', 'phone', …
Value – lowercased, trimmed indexed value
The composite index idx_lookup (IndexType, ObjType, Value) covers the
primary search pattern:
WHERE IndexType = ? AND ObjType IN (…) AND Value = ?
which is a point lookup — no LIKE required.
(constant) id
Migration: Backfill FullTextIndex for accounting entities
Accounts und FiscalYears wurden bisher ohne FullTextIndex angelegt,
weil die accounting-backend Services (accountService, fiscalYearService)
die Spalte nicht befuellt haben.
Dadurch findet MATCH...AGAINST auf ObjectBase.FullTextIndex
diese Eintraege nicht, da der Wert NULL ist.
Diese Migration setzt FullTextIndex auf den Display-Wert fuer:
- Type = 'account' (Display = "1200 - Kassenbestand")
- Type = 'fiscalyear' (Display = "2025")
- Type = 'costcenter' (Display der Group/Event)
(constant) id
Migration: Add FULLTEXT index on ObjectBase.FullTextIndex
ObjectBase hat das Feld `FullTextIndex`, aber bisher keinen FULLTEXT-Index darauf.
Dadurch versagen MATCH...AGAINST Queries mit "Can't find FULLTEXT index matching the column list".
Diese Migration:
1. Setzt system_versioning_alter_history = KEEP (ObjectBase ist system-versioned)
2. Erstellt einen FULLTEXT-Index auf `FullTextIndex`
(constant) id
Migration: Swap lat/lng in Member.Geo for all rows
Der Code hat bisher POINT(lat lng) geschrieben, MariaDB/MySQL erwartet aber
POINT(lng lat). Alle bestehenden Geo-Einträge sind daher falsch herum.
Diese Migration:
1. Liest jedes Member.Geo (POINT)
2. Schreibt es mit getauschten Koordinaten zurück: POINT(ST_Y, ST_X)
3. Member ist nicht system-versioned, daher kein SET HISTORY nötig
(constant) id
Invariante: **genau ein aktiver `memberA`-Link pro Objekt.**
Diese Migration ist der kanonische Ort der Regel. Sie stand bisher weder in
der Doku (`DB-Docu > Backend > Datenstruktur > Link-Legende` beschreibt
`memberA` als "Administrative (erste Ebene) Mitgliedschaft", sagt aber nichts
zur Eindeutigkeit) noch strukturell in der Datenbank. Durchgesetzt war sie
nur als App-Guard in **einem** Pfad
(`personHelpers.js` → `handleGroupMembershipMigration`), und dort dreifach
eingeschraenkt: nur beim Uebertritt, nur fuer Quelle `person`/`extern`, nur
fuer Ziel `group`/`ggroup`.
Die uebrigen ~20 Insert-Stellen (`entry`, `group`, `location`, `job`,
`email`, `list`, `dlist`, `guest`, `event`, `project`, `runner`, `tailnet`)
umgehen diesen Guard vollstaendig. Deshalb ein DB-Trigger: er greift
zentral, auch fuer `INSERT IGNORE`.
## Datenlage bei Einfuehrung (Prod, 2026-09-25)
- 261.696 Objekte mit aktivem `memberA`, davon **95 mit mehreren**
(92x zwei, 3x drei) = 98 ueberschuessige Links → 99,96 % konform
- Aufschluesselung der Verstoesse: `person` 71, `extern` 9, `entry` 9,
`group` 1, 5 verwaist (Link ohne `ObjectBase`-Zeile)
- Ziele ausschliesslich `group` (158 Links) und `dlist` (20 Links)
- Kein Objekt hat `memberA` auf **verschiedene Ziel-Typen** — die
Mehrfach-Links liegen immer auf demselben Ziel-Typ
- `memberS` (106 Objekte): 0 Duplikate, aber alle 106 haben **zusaetzlich**
einen `memberA` → die Regel ist typ-spezifisch, nie "eine
Mitgliedschaft insgesamt"
## Verifiziertes Verhalten (Wegwerf-`mariadb:11.7`, prod-identische Struktur
`WITH SYSTEM VERSIONING` + `PARTITION BY SYSTEM_TIME`)
1. `CREATE TRIGGER` auf partitionierter **und** system-versionierter
Tabelle funktioniert (MySQL verbietet das, MariaDB nicht).
2. `INSERT IGNORE` schluckt das `SIGNAL` **nicht** (Fehler 1644 kommt
trotzdem) — Voraussetzung dafuer, dass die vielen `INSERT IGNORE`
Stellen die Regel ueberhaupt spueren.
3. **Kein `BEFORE UPDATE` noetig.** Die internen Versionierungs-Inserts
feuern diesen `BEFORE INSERT`-Trigger nicht: ein Objekt mit zwei
aktiven `memberA`-Links liess sich nach Trigger-Einbau weiterhin
`UPDATE`n. Haette die Versionierungs-Buchhaltung den Trigger gefeuert,
haette die Pruefung den anderen Duplikat-Link gefunden und blockiert.
4. Altbestand bleibt **editier- und loeschbar**. Daraus folgt: die
Migration ist nicht-brechend und benoetigt **kein** Daten-Pre-Cleanup.
(Die 95 Verstoesse raeumt separat `GET /maintenance/personConsistency?fix=true`.)
5. `UIDTarget <> NEW.UIDTarget` ist **Pflicht**, nicht Kosmetik: der
Trigger feuert *vor* der Duplikatspruefung, ein zweiter `INSERT IGNORE`
**desselben** Links (heute ein stiller No-Op) wuerde sonst 1644 werfen.
Ohne diese Bedingung brechen idempotente Re-Inserts an ~20 Stellen.
6. Pruefung laeuft ueber den Index (`PRIMARY`, `rows: 1`), kein Scan.
7. `SIGNAL ... SET MESSAGE_TEXT = CONCAT(...)` ist **nicht** erlaubt
("Undeclared variable: CONCAT") — die SET-Klausel akzeptiert nur
Literale oder Variablen. Ausweg: `DECLARE msg VARCHAR(128)` +
`SET msg = CONCAT(...)` + `SIGNAL ... = msg`. Nur so laesst sich die
Objekt-UID in die Fehlermeldung aufnehmen.
## Reihenfolge-Anforderung an den Aufrufer
Der alte Link muss **vor** dem neuen `INSERT` geschlossen sein, sonst
blockiert der Trigger. Die lebenden Pfade halten das ein:
`migratePerson.js` loescht `deltaOldPlus` (enthaelt immer das alte
`memberA`-Ziel) vor dem `INSERT` des neuen Ziels.
## Folge fuer die Anwendung
Ein Verstoss ist ab jetzt `errno 1644` / `SQLState 45000`. Die API-Schicht
sollte das auf **409** (oder 400) mappen — sonst wird aus einem
Datenproblem ein 500er.
Rueckbau: `DROP TRIGGER IF EXISTS trg_links_single_membera_bi;`
(constant) id
Migration: avatar → array
Converts Member.Data.avatar from a single file URL string to an array
containing just the filename. If the stored URL already contains a `?`
query string that part is kept; otherwise `?date=` is appended
using the current wall-clock time so clients can bust their caches.
Before: "avatar.jpg" or "https://…/UID/avatar/avatar.jpg"
After: ["avatar.jpg?date=2026-03-29"] (filename only, array)
Remove `export const disabled = true` to enable this migration.
(constant) id
Migration: Resize AIEmbeddings.Embedding column from VECTOR(768) to VECTOR(1536)
Cohere embed-multilingual-v4.0 uses 1536 dimensions (vs. 768 from OpenAI
text-embedding-3-small). MariaDB cannot ALTER a VECTOR column in-place, so
we drop the old column and re-add it with the new size. All existing
embeddings are cleared — they will be regenerated on next entity update.
The vector-key index must be dropped before the column can be dropped.
(constant) id
Migration: ObjectBase.Data — fix NULLs and enforce NOT NULL DEFAULT '{}'
Root cause of the ER_NET_PACKET_TOO_LARGE corruption incident (2026-04-12):
When ObjectBase.Data was NULL the persons endpoint returned it as the literal
string "null". The frontend echoed that string back in a subsequent PUT, where
it was spread into the request body and then JSON.stringify()ed on every save,
producing geometrically growing escaped backslash sequences.
This migration:
1. Replaces all NULL and literal 'null' values with '{}' in the current rows.
2. Alters the column to NOT NULL DEFAULT '{}' so the DB rejects future NULLs.
Note: system-versioned history rows are not writable; the ALTER TABLE will
handle them implicitly by coercing any remaining NULLs in history to the
column default during the table rebuild.
(constant) id
AP 30 — Rechte-Vererbung fuer Bestands-Shares materialisieren.
Neue Shares ziehen ihren Rechte-Satz beim Anlegen/Verlinken nach
(`./20260924-project-share-target-model` nur strukturell, die Vererbung selbst
in `RouterProject/projectShare/access.js`). Der **Bestand** hat nur die
Owner-Zeile: die Projekt-Rechte sind nirgends am Share materialisiert, und es
gibt kein Rechte-Event, aus dem eine Projektion sie gewinnen koennte.
Dieses Modul holt das nach — je Share:
1. den Filter-Satz des Projekts spiegeln (dieselbe Struktur wie bei Listen),
2. `Visible` neu materialisieren und die `/add|/remove/{shareType}/{level}/{uid}`-
Events melden (macht `listRebuildAccess` selbst).
Damit wird `share_grants` in der RAG-Projektion **ableitbar** statt gerechnet:
die Kette Members (`Visible`) → Event → Projektion steht dann lueckenlos.
Idempotent. Die Organisation kommt ueber die Link-Kette
(`getOrganizationForShare`) — das Modul enthaelt keine UID.
(constant) id
Migration: Link existing runners to their owner groups and seed Visible admin.
Runners now follow the same visibility model as projects:
- `shared` runners get a `memberA` link to their org root group
- every runner gets a `Visible admin` row for its creator (UIDuser)
No schema change is needed — the `Links` and `Visible` tables already exist.
(constant) includeChangeability
Include changeability filter
(constant) includeVisibility
Include visibility filter
initPromise :Promise.<boolean>|null
Der laufende Initialisierungsversuch — alle Aufrufer teilen sich **einen**.
Ohne das teilen sie sich nur den Zustand `isInitialized`, und der wird erst
NACH `await client.connect()` gesetzt. Jeder Aufrufer, der in diesem Fenster
startet, sieht `false`, erzeugt seinen eigenen Client und schreibt ihn in
`pubClient`. Die Listener-Zeile liest `pubClient` danach **erneut** und trifft
damit den ZULETZT erzeugten Client: beim Serverstart waren das 12 verbundene
Clients (11 davon verwaist) und 11 `error`-Listener auf einem — genau die
`MaxListenersExceededWarning: 11 error listeners added to [Commander]`
(`Commander` ist die Client-Klasse von `@redis/client`).
Type:
- Promise.<boolean> | null
- Source:
(constant) initPublicBucket
Ensure the public bucket exists and is publicly readable.
Uses publicMinioClient if available (independent endpoint),
otherwise falls back to myMinioClient for backwards compatibility.
- Source:
(constant) insertList
Insert a new list or dlist
- Source:
(constant) insertMemberLinks
Inserts `member` Links for delta groups, skipping person/extern/guest types
(which handle their own link creation).
(constant) insertOrUpdateAchievement
Handles the insertion or update of an achievement for a member
- Source:
(constant) invalidateCacheController
- Source:
(constant) invalidateUserCache
Invalidate user cache for a specific user (call when user data changes)
- Source:
(constant) invalidateUserCacheService
Invalidate user cache for one/multiple/all users.
- Source:
ioInstance :SocketIOServer|null
Type:
- SocketIOServer | null
- Source:
(constant) isAdmin
Check if user is admin for current organization
MIGRATION: Checks Keycloak orgRoles first, then falls back to database
- Source:
(constant) isAdminOrga
Check if user is admin for an organization
MIGRATION: Checks both Keycloak orgRoles (new) AND database admin list (legacy)
Once bot sync is complete, database check can be removed
- Source:
(constant) isBaseAdmin
Check if the base user (real user, not impersonated) has admin rights
MIGRATION: Checks Keycloak orgRoles first, then falls back to database
- Source:
isInitialized :boolean
Type:
- boolean
- Source:
(constant) isObjectInOrg
Does this object belong to the given organization?
Replaces the former `ObjectBase.UIDBelongsTo = ` filter, which no longer
holds once objects carry their self-reference / parent there
(project, share). Both sides are compared as canonical UUID strings.
- Source:
(constant) isUsableSharedRunner
Ist der Runner als Projekt-/öffentlicher Runner nutzbar (Soll: nicht
personal, nicht revoked, in der Orga)?
- Source:
(constant) isUserDataCached
Check if user data exists in cache
- Source:
(constant) isValidUID
Checks if the given UID is a valid UUID string with a specific prefix.
The UID must be a string that matches the following pattern:
- Starts with "UUID-"
- Follows the UUID version 1-5 format
- Source:
(constant) isValidUid
Prüft, ob ein Wert eine UID in der hier gültigen Form ist.
(constant) jobQualified
Checks if a person is qualified for a job based on their achievements.
- Source:
(constant) kvDel
- Source:
(constant) kvExists
- Source:
(constant) kvGet
- Source:
(constant) kvSetEx
Key mit TTL setzen. @returns {Promise} true bei Erfolg.
- Source:
lastMailAt
Zeitpunkt der letzten Fehler-Mail (ms).
- Source:
(constant) linkShare
Ein **bestehendes** Repo an ein Projekt haengen (verlangt **changeable**).
Das legt einen **Ableger** an: ein eigenes `ObjectBase`-Objekt mit eigener
UID, das in `UIDBelongsTo` auf den **Basis-Share** (= der Root, die
Index-Identitaet) zeigt und die projekt-eigenen Einstellungen traegt. Der
Basis-Share bleibt unangetastet — derselbe Code wird damit **einmal**
indexiert, auch wenn er in mehreren Projekten haengt
([Shares eines Projekts](https://members.app.commtool.org/-/001-Backend/Datenstruktur/dProjectShares)).
**Harte Regel: ein Root hoechstens einmal je Projekt.** Haengt bereits ein
Objekt desselben Roots am Projekt, ist der Aufruf idempotent (es ist genau
dieses Objekt) oder ein `409 SHARE_ALREADY_IN_PROJECT`. Damit kann derselbe
Root nicht als Basis **und** als Ableger im selben Projekt liegen.
`linkType` wird nur noch **validiert** (Alt-Alias), nie geschrieben: der
Modus ist eine Eigenschaft des Shares (§13.9 Punkt 3).
(constant) linkShare
POST /project/project/:projectUid/shares/:shareUid/link
(constant) listAchievementsPerson
Lists all achievements for a specific person
- Source:
(constant) listCredentials
List credentials visible to the caller:
- org-scoped credentials: org admins only
- user-scoped credentials: the owning user sees his own; org admins see all
(constant) listCredentials
GET /project/credential
(constant) listEventGroupsController
List event groups controller with pagination support
Handles GET /:UID
(constant) listEventsController
List events controller with pagination support
Handles GET /
- Source:
(constant) listFiles
List files in directory (GET /:UID)
- Source:
(constant) listJobsController
List jobs for event with pagination support
Handles GET /jobs/:UID
(constant) listLanguageFilesByAppController
List language files for a specific application.
Handles GET /:app
- Source:
(constant) listLanguageFilesController
List all available language files.
Handles GET /
- Source:
(constant) listProjects
GET /project/project
(constant) listRepoLanguageSlicesController
Dünne Überschicht aller Bot-Repos für die Session-Organisation.
Handles GET /?lang=de
- Source:
(constant) listRootGet
Controller for GET /list/
- Source:
(constant) listRunners
List runners of the current org the user can see (visible | changeable |
admin). Org admins and bots see all runners of the organization. Non-admins
are filtered through the Visible table (creator/admin row + addVisibility
group membership) — exactly the project `getListing` pattern.
- Source:
(constant) listRunners
GET /api/runners
- Source:
(constant) listShares
List all shares of a project (requires at least visible on the project).
Shares are resolved through the project's memberA/member links — in both
directions while the old rows still exist.
(constant) listShares
GET /project/project/:projectUid/shares
(constant) listTailnetRunners
List runners explicitly assigned to a tailnet network
(Links.Type='tailnetRunner', UID=tailnet, UIDTarget=runner).
- Source:
(constant) listTailnetRunners
GET /api/tailnets/:UID/runners
(constant) listTailnets
List tailnet networks of the current org the user can see (visible |
changeable | admin). Org admins and bots see all networks. Optional
`group_uid` filter (only networks of that owner group) — same as projects.
- Source:
(constant) listTailnets
GET /api/tailnets
(constant) loadVisibleScopes
Laedt die Sichtbarkeits-Zeilen zu einer Menge von Objekten.
Bewusst nur die uebergebenen Objekte: der Aufrufer weiss, welche Zeilen sein
Lauf ueberhaupt anfassen kann, und alles andere waere teurer, nicht genauer.
`cast: ['UUID']` ist hier **der Punkt der Normalisierung**: die Spalten
`Visible.UID` und `Visible.UIDUser` sind `binary(16)` und kaemen sonst als
Rohbuffer an. Jeder Vergleich muesste sie dann erneut umwandeln — genau das
hat den Server blockiert (ein `asUuid` pro Paarvergleich, siehe
publishVisibilityDiff). Einmal hier umgewandelt, sind die Zeilen
ueberall im Weiteren direkt vergleichbar.
- Source:
(constant) logError
Betriebsfehler: protokollieren, **nicht** mailen.
- Source:
(constant) logFatal
Echter Fatal: protokollieren **und** mailen (gedrosselt, wenn konfiguriert).
Gedacht fuer den Startpfad — wenn der Server nicht hochkommt, ist das die
einzige Meldung, die jemand mitbekommt.
- Source:
logStreamDb
- Source:
logStreamError
- Source:
logStreamLogin
- Source:
logStreamRead
- Source:
logStreamUpdateDb
- Source:
(constant) loginLogger
Logs login events.
- Source:
(constant) matchExcludersList
Matches objects against excluder lists and handles membership updates
(constant) matchObjectsLists
Matches objects against list filters and updates dynamic lists accordingly
(constant) memberAConsistency
Report and (optionally) repair duplicate and dangling `memberA` links.
(constant) mergeTemplateData
Merges existing template data with new data, preserving customizations.
Default values support optional version-based enforcement:
- The bot may supply a `defaultVersions` object alongside `defaults`,
mapping each key to a monotonically increasing version number.
- On re-registration a key is overwritten only when the bot's version is
strictly greater than the stored version.
- Admin-edited values are preserved as long as the bot does not bump the
version for that key.
- Templates that do not supply `defaultVersions` continue to work as
before: existing values are always preserved (backwards-compatible).
- `defaultsOriginal` is always overwritten with the bot's current defaults
so the admin UI can offer a "reset to bot default" action at any time.
- Source:
(constant) metadataFromRow
Extract the metadata object from a project row's `metadata_json` envelope
(`JSON_OBJECT('value', JSON_EXTRACT(Data, '$.metadata'))`).
With `{ cast: ['json'] }` the driver already parses the envelope into an
object (`{ value: {...} }`), while legacy/test call paths may still deliver a
JSON string. Both forms are normalized here; anything else yields `{}`.
- Source:
(constant) migrate
Adds a Sequence column to TransactionLines to preserve
booking line ordering (PosNr from old KPE system).
Uses IF NOT EXISTS to stay idempotent.
(constant) migrate
Fuehrt die Datenmigration aus.
(constant) migrate
Adds TUID (human-readable UUID) virtual columns to both transaction tables,
matching the same pattern used in ObjectBase, Member, Links, AIEmbeddings, and Visible.
TUID is generated as: bytes[5-8]-bytes[3-4]-bytes[1-2]-bytes[9-10]-bytes[11-16]
This mirrors the byte ordering of UUID v1 hex display, which is the format
the CommTool @commtool/sql-query library (HEX2uuid/UUID2hex) expects.
TransactionLines.UIDTransaction gets TUIDTransaction as a virtual FK display column.
(constant) migrate
Fuehrt den Owner-Backfill aus.
(constant) migrate
(constant) migrate
(constant) migrate
(constant) migrate
The replay endpoint (GET /api/kpe20/events) filters the eventLog by the
organization, which previously required a full scan of millions of rows via
JSON_VALUE(Data, '$.UIDorga'). A generated virtual column plus an index on
(UIDorga, seq) makes org-scoped replay and high-watermark lookups fast.
(constant) migrate
Runner objects live in ObjectBase (topology, no versioned runtime state).
`deployment`/`deploymentEndpoint` literals were dropped with the members
runtime layer (see 20260907-runner-companion.js) — the runtime moved to the
session (ide-server).
(constant) migrate
(constant) migrate
A share is linked to exactly one project via Links.Type='projectShare'
with Links.UID = Project-UID and Links.UIDTarget = Share-UID. In addition
Share.UIDBelongsTo = Project-UID is used as a fast association query. Both
representations are written in the same transaction.
(constant) migrate
The cross-service contract (080-Workspaces/018-Cross-Service-Contracts.mdx)
requires replay via an opaque, strictly ordered cursor. The existing eventLog
PK is (Timestamp, EventKey), which is not a reliable monotonic cursor. A
BIGINT AUTO_INCREMENT seq column provides the stable ordering needed by the
GET /api/kpe20/events replay endpoint.
(constant) migrate
Drops and recreates Transactions + TransactionLines tables.
The new Transactions table uses a `Data` JSON column as single source of truth.
Previously separate columns (DocumentNumber, Date, Status, TransactionType,
TotalAmount, DocumentSequenceNumber, FullTextIndex) are now PERSISTENT
GENERATED COLUMNS derived from `Data`.
TransactionLines stays unchanged.
(constant) migrate
(constant) migrate
Project objects are the Members source of truth for project-wide metadata.
Repository- and directory-shares are share objects belonging to exactly one
project. None of these target types gets its own visible-* filter type —
shares derive all rights from their project (see workspace plan 025).
(constant) migrate
Tailnet networks are ObjectsBase objects (Type='tailnet'), one per network a
team can create. Multiple networks per group are allowed — the owner group is
tracked via Links.Type='memberA' (tailnet -> group), exactly like projects.
Links.Type='tailnetRunner': UID = Tailnet-UID, UIDTarget = Runner-UID.
A runner can be assigned to one or more networks; assigning/removing is an
explicit control-plane operation (the Addon "Tailnet-Netze" group tab).
`deployment`/`deploymentEndpoint` (ObjectBase) and `deploymentRunner` (Links)
were dropped — they belonged to the members runtime layer (20260907-runner-companion).
(constant) migrate
(constant) migrate
Adds denormalized columns to Transactions so the transaction list
can be fetched without JOINing on TransactionLines every time.
(constant) migrate
(constant) migrate
Erzwingt die Eindeutigkeit von `memberA`-Links auf DB-Ebene.
Legt einen `BEFORE INSERT`-Trigger an. Nur `INSERT` — siehe Migrationkopf,
Punkt 3: `UPDATE` braucht keine Pruefung, weil weder die legitimen Updates
noch die internen Versionierungs-Inserts den Trigger faelschlich ausloesen.
Idempotent: der Trigger wird verworfen und neu angelegt, damit eine
spaetere Aenderung der Definition beim erneuten Lauf greift.
(constant) migrate
(constant) migrate
(constant) migrate
Fuehrt den Backfill aus.
(constant) migrate
Ergänzt `botRepo` in `Links.Type`.
Ein „Bot-Repo" bündelt die Bots eines Repositories (z. B. alle Bots von
`basic-bots`) und ist bewusst **nicht** organisationsgebunden: die UID wird im
Repo selbst vergeben (`botRepo.json`) und existiert nur als Link-Endpunkt —
genau wie die Bot-UIDs, die ebenfalls keine `ObjectBase`-Zeilen sind.
`Links.Type='botRepo'`: UID = Repo-UID, UIDTarget = Bot-UID.
Bewusst nur eine Migration auf `Links.Type`: `ObjectBase` bleibt unberührt,
es wird **kein** neuer Objekt-Typ gebraucht. Eine `Links`-Zeile trägt keine
Datenspalte, es wird also nichts „am Repo" gespeichert — die Beziehung ist
der ganze Inhalt. Sollte später doch Repo-Metadaten (Name, Icon, …) nötig
werden, ist der Weg eine Companion-Tabelle analog zu `RunnerData`.
Bewusst **nicht** `appBot`/`app` genannt: `app`, `appDomain` und `appAsset`
sind im App-Registry-Konzept (`PLAN-app-registry.md`) für org-gebundene,
gehostete Frontends reserviert (Domain, Branding, PWA-Icons) — hier ist
dagegen ein globales Bot-Repo gemeint.
## Warum die vorhandenen Werte gelesen werden
Die Enum-Listen sind je Umgebung auseinandergelaufen und werden von der
Migrationskette aufgebaut (`initTables.sql` ist ein alter Stand, jede
Link-Typ-Migration ergänzt). Prod hat die 2026-09 ausgemusterte
Runner-Runtime bereits hinter sich, Dev nicht:
- Dev (`member`) führt zusätzlich `deploymentRunner`, `workspaceRunner` und
`workspaceLease` — und zwar **in der History**. `Links` ist
system-versioniert und nach Zeit partitioniert; `system_versioning_alter_history
= 1` prüft das ALTER gegen diese Partitionen. Eine Liste ohne diese Werte
schneidet dort ab und die Migration bricht mit „Data truncated for column
'Type'" ab (Zeile 295490).
- Prod führt sie nicht und soll sie auch nicht bekommen: `20260827-tailnet-object-types`
hat sie dort bewusst entfernt.
Deshalb wird `botRepo` an die **jeweils vorhandene** Liste angehängt, statt
eine kanonische Liste zu schreiben. Eine feste Liste wäre in beide Richtungen
falsch: zu kurz für Dev (Abbruch), zu lang für Prod (rüstet ausgemusterte
Typen wieder ein und macht die Entfernung aus `20260827` rückgängig).
Zeichensatz und Collation kommen ebenfalls aus der Spalte, damit die
Migration dort nichts umstellt.
(constant) migrate
(constant) migrateDB
Migrates the database by running all pending migration modules.
Migrations are JS files in src/config/migrations/ and support both SQL
schema changes and JSON data transformations.
Applied migrations are tracked in the dbVersion table.
- Source:
(constant) migrateFamilyAction
Handles `family` and `familyB` migration actions.
Moves a person's family or familyFees link from the old family object to the
new one. If the old family object has no remaining members the orphaned Member
row is deleted; otherwise family fees are recalculated for it.
Either way, fees are recalculated for the new family object.
(constant) migratePersonAction
Handles `person` and `extern` migration actions.
Computes the symmetric difference between the old and new group memberships,
then:
- Rebuilds visibility for the groups gained by the move.
- Re-evaluates list membership for the person in all new groups.
- Re-checks list entries for the person.
- Fires `/add/group/…` events for gained groups and `/remove/group/…` for lost ones.
(constant) minTimestamp
GET /minTimestamp/:UID — return the minimum allowed backdate timestamp (ms)
for a group object, constrained by its latest history row.
- Source:
(constant) minTimestamp
GET /minTimestamp/:group/:UID — return the minimum allowed backdate timestamp (ms)
for adding a person/extern to a specific target group.
- Source:
(constant) mirrorProjectFilters
Die Rechte-Filter des Projekts auf den Share spiegeln: hinzufuegen, was das
Projekt hat; entfernen, was es nicht (mehr) hat. Der Share bekommt damit
**denselben** Filter-Satz wie das Projekt und beim naechsten Rebuild
dieselben `Visible`-Zeilen.
Idempotent und rein strukturell (keine Rechte-Auswertung).
(constant) monitorDelayedUpdates
Processes the `delayedUpdateTimestamps` map and flushes any entries that
have been quiet for longer than the debounce threshold (2.5 s).
Should be invoked on a periodic interval (e.g. `setInterval`) by the
server startup code. For each overdue entry it:
1. Finds all clients with a matching UID in their `updateAbo` Set.
2. Emits `{ update: true, UID, timestamp }` to those clients.
3. Removes the entry from `delayedUpdateTimestamps`.
This is the trailing-edge part of the two-phase debounce implemented
together with addUpdateList.
- Source:
(constant) mysqlTimeToRfc3339
Convert a MariaDB timestamp string ('YYYY-MM-DD HH:MM:SS.ffffff', UTC) to the
contract RFC3339 format with six fractional digits.
(constant) name
Human-readable description shown in progress output
(constant) nameReplace
Helper function to replace special characters with underscores
(constant) normalizeDomainEntry
Bringt einen Domain-Eintrag auf die Form `{ type, status }`.
Nimmt **beide** Formen an, denn der Altbestand in Vault ist eine
Zeichenkette (`{"kpe.de":"verified"}`). Die Zuordnung ist eindeutig, weil die
alten Werte sich gegenseitig ausschließen:
| Alt | `type` | `status` | Begründung |
|---|---|---|---|
| `internal` | internal | verified | Ein System-Präfix liegt im eigenen Namensraum — nachzuweisen gibt es nichts |
| `external` | external | pending | Eine Kunden-Domain bleibt offen, bis der DNS-Eintrag zeigt |
| `verified` | external | verified | Nur Kunden-Domains wurden je von Hand verifiziert — sie tragen einen Punkt |
(constant) normalizeDomainMap
Bringt eine ganze Domain-Map auf `{ domain: {type, status} }`.
(constant) normalizeDomainValue
Normalisiert einen Domain-Namen: Kleinschreibung, ohne führenden `*.`/`.` und
ohne abschließenden Punkt. `*.Kpe.de.` → `kpe.de`.
(constant) normalizeUid
Bringt eine UID in die gültige Form. Akzeptiert bereits korrekte Werte,
ein `Buffer(16)` (wie ihn `UIDV1()` liefert, via `HEX2uuid` umgesetzt) und
die rohe 36-Zeichen-Form.
Bewusst tolerant beim Lesen, streng beim Schreiben: Werte aus `session`,
Vault oder einer älteren Zeile sind nicht immer schon normalisiert.
(constant) objectInOrgSql
The same rule as `getOrganizationForObject`, but as a SQL predicate so a
listing can scope many rows in one query instead of resolving each object.
Bind the organization **twice** (the predicate uses two placeholders) — both
times as the value you would pass as `orgUID`.
- Source:
(constant) optimizeDatabase
Optimizes database tables
- Source:
(constant) optimizeTables
Utility function to optimize and analyze all tables in the database
- Source:
(constant) paginateCollectionsUIDController
Pagination pre-handler for GET /:UID.
- Source:
(constant) parseEnumValues
Liest die Werte einer `enum(...)`-Spaltendefinition aus.
Die Werte dürfen Kommas und Klammern enthalten, deshalb wird zeichenweise
gelesen statt an Kommas zu trennen. MySQL/MariaDB verdoppelt Anführungszeichen
innerhalb eines Werts (`'a''b'`).
Example
parseEnumValues("enum('a','b')") // ['a', 'b']
(constant) parseTimestamp
Parses the timestamp from a tree action and produces the SQL
`FOR SYSTEM_TIME AS OF` clause string.
(constant) parseTimestamp
Normalises a raw action timestamp into a numeric value and the
corresponding `FOR SYSTEM_TIME AS OF …` SQL clause.
(constant) patchRunnerData
Update runner Data fields (tailnet, capabilities) — schreibt nur bei echten
Änderungen (kein versioniertes ObjectBase-Wachstum durch identische
Heartbeat-Meldungen).
- Source:
(constant) personAdmin
Checks if the logged-in user has admin rights for a given person UID
- Source:
(constant) personConsistency
Report and (optionally) repair person level drift for the current organisation.
GET /maintenance/personConsistency
→ report only, no writes.
GET /maintenance/personConsistency?fix=true
→ additionally align `Member.Data.stage`/`hierarchie` with `ObjectBase`.
Surplus `memberA` links are handled by `memberAConsistency`.
(constant) personDuplicates
Finds potential duplicate person records based on phonetic similarity
- Source:
(constant) personHistorie
Retrieves the historical group membership information for a person
- Source:
(constant) personRebuildAccountingAccess
Rebuilds accounting visibility (Visible.Accounting) for a person based on
their accountingVisible/accountingChangeable filter objects (rule-based access).
Evaluates the filter rules and sets Visible.Accounting entries. Respects
hierarchy: admin > changeable > visible — never overwrites a higher level.
This is a separate step from personRebuildAccess and should only be called
when accounting rules change (financialMaster toggle or accountingAccess/
accountingWriteAccess changes), NOT on every visibility rebuild.
- Source:
(constant) postCollectionsController
Controller for POST /
- Source:
(constant) postCountsController
Controller for POST /counts
- Source:
(constant) postDlistEntryController
Handle POST /dlist/params/:UIDlist/:UIDperson — update entry params for a dynamic list
- Source:
(constant) postDlistUID
Controller for POST /dlist/:UID
- Source:
(constant) postEntry
Update extra parameters for a person's entry in a list
- Source:
(constant) postListEntryController
Handle POST /list/params/:UIDlist/:UIDperson — update entry params for a static list
- Source:
(constant) postListUID
Controller for POST /list/:UID
- Source:
(constant) postShare
PUT /project/project/:projectUid/shares
(constant) postSnapshotCurrent
POST /projects/:projectUid/snapshot/current
(constant) preWarmUserCache
Pre-warm cache with commonly accessed users
- Source:
(constant) processExclude
Processes exclude filter operations
(constant) processGgroupMemberships
Handles guest-group (ggroup) membership for the object being added.
Creates guest records or cascades group guests as appropriate.
(constant) processInclude
Processes include filter operations
(constant) processIncludeFilter
Processes include filter and creates new entries based on filter criteria
(constant) processLastschrift
Processes SEPA mandate data for family fees
(constant) projectInOrg
Verify that a project exists within the current organization.
The organization is derived over the owner group (`project → memberA → group
→ … → root group`), not read from `UIDBelongsTo` — that field now holds the
project's own UID.
- Source:
(constant) projectSelect
Full project SELECT used for reads. `source_updated_at` is derived from the
temporal row start (ValidFrom) with microsecond precision.
- Source:
(constant) projectShareRoots
Alle Shares eines Projekts mit ihrem Root — beide Link-Richtungen.
Das ist die Grundlage der harten Root-Regel: **ein Root hoechstens einmal je
Projekt**. Ohne sie haengen Basis und Ableger desselben Repos doppelt im
Projekt.
(constant) projectToContract
Map an ObjectBase row to the contract Project object.
- Source:
(constant) projectsOfShare
Die Projekte eines Shares — beide Link-Richtungen.
(constant) projectsOfShares
Die Projekte mehrerer Shares auf einmal — fuer die Anzeige der **Herkunft**
eines Ablegers („kommt von Projekt X") ohne eine Abfrage je Zeile.
(constant) promoteRoot
Die Basis eines Roots auf einen Ableger umhaengen: der Nachfolger wird die
neue Basis (Selbstreferenz), alle uebrigen Ableger zeigen auf ihn. Danach ist
der alte Root frei — genau das braucht „Basis loeschen, aber ein Ableger soll
sie ersetzen".
Innerhalb einer Transaktion **muss** `connection` mitgegeben werden.
pubClient :redis.RedisClientType|null
Type:
- redis.RedisClientType | null
- Source:
(constant) publicObjectUrl
Build the direct public URL for an object in the public bucket.
First checks S3publicBaseUrl for a full override, then tries
dedicated public S3 connection vars (publicS3endPoint etc.),
and finally falls back to the protected S3 endpoint as before.
- Source:
(constant) publishChangeEvent
Publishes change events for person modifications.
Notifies related entities (groups, organizations) about changes to a person.
- Source:
(constant) publishEvent
Publishes an event to Redis with multi-tenant organization scoping and
persists it to eventLog for replay.
- Source:
(constant) publishGroupEvent
Publishes an event for a person joining or leaving an organization.
Notifies the group and its parent groups about the person's status change.
- Source:
(constant) publishGroupEvents
Publishes `/add/group/…` or `/remove/group/…` events for each group in the
provided list.
(constant) publishMembershipEvents
Publishes membership-change events for all groups in `deltaPlus`.
(constant) publishToRedis
Publish a prepared payload to Redis (live channel). Never throws.
- Source:
(constant) publishVisibilityDiff
Vergleicht zwei Staende und meldet die Differenz.
Verglichen wird das Paar `(UID, UIDUser)` **inklusive** Level: eine Zeile, die
von `visible` auf `changeable` wandert, ist kein `add` auf ein leeres Blatt,
sondern ein `add` auf ein bereits gemeldetes — der Bot fuehrt den staerkeren
Stand. Ein `remove` fuer ein Level, das die Zeile nie hatte, entsteht dadurch
nicht.
Reihenfolge: erst `remove`, dann `add` — ein Herabstufen (Loeschen und neu
Anlegen in einem Lauf) kommt so in der richtigen Ordnung an.
Laufzeit: **O(n+m)**. Die kanonischen Schluessel werden je Zeile genau einmal
gebildet (scopeKey) und ueber zwei `Set` verglichen, statt jedes Paar
erneut zu normalisieren.
- Source:
(constant) putCredential
PUT /project/credential
(constant) putDlistOwner
Controller for PUT /dlist/:owner
- Source:
(constant) putEntry
Add one or more persons as entries to a static list
- Source:
(constant) putEntryController
Handle PUT /list/person/:UIDlist — add persons to a static list
- Source:
(constant) putGroup
PUT /:UIDparent — create or update a group under the given parent.
- Source:
(constant) putListOwner
Controller for PUT /list/:owner
- Source:
(constant) putProject
PUT /project/project/:group
(constant) putRepoLanguageController
Schreibt eine Ebene. Ohne `:UIDroot` die geteilte Ebene, sonst die der
angegebenen Organisation.
Handles PUT /:repo/:lang und PUT /:repo/:UIDroot/:lang
- Source:
(constant) putTailnet
PUT /api/tailnets/:group
(constant) putUser
Ensure identifyer link and self-visibility are in sync for one person.
- Source:
(constant) putUserController
- Source:
(constant) queryAllGroupsOf
Returns every group `UIDTarget` is currently a member of.
(constant) queryGainedGroups
Returns the groups that belong to `UIDnewTarget` but are **not**
exclusively shared with `UIDoldTarget`, together with the derived delta arrays.
(constant) queryLostGroups
Returns the groups that belong to `UIDoldTarget` but **not** to
`UIDnewTarget`, together with the derived delta arrays.
(constant) queueRunning :Record.<string, boolean>
Type:
- Record.<string, boolean>
- Source:
(constant) readAgeUpdateStats
Stats für eine Organisation lesen.
Wird von members-back (`GET /person/ageUpdate/stats`) aufgerufen.
- Source:
(constant) readLogger
Logs read/GET requests.
- Source:
(constant) readProjectRunnerUid
Projekt-Runner aus `metadata.runnerUid` lesen (Wire-UID „UUID-…" oder null).
Quelle: Project.Data.metadata.runnerUid (Projekteinstellung „öffentlicher
Runner", Soll-Auflösung beim Session-Anlegen).
- Source:
(constant) readRunnerMeta
Minimales Runner-Record für interne Validierung/Auflösung
(kein Sichtbarkeits-Check — der Aufrufer prüft Orga/Auth selbst).
- Source:
(constant) rebuildAccountingVisibility
Rebuilds accounting visibility for ALL function templates in the organization.
Iterates over every function template and calls syncAccountingForFunction,
which in turn re-evaluates Visible.Accounting for every job linked to that function.
Sends progress events via WebSocket.
(constant) rebuildEventAccess
Rebuild event access completely
Deletes all visibility filters and visibilities, then rebuilds them
(constant) rebuildEventVisibility
Rebuild event visibility from scratch
(constant) rebuildExternAccess
POST /rebuildAccess/:UID — Rebuild visibility and list access for an extern.
(constant) rebuildExternAccess
Rebuild visibility and list access for an extern and all its job objects.
Delegates to the shared helper in personHelpers.
- Source:
(async, constant) rebuildFeesGroup
Rebuilds the fees for all families that have a member in the specified group.
This has to be called, if the fees of a group have changed, so that all families with members in this group
will have their fees recalculated according to the new group fee rules.
- Source:
(constant) rebuildListEntries
Rebuilds list entries
- Source:
(constant) rebuildPersonAccess
POST /rebuildAccess/:UID — Rebuild visibility and list access for a person.
- Source:
(constant) rebuildSearchIndex
Rebuild the SearchIndex table for a given object type
Clears and rebuilds search index entries (email, phone, etc.) for all objects
of the specified type in the current organization.
(constant) rebuildShareAccess
`Visible` eines Shares neu materialisieren und die Rechte-Events melden.
Bewusst ueber die Queue (wie `rebuildListVisibility` bei Listen): der Rebuild
laeuft asynchron, ein Fehler dort darf das Anlegen/Linken nicht zurueckrollen.
(constant) reconcileShare
Einen Share vollstaendig nachziehen: Filter spiegeln **und** Rechte
materialisieren. Nach Anlegen, Verlinken und Entlinken aufrufen.
(constant) reconcileSharesOfProject
Alle Shares eines Projekts nachziehen — nach einer Aenderung an den
Projekt-Rechten und beim Anlegen/Entfernen von Shares.
(constant) recreateAllJobsController
Recreate all jobs for organization
Handles GET /recreate
(constant) recreateEventsLocations
Recreates event / location objects.
ObjectBase holds system-versioned fields; Member holds display fields.
(constant) recreateFunctions
Recreates function template objects.
ObjectBase only (UIDBelongsTo = org UID). Uses DELETE+INSERT.
(constant) recreateGgroups
Recreates ggroup (guest group) objects.
All fields live in ObjectBase — no own Member row.
Uses versioned UPDATE (not DELETE+INSERT) to preserve clean history.
(constant) recreateJobs
Recreates job objects.
ObjectBase only (UIDBelongsTo = person UID). Uses DELETE+INSERT because
jobs carry a computed `dindex` (qualification status) that must be replaced.
(constant) recreateJobsFunction
Recreates jobs based on a specified function template.
This function retrieves all jobs associated with a given function template
and recreates them based on the template using the recreateJobs function.
it should be called, when a function template is updated without changed to the achievement rules.
- Source:
(constant) recreateJobsGroup
Recreates all jobs within a specified group using templates.
This function fetches all job objects associated with a given group,
optionally at a specific point in time (using system versioning),
and recreates them based on their templates using the recreateJobs function.
this function should be called, when a group is updated.
- Source:
(constant) recreateJobsOrga
Recreates all jobs for an organization based on templates.
This function queries the database for all job objects belonging to the organization,
optionally as of a specific timestamp, and then recreates them. It retrieves related
data such as person, function, group, and member information needed for job recreation.
- Source:
(constant) recreateObjects
Recreates all objects of the organization of the given type via the template
- Source:
(constant) recreatePersonJobsController
Recreate jobs for a person
Handles GET /recreate/:UIDperson
(constant) recreatePersonsExternGuests
Recreates person / extern / guest objects.
Inherits stage, hierarchie and banner from the member group, then re-renders.
(constant) recreateWithMemberRow
Recreates group / list / dlist / email (and any other type that stores its
data in Member.Data and display fields in Member).
ObjectBase holds system-versioned fields; Member holds display fields.
redisUrl :string
Type:
- string
- Source:
(constant) reduceFiltered
Reduces filtered results to unique entries by UIDBelongsTo
(constant) reduceObjects
Reduces duplicate objects to unique entries based on UIDBelongsTo comparison
(constant) regenerateEmbeddings
Regenerate AI embeddings for all objects of a specific type
Endpoint to regenerate embeddings for person, extern, guest, or job types.
Processes objects in batches with single API call per batch for cost efficiency.
(constant) registerBotActionTemplates
Register bot action templates (POST /register)
(constant) registryAuth
Das Gate für den gesamten Router — `bot` und `employee`, kein Mandanten-Nutzer.
Begründung und Abgrenzung: Dateikopf.
Statischer Import ist hier in Ordnung: `addons.js` macht es genauso, und die
Middleware wird erst beim ersten Request ausgeführt — zu diesem Zeitpunkt sind
die Secrets konfiguriert.
- Source:
(constant) removeDlistFilterAction
Handles removal of dlist filter actions: include, exclude, intersect.
Removes all entries produced by the filter, deletes the filter's Links
and ObjectBase row, then flags the target list for WebSocket update.
(constant) removeFamilyAction
Handles the `family` remove action.
Deletes the `family` / `familyFees` link from the family object.
If that was the last member the orphaned Member row is cleaned up;
otherwise membership fees are recalculated for the affected family.
(constant) removeFilter
Removes a filter from a target and cleans up associated entries
(constant) removeListEntryAction
Handles the `list` remove action.
Fired when a list entry is deleted. Cleans up any `member`, `member0`, and
`dynamic` Links that dlists created for this entry, and notifies subscribers
via event-bus and WebSocket.
The entry itself has already been deleted by the time this action runs;
only the downstream dlist bookkeeping remains.
(constant) removeMemberAction
Handles tree-structural remove actions for objects leaving a group hierarchy.
Supported types: `job`, `guest`, `groupGuest`, `group`, `person`, `extern`,
`achievement` (stub), `event`, `eventJob`.
Steps:
1. Resolve current memberships of the removed object.
2. Delete `member` links for `job` and `guest`.
3. Job-specific: delete visibility filters and rebuild the holding person's access.
4. groupGuest: queue removal for all guest children; clean up memberG/memberGA links.
5. guest: delete the guest ObjectBase row.
6. group: cascade-delete member links; queue filter removals; re-check list entries.
7. person/guest/extern/job: re-check list entries via `checkEntries`.
8. event: rebuild object visibility.
9. Publish removal events and trigger WebSocket updates.
(constant) removeMemberFromFamily
Remove member from family (DELETE /:UIDmember)
Removes a member from their current family and creates a new family for them.
Supports rebate mode for fee calculations.
- Source:
(constant) removePersonsFromEmail
Remove persons from email
- Source:
(constant) removeTailnetRunner
Remove a runner from a tailnet network.
- Source:
(constant) removeTailnetRunner
DELETE /api/tailnets/:UID/runners/:runnerUID
(constant) removeVisibilityFilterAction
Handles removal of visibility filter actions: visible, changeable.
Removes the filter's `list` Link, deletes the filter object itself, then
rebuilds the access table for the affected list/dlist/email and flags it
for WebSocket update.
(constant) renewalMaintenance
Maintenance endpoint to check renewal of achievements
- Source:
(constant) replayEvents
Replay events for an organization in strict cursor order.
- Source:
(async, constant) requalify
Requalifies all jobs based on the given function template.
This function fetches all jobs related to the specified function template and updates their qualification status.
It also recreates the jobs with the updated qualification status.
This function should be called, when a function template including its achievement rules is updated.
- Source:
(constant) requalifyPersons
Requalifies all jobs based on function templates
- Source:
requestQueue :Array.<{requestFn: function(), resolve: function(), reject: function()}>
Type:
- Array.<{requestFn: function(), resolve: function(), reject: function()}>
- Source:
(constant) requestUpdateLogger
Logs write/update requests.
- Source:
(constant) requireRunnerAuth
Middleware: authenticate runner credential.
(constant) resolveGroupDelta
Queries the transitive group memberships (delta) for the given target group.
(constant) resolveSecondLevelGroups
Queries the second-level parent groups needed for visual display updates.
(constant) resolveVisibilityOrganization
Die Organisation fuer ein Event-Payload, wenn der Aufrufer keine mitgibt.
Der Aufrufer weiss sie in aller Regel (`action.UIDroot`, `req.session.root`)
und reicht sie durch — hier steht nur der Rueckfall. Ohne ihn traegt das Event
die Null-Organisation, obwohl die Zeile am richtigen Objekt landet; genau
dieser Fehler war schon einmal da (Commit `0738a87`).
Der Import ist dynamisch, damit die Baum-Ebene nicht zyklisch auf die
Router-Ebene zeigt (`organizationUtils` zieht `Router/orga/service.js`).
- Source:
(constant) restartActionTemplate
Restart action template (POST /restart/:UID)
(constant) restorePerson
Restores a person from historical data
- Source:
(constant) reverseGeocode
Reverse geocode coordinates to a place name
- Source:
(constant) reverseGeocodeController
Reverse geocode: coordinates → place name
(constant) revertList
Revert all entries in a list to their state at a given timestamp
- Source:
(constant) revertListController
Handle POST /list/revert/:UID/:timestamp — revert list entries to a previous state
- Source:
(constant) revokeRunner
Soft-revoke: set Data.status=revoked, keep ObjectBase for audit. Die
Companion-Auth (Member.lookup_key ↔ ObjectBase) lehnt weitere Requests ab.
- Source:
(constant) revokeRunner
POST /api/runners/:UID/revoke
- Source:
(constant) rfc3339ToMysql
Convert an RFC3339 timestamp (with optional timezone offset and microsecond
fraction) into a UTC MariaDB timestamp literal for FOR SYSTEM_TIME AS OF.
Returns null when the value is not parseable.
(constant) rootOfShare
Der Root (Basis-Share) eines Share-Objekts, plus die Angabe, ob es selbst die
Basis ist. `null`, wenn die UID kein Share ist.
(constant) runMigrations
Discovers and runs all pending migration modules from this directory.
Each migration file must export:
- `id` {string} — stable unique identifier used to track applied state
- `name` {string} — human-readable description shown in progress
- `migrate` {async fn} — receives the migration API and performs the change
Migrations support any logic: SQL schema changes, JSON data transformations,
backfills, or any combination. Files are executed in alphabetical filename order.
(constant) runnerInOrg
- Source:
(constant) sanitizeActions
Sanitize an actions payload from a function template. Malformed entries
(non-strings) are dropped. Unknown *string* actions are preserved: the
server only ever *grants* actions it knows (`hasActionPermission`), stored
unknown actions are inert and stay available for forward compatibility.
- Source:
(constant) saveAppsController
PUT /kpe20/orgaSettings/apps
Saves the app registry for the current organisation.
Request body: { [appId]: { domain, roles, title, description?, port? } }
(constant) saveDomainsController
PUT /kpe20/orgaSettings/domains
Validates and saves the domain settings for the current organisation.
Request body: { [domain: string]: "internal" | "external" }
(constant) scanHostKey
Scan the SSH host keys of a Git host via `ssh-keyscan`.
The host key is the server identity of the Git host (used for
`StrictHostKeyChecking` when the worker clones). It is NOT the credential
key pair — the credential is the client identity.
`ssh-keyscan` runs on the backend because browsers cannot execute it. The
host is validated strictly (hostname + optional port) and passed as a plain
argv element (no shell), so there is no command injection surface.
Only ed25519 keys are collected — the modern, strongly recommended type.
(constant) scanHostKey
GET /project/credential/hostkey?host=...
(constant) searchShares
Search org-owned repository/directory shares the current user may see.
Visibility is derived from the projects a share is linked to: a share is
returned when at least one of its linked projects is visible to the user
(admin users see all org shares). Because a share carries no organization
itself, the result is additionally scoped to the caller's organization via
the project chain.
(constant) searchShares
GET /project/project/shares/search
(constant) sendError
Send an error response using the contract envelope.
- Source:
(constant) sendErrorFrom
Normalize a thrown error into a contract error response. Errors that already
carry an ApiError shape (code/status) are honoured; everything else becomes 500.
- Source:
(constant) sendMessageBuffered
Sends a message to a client, buffering update messages when the socket is
temporarily disconnected.
**Behaviour:**
- If `message.update` is truthy, the message is treated as a list-update
notification. Only the latest such notification per client is kept
(`clientData.updateBuffer`). If the socket is connected it is sent
immediately and the buffer is cleared; otherwise it waits.
- Any other message payload is forwarded directly via `socket.emit`
(Socket.IO itself buffers these internally when disconnected).
- When called with `message = null` the function flushes any buffered
update notification. This is triggered automatically after a successful
`setRoot` event.
- Source:
(constant) sendOk
Send a successful response using the contract envelope.
- Source:
(constant) sendProgressStatus
Broadcasts a long-running operation's progress status to all clients of an organisation.
This is an organisation-wide broadcast — every connected client whose
`UIDroot` matches `root` receives `{ progressStatus: status }`.
The frontend forwards the payload to any registered `cbProgressData` callbacks.
Typical callers: import jobs, bulk operations, async queue workers.
- Source:
(constant) sendTransaction
Sends a transaction-completion message to a single specific client.
Unlike the organisation-wide broadcast functions, this targets exactly one
socket by its `socketID` room. Emits a `transactionComplete` event with
the provided data payload.
Called after a bulk database transaction finishes, so the initiating client
can refresh its state.
- Source:
(constant) sendWebSocketUpdates
Sends WebSocket update notifications for all affected groups.
Person and extern updates are skipped (handled by migratePerson storage).
(constant) setOrgaRoot
Set the organization root for the current session
- Source:
(constant) setResponsibleController
Set responsible person
Handles POST /responsible/:event
(constant) setupSocketHandlers
Registers all Socket.IO connection and event handlers.
Called once at server startup with the Socket.IO `io` instance.
Stores `io` in the module-level `ioInstance` so that other exported
functions (e.g. `broadcastUpdate`) can reach connected sockets.
**Connection lifecycle:**
1. Validates JWT-authenticated `socket.data.user` (set upstream by auth middleware).
2. Requires a `?id=` query parameter to derive a stable `socketID`.
3. Requires organisation context from `x-organization` header or token claims.
4. Creates an entry in clientDataStore keyed by `socketID`.
5. Joins the socket to a private room named `socketID` for targeted emits.
6. Registers per-socket event handlers (see below).
7. Removes the `clientDataStore` entry on disconnect.
**Socket events handled per connection:**
| Event | Payload | Effect |
|-----------------|-------------------------------|--------|
| `transaction` | `{ start: boolean }` | Marks bulk-transaction mode on `socket.data.transaction` |
| `monitor` | `{ UID: string\|null }` | Adds list UID to `updateAbo` Set; `null` clears the Set |
| `monitorObject` | `{ UID: string\|null }` | Adds object UID to `objectAbo` Set; `null` clears the Set |
| `setRoot` | `{ UIDroot: string }` | Updates active organisation for the connection |
| `disconnect` | reason string | Removes entry from `clientDataStore` |
| `error` | Error | Logs the socket error |
- Source:
(constant) shareEmail
Share email (delegate to list share function)
- Source:
(constant) shareLinkToContract
Map a raw ObjectBase row to the contract Share object for a share that also
carries a memberA/member link (used by snapshot reads over system time).
- Source:
(constant) shareToContract
Map an ObjectBase row to the contract Share object. The share belongs to the
org, so the contract `project_uid` is taken from the project context that
resolved the share (via memberA/member link), never from UIDBelongsTo.
- Source:
(constant) sharedFieldsChanged
Die geteilten Felder, die eine Schreiboperation WIRKLICH veraendert hat.
Praesenz allein genuegt nicht: ein Aufrufer kann das ganze Objekt schicken, obwohl
der geteilte Teil unveraendert ist. Nur bei einer echten Aenderung darf bei den
Geschwistern abgeglichen — also auch entfernt — werden.
Konten werden entschluesselt verglichen (`accountsEqual`): `IBANencrypted` traegt
einen zufaelligen Salt, ein Vergleich der Rohdaten schluege deshalb immer fehl.
- Source:
(constant) sharedFieldsTouched
Die geteilten Felder, die eine Schreiboperation tatsaechlich geliefert hat.
Grundlage ist der PATCH (`req.body`), nicht die gemergten Daten: nur so ist
„Feld war nicht Teil dieses Speicherns" von „Feld wurde geleert" zu trennen. Die
gemergten Daten tragen den alten Wert immer weiter, ein Loeschen sieht dort aus
wie „unveraendert".
- Source:
(constant) sharedKeyOf
Schluessel, an dem ein geteilter Eintrag wiedererkannt wird. Ein leerer
Rueckgabewert heisst „kein Schluessel" — solche Eintraege werden nur angehaengt,
nie zugeordnet, damit zwei leere Eintraege nicht miteinander verschmelzen.
- Source:
(constant) sharesOfProject
Die Shares eines Projekts — beide Link-Richtungen.
(constant) snapshotAsOf
GET snapshot?asOf= - historical snapshot.
(constant) snapshotCurrent
POST snapshot/current - authoritative DB time snapshot.
(constant) statsKey
Redis-Key für eine Organisation.
- Source:
(constant) suggestFamiliesByLastName
Suggest families by last name (GET /suggest/:lastName)
Provides family suggestions based on phonetic matching of last names.
Uses double metaphone algorithm for fuzzy matching and scoring.
- Source:
(constant) sweepDisconnectedClients
Verwirft Clients, die sich innerhalb der Schonfrist nicht neu verbunden haben.
Ohne das wuerde jeder abgestuerzte Tab seinen Eintrag dauerhaft behalten.
- Source:
(constant) syncBySourceController
- Source:
(constant) syncShareFilters
Den Filter-Spiegel eines Shares an seinen aktuellen Projekt-Link angleichen.
Ohne Projekt (z. B. nach dem Entlinken) fallen die gespiegelten Filter weg.
Kein Rebuild — nur die Struktur.
Innerhalb einer Transaktion **muss** `connection` mitgegeben werden, damit
Share und Filter-Satz zusammen committen.
(constant) syncUsers
Sync identifyer links for selected persons.
- Source:
(constant) syncUsersBySource
Sync identifyer links and add self-visibility for all persons in one orga.
- Source:
(constant) syncUsersController
- Source:
targetUID
`l.UIDTarget`, never `t.UID`: a dangling link has no `t` row, and the
target UID is exactly what the removal has to match on.
(constant) toLegacyDomainValue
Gegenstück zu normalizeDomainEntry für den **Vault-Schreibpfad**.
Solange `REGISTRY_READ_MODE` nicht auf `db` steht, schreibt `saveOrgDomains`
weiter nach Vault — und dort lesen `shared-auth` (`organizationDomains.js`)
und die übrigen Konsumenten eine **Zeichenkette**: sie vergleichen mit
`state === 'internal'` bzw. `state === 'verified'`. Ein Objekt dort würde sie
brechen. Die Rückübersetzung ist verlustfrei, weil die drei Altwerte die drei
sinnvollen Kombinationen genau abdecken.
(constant) toProject
Convert a raw project row (with metadata_json envelope) to the app object
shape: UID/Title/Display/Data (+ source_updated_at). Same convention as
list/dlist/location/event objects.
- Source:
(constant) toRfc3339Micro
Format a DB timestamp (Date, string or number) as RFC3339 with microsecond
precision (the contract's API time format).
- Source:
(constant) toRunner
- Source:
(constant) toShare
Map a share row to the app Share object (UID / Type / project_uid / metadata
/ linkType). `project_uid` comes from the project context; the organization is
no longer readable from the share itself.
(constant) toTailnet
- Source:
(constant) triggerMaintenance
Triggers the organisations tree queue
- Source:
(constant) typesIn
Build a SQL IN (...) list of quoted literals from a type set.
- Source:
(constant) unlinkShare
Remove a link between a share and a project (requires **changeable**).
Fires the matching level event; the share object itself is never removed here.
(constant) unlinkShare
DELETE /project/project/:projectUid/shares/:shareUid/link
(async, constant) updateAchievements
Consolidates all achievement UIDs for a person into their Data object.
This function fetches all achievements linked to a specific person, encodes their UIDs in base64 format,
and stores this consolidated array in the person's Data object. This approach enables faster filtering
and querying of persons based on their achievements without needing to perform complex joins each time.
The function only updates the person's record if the achievements data has actually changed,
minimizing unnecessary database operations.
- Source:
(constant) updateAction
Update an existing action
- Source:
(constant) updateActionTemplate
Update action template (PUT / and POST /)
Handles single template update for the current organization only.
For multi-org bot registration and creation, use POST /register endpoint instead.
(constant) updateConfig
Notifies all clients of an organisation that the app configuration has changed.
This is an organisation-wide broadcast — every connected client whose
`UIDroot` matches `root` receives `{ updateConfig: , app }`.
The frontend re-fetches config for the specified app.
- Source:
(constant) updateEmail
Update email data
- Source:
(constant) updateEventController
Update event controller
Handles POST /:UID
- Source:
(constant) updateEventList
Update a list for an event
(constant) updateEventListController
Controller for updating an event list
(constant) updateEventTemplate
Update an existing event template
(constant) updateEventTemplateController
Controller for updating an existing event template (POST)
(constant) updateExistingExtern
Update an existing extern record, or migrate a person record to type 'extern'.
Delegates shared update logic to personHelpers.
Note: `req` is forwarded because updatePersonData / handleGroupMembershipMigration
internally require session information from it.
- Source:
(constant) updateExistingPerson
Update an existing person record, or migrate an extern record to type 'person'.
Note: `req` is still forwarded because the shared personHelpers functions that
handle DB updates (`updatePersonData`, `handleGroupMembershipMigration`) require
session information from it internally.
- Source:
(constant) updateExtern
POST /:UID — Partial update of extern data.
By default only accepts 'extern' type.
With query parameter `?strict`, also accepts 'person' type.
(constant) updateFamily
Update family data (POST /:UID)
Updates family information including address, contact details, accounts, and fee address.
Handles both family-level updates and member-specific updates.
- Source:
(constant) updateGroup
POST /:UID — partial update of an existing group.
- Source:
(constant) updateGroupData
Updates an existing group's display data, sister-group link, banner, and fees.
Unlike createOrUpdateGroup, this merges the request body over existing data
rather than replacing it, and rejects changes to immutable fields.
- Source:
(constant) updateGroupMembership
POST /:group/:UID — Update group membership for an extern.
- Source:
(constant) updateGroupMembership
Updates group membership for a person
- Source:
(constant) updateGroupMembershipShared
Shared function to update group membership for person or extern
Response: `result` carries the new state of the object in the same shape the websocket
clients receive (`{ person, parent }`), so a caller can apply it directly instead of
re-fetching or waiting for the socket echo. It used to be `null`, which forced clients
to reload and left them with a stale view until the echo arrived.
- Source:
(constant) updateJobController
Update job template
Handles POST /:UID
(constant) updateList
Update existing list/dlist
- Source:
(constant) updateMemberDisplay
Updates the display fields in the Member table (no system versioning).
(constant) updateObjectBaseVersioned
Updates system-versioned ObjectBase columns with proper backDate handling.
Finds the earliest safe timestamp for the backDate and wraps the UPDATE in a
transaction so MariaDB records the change against the existing history row.
(constant) updatePerson
Updates a person's data in the database (partial update endpoint)
This endpoint (POST /:UID) performs partial data updates by merging the request body
with existing data. It only updates the Member table, not ObjectBase fields.
Used by both person and extern endpoints via delegation.
By default accepts both 'person' and 'extern' types.
With query parameter `?strict`, only 'person' type is accepted.
- Source:
(constant) updatePersonData
Updates person data in both ObjectBase and Member tables (full object update)
This function is used during create/update operations (PUT /:group) where a complete
object with all rendered fields is provided. It updates both database tables, handles
event publishing, family address updates, and WebSocket notifications.
Difference from updatePersonPartial:
- Updates BOTH ObjectBase and Member tables (vs. Member only)
- Expects a fully rendered object with Title, Display, SortBase, etc. (vs. partial data)
- Used for PUT operations during create/update flows (vs. POST for partial updates)
- Uses publishChangeEvent for group hierarchy (vs. direct publishEvent)
- Source:
(constant) updatePersonIdentifyer
Update a person's UIDuser and rebuild identifyer link.
- Source:
(constant) updatePersonIdentifyerController
- Source:
(constant) updatePersonPartial
Updates person/extern data with partial merge (used by POST /:UID endpoint)
This function performs partial data updates by merging the provided data with existing
Member data. It only updates the Member table, not ObjectBase fields. Used by both
person and extern POST endpoints for partial updates without changing group membership.
Difference from updatePersonData:
- Updates ONLY Member table (vs. both ObjectBase and Member)
- Performs partial merge with existing data (vs. full object replacement)
- Uses direct publishEvent (vs. publishChangeEvent through group hierarchy)
- No group membership handling (vs. full create/update flow)
- Source:
(constant) updatePersonStatus
Update person status in email
- Source:
(constant) updateProject
Update project metadata (requires admin on the project).
- Source:
(constant) updateProject
POST /project/project/:UID
(constant) updateQueueStatus
Broadcasts the current job-queue status to all clients of an organisation.
This is an organisation-wide broadcast — every connected client whose
`UIDroot` matches `root` receives the message, regardless of individual
subscriptions. The status is also cached in `clientData.queueStatus` for
reference.
The frontend receives `{ root, queueStatus }` and calls any registered
`setQueueStatus` callbacks across all active subscriptions.
- Source:
(constant) updateShare
Update share metadata (requires admin on the project or `Visible.admin` on
the share, i.e. the owner).
(constant) updateShare
POST /project/project/:projectUid/shares/:shareUid
(constant) updateTailnet
- Source:
(constant) updateTailnet
POST /api/tailnets/:UID
(constant) updateTemplate
Notifies all clients of an organisation that their UI template has changed.
This is an organisation-wide broadcast — every connected client whose
`UIDroot` matches `root` receives `{ root, updateTemplate: }`.
The frontend reacts by re-fetching the template configuration.
- Source:
(constant) updateTemplateEvent
Like updateTemplate but for the event application specifically.
Emits `{ root, updateTemplateEvent: }` to all matching clients.
- Source:
(constant) updateUserSettings
Notifies all sockets of a specific user that their own settings have changed.
Only targets sockets belonging to the given Keycloak userUID, so other users
in the same organisation are not affected.
- Source:
(constant) uploadAppIconController
POST /kpe20/orgaSettings/apps/:appId/icon
Upload an icon image for a specific app to the public bucket.
Stores the resulting public URL in Vault under apps[appId].icon.
(constant) uploadAvatarFiles
Upload avatar images (POST /avatar/:UID)
- Source:
(constant) uploadBannerFiles
Upload banner images (POST /banner/:UID)
- Source:
(constant) uploadEditorFiles
Upload editor files (POST /editor/:expire/:UID)
- Source:
(constant) uploadImagePdfFiles
Upload image and PDF files (POST /imagePdf/:UID)
- Source:
(constant) uploadLanguageFileController
Upload a language file.
Handles POST /:api
- Source:
(constant) uploadLanguageOrChangelog
Upload language or changelog files (POST /:type(language|changelog)/:api)
- Source:
(constant) uploadPrivateFiles
Upload private files (POST /:UID)
- Source:
(constant) uploadPrivateRailFiles
Upload private rail files (POST /rail/:UID)
- Source:
(constant) uploadPublicFiles
Upload public files with expiration (POST /public/:expire/:UID)
- Source:
(constant) uploadPublicRailFiles
Upload public rail files for apps (POST /rail/:app/)
- Source:
(constant) upsertPortalPendingController
- Source:
(constant) upsertRuntime
Topologie-Heartbeat in die Companion-Zeile (Member.Data) schreiben — nie in
ObjectBase (kein History-Wachstum). Der Runner bleibt Topologie-Teil von
members; Job-/Session-Runtime liegt beim Zusteller (ide-server).
- Source:
(constant) upsertSearchIndex
Replace all SearchIndex entries for a given object + index type.
Deletes existing rows then inserts the new values.
Safe to call with an empty values array (just removes stale entries).
- Source:
(constant) usertPortalPending
Create or refresh a portal pending link and token payload.
- Source:
(constant) validateBodyUUID
Validator for request body UIDs
Some endpoints receive UIDs in the request body
- Source:
Example
api.put('/', validateBodyUUID(['UID', 'UIDparent'], false), controller.create);
(constant) validateDomainEntry
Prüft einen einzelnen Domain-Eintrag gegen die Regeln seiner Art.
Gibt eine Meldung für **Menschen** zurück, keinen Code: sie landet direkt in
der Fehlerliste der Oberfläche.
(constant) validateMultipleUIDs
Convenience middleware for validating multiple common UUID parameters
Useful for endpoints that accept multiple UIDs
- Source:
Example
api.put('/:member/:group/:function', validateMultipleUIDs, controller.create);
(constant) validateQueryUUID
Validator for query parameters (not route params)
Some endpoints receive UIDs in query strings
- Source:
Example
api.get('/search', validateQueryUUID(['user', 'loginUser']), controller.search);
(constant) validateRegisterInput
Validates input for register endpoint
- Source:
(constant) validateSingleUID
Convenience middleware for validating a single UID parameter
Most common use case for DELETE, GET, POST endpoints
- Source:
Example
api.delete('/:UID', validateSingleUID, controller.delete);
(constant) validateUUID
Validates UUID parameters in the request
- Source:
Examples
// Validate single parameter
api.delete('/:UID', validateUUID(['UID']), controller.delete);
// Validate multiple parameters
api.put('/:member/:group', validateUUID(['member', 'group']), controller.update);
(constant) validateUUIDParam
Express param middleware for automatic validation
Apply this to automatically validate any route parameter
- Source:
Example
// Automatically validate all :UID parameters in this router
api.param('UID', validateUUIDParam('UID'));
// Now all routes with :UID are automatically protected
api.delete('/:UID', controller.delete);
api.get('/:UID', controller.get);
(constant) validateUser
Validate user access for an organization and populate cache
(constant) validateUserForOrganization
Validate user access to organization with caching
- Source:
(constant) visibilityFilter
Visibility filter function
(constant) visibilityList
Visibility list filter
(constant) writeEventLog
Persist an event to eventLog. When `connection` is provided (inside a
transaction) the write joins the same transaction as the business change, so
no permanent "data change without event" can occur.
The primary key is (Timestamp, EventKey) at microsecond precision; a repeat
publish within the same microsecond refreshes the payload instead of losing it.
- Source:
(constant) wsFakeUser
Pushes a fake-login (impersonation) state update to all sockets of a user.
When an admin impersonates another user the session's `fakeLogin` flag
changes. This broadcasts `{ baseUser, user, fakeLogin }` to every socket
belonging to that user so the frontend can update the displayed identity
without a page reload.
- Source:
Methods
accountKey()
Schluessel, an dem ein Konto wiedererkannt wird: die IBAN. `IBANshow` ist beim
verschluesselten Konto maskiert (XXXXXX), taugt aber als Rueckfall, wenn gar keine
IBAN vorliegt. Ein leerer Rueckgabewert heisst „kein Schluessel" — solche Konten
werden nie zugeordnet, damit zwei leere Eintraege nicht verschmelzen.
Entschluesselt wird nur eine KOPIE, ausschliesslich fuer den Schluessel: der
Eintrag selbst bleibt in der Form, in der er vorliegt. Sonst wanderte die IBAN
im Klartext in die Ablage (siehe `familyAccountsOf` und `adjustMemberData`).
- Source:
(async) accountsReadOnly(req) → {Promise.<boolean>}
Checks if the current user has read-only access to group accounts.
Returns true if accounts should be protected from writes for this user.
Parameters:
| Name | Type | Description |
|---|---|---|
req |
ExpressRequestAuthorized |
- Source:
Returns:
- Type
- Promise.<boolean>
(async) addAction(action)
Handles various actions related to adding objects to a tree structure, such as filters, memberships, visibility,
and other hierarchical updates. This function processes different types of actions and performs corresponding
database operations and updates.
Parameters:
| Name | Type | Description |
|---|---|---|
action |
treeAction | The action object containing details about the operation to be performed. |
- Source:
Throws:
-
Throws an error if any database operation or action processing fails.
- Type
- Error
(async) addAction()
- Source:
(async) adjustMemberData(member, object, organization, touchedopt) → {Promise.<(Object|undefined)>}
Adjusts member data with family-shared information (address, email, phone, accounts)
Parameters:
| Name | Type | Attributes | Description |
|---|---|---|---|
member |
FamilyMemberObject | The family member to update | |
object |
FamilyMemberObject | The source object containing family data | |
organization |
string | Organization UID for event publishing | |
touched |
Array.<string> |
<optional> |
Shared fields the caller supplied. When set, those fields are reconciled against the source: shared entries the source no longer carries are REMOVED. When omitted, the sync stays purely additive (nothing is ever removed) — that keeps full-database callers harmless. |
- Source:
Returns:
Updated member data or undefined
- Type
- Promise.<(Object|undefined)>
(async) alterEnum(query, table, values)
Führt ein `ALTER TABLE … MODIFY COLUMN Type enum(…)` auf einer
system-versioned Tabelle aus. Setzt und entfernt das Session-Flag selbst,
damit kein Aufrufer es vergessen kann.
Parameters:
| Name | Type | Description |
|---|---|---|
query |
function | |
table |
string | |
values |
Array.<string> |
app2serverURL(baseDir) → {object}
Combine OpenAPI documents by resolving $ref references
Parameters:
| Name | Type | Description |
|---|---|---|
baseDir |
string | Base directory for resolving references |
- Source:
Returns:
- Combined OpenAPI document
- Type
- object
appBaseDomain() → {string}
Basis-Domain für interne Hosts, zur **Laufzeit** gelesen.
Nicht als Modulkonstante: in den Dev-Umgebungen steht `APP_BASE_DOMAIN` auf
`dev.commtool.org`, und ob die Variable beim Import schon gesetzt ist, hängt
davon ab, wer die Datei zuerst lädt. Ein einmal eingefrorener Wert würde dort
Hosts für die Produktions-Basis erzeugen — die Auflösung fände nichts.
Returns:
- Type
- string
appendMissing(current, wanted) → {Array.<string>|null}
Hängt fehlende Literale an ein `enum` an — in der Reihenfolge, in der sie
übergeben werden. Vorhandene Werte behalten ihre Position, damit bestehende
Zeilen ihren Index nicht verlieren.
Parameters:
| Name | Type | Description |
|---|---|---|
current |
Array.<string> | |
wanted |
Array.<string> |
Returns:
neue Liste, oder `null` wenn nichts fehlt
- Type
- Array.<string> | null
appendOutOfHierarchyLevels()
Assigns extra levels for out-of-hierarchy nodes when explicitly requested.
Parameters:
| Type | Description |
|---|---|
- Source:
applyLifeCycle(bucket) → {Promise.<void>}
Apply the lifecycle rules and never let a rejection escape.
A failing `setBucketLifecycle` must not hang startup: `initBucket` resolves
only via `applyLifeCycle`, so an unhandled rejection there would leave the
promise pending forever and `initS3` would silently stop (the outer
try/catch cannot catch a hang). The bucket stays usable, only the automatic
expiry of `public//...` files would not be in place - worth a loud
log line.
Parameters:
| Name | Type | Description |
|---|---|---|
bucket |
string |
- Source:
Returns:
- Type
- Promise.<void>
asUuid(value) → {string}
UID als Schluessel — **ohne** `Buffer.equals`.
Zwei Gruende, beide schon einmal teuer:
1. `listRebuildAccess` setzt `UID` aus seinem Parameter zusammen, nicht aus
der Abfrage: dort steht mal ein Buffer (Aufrufer `UUID2hex`), mal ein
String (Queue-Action). Ein `.equals` auf dem String wirft — und weil der
Aufruf im `try` der Rebuild-Funktion steht, verschwaende der Wurf die
**ganze** Meldung (genau der Fehler war schon da: der Personen-Rebuild
meldete nichts, weil `Visible.UIDUser` fehlte).
2. `binary(16)` in MariaDB traegt die ersten acht Bytes eines `UUID-…` in
geswappter Reihenfolge: `dacaa7a1-94b7-11f1-…` liegt als
`11f194b7dacaa7a1…` in der Zeile. `toString('hex')` ergaebe also einen
Hex-String, der **nicht** die UID ist — im Event-Key stuende eine UUID,
die es nicht gibt, und die Projektion haengte ihre Grants an ein
Phantom-Objekt. Deshalb `HEX2uuid` (dasselbe Paar wie in `sql-query`).
Verglichen wird die kanonische Form `UUID-…`, nicht der Rohbuffer.
Parameters:
| Name | Type | Description |
|---|---|---|
value |
unknown |
- Source:
Returns:
`UUID-…`, oder der Rohwert, wenn er nicht deutbar ist
- Type
- string
assertNoInvariantRisk(source, decision)
Guards the one assumption the repair leans on: as soon as a link to an EXISTING
target is removed, exactly one such link has to be kept. Without that the object
would silently end up with zero `memberA` links.
Parameters:
| Name | Type | Description |
|---|---|---|
source |
any | |
decision |
any |
(async) assertUsableProjectRunner(runnerUid, orgHex)
Validiert einen Projekt-Runner (metadata.runnerUid): muss ein `shared`-/
`team`-Runner derselben Orga sein (nicht `personal`, nicht `revoked`).
Leerer Wert (null/'') ist erlaubt und entfernt die Einstellung.
Parameters:
| Name | Type | Description |
|---|---|---|
runnerUid |
unknown | — Wire-UID („UUID-…") oder leer |
orgHex |
string |
- Source:
(async) authorizeRef(req, ref) → {Promise.<{uid: string, name: string}>}
Split a credentialsRef ({UID}/{name}) and authorize:
- user credential: only the owning user (or org admin)
- org credential: only org admins
Parameters:
| Name | Type | Description |
|---|---|---|
req |
ExpressRequestAuthorized | |
ref |
string |
Returns:
- Type
- Promise.<{uid: string, name: string}>
avatarUrlGen()
URL generator for avatar file uploads
- Source:
bannerUrlGen()
URL generator for banner file uploads
- Source:
bufferToEmbedding(buffer) → {Float32Array}
Convert Buffer from database to Float32Array
Parameters:
| Name | Type | Description |
|---|---|---|
buffer |
Buffer | Binary buffer from DB |
- Source:
Returns:
- Embedding vector
- Type
- Float32Array
bufferToUUID(buf) → {string}
MariaDB binary(16) mixed-endian buffer → standard uuid string.
Parameters:
| Name | Type | Description |
|---|---|---|
buf |
Buffer |
- Source:
Returns:
- Type
- string
buildCompactNodeLabel(node, fallback) → {string}
Builds a compact node label for tree rendering.
Parameters:
| Name | Type | Description |
|---|---|---|
node |
any | |
fallback |
string |
- Source:
Returns:
- Type
- string
buildCursor(seq) → {string}
Build a cursor from a numeric seq.
Parameters:
| Name | Type | Description |
|---|---|---|
seq |
number |
- Source:
Returns:
- Type
- string
buildDataFields(req, groupBannerTableopt) → {string}
Build extra SELECT column fragment from `Data` / `ExtraData` / `dataFilter` /
`groupBanner` query parameters.
Parameters:
| Name | Type | Attributes | Default | Description |
|---|---|---|---|---|
req |
ExpressRequestAuthorized | |||
groupBannerTable |
string |
<optional> |
'pmember' | Table alias that holds banner data |
- Source:
Returns:
- Type
- string
buildNodeAdjacency()
Builds adjacency maps for fast traversal.
- memberA is directed: parent -> child
- memberS is undirected: sibling <-> sibling
Parameters:
| Type | Description |
|---|---|
- Source:
(async) buildSnapshot(uidHex, orgHex, mysqlTime) → {Promise.<Object>}
Build the snapshot for a resolved UTC MySQL timestamp.
Parameters:
| Name | Type | Description |
|---|---|---|
uidHex |
string | |
orgHex |
string | |
mysqlTime |
string | 'YYYY-MM-DD HH:MM:SS.ffffff' UTC |
Returns:
- Type
- Promise.<Object>
calculateContentHash(text) → {Buffer}
Calculate content hash for change detection
Parameters:
| Name | Type | Description |
|---|---|---|
text |
string | Text to hash |
- Source:
Returns:
- SHA256 hash
- Type
- Buffer
changelogUrlGen()
URL generator for changelog file uploads
- Source:
(async) checkEntries(ObjectUID, UIDOrga) → {Promise.<void>}
Checks all entries for the given objects against their filters and performs necessary cleanup.
This function processes multiple objects, retrieves their filters, and checks each entry
to ensure they still match the current filter criteria, removing entries that no longer qualify.
Parameters:
| Name | Type | Description |
|---|---|---|
ObjectUID |
Buffer | Array.<Buffer> | Single object UID or array of object UIDs to check |
UIDOrga |
string | The organization UUID for multi-tenant scoping (must be string) |
Throws:
Will log an error if any operation fails during execution.
Returns:
- Type
- Promise.<void>
(async) checkEntry(entry, UIDOrga)
Checks a single entry against its filters and performs necessary add/remove operations.
This function evaluates whether an entry should be included or excluded from a list based on
the current filter state and publishes appropriate events for membership changes.
Parameters:
| Name | Type | Description |
|---|---|---|
entry |
any | Entry object with filter information and membership data |
UIDOrga |
string | The unique identifier of the organization (required) |
Throws:
Will log an error if any operation fails during execution.
(async) checkPersonListMember(UID, UIDOrga) → {Promise.<void>}
Asynchronously checks if a person is a member of specific lists/dynamic lists dlist) and performs related operations.
This is clled, when an objet has been updated or created, to check if any dynamic list has to be updated
This function retrieves all groups (sources) that the object identified by the given UID is a member of,
matches the object against those lists, by checking all object filters attached to the sources/groups, if they pass the filters
and performs necessary updates to the entries of the dynamic lists attached to these filters
In a second step it checks, if therre are entries which do not match the filters anymore and removes them from the dlist
Parameters:
| Name | Type | Description |
|---|---|---|
UID |
string | Buffer | The unique identifier of the object to check membership for. |
UIDOrga |
string | The unique identifier of the organization. |
Throws:
Will log an error if any operation fails during execution.
Returns:
Resolves when the operations are completed.
- Type
- Promise.<void>
(async) cleanEventLog(timestampopt) → {Promise.<void>}
- Deletes events from the `eventLog` table where the `Timestamp` is older than the specified timestamp.
- If `timestamp` is `null`, the default value is calculated as the UNIX timestamp of the first day of the previous month.
- After deletion, the `eventLog` table is optimized to reclaim storage and improve performance.
- The database transaction is executed with a query timeout of 100 seconds.
Parameters:
| Name | Type | Attributes | Default | Description |
|---|---|---|---|---|
timestamp |
number | null |
<optional> |
null | The UNIX timestamp (in seconds) to use as the cutoff for deletion. Defaults to the first day of the previous month if not provided. |
- Source:
Throws:
-
Logs any errors encountered during the operation.
- Type
- Error
Returns:
Resolves when the cleanup operation is complete.
- Type
- Promise.<void>
(async) clearMirroredFilters()
Alle gespiegelten Filter-Links eines Shares entfernen (kein Projekt mehr).
clearOrgReleaseOverride(appKey, orgId) → {Promise.<boolean>}
Nimmt eine Organisation aus dem Canary. Ab hier folgt sie wieder dem Zeiger —
das ist der Rollback, und er braucht keinen Rückbau eines Deployments.
Parameters:
| Name | Type | Description |
|---|---|---|
appKey |
string | |
orgId |
string |
Returns:
true, wenn wirklich ein Eintrag entfernt wurde
- Type
- Promise.<boolean>
compareDOWN(filterA, filterB) → {number}
Comparator function for sorting filters in descending order during filter removal.
Sorts by filter type priority: include > exclude > intersect (reverse order)
Parameters:
| Name | Type | Description |
|---|---|---|
filterA |
Object | First filter object with Type property |
filterB |
Object | Second filter object with Type property |
- Source:
Returns:
-1 if filterA < filterB, 0 if equal, 1 if filterA > filterB
- Type
- number
compareUP(filterA, filterB) → {number}
Comparator function for sorting filters in ascending order during filter rebuilding.
Sorts by filter type priority: include > exclude > intersect
Parameters:
| Name | Type | Description |
|---|---|---|
filterA |
Object | First filter object with Type property |
filterB |
Object | Second filter object with Type property |
- Source:
Returns:
-1 if filterA < filterB, 0 if equal, 1 if filterA > filterB
- Type
- number
(async) compressTable(tableName, entityKeys, chunkSize, dryRun, connection, onProgress) → {Promise.<{squeezed: number, inserted: number, originalTotal: number, newTotal: number}>}
Compress a single system-versioned table.
Parameters:
| Name | Type | Description |
|---|---|---|
tableName |
string | |
entityKeys |
Array.<string> | |
chunkSize |
number | |
dryRun |
boolean | |
connection |
||
onProgress |
Returns:
- Type
- Promise.<{squeezed: number, inserted: number, originalTotal: number, newTotal: number}>
computeNodeLevels()
Computes rank levels for nodes by traversing memberS bands and memberA jumps.
Parameters:
| Type | Description |
|---|---|
- Source:
configLifeCycle(bucket) → {Promise.<void>}
Lifecycle rules for a bucket - the ONLY place these rules are maintained.
`setBucketLifecycle` REPLACES the complete lifecycle configuration of the
bucket: rules added manually in the Backblaze/MinIO console (or by another
service) are gone with the next start of this backend. So change rules here,
never in the console.
Scope: only `public//...` keys are covered, matching `publicUrlGen`
(`public///.`). `public/0` deliberately has NO rule
(= never expires). Object artefacts (`/...`) and app assets
(`locations/...`, `events/...`, `admin/...`, `config/...`, `db/...`) are
intentionally NOT covered and therefore kept forever.
Parameters:
| Name | Type | Description |
|---|---|---|
bucket |
string |
- Source:
Returns:
- Type
- Promise.<void>
(async) connectRedis() → {Promise.<boolean>}
Einmaliger Verbindungsaufbau. Nur über initializeRedis aufrufen.
- Source:
Returns:
true bei Erfolg, false solange die Secrets fehlen
- Type
- Promise.<boolean>
cosineSimilarity(a, b) → {number}
Calculate cosine similarity between two embeddings
Parameters:
| Name | Type | Description |
|---|---|---|
a |
Float32Array | First embedding |
b |
Float32Array | Second embedding |
- Source:
Returns:
- Similarity score (0-1)
- Type
- number
createNodeHierarchyLookup(nodeMap)
Creates a hierarchy lookup function for nodes.
Parameters:
| Name | Type | Description |
|---|---|---|
nodeMap |
Map.<string, any> |
- Source:
Returns:
credentialsRootFor()
KV2 credentials root for one organisation.
decrypt(encryptedData, passphrase) → {string}
Decrypts an AES-128 encrypted Base64 string using a passphrase.
Parameters:
| Name | Type | Description |
|---|---|---|
encryptedData |
string | The encrypted string (Salt and encrypted text in Base64, separated by colons). Supports both old format (salt:iv:encrypted) and new format (salt:encrypted). |
passphrase |
string | The passphrase used to derive the decryption key and IV. |
- Source:
Returns:
- The decrypted Base64 string.
- Type
- string
(async) deleteAppByUid(uid, appIdopt)
Löscht eine App samt ihrer Links.
`appDomain` wird beim Löschen mitgenommen, obwohl der Bestand seit der
Umstellung keine solchen Links mehr anlegt: ein früherer Import hat sie
erzeugt, und ein Löschpfad, der sie stehen ließe, hinterließe Karteileichen.
Parameters:
| Name | Type | Attributes | Description |
|---|---|---|---|
uid |
string | ||
appId |
string |
<optional> |
nur für den Log |
(async) deleteDir()
Helper function to delete directories recursively
- Source:
(async) deleteFunctionTemplate(req, res) → {Promise.<void>}
Deletes a function template if there are no jobs linked to it.
This function checks if any jobs are associated with the specified function template.
If jobs are found, deletion is denied and a message is returned listing the members with jobs.
If no jobs are linked, the function template and all related achievement links are deleted.
The template update function is called after successful deletion.
Parameters:
| Name | Type | Description |
|---|---|---|
req |
Object | Express request object, expects `params.UID` for the template UID and `session.root` for the root user UID. |
res |
Object | Express response object used to send JSON responses. |
Returns:
Sends a JSON response indicating success or failure.
- Type
- Promise.<void>
(async) deleteVaultSecret(path) → {Promise.<boolean>}
Permanently delete a KV2 secret (all versions) via the metadata API.
`@commtool/vault-secrets` has no delete function, so we issue the DELETE
directly. The KV2 metadata endpoint (`{mount}/metadata/{path}`) removes the
secret including version history; the `data` endpoint alone would only soft-
delete the latest version.
Parameters:
| Name | Type | Description |
|---|---|---|
path |
string | KV2 data path, e.g. `orgas/data/{orgId}/credentials/{ref}` |
Returns:
true when the secret was deleted
- Type
- Promise.<boolean>
deliverToClient(socketID, clientData, event, payload)
Stellt eine Nachricht zu — oder puffert sie, wenn der Client nicht
empfangsbereit ist.
Nicht empfangsbereit heisst:
- getrennt (`connected === false`), oder
- gerade neu verbunden, aber noch nicht neu registriert (`resynced === false`).
Der zweite Fall ist fuer die Reihenfolge noetig: ohne ihn koennte eine
Nachricht, die unmittelbar nach dem Verbinden eintrifft, VOR den
nachgelieferten aelteren Nachrichten ankommen. Betroffen ist nur das kurze
Fenster bis `syncSubscriptions()` im Client gelaufen ist.
Aufgerufen wird der Helfer nur von den Pfaden, die der Client auch wirklich
konsumiert (`addUpdateEntry`, `addUpdateList`). Die uebrigen Broadcasts
(Queue-Status, Template, Config, …) verhalten sich unveraendert.
Parameters:
| Name | Type | Description |
|---|---|---|
socketID |
string | |
clientData |
object | |
event |
string | |
payload |
object |
- Source:
deriveKeyAndIv(passphrase) → {Object}
Derives a key and IV from a passphrase.
Parameters:
| Name | Type | Description |
|---|---|---|
passphrase |
string | The passphrase to derive key and IV from. |
- Source:
Returns:
- The derived key, IV, and salt in base64 format.
- Type
- Object
detectOutOfHierarchyNodes()
Detects nodes that violate the expected memberA hierarchy step (+1).
Parameters:
| Type | Description |
|---|---|
- Source:
detectVisibleNodes()
Detects visible nodes from computed levels and hierarchy flags.
Parameters:
| Type | Description |
|---|---|
- Source:
(async) duplicateGitUrlWarnings(rootUid, gitUrl, orgUuid) → {Promise.<Array.<{code: string, share_uid: string, repositoryKey: (string|null)}>>}
Nicht blockierender Hinweis: ein **anderer** Root derselben Organisation
traegt dieselbe Git-URL. Das ist ein Hinweis fuer die Oberflaeche („meintest
du ‚bestehendes hinzufuegen'?") — **keine** Regel: dieselbe URL kann legitim
zweimal existieren (Fork, zweiter Klon), und die Identitaet ist die UID, nie
die URL.
Parameters:
| Name | Type | Description |
|---|---|---|
rootUid |
string | Buffer | |
gitUrl |
unknown | |
orgUuid |
string |
Returns:
- Type
- Promise.<Array.<{code: string, share_uid: string, repositoryKey: (string|null)}>>
embeddingToBuffer(embedding) → {Buffer}
Convert Float32Array to Buffer for database storage
Parameters:
| Name | Type | Description |
|---|---|---|
embedding |
Float32Array | Embedding vector |
- Source:
Returns:
- Binary buffer
- Type
- Buffer
encodeKrokiGraph(diagramSource) → {string}
Encodes Graphviz text for Kroki using deflate + base64url.
Parameters:
| Name | Type | Description |
|---|---|---|
diagramSource |
string |
- Source:
Returns:
- Type
- string
encrypt(base64String, passphrase) → {string}
Encrypts a Base64-encoded string with AES-128 using a passphrase.
Parameters:
| Name | Type | Description |
|---|---|---|
base64String |
string | The Base64-encoded string to encrypt. |
passphrase |
string | The passphrase used to derive the encryption key and IV. |
- Source:
Returns:
- The encrypted string in Base64 format.
- Type
- string
(async) ensureVersionTable()
Ensures the dbVersion tracking table exists.
Safe to call on both fresh and existing databases.
escapeHtml(value)
Parameters:
| Name | Type | Description |
|---|---|---|
value |
string |
- Source:
(async) exclude(entry, UIDOrga)
Creates an exclusion link for an entry and publishes remove events to notify about the exclusion.
This function is called when an entry should be excluded from a list due to filter matching.
Parameters:
| Name | Type | Description |
|---|---|---|
entry |
any | Entry object containing UIDEntry, UIDList, UIDBelongsTo |
UIDOrga |
string | The unique identifier of the organization (required) |
Throws:
Will log an error if any operation fails during execution.
expandMemberSBand(roots)
Expands one memberS-connected band and computes local ranks from roots.
Parameters:
| Name | Type | Description |
|---|---|---|
roots |
Map.<string, number> | |
|
- Source:
extractAchievements(filter) → {Array.<string>}
Extracts all achievement qualifiers (as strings) from a potentially nested filter object or array.
The function traverses the filter structure recursively, collecting all string values found at any depth.
The filter can be a string, an object with a single key whose value is another filter or array of filters, or an array of such filters.
Parameters:
| Name | Type | Description |
|---|---|---|
filter |
Object | Array | string | The filter structure to extract achievement qualifiers from. |
Returns:
An array of extracted achievement qualifier strings.
- Type
- Array.<string>
fail(req, res, status, message) → {void}
Parameters:
| Name | Type | Description |
|---|---|---|
req |
ExpressRequestAuthorized | |
res |
ExpressResponse | |
status |
number | |
message |
string |
- Source:
Returns:
- Type
- void
familyAccountsOf(object) → {Array.<Object>}
Die geteilten Konten der Quelle — in der Form, in der sie vorliegen.
Bewusst NICHT entschluesselt: was hier durchgereicht wird, wird beim Geschwister
gespeichert. Ein verschluesseltes Konto bleibt deshalb verschluesselt. Fuer den
Abgleich braucht es die IBAN nur als Schluessel, und den bildet `accountKey`.
Bei person/extern traegt `accounts` auch die eigenen Konten; geteilt sind nur
family/familyFees. Bei einem Familienobjekt ist der Satz selbst der geteilte.
Parameters:
| Name | Type | Description |
|---|---|---|
object |
FamilyMemberObject |
- Source:
Returns:
- Type
- Array.<Object>
familyEntriesOf(object, field) → {Array.<Object>}
Die geteilten Eintraege EINES Feldes in der Quelle.
Bei person/extern traegt das Feld auch die eigenen Eintraege des Mitglieds — hier
zaehlen nur die geteilten. Bei einer Familie ist das Feld (sofern gesetzt) selbst
der geteilte Satz; historisch als Einzelobjekt oder Skalar.
Parameters:
| Name | Type | Description |
|---|---|---|
object |
FamilyMemberObject | |
field |
string |
- Source:
Returns:
- Type
- Array.<Object>
fetchMemberALinks() → {Promise.<Array.<any>>}
Every active `memberA` link whose source holds more than one of them, plus every
link whose target no longer exists (`TargetExists = 0` → dangling).
## Why this is written in two phases
The obvious single-pass form
```sql
LEFT JOIN ObjectBase t ON (t.UID = l.UIDTarget AND t.ValidUntil > NOW())
WHERE … AND (dup OR t.UID IS NULL)
```
does not come back on production-sized data: `Links` holds ~470k active links, and
a `LEFT JOIN` is evaluated for every one of them before the `WHERE` narrows
anything, so `ObjectBase` is probed ~470k times. The candidate set is only ~2k rows,
so the candidate links are selected FIRST (the derived table below) and enriched
afterwards — that drops the runtime from minutes to a few hundred milliseconds.
The source is LEFT JOINed: the links of a deleted object are still in the table and
must not silently disappear from the report.
Returns:
- Type
- Promise.<Array.<any>>
fetchPersonRows(rootHex) → {Promise.<Array.<any>>}
Every person/extern of the organisation with both level representations and one
row per `memberA` group link (LEFT JOIN, so persons without a group link are kept).
Parameters:
| Name | Type | Description |
|---|---|---|
rootHex |
Buffer | binary UID of the organisation root |
Returns:
- Type
- Promise.<Array.<any>>
(async) fetchUserFromMembersAPI(userUID, orgaUID, authToken) → {Object}
Fetch user data from members API to populate cache
Parameters:
| Name | Type | Description |
|---|---|---|
userUID |
string | User UUID |
orgaUID |
string | Organization UUID |
authToken |
string | Auth token for API call |
- Source:
Returns:
- User data or null
- Type
- Object
filterImage()
Helper function to filter image file types
- Source:
filterImagePdf()
Helper function to filter image and PDF file types
- Source:
filterJSON()
Helper function to filter JSON file types
- Source:
filterVisibleEdges()
Filters edges to only those that should be rendered.
Parameters:
| Type | Description |
|---|---|
- Source:
findSimilarEntities(referenceUID, options) → {Promise.<Array>}
Find entities similar to a reference entity
Parameters:
| Name | Type | Description |
|---|---|---|
referenceUID |
Buffer | string | UID of the reference entity (Buffer or hex string) |
options |
Object | Search options (same as searchSimilarEntities) |
- Source:
Returns:
- Array of similar entities
- Type
- Promise.<Array>
flushPendingMessages()
Leert den Puffer in Einfuegereihenfolge (FIFO).
- Source:
generateEmbedding(text, options) → {Promise.<Float32Array>}
Generate single embedding from text
Parameters:
| Name | Type | Description |
|---|---|---|
text |
string | Text to embed |
options |
Object | Generation options (see generateEmbeddings) |
- Source:
Returns:
- Embedding vector
- Type
- Promise.<Float32Array>
generateEmbeddings(texts, options) → {Promise.<Array.<Float32Array>>}
Generate embeddings from text using configured backend
Parameters:
| Name | Type | Description | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
texts |
string | Array.<string> | Text(s) to embed | |||||||||||||||
options |
Object | Generation options
Properties
|
- Source:
Returns:
- Array of embedding vectors
- Type
- Promise.<Array.<Float32Array>>
(async) generateEmbeddingsCohere()
Generate embeddings using Cohere
- Source:
(async) generateEmbeddingsOllama()
Generate embeddings using Ollama
- Source:
(async) generateEmbeddingsOpenAI()
Generate embeddings using OpenAI
- Source:
(async) generateEmbeddingsTEI()
Generate embeddings using TEI (HuggingFace Text Embeddings Inference)
- Source:
(async) getAchievement(req, res) → {Promise.<void>}
Retrieves an achievement by UID, checking user permissions.
If timestamp is provided, retrieves the achievement as it was at that time.
Returns achievement details including template data and authorization status.
Parameters:
| Name | Type | Description | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
req |
Object | Express request object
Properties
|
||||||||||||||||||||||||||||||||
res |
Object | Express response object |
- Source:
Returns:
- Sends JSON response with achievement data or error message
- Type
- Promise.<void>
getAllCorsOrigins() → {Promise.<Array.<string>>}
CORS-Origins: **alles außer** den internen Domains.
Die Negation ist Absicht. Ein `=== 'external'` stand hier einmal, und weil im
Bestand `verified` lag (`{"kpe.de":"verified"}`), traf es **nichts** — die
Liste war leer, obwohl vier Kunden-Domains existierten. Mit getrenntem
`type`/`status` wäre `=== 'external'` zwar wieder richtig, aber die Negation
bleibt die robustere Aussage: eine neue Domänenart soll nicht stillschweigend
aus CORS herausfallen.
Die App-Hosts stehen bewusst **nicht** darin: sie liegen unterhalb einer
dieser Domains (`admin.app.kpe.de` ⊂ `kpe.de`), und `createCorsOriginChecker`
prüft mit Punktgrenze auf die Basis-Domain. Sie einzeln aufzuzählen wäre
doppelt gemoppelt — und bei jeder neuen App zu ändern.
Returns:
- Type
- Promise.<Array.<string>>
getAllDomainMappings() → {Promise.<Array.<{domain: string, org: string, orgId: string, app: string, type: string}>>}
Die Routing-Tabelle: für jede App der Host, unter dem sie läuft.
Form wie bei `shared-auth` (`loadOrganizationDomains`): `domain` ist der
**fertige Host**, nicht der Rohtwert aus dem App-Eintrag. Genau daran hängt
die Erkennung „welche Organisation bedient dieser Host" — würde hier `sjm`
statt `sjm.admin.app.commtool.org` stehen, fände die Erkennung nichts.
Returns:
- Type
- Promise.<Array.<{domain: string, org: string, orgId: string, app: string, type: string}>>
(async) getAllFilters(UID) → {Promise.<Array>}
Retrieves all filters (include, exclude, intersect) associated with a specific dynamic list.
Parameters:
| Name | Type | Description |
|---|---|---|
UID |
string | The unique identifier of the dynamic list |
- Source:
Returns:
Array of filter objects with UID, Type, Data, Title, Display, UIDBelongsTo, and SourceType
- Type
- Promise.<Array>
getAllOrgDomains() → {Promise.<Record.<string, Record.<string, {type: string, status: string}>>>}
Alle Domains aller Organisationen (für den Konflikt-Check).
Returns:
- Type
- Promise.<Record.<string, Record.<string, {type: string, status: string}>>>
getAllOrgDomains() → {Promise.<Record.<string, Record.<string, string>>>}
Load domain maps for every organisation from Vault.
Wird vom `registryService` **nur** im Rückfall gebraucht: bei
`REGISTRY_READ_MODE=vault` und im `dual`-Modus, wenn die Datenbank leer ist.
Ohne diese Funktion liefe der Rückfall ins Leere (`undefined` aufrufen) — und
zwar genau dann, wenn man ihn braucht, nämlich beim Zurückschalten.
- Source:
Returns:
- Type
- Promise.<Record.<string, Record.<string, string>>>
getAppCatalog() → {Promise.<Record.<string, object>>}
Der App-Katalog: die Vorlage, aus der eine neue Organisation ihre Apps
bekommt.
Bewusst ein Durchgriff auf Vault und **keine** DB-Variante. Der Katalog hängt
an keinem Mandanten (`orgas/data/default/apps`), und der Import lässt den
`default`-Ordner ausdrücklich aus, damit kein Objekt ohne `UIDBelongsTo`
entsteht. Ein `readsDb()`-Zweig würde hier nur so tun, als gäbe es Zeilen.
Returns:
- Type
- Promise.<Record.<string, object>>
getAppCatalog() → {Promise.<Record.<string, AppEntry>>}
Der App-Katalog — welche Apps es überhaupt gibt.
Er liegt unter einem **reservierten** Pfad, `orgas/data/default/apps`, und ist
keine Organisation: keine UID, keine Domains, keine Rollen-Sichtbarkeit —
nur die Metadaten, die eine neue Organisation anbieten können soll
(`title`, `description`, `icon`, `category`, `roles`).
Eine neue Organisation kopiert daraus **einmal**; einen Overlay gibt es
bewusst nicht. Ein Vorlagenstand, der sich unter den Mandanten weiterbewegt,
macht die Frage „woher stammt dieser Eintrag" unbeantwortbar — und ein
Katalog, der beim Lesen gemischt wird, sähe für zwei Aufrufer verschieden aus,
je nachdem, wer ihn zuerst gelesen hat.
Es gibt hier keine DB-Variante: der Import lässt `default` ausdrücklich aus
(`migrateVaultToRegistry.js` überspringt Nicht-UUID-Ordner), damit kein
verwaister Mandant ohne `UIDBelongsTo` entsteht.
- Source:
Returns:
- Type
- Promise.<Record.<string, AppEntry>>
getAppManifest(orgId, appId) → {Promise.<(object|null)>}
PWA-Manifest einer App (Branding aus dem Registry statt Vault-PWA-Cache).
Parameters:
| Name | Type | Description |
|---|---|---|
orgId |
string | |
appId |
string |
Returns:
- Type
- Promise.<(object|null)>
getApps(orgId) → {Promise.<Record.<string, AppEntry>>}
Load the app registry for one organisation.
Parameters:
| Name | Type | Description |
|---|---|---|
orgId |
string |
- Source:
Returns:
- Type
- Promise.<Record.<string, AppEntry>>
getClient()
Liefert den geteilten Redis-Client. Bewusst lazy: `getRedisClient()` wirft,
falls `configureAuth()` noch nicht gelaufen ist — das darf beim Modul-Import
nicht passieren, sondern erst beim tatsächlichen Request (nach Serverstart).
- Source:
Returns:
getDomains(orgId) → {Promise.<Record.<string, string>>}
Load domain settings for one organisation.
This is the **Vault side** of the registry: `registryService` uses it as its
fallback while `REGISTRY_READ_MODE` is not yet `db`. The validation rules and
the cross-organisation conflict check used to live here as well — they moved
to `registryTypes.js` / `registryService.js`, because checking a value is not
the same job as fetching it: they must run against the store that is being
written to, not against the one this module happens to read.
Parameters:
| Name | Type | Description |
|---|---|---|
orgId |
string |
- Source:
Returns:
- Type
- Promise.<Record.<string, string>>
getEmbeddingServerInfo() → {Promise.<Object>}
Get embedding server info
- Source:
Returns:
- Type
- Promise.<Object>
(async) getEnumValues(query, table, column) → {Promise.<(Array.<string>|null)>}
Liest die Enum-Literale einer Spalte aus `information_schema`.
Parameters:
| Name | Type | Description |
|---|---|---|
query |
function | |
table |
string | |
column |
string |
Returns:
Literale, oder `null` wenn keine enum-Spalte
- Type
- Promise.<(Array.<string>|null)>
getFinancialMasterFlag(functionData, jobDataopt) → {Object}
Extracts the financialMaster flag from a job or function template.
Priority: job-level `accounting.financialMaster` > function template `financialMaster`
Parameters:
| Name | Type | Attributes | Description |
|---|---|---|---|
functionData |
Object | Data from the function template (ObjectBase.Data) | |
jobData |
Object |
<optional> |
Data from the job (ObjectBase.Data), may contain accounting block |
- Source:
Returns:
- Type
- Object
(async) getFunctionTemplates(req, res) → {Promise.<void>}
Retrieves all function template names and UIDs for the organization.
Parameters:
| Name | Type | Description |
|---|---|---|
req |
Object | Express request object, expects `session.root` for the root user UID. |
res |
Object | Express response object used to send JSON responses. |
Returns:
Sends a JSON response with the function templates list.
- Type
- Promise.<void>
getHash(row, entityKeySet, allColumns) → {string}
Compute a hash of the non-key, non-temporal columns for a row.
Two rows with the same hash can be collapsed (the earlier history row removed).
Parameters:
| Name | Type | Description |
|---|---|---|
row |
Object | |
entityKeySet |
Set.<string> | |
allColumns |
Array.<{Field: string, isVirtual: boolean}> |
Returns:
- Type
- string
getHierarchyBorderColor(hierarchie, hierarchyColors) → {string}
Returns border color for a group hierarchy from config/palette.
Parameters:
| Name | Type | Description |
|---|---|---|
hierarchie |
number | string | null | undefined | |
hierarchyColors |
Array.<any> | Object | null |
- Source:
Returns:
- Type
- string
getHierarchyColorConfig(config) → {Array.<any>|Object|null}
Resolves hierarchy colors from merged config.
Parameters:
| Name | Type | Description |
|---|---|---|
config |
any |
- Source:
Returns:
- Type
- Array.<any> | Object | null
(async) getJobsByFunctionTemplate(req, res) → {Promise.<void>}
Retrieves jobs associated with a specific function template.
Parameters:
| Name | Type | Description |
|---|---|---|
req |
Object | Express request object, expects `params.UID` for the template UID and optional query parameters. |
res |
Object | Express response object used to send JSON responses. |
Returns:
Sends a JSON response with the jobs data.
- Type
- Promise.<void>
getMailSettings(orgId) → {Promise.<(object|null)>}
Load mail/SMTP settings for one organisation.
The password is masked before being returned.
Parameters:
| Name | Type | Description |
|---|---|---|
orgId |
string |
- Source:
Returns:
- Type
- Promise.<(object|null)>
getMemberAStep(parentHex, childHex) → {number|null}
Returns memberA rank increment from parent to child.
Invalid hierarchy transitions return null and are omitted.
Parameters:
| Name | Type | Description |
|---|---|---|
parentHex |
string | |
childHex |
string | |
|
- Source:
Returns:
- Type
- number | null
getNonVirtualColumns(columns) → {Array.<{Field: string, isVirtual: boolean}>}
Determine which columns are VIRTUAL / GENERATED (excluded from INSERT).
Parameters:
| Name | Type | Description |
|---|---|---|
columns |
Array.<{Field: string, Extra: string}> | Result of SHOW COLUMNS |
Returns:
- Type
- Array.<{Field: string, isVirtual: boolean}>
(async) getObjectFilters(ObjectUID) → {Promise.<(Array.<any>|undefined)>}
Retrieves all filters and related data for objects that need to be checked for dynamic list membership.
This function queries the database to get filter information for objects and their associated lists.
Parameters:
| Name | Type | Description |
|---|---|---|
ObjectUID |
Buffer | Array.<Buffer> | Single object UID or array of object UIDs to get filters for |
- Source:
Returns:
Array of filter objects with complete filter and object information
- Type
- Promise.<(Array.<any>|undefined)>
getOrgApp(orgId, appId) → {Promise.<(object|null)>}
Eine einzelne App einer Organisation.
Parameters:
| Name | Type | Description |
|---|---|---|
orgId |
string | |
appId |
string |
Returns:
- Type
- Promise.<(object|null)>
getOrgApps(orgId) → {Promise.<Record.<string, object>>}
Alle Apps einer Organisation.
Parameters:
| Name | Type | Description |
|---|---|---|
orgId |
string | UID in `UUID-`-Form |
Returns:
`{ [appId]: AppEntry }`
- Type
- Promise.<Record.<string, object>>
getOrgDomains(orgId) → {Promise.<Record.<string, {type: string, status: string}>>}
Alle Domains einer Organisation.
Rückgabe ist `{ [domain]: { type, status } }` — **eine** Form für Aufrufer
und Oberfläche. Der Altbestand (in Vault wie in der Datenbank) trägt statt
dessen eine Zeichenkette wie `"verified"`; übersetzt wird hier einmal, nicht
bei jedem Konsumenten.
Parameters:
| Name | Type | Description |
|---|---|---|
orgId |
string |
Returns:
- Type
- Promise.<Record.<string, {type: string, status: string}>>
(async) getOrgSection(orgId, section) → {Promise.<(object|null)>}
Load a named section for one organisation from Vault.
Returns `null` on missing key or error.
Parameters:
| Name | Type | Description |
|---|---|---|
orgId |
string | |
section |
string | e.g. 'domains', 'apps', 'mail' |
- Source:
Returns:
- Type
- Promise.<(object|null)>
getRequiredDbRoles() → {Array.<string>}
Get the required database roles based on NODE_ENV
Production: db-user, db-admin
Development: db-dev, db-admin
Demonstration: db-demo, db-admin
- Source:
Returns:
Array of valid roles for current environment
- Type
- Array.<string>
getStageColorConfig(config) → {Array.<any>|Object|null}
Resolves stage colors from merged config.
Parameters:
| Name | Type | Description |
|---|---|---|
config |
any |
- Source:
Returns:
- Type
- Array.<any> | Object | null
getStageDotColor(stage, stageColors) → {string}
Parameters:
| Name | Type | Description |
|---|---|---|
stage |
number | string | null | undefined | |
stageColors |
Array.<any> | Object | null |
- Source:
Returns:
- Type
- string
groupBySource(rows) → {Map.<string, any>}
Groups the flat link rows per source object.
Keyed by the UUID string — two `Buffer`s with equal content would be different Map
keys and split one object with several links into several single-link objects.
Parameters:
| Name | Type | Description |
|---|---|---|
rows |
Array.<any> |
Returns:
- Type
- Map.<string, any>
(async) groupEntriesFilter(myObjectFilters) → {Promise.<(Record.<string, any>|undefined)>}
Groups filter data by entry UID and organizes filters into include/exclude/intersect categories.
This function processes raw filter data from the database and structures it for efficient
filtering operations, creating an object keyed by entry UIDs with complete filter information.
Parameters:
| Name | Type | Description |
|---|---|---|
myObjectFilters |
Array.<any> | Array of raw filter objects from the database query |
Throws:
Will log an error if any operation fails during execution.
Returns:
Object keyed by entry UIDs containing organized filter data
- Type
- Promise.<(Record.<string, any>|undefined)>
groupNames(session) → {Array.<string>}
Keycloak-Gruppen normalisieren: liefert `/employees` und `employees` identisch.
(Keycloak stellt Group-Pfade je nach Mapper mit oder ohne führenden Slash aus.)
Parameters:
| Name | Type | Description |
|---|---|---|
session |
Object |
- Source:
Returns:
- Type
- Array.<string>
(async) handleSisterLink(req, UID) → {Promise.<(object|null)>}
Manages the sister-group link for a group.
If belongsTo changes, removes old memberS link, inserts new one, and syncs members.
Parameters:
| Name | Type | Description |
|---|---|---|
req |
ExpressRequestAuthorized | |
UID |
Buffer | Group UID (hex Buffer) |
- Source:
Returns:
The new sister-group object, or null if unchanged/removed
- Type
- Promise.<(object|null)>
(async) hasColumn()
`Member.credential_id` (20260926) war typ-gebunden benannt: eine Spalte, die
genau ein Objekttyp mit wenigen Zeilen benutzt. Diese Migration ersetzt sie
durch einen **generischen Lookup-Slot**.
## Konvention (neu, hier festgehalten)
`Member.lookup_key` ist der **eine opake externe Schlüssel** eines Objekts:
ein Wert, der von außen kommt (Client, Registrierung, Import) und über den
das Objekt gesucht wird — NICHT die Objekt-UID.
- **Ein Slot pro Objekt, global eindeutig.** Die Bedeutung ergibt sich aus
`ObjectBase.Type`, nicht aus dem Spaltennamen. Kein Typ-spezifisches Feld,
keine zweite Spalte für den nächsten Typ.
- **Die Werte sind präfixiert** (`rc_…` für Runner-Credentials). Deshalb
genügt ein globaler UNIQUE statt `(Type, key)` — Letzteres bräuchte `Type`
in `Member` (Denormalisierung) und bringt für einen Zufallswert nichts.
- **Kein anderer Zweck.** `Member.Data` trägt den heißen Zustand (Heartbeat,
Capacity); `lookup_key` trägt nur den Suchschlüssel. Beides zu vermischen
macht den Unique-Konflikt zur Zufallsfehlerquelle an unerwarteten Stellen.
Die Rolle ist damit `SortName`/`FullTextIndex`/`PhonetikIndex`/`Geo`
gleichgestellt: eine ausgezogene Spalte, die Lookup oder Sort dient — die
Member-Konvention (080-Workspaces/040-Ist-Zustand: „heißer Zustand im
Data-JSON, ausgezogene Spalten nur für Lookup/Sort").
## Ablauf
1. `lookup_key` an `Member` anlegen (varchar(128): ein generischer Slot
sollte nicht auf die Länge seines ersten Nutzers dimensioniert sein).
2. UNIQUE `member_lookup_key_uq` anlegen.
3. Bestehende `credential_id`-Werte übernehmen, ihren Index und die Spalte
entfernen.
Idempotent: eine frische DB (initTables.sql kennt bereits `lookup_key`, kein
`credential_id`) überspringt alles; eine laufende DB landet im selben
Zustand. Hinweis: `20260926` legt `credential_id` weiterhin an — auf einem
frischen Boot entsteht sie leer und wird hier wieder entfernt, weil die
Migration bereits appliziert ist und nicht mehr geändert werden darf.
(async) hasColumn()
`RunnerData` war eine eigene Companion-Tabelle für den heißen Runner-Zustand
(20260907-runner-companion) — dabei ist `Member` im Schema längst die
**generische Companion-Tabelle** nach dem Member-Muster: gleiche UID wie das
`ObjectBase`-Objekt, nicht system-versioniert, `Data`-JSON trägt den heißen
Zustand, nur der gesuchte Lookup liegt als Spalte daneben. Gruppen, Events,
Locations, Familien und E-Mails liegen dort schon — Runner brauchen keine
zweite Tabelle.
Diese Migration zieht den Runner-Zustand nach `Member`:
- `credential_id` (UNIQUE) → neue Lookup-Spalte an `Member` (Runner-Auth;
inverser Lookup „Credential-ID → Runner")
- `public_key` → entfällt als Spalte. Der Key steht bereits in
`ObjectBase.Data.publicKey` (von `createRunner`
gesetzt) und ist damit die eine Quelle. Für
Bestands-Runner, die vor 20260907 entstanden
sind, wird er hier einmalig nachgezogen —
sonst könnten sie sich nach dem Drop nicht
mehr authentifizieren.
- `Data` (heartbeat/capacity/lastSeenAt) → `Member.Data` des Runner-Objekts
Danach wird `RunnerData` gedroppt. Idempotent: ein frischer Boot (Member hat
die Spalte schon aus `initTables.sql`, `RunnerData` existiert nicht) und eine
laufende dev-DB (Spalte fehlt, `RunnerData` ist da) landen im selben Zustand.
Hinweis: `20260907-runner-companion` legt `RunnerData` weiterhin an — auf einem
frischen Boot entsteht sie leer und wird hier wieder entfernt. Die Migration
bleibt unangetastet, weil sie auf laufenden DBs bereits appliziert ist.
(async) hasTable()
AP 1 (050-Migrationsplan): Die Mitglieder-Runtime-/Deployment-Ebene wird aus
members entfernt — Runners bleiben reine Topologie-Objekte (ObjectBase
Type='runner') mit einer Member-Muster-Companion-Zeile (`RunnerData`) für
heißen, nicht versionierten Zustand:
- credential_id/public_key → gezogene Lookup-Spalten (Runner-Auth)
- heartbeat/capacity → Data-JSON (Topologie-Heartbeat)
Bestehende Daten werden aus `runner_credentials`/`runner_runtime` übernommen.
Die 1:n-/Queue-/Transfer-Tabellen werden gedroppt; `workspace`/`deployment`-
Objekte + deren Links/Visible-Zeilen werden entfernt.
KEINE ObjectBase/Links-Enum-MODIFY in dieser Migration: ObjectBase ist system-
versioniert, History-Partitionen enthalten alte Zeilen der entfernten Literale
(workspace/deployment/…) — ein MODIFY liefe in „Data truncated". Frische DBs
entstehen ohnehin ohne die Literale (die erzeugenden Migrationen sind aus der
Kette entfernt, vgl. 20260813-runner-object-types/20260827-tailnet-object-types);
bestehende dev-DBs behalten einen harmlosen Enum-Superset.
Gilt für laufende dev-DBs (dbVersion) UND frische Boots (alphabetisch nach
20260827-tailnet-object-types).
hashSensitiveValue(value, type) → {string}
Create deterministic hash for sensitive string data
Same input always produces same hash (preserves uniqueness in embeddings)
Parameters:
| Name | Type | Description |
|---|---|---|
value |
string | Value to hash |
type |
string | Type of value (email, phone, etc.) |
- Source:
Returns:
- Hashed placeholder
- Type
- string
(async) initializeRedis() → {Promise.<boolean>}
Initialize the Redis connection after secrets are loaded.
Nebenläufigkeitsvertrag: parallele Aufrufe starten **einen** Versuch und
bekommen dasselbe Ergebnis. Ein Versuch, der `false` liefert (Secrets noch
nicht geladen) oder wirft, wird **nicht** festgeschrieben — der nächste
Aufruf versucht es erneut. Genau darauf ist der Startpfad angewiesen:
`server.js` ruft früh (noch ohne Secrets) auf und bekommt `false`, die erste
Welle `publishEvent` initialisiert danach parallel.
- Source:
Returns:
Returns true if initialized successfully, false otherwise
- Type
- Promise.<boolean>
(async) insertAchievement(object, achievementUID, templateUID, session, memberUID, connection) → {Promise.<void>}
import { parseTimestampToSeconds } from '../../utils/parseTimestamp.js'
Inserts a new achievement into the database.
Parameters:
| Name | Type | Description |
|---|---|---|
object |
Object | Achievement object to insert |
achievementUID |
string | Buffer | UUID (hex string or Buffer) of the achievement |
templateUID |
string | Buffer | UUID (hex string or Buffer) of the template |
session |
Object | User session data |
memberUID |
string | Buffer | UUID (hex string or Buffer) of the member |
connection |
Object | Database connection for transaction |
Returns:
- Type
- Promise.<void>
(async) insertFunction() → {Promise.<void>}
Inserts or updates a function template in the database, manages related links and triggers updates.
- Renders a function object from a template and request body.
- Cleans up and compresses access filters to avoid unnecessary filter creation.
- Inserts or updates the function in the ObjectBase table.
- Handles achievement links and triggers requalification or visibility adjustments if needed.
- If the function template changes, rebuilds all jobs based on this template and updates their data.
Parameters:
| Type | Description |
|---|---|
Returns:
- Type
- Promise.<void>
(async) insertOrUpdateAchievement(req, res) → {Promise.<void>}
Handles the insertion or update of an achievement for a member.
This function processes achievement data from the request, checks user authorization,
validates the member and template, and either creates a new achievement or updates an existing one.
It also handles achievement renewal logic and queues necessary updates.
Parameters:
| Name | Type | Description | ||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
req |
Object | The request object
Properties
|
||||||||||||||||||||||||||||||||||||||||||||||
res |
Object | The response object |
Throws:
-
- Logs errors with errorLoggerUpdate
- Type
- Error
Returns:
- Sends JSON response with success status and result
- Type
- Promise.<void>
(async) insertOrUpdateJob(req, res) → {Promise.<void>}
Inserts or updates a job in the system.
This function handles the creation of a job for a member in a specific group with a specified function.
It performs several authorization checks and validations before proceeding with the insertion.
Parameters:
| Name | Type | Description | ||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
req |
Object | The HTTP request object
Properties
|
||||||||||||||||||||||||||||||||||||||||||||||||||
res |
Object | The HTTP response object |
- Source:
Throws:
-
- Logs errors through errorLoggerUpdate
- Type
- Error
Returns:
- Sends a JSON response with success status and result or error message
- Type
- Promise.<void>
(async) invalidateRegistryCache(orgId)
Sagt den Consumern, dass sich das Registry dieser Organisation geändert hat.
Best-effort: ein fehlendes Redis darf das Speichern nicht scheitern lassen.
Parameters:
| Name | Type | Description |
|---|---|---|
orgId |
string |
isEmbeddingServerHealthy() → {Promise.<boolean>}
Check if embedding server is healthy
- Source:
Returns:
- Type
- Promise.<boolean>
isEnabled()
Feature flags for the staged rollout of the Project/Share feature set.
Flags default to ENABLED so the API is usable right after the schema
migration; set them to 'false'/'0' to keep Project functions disabled until
rollout is complete (see 080-Workspaces/025-Members-Backend-Implementation-Plan.mdx).
- PROJECT_OBJECTS_ENABLED gates Project/Share CRUD routers
- NORMALIZED_VISIBLE_SCOPES gates the normalized visible/changeable/admin
rebuild for generic target scopes (projects and
accounting target types)
- PROJECT_EVENTS_ENABLED gates lifecycle + rights-delta event publishing
- RUNNER_OBJECTS_ENABLED gates Runner/Deployment control-plane routers
- Source:
isFamilyEntry()
Ein geteilter Eintrag ist 'family' (Adresse/E-Mail/Telefon) bzw. 'familyFees' (Konto).
- Source:
isFixRequested(value) → {boolean}
Parameters:
| Name | Type | Description |
|---|---|---|
value |
unknown |
Returns:
- Type
- boolean
isFixRequested(value) → {boolean}
Parameters:
| Name | Type | Description |
|---|---|---|
value |
unknown |
Returns:
- Type
- boolean
isReadOnly(metadata)
Is this share write-locked? (`metadata.mode` = readOnly / reference)
Parameters:
| Name | Type | Description |
|---|---|---|
metadata |
Object |
isRootGroupSql(alias) → {string}
SQL fragment: is `${alias}` a top-level group (an organization)?
Two equivalent markers exist in the data, and both are accepted:
- `Data.root = true` (the flag set by the org setup), and
- a `memberSys` self-link (`UID = UIDTarget = `), which marks the top
level of the group tree.
Test fixtures use the `memberSys` self-link; checking only `Data.root` would
miss organizations that carry just that marker.
Parameters:
| Name | Type | Description |
|---|---|---|
alias |
string | the ObjectBase alias |
- Source:
Returns:
- Type
- string
isValidHost()
Validate a Git host name (hostname / host:port).
isValidHostKey()
Validate a known_hosts line: " ".
isValidLogInterval(interval) → {boolean}
Validates log interval format
Parameters:
| Name | Type | Description |
|---|---|---|
interval |
string | Interval string to validate (e.g., '1d', '1h') |
- Source:
Returns:
- Type
- boolean
isValidLogSize(size) → {boolean}
Validates log size format
Parameters:
| Name | Type | Description |
|---|---|---|
size |
string | Size string to validate (e.g., '20M', '1G') |
- Source:
Returns:
- Type
- boolean
isValidName()
Validate a credential name (path-safe, used in the Vault path).
keyComponents()
Helper function to generate key components for file paths
- Source:
languageUrlGen()
URL generator for language file uploads
- Source:
levelOf(metadata) → {'read'|'write'}
Wire `linkType` derived from the share mode — the former write/read link level
is now a property of the share.
Parameters:
| Name | Type | Description |
|---|---|---|
metadata |
Object |
Returns:
- Type
- 'read' | 'write'
(async) linkExcludeIntersect(UIDFilter, filtered, target, connection) → {Promise.<void>}
Creates database links for excluded/intersected objects
Parameters:
| Name | Type | Description |
|---|---|---|
UIDFilter |
Buffer | Filter UID |
filtered |
Array.<any> | Filtered objects to exclude/intersect |
target |
Buffer | Target list UID |
connection |
any | Database connection |
Returns:
- Type
- Promise.<void>
(async) linkIncludes(sourceType, UIDFilter, filtered, target, execConnection) → {Promise.<void>}
Creates database links for included objects
Parameters:
| Name | Type | Description |
|---|---|---|
sourceType |
string | Source type ('group' or 'list') |
UIDFilter |
Buffer | Filter UID |
filtered |
Array.<any> | Filtered objects to link |
target |
Buffer | Target list UID |
execConnection |
any | Database connection |
Returns:
- Type
- Promise.<void>
linkReport(link) → {any}
Link shape for the report.
The UID is rendered with `HEX2uuid`, which yields the canonical `UUID-…` form the
API uses everywhere. The generated SQL column `TUIDTarget` must NOT be used for
this: it renders the same bytes WITHOUT the `UUID-` prefix, so report and API
would disagree on the format.
Parameters:
| Name | Type | Description |
|---|---|---|
link |
any |
Returns:
- Type
- any
(async) listAchievementsPerson(req, res) → {Promise.<void>}
Fetches achievements associated with a person, joining with template data.
Can return achievements as of a specific point in time.
Can filter to return only unique (latest) achievements.
Parameters:
| Name | Type | Description | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
req |
Object | Express request object.
Properties
|
||||||||||||||||||||||||||||||||||||
res |
Object | Express response object. |
- Source:
Throws:
-
- Logs any errors encountered during database operations.
- Type
- Error
Returns:
- Returns a JSON response with success status and result array.
- Type
- Promise.<void>
listAppReleases(appKey) → {Promise.<Array.<object>>}
Alle Releases einer App, Zeiger zuerst.
Parameters:
| Name | Type | Description |
|---|---|---|
appKey |
string |
Returns:
- Type
- Promise.<Array.<object>>
listShares()
Parameters:
| Type | Description |
|---|---|
- Source:
(async) listVaultKeys(path) → {Promise.<Array.<string>>}
List credential names directly under a Vault directory. Returns [] when the
path does not exist (no credentials yet).
Parameters:
| Name | Type | Description |
|---|---|---|
path |
string |
Returns:
- Type
- Promise.<Array.<string>>
(async) loadAllConfigs()
Vollstaendiger Config-Durchlauf: liest alle Orga-YAMLs (admin + app-spezifisch) und
baut `configs`/`mergedConfigs` von Grund auf neu auf.
ACHTUNG: Setzt unterwegs `configs[app] = {}` zurueck und ist deshalb *nicht*
nebenlaeufig ausfuehrbar. Immer ueber `readAllConfigs()` bzw. `ensureConfigsLoaded()`
aufrufen — niemals direkt.
- Source:
mailConfig() → {Object|null}
Liest die Mail-Konfiguration — oder `null`, wenn sie unvollstaendig ist.
Der alte Guard lautete
`if(process.env.mailHost && process.env.mailPort && process.env.mailUser && process.env.mailPassword, process.env.errorMail)`.
Das ist ein **Komma-Operator**: der linke Ausdruck wird ausgewertet und
verworfen, geprueft wird nur `errorMail`. Eine halb gesetzte Konfiguration
lief damit in `createTransport` und warf erst beim Senden.
- Source:
Returns:
- Type
- Object | null
mapRelease(row)
Zeile → Release-Objekt. `Backends` kommt über den JSON-Cast bereits als
Objekt; `null` bleibt `null` und bedeutet „keine Backend-URLs im Release".
Parameters:
| Name | Type | Description |
|---|---|---|
row |
object |
mergeCustomizer()
Lodash merge customizer - do not merge arrays
- Source:
(async) migrateAchievementDates() → {Promise.<void>}
Migrates achievements from 05.01.2023 to 01.01.1900
Uses delete and re-insert to handle system versioned table
- Source:
Returns:
- Type
- Promise.<void>
(async) migrateAction(action) → {Promise.<void>}
Handles migration actions for moving objects between different groups or targets.
Delegates to type-specific submodules.
Parameters:
| Name | Type | Description |
|---|---|---|
action |
treeAction | The migration action object containing source and target information |
- Source:
Throws:
-
Throws an error if migration is attempted for unsupported object types
- Type
- Error
Returns:
- Type
- Promise.<void>
(async) migrateAction()
- Source:
minioput()
Put in MinIO — public bucket über den publicClient, sonst der interne
minioput()
Put a Buffer into MinIO — uses publicMinioClient for PUBLIC_BUCKET, myMinioClient otherwise
- Source:
modeFromLinkType(linkType) → {string|null}
Resolve an incoming `linkType` into a share mode. `memberA`/`write` mean
editable, `member`/`read` mean read only — that is the faithful translation of
the old link level into the new share property.
Parameters:
| Name | Type | Description |
|---|---|---|
linkType |
unknown |
Returns:
the mode, or null for an unknown value
- Type
- string | null
nameReplace(str) → {string}
Replace special characters in a string with underscores
Used to create valid property names from parameter names
Parameters:
| Name | Type | Description |
|---|---|---|
str |
string |
- Source:
Returns:
- Type
- string
newAvatar(api)
Parameters:
| Name | Type | Description |
|---|---|---|
api |
Object |
newAvatar(api)
Parameters:
| Name | Type | Description |
|---|---|---|
api |
Object |
(async) newUid() → {Promise.<Buffer>}
Erzeugt eine UID **in der Datenbank** (`UIDV1()` = echte, sortierbare v1).
Bewusst nicht in JavaScript: `randomUUID()` wäre eine v4, und ihr Hex-String
passt in keines der SQL-UUID-Formate (§ registryTypes).
Returns:
16 Byte
- Type
- Promise.<Buffer>
normalizeDomainMapByOrg(map) → {Record.<string, Record.<string, {type: string, status: string}>>}
Wie `normalizeDomainMap`, nur eine Ebene tiefer: `{ orgId: { domain: entry } }`.
Der Konflikt-Check vergleicht über **alle** Organisationen und muss dafür
dieselbe Form sehen wie eine einzelne Organisation.
Parameters:
| Name | Type | Description |
|---|---|---|
map |
unknown |
Returns:
- Type
- Record.<string, Record.<string, {type: string, status: string}>>
normalizeGitUrl(url) → {string}
Normalisiert eine Git-URL fuer den **Hinweis**-Vergleich (nie fuer eine
Identitaet — die ist die UID-Kette). Protokoll, `git@host:`-Praefix,
`.git`-Suffix, Trailing-Slash und Gross-/Kleinschreibung fallen weg.
Parameters:
| Name | Type | Description |
|---|---|---|
url |
unknown |
Returns:
leer, wenn keine URL brauchbar ist
- Type
- string
normalizeMode(mode) → {string}
Canonical `metadata.mode`. Tolerates case/whitespace and the legacy
`reference` spelling, which stays a **block** (fail-closed).
Parameters:
| Name | Type | Description |
|---|---|---|
mode |
unknown |
Returns:
- Type
- string
normalizeValue(value, type) → {string}
Normalize sensitive values before hashing for consistency
Same value in different formats produces same hash
Parameters:
| Name | Type | Description |
|---|---|---|
value |
string | Raw value to normalize |
type |
string | Type of value (email, phone, iban, etc.) |
- Source:
Returns:
- Normalized value
- Type
- string
num(value) → {number|null}
SQL returns `JSON_VALUE` results as strings; levels are compared numerically.
Parameters:
| Name | Type | Description |
|---|---|---|
value |
unknown |
Returns:
- Type
- number | null
num(value) → {number|null}
SQL returns numeric columns as strings; levels are compared numerically.
Parameters:
| Name | Type | Description |
|---|---|---|
value |
unknown |
Returns:
- Type
- number | null
openAPIMiddleware(baseDir)
Middleware to serve combined OpenAPI documentation
Parameters:
| Name | Type | Description |
|---|---|---|
baseDir |
string | Base directory for resolving references |
- Source:
parseCursor(cursor) → {number}
Parse an opaque cursor into its numeric seq.
Parameters:
| Name | Type | Description |
|---|---|---|
cursor |
string |
- Source:
Returns:
- Type
- number
parseTimestampToSeconds(val) → {number|null}
Parse an incoming query timestamp parameter.
Accepts a number or string (in milliseconds) and returns seconds (number) or null.
Returns null for undefined, null, NaN, or empty string.
Parameters:
| Name | Type | Description |
|---|---|---|
val |
string | number | undefined | null |
- Source:
Returns:
- Type
- number | null
parseTimestampToSecondsOrDefault(val, fallback) → {number}
Parse an incoming timestamp value (milliseconds) and return a seconds value or fallback number.
Parameters:
| Name | Type | Description |
|---|---|---|
val |
string | number | undefined | null | |
fallback |
number | fallback seconds value when val is not present |
- Source:
Returns:
- Type
- number
privateUrlGen()
URL generator for private file uploads.
`prefix` is an optional sub-folder (e.g. 'avatar', 'documents', 'editor').
Clients that cannot send multipart fields - such as CKEditor's
SimpleUploadAdapter, which only posts the file - omit it; the artefact then
lives directly under the object folder, i.e. `/`. That keeps it
an asset of the object itself and makes it show up in `GET /files/{UID}`.
- Source:
(async) processGroupEntry(ObjectFilter, of)
Processes a single filter entry and groups it under the appropriate entry key in the ObjectFilter.
This function organizes filter data by entry UID, creating or updating filter collections
for include, exclude, and intersect filters, along with membership information.
Parameters:
| Name | Type | Description |
|---|---|---|
ObjectFilter |
Record.<string, any> | The object to store grouped filter data, keyed by entry UID |
of |
any | Filter object containing entry data, filter information, and list details |
Throws:
Will log an error if any operation fails during execution.
(async) processIncludeFilter(include, myObjects) → {Promise.<void>}
Processes the inclusion filter and updates the database with the filtered objects.
This function handles the creation of new entries, linking entries to filters, and
processing exclude and intersect filters for newly created entries.
Parameters:
| Name | Type | Description |
|---|---|---|
include |
Object | The inclusion filter object containing filter data and source type. |
myObjects |
Array.<Object> | Array of objects to be filtered and processed. |
Throws:
-
Logs and throws an error if any issue occurs during processing.
- Type
- Error
Returns:
Resolves when the processing is complete.
- Type
- Promise.<void>
Example
const include = {
Data: { filter criteria here },
SourceType: 'group',
listUID: 'someUID',
UID: 'filterUID'
};
const myObjects = [/ array of objects as retrieved by getObjects/];
await processIncludeFilter(include, myObjects);
processTraversalLayer() → {Array.<Buffer>}
Applies one traversal layer and computes the next frontier.
Parameters:
| Type | Description |
|---|---|
- Source:
Returns:
- Type
- Array.<Buffer>
projectOnto(shape, value) → {unknown}
Legt `value` auf die Felder von `shape` um.
Ohne diesen Schritt wäre der Bericht **immer** rot, und zwar aus einem Grund,
der kein Fehler ist: die Registry hängt dem App-Objekt die `appId` an (sie ist
der Map-Schlüssel und liegt deshalb in `Data`), Vault kennt das Feld nicht.
Ein `JSON.stringify`-Vergleich sähe darin eine Abweichung und meldete jede App
als „~ abweichend" — ein Bericht, der nie grün wird, wird nicht gelesen.
Verglichen wird deshalb genau das, was Vault **behauptet**: ein Feld, das in
Vault fehlt, kann in der DB hinzukommen. Ein Feld, das Vault hat und die DB
verloren hat, bleibt eine Abweichung.
Parameters:
| Name | Type | Description |
|---|---|---|
shape |
unknown | die Vault-Seite (bestimmt die Feldmenge) |
value |
unknown | die DB-Seite |
- Source:
Returns:
- Type
- unknown
projectShares()
Register project share filter routes on a project router.
Projects reuse the complete list/dlist/email share mechanism
(`/api/kpe20/project/share/...`): filters are stored as ObjectBase entries
of Type visible/changeable, linked to the project via a Links.Type='list'
link, and materialized through the same visibility pipeline.
Parameters:
| Type | Description |
|---|---|
- Source:
publicUrlGen()
URL generator for public file uploads
- Source:
publishLevels(verb, rows, organization) → {void}
Fasst Zeilen zu einem Event je `(ObjectType, Level, Object)` zusammen.
Parameters:
| Name | Type | Description |
|---|---|---|
verb |
'add' | 'remove' | |
rows |
Array.<VisibleScope> | |
organization |
string | Buffer | null |
- Source:
Returns:
- Type
- void
q()
Waehlt `connection.query` (Transaktion) oder den Pool.
(async) queryGroupLinksForFrontier(userUID, frontier)
Loads one traversal layer for the current frontier.
Parameters:
| Name | Type | Description |
|---|---|---|
userUID |
Buffer | |
frontier |
Array.<Buffer> |
- Source:
(async) queueAdd(root, user, type, UID, UIDBelongsTo, oldTarget, newTarget, timestampopt) → {Promise.<void>}
Adds a single action to the tree queue and triggers queue processing.
Parameters:
| Name | Type | Attributes | Default | Description |
|---|---|---|---|---|
root |
Buffer | string | The UID of the root organization (Buffer or hex string) | ||
user |
Buffer | string | The UID of the user requesting the action | ||
type |
string | The type of action to be performed | ||
UID |
Buffer | string | The UID of the object being acted upon | ||
UIDBelongsTo |
Buffer | string | The UID of the parent object | ||
oldTarget |
Buffer | string | null | The UID of the old target object (for migrations/removals) | ||
newTarget |
Buffer | string | null | The UID of the new target object (for additions/migrations) | ||
timestamp |
number | null |
<optional> |
null | Optional timestamp for the action |
- Source:
Returns:
- Type
- Promise.<void>
(async) queueAddArray(req) → {Promise.<void>}
Adds multiple actions to the tree queue in batch and triggers queue processing.
Parameters:
| Name | Type | Description | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
req |
Object | Express request object with session data
Properties
|
|||||||||||||||
|
- Source:
Returns:
- Type
- Promise.<void>
quoteGraphviz(value) → {string}
Builds a Graphviz-compatible, quoted label.
Parameters:
| Name | Type | Description |
|---|---|---|
value |
string |
- Source:
Returns:
- Type
- string
rateLimitedRequest(requestFn) → {Promise.<any>}
Rate limiter that ensures we don't exceed LocationIQ free tier limits
Parameters:
| Name | Type | Description |
|---|---|---|
requestFn |
function | The async function to execute |
- Source:
Returns:
- Type
- Promise.<any>
(async) readCode() → {Promise.<({code: string, tenant: string, createdBy: string, status: string, mode: string, groupUID: (string|null), tailnetUID: (string|null)}|null)>}
Returns:
- Type
- Promise.<({code: string, tenant: string, createdBy: string, status: string, mode: string, groupUID: (string|null), tailnetUID: (string|null)}|null)>
readLang(req) → {string}
Sprachcode aus Query oder Pfad, mit Default.
Parameters:
| Name | Type | Description |
|---|---|---|
req |
ExpressRequestAuthorized |
- Source:
Returns:
- Type
- string
readMode() → {'vault'|'dual'|'db'}
Returns:
- Type
- 'vault' | 'dual' | 'db'
(async) readProjectRows(uidHex)
Read current project rows.
Parameters:
| Name | Type | Description |
|---|---|---|
uidHex |
string |
- Source:
(async) readRunnerCompanion(uidHex) → {Promise.<({last_seen_at: (string|null), capacity: object, heartbeat: object, lookup_key: (string|null)}|null)>}
Companion-Zeile eines Runners — heißer, nicht versionierter Zustand nach dem
Member-Muster, in der generischen `Member`-Tabelle: `lookup_key` als
Lookup-Slot, heartbeat/capacity/lastSeenAt im `Data`-JSON.
Parameters:
| Name | Type | Description |
|---|---|---|
uidHex |
string |
- Source:
Returns:
- Type
- Promise.<({last_seen_at: (string|null), capacity: object, heartbeat: object, lookup_key: (string|null)}|null)>
(async) readRunnerGroupLink(runnerHex)
Parameters:
| Name | Type | Description |
|---|---|---|
runnerHex |
string |
- Source:
(async) readRunnerRow(uidHex)
Parameters:
| Name | Type | Description |
|---|---|---|
uidHex |
string |
- Source:
(async) readShare(shareHex, projectHex)
Read a share row that is linked to the given project. Returns the share with
its link type, or null when the share does not exist or has no link to this
project. Both link directions are accepted during the migration.
Parameters:
| Name | Type | Description |
|---|---|---|
shareHex |
string | |
projectHex |
string |
(async) readTailnetGroupLink(tailnetHex)
Parameters:
| Name | Type | Description |
|---|---|---|
tailnetHex |
string |
- Source:
(async) readTailnetRow(uidHex)
Parameters:
| Name | Type | Description |
|---|---|---|
uidHex |
string |
- Source:
(async) readVaultSecret(path) → {Promise.<(Object|null)>}
Read one credential secret. Returns null when missing OR when the private
key was blanked (a soft/blanked delete leaves an empty private_key behind —
such entries are zombies and must not surface in the API).
Parameters:
| Name | Type | Description |
|---|---|---|
path |
string |
Returns:
- Type
- Promise.<(Object|null)>
readsDb()
Liest aus der DB? (`dual` und `db` lesen beide zuerst die DB)
(async) rebuildFees(UIDfamily, organization) → {Promise.<boolean>}
Rebuilds the fee structure for a given family by UID.
This function fetches all family members, recalculates their fee indices,
updates the fee address if necessary, and persists any changes to the database.
It also publishes relevant events if the fee structure has changed.
This has to be called if a family cahnged, e.g. a new member was added or removed,
Parameters:
| Name | Type | Description |
|---|---|---|
UIDfamily |
Buffer | The UID of the family whose fees are to be rebuilt. |
organization |
string | The organization UID for multi-tenant context. |
- Source:
Throws:
Will log and handle errors internally if any database or processing error occurs.
Returns:
Returns true if the operation was successful, false otherwise.
- Type
- Promise.<boolean>
(async) rebuildFiltersDlist(rebuildFilters, UIDOrga) → {Promise.<Object>}
Rebuilds dynamic list filters and publishes appropriate events for membership changes.
This function removes and re-adds filters within a transaction, then compares the before/after
state to publish add/remove events for affected list members.
Parameters:
| Name | Type | Description |
|---|---|---|
rebuildFilters |
Array | Array of filter objects to rebuild, each containing UIDFilter, UIDList, and Type |
UIDOrga |
string | Buffer | The unique identifier of the organization (required) |
- Source:
Throws:
Will log an error if any operation fails during execution.
Returns:
Result object containing beAdded and removed arrays
- Type
- Promise.<Object>
rebuildFullTree(identifyer, options) → {Promise.<{deleted: Array, added: Array}>}
High-level rebuild that ensures groups are rebuilt first (sorted by hierarchie ASC),
so that group-to-group member links are correct before rebuilding persons/jobs/externs
whose member links depend on the group tree.
Parameters:
| Name | Type | Description |
|---|---|---|
identifyer |
string | Object type ('person','group','extern','event','job') or a UUID string |
options |
Returns:
- Type
- Promise.<{deleted: Array, added: Array}>
rebuildMembershipTree(UIDs, options) → {Promise.<{deleted: Array.<{UID: Buffer, UIDTarget: Buffer, DisplayMain: string, Display: string}>, added: Array.<{UID: Buffer, UIDTarget: Buffer, DisplayMain: string, Display: string, success: boolean}>}>}
Rebuilds the membership tree for a set of UIDs.
Uses a virtual transaction to compute the desired link state without committing,
then diffs against the current state and applies only the necessary inserts/deletes.
This avoids touching existing rows in the system-versioned Links table (updates would
create unnecessary history entries).
Parameters:
| Name | Type | Description |
|---|---|---|
UIDs |
Array.<Buffer> | hex Buffer UIDs of the objects to rebuild |
options |
Returns:
- Type
- Promise.<{deleted: Array.<{UID: Buffer, UIDTarget: Buffer, DisplayMain: string, Display: string}>, added: Array.<{UID: Buffer, UIDTarget: Buffer, DisplayMain: string, Display: string, success: boolean}>}>
reconcileAccounts(data, desired, remove)
Gleicht die geteilten Konten auf einem Mitglied ab — dieselbe Regel wie
`reconcileSharedField`, nur mit der entschluesselten IBAN als Schluessel.
Eigene Konten bleiben unberuehrt. Mit `remove` fallen die geteilten Konten weg, die
es in der Quelle nicht mehr gibt — das ist der Fall „geloescht" bzw. „auf privat
umgestuft", der laut UI fuer die ganze Familie gilt. Ein geteiltes Konto wird
insbesondere NICHT still auf einen anderen Typ herabgestuft.
Uebernommen wird jeweils der Eintrag so, wie er vorliegt — Klartext-IBANs werden
weder eingefuehrt noch entfernt.
Parameters:
| Name | Type | Description |
|---|---|---|
data |
Object | Member.Data des Mitglieds (wird veraendert) |
desired |
Array.<Object> | |
remove |
boolean |
- Source:
reconcileSharedField(data, field, desired, remove)
Gleicht EIN geteiltes Feld auf einem Mitglied ab.
`desired` sind die geteilten Eintraege der Quelle — sie sind der gewuenschte
geteilte Satz. Fehlende werden ergaenzt, geaenderte aktualisiert. Mit `remove`
werden zusaetzlich die geteilten Eintraege entfernt, die es in der Quelle nicht
mehr gibt: das ist der Fall „geloescht" bzw. „herabgestuft", der laut UI fuer die
ganze Familie gilt. Eintraege mit eigenem Typ bleiben immer unberuehrt — ein
geteilter Eintrag wird insbesondere NICHT still herabgestuft.
Parameters:
| Name | Type | Description |
|---|---|---|
data |
Object | Member.Data des Mitglieds (wird veraendert) |
field |
string | |
desired |
Array.<Object> | |
remove |
boolean |
- Source:
(async) recreateBanner(UID, banner, bannerRules, req)
Recursively propagates a banner update to child groups, jobs, and persons.
Parameters:
| Name | Type | Description |
|---|---|---|
UID |
Buffer | Group UID |
banner |
string | Banner URL/path |
bannerRules |
object | Banner inheritance rules |
req |
ExpressRequestAuthorized |
- Source:
(async) recreateJobs(req, jobs, timestamp, requalifyopt)
This function iterates through a list of jobs and updates their corresponding
entries in the ObjectBase database table. It processes job qualification status,
renders objects from templates, and updates various fields including Title,
qualification index, hierarchie, stage, gender
Parameters:
| Name | Type | Attributes | Default | Description |
|---|---|---|---|---|
req |
Object | The request object containing session information. | ||
jobs |
Array | Array of job objects to be recreated. | ||
timestamp |
number | The timestamp to use for updating objects. | ||
requalify |
boolean |
<optional> |
false | Whether to requalify the jobs. |
- Source:
Throws:
-
Logs error if job recreation fails.
- Type
- Error
(async) recreateJobsPerson(req, res, requalifyopt) → {Promise.<void>}
Retrieves job data for a person at a specific point in time (if timestamp provided),
then recreates the jobs using the recreateJobs function. When requalify is true,
it recalculates job qualification status based on achievements if requalify is true (default).
then recreates the jobs using the recreateJobs function.
Parameters:
| Name | Type | Attributes | Default | Description | |||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
req |
Object | Express request object
Properties
|
|||||||||||||||||||||||||
res |
Object | Express response object | |||||||||||||||||||||||||
requalify |
boolean |
<optional> |
true | Whether to recalculate job qualification status based on achievements |
- Source:
Throws:
-
- Logs error to error logger if query fails
- Type
- Error
Returns:
- Sends JSON response indicating success or handles error
- Type
- Promise.<void>
(async) recreateJobsPerson(req, UIDperson, requalifyopt) → {Promise.<Object>}
Recreates jobs for a specific person based on their UID.
This function fetches job data from the database for a person, potentially as of a specific timestamp,
and then recreates these jobs. It can also requalify the jobs as part of the process.
Parameters:
| Name | Type | Attributes | Default | Description |
|---|---|---|---|---|
req |
Object | The request object containing query parameters. | ||
UIDperson |
string | Buffer | The UID (hex string or Buffer) of the person whose jobs need to be recreated. | ||
requalify |
boolean |
<optional> |
true | Whether to requalify the jobs during recreation. |
- Source:
Throws:
-
- Any errors that occur during the job recreation process are logged.
- Type
- Error
Returns:
- A promise that resolves when the jobs have been recreated.
- Type
- Promise.<Object>
reduceResult(result) → {Array.<object>}
Deduplicate a raw query result set:
- Skip entries with linkType `member0`
- Merge Data objects for duplicate UIDBelongsTo values
- Omit entries entirely if a `member0` counterpart exists
Parameters:
| Name | Type | Description |
|---|---|---|
result |
Array.<object> | Raw query result rows |
- Source:
Returns:
Deduplicated result
- Type
- Array.<object>
(async) removeAction(action) → {Promise.<void>}
Handles various removal actions in the tree structure.
Delegates to type-specific submodules.
Note on dispatch order: the filter-type guards (include/exclude/intersect and
visible/changeable) use plain `if` blocks and are not mutually exclusive with
the final `else` branch of the listMember/list/family chain. This matches the
original logic exactly.
Parameters:
| Name | Type | Description |
|---|---|---|
action |
treeAction |
- Source:
Returns:
- Type
- Promise.<void>
(async) removeAction()
- Source:
(async) removeFilter(req) → {Promise.<Object>}
Remove a specific filter by UID
Parameters:
| Name | Type | Description |
|---|---|---|
req |
ExpressRequestAuthorized | Express request object |
- Source:
Returns:
Result object with success status
- Type
- Promise.<Object>
removeMemberALink(sourceUID, targetUID)
Removes one `memberA` link. Deletes only the current version; the history row
survives the system versioning and stays inspectable.
`ValidUntil > NOW()` is repeated on purpose: in a system-versioned table it is the
"is current" test, so a link that another process removed in between is a no-op.
Parameters:
| Name | Type | Description |
|---|---|---|
sourceUID |
Buffer | binary UID of the link owner |
targetUID |
Buffer | binary UID of the target to remove |
(async) removeObjects(myDEObjects, exclude, listUID) → {Promise.<(Array.<any>|undefined)>}
Removes objects based on exclude filter criteria
Parameters:
| Name | Type | Description |
|---|---|---|
myDEObjects |
Array.<any> | Array of objects to potentially remove |
exclude |
any | Exclude filter object |
listUID |
any | List identifier |
Returns:
- Array of removed objects
- Type
- Promise.<(Array.<any>|undefined)>
requestId()
Parameters:
| Type | Description |
|---|---|
- Source:
(async) requireAccessible(req, uidHex, orgHex)
Authorize that the project is currently accessible to the caller (snapshots
are service operations gated by the current event-driven rights).
Parameters:
| Name | Type | Description |
|---|---|---|
req |
ExpressRequestAuthorized | |
uidHex |
string | |
orgHex |
string |
(async) requireProjectAdmin(req, uidHex)
Authorize: the user may administrate the project (Visible.Type='admin' or org admin).
Parameters:
| Name | Type | Description |
|---|---|---|
req |
ExpressRequestAuthorized | |
uidHex |
string |
- Source:
(async) requireProjectChangeable(req, projectHex) → {Promise.<void>}
Verify the project exists in the organization and the user may **change** it
(Visible `changeable` or `admin`). Changeable is enough to create or link
shares — the creator becomes the owner of the new share.
Parameters:
| Name | Type | Description |
|---|---|---|
req |
ExpressRequestAuthorized | |
projectHex |
string |
Returns:
- Type
- Promise.<void>
(async) requireProjectVisible(req, projectHex) → {Promise.<void>}
Verify the project is at least visible to the user (share read is derived
from the project right).
Parameters:
| Name | Type | Description |
|---|---|---|
req |
ExpressRequestAuthorized | |
projectHex |
string |
Returns:
- Type
- Promise.<void>
(async) requireShareAdmin(req, projectHex, shareHex) → {Promise.<void>}
Verify the user may administer this share: project `admin` (inherited) or
`Visible.admin` on the share itself (the owner).
Parameters:
| Name | Type | Description |
|---|---|---|
req |
ExpressRequestAuthorized | |
projectHex |
string | |
shareHex |
string |
Returns:
- Type
- Promise.<void>
resolveDomain(host) → {Promise.<({orgId: string, appId: (string|null), appUid: string}|null)>}
Host → Organisation + App.
Gesucht wird über das Feld `domain` der App, nicht über ein Domain-Objekt:
eine frühere Fassung legte für jeden App-Host ein eigenes `appDomain`-Objekt
an und verlinkte es. Das machte den Domain-Bestand einer Organisation
ununterscheidbar von ihrer Routing-Tabelle — dieselbe Liste trug plötzlich
Mandanten-Domains (Cookie-Umfang, CORS) und App-Hosts. Der Host steht dort,
wo der Admin ihn einträgt: im App-Eintrag.
Der Vergleich läuft über appHostFor, also über **dieselbe** Regel,
nach der der Host aufgebaut wird. Ein reiner String-Vergleich mit dem
`domain`-Feld wäre falsch: bei einem internen Präfix (`sjm`) steht dort nicht
der Host, sondern nur `sjm`.
Kosten: ein Durchlauf über die App-Objekte (gemessen 29 in dieser Datenbank).
Ein Index wäre erst bei einem Vielfachen davon nötig.
Parameters:
| Name | Type | Description |
|---|---|---|
host |
string |
Returns:
- Type
- Promise.<({orgId: string, appId: (string|null), appUid: string}|null)>
(async) resolveProjectGroup(groupHex, orgHex) → {Promise.<({UID: string, Title: string, Display: string, Data: Object}|null)>}
Resolve the project group (owner group) for a project.
Follows the list/dlist pattern: the requested group is loaded, falling back
to the root group of the organization if it does not exist or is not a
group/event object.
Parameters:
| Name | Type | Description |
|---|---|---|
groupHex |
string | requested group UID (hex) or organization UID |
orgHex |
string | organization UID (hex) |
- Source:
Returns:
- Type
- Promise.<({UID: string, Title: string, Display: string, Data: Object}|null)>
(async) resolveProjectShare(projectHex, shareHex) → {Promise.<(string|null)>}
Das Share-Objekt, das **dieses** Projekt fuer einen Root benutzt.
Ein Aufrufer darf den Root nennen (den die Suche vor dem Ableger-Modell
zeigte) oder das Projekt-Objekt selbst (Basis/Ableger) — beides muss denselben
Treffer geben. Die harte Regel „ein Root hoechstens einmal je Projekt" macht
die Auflösung eindeutig, deshalb ist sie kein Raten.
Parameters:
| Name | Type | Description |
|---|---|---|
projectHex |
string | |
shareHex |
string |
Returns:
die UID des Projekt-Objekts
- Type
- Promise.<(string|null)>
resolveRelease(appKey, orgIdopt)
Löst die Auslieferung auf: Canary zuerst, sonst der Zeiger, sonst nichts.
Die Reihenfolge ist der Kern des Modells — sie macht Canary **additiv**. Wer
keinen Override hat, folgt `Current`; es gibt keinen Zustand, in dem eine
Organisation versehentlich kein Release mehr hat, nur weil Canary eingeführt
wurde.
Parameters:
| Name | Type | Attributes | Description |
|---|---|---|---|
appKey |
string | ||
orgId |
string |
<optional> |
UID in `UUID-`-Form (41 Zeichen) |
Returns:
resolveReleaseEnv(appKey, orgId) → {Promise.<({env: (object|null), version: string, source: ReleaseSource}|null)>}
Der `window.env`-Aufsatz eines Releases — die Schlüssel aus
`AppRelease.Backends`, **unverändert**.
Bewusst ohne Übersetzungstabelle: die Backends liegen bereits unter dem Namen,
unter dem das Frontend sie liest (`api`, `apiPortal`, …). Eine Umbenennung an
dieser Stelle würde nur eine zweite Stelle schaffen, an der ein neuer
Backend-Name nachgetragen werden müsste — und eine, die niemand beim Anlegen
eines Releases sieht.
`env: null` ist eine **gültige** Antwort und heißt „kein Release-Aufsatz":
der Consumer behält dann seine Deployment-Werte. `null` als ganzer
Rückgabewert dagegen heißt „die App hat überhaupt kein Release" — der
Unterschied zwischen „kein Canary" und „falsche App", und der Grund für den
404 in der Route.
Parameters:
| Name | Type | Description |
|---|---|---|
appKey |
string | |
orgId |
string |
Returns:
- Type
- Promise.<({env: (object|null), version: string, source: ReleaseSource}|null)>
(async) resolveRunnerGroup(groupHex, orgHex)
Resolve the owner group of a runner. `groupHex` may be a group/event UID or
an org UID (root group). Falls back to the org root group when the given
group is not a group/event object — exactly like project `resolveProjectGroup`.
Parameters:
| Name | Type | Description |
|---|---|---|
groupHex |
string | |
orgHex |
string |
- Source:
(async) resolveTailnetGroup(groupHex, orgHex)
Resolve the owner group of a tailnet. `groupHex` may be a group/event UID or
an org UID (root group). Falls back to the org root group when the given
group is not a group/event object — exactly like project `resolveProjectGroup`.
Parameters:
| Name | Type | Description |
|---|---|---|
groupHex |
string | |
orgHex |
string |
- Source:
resolveTreeUIDs(identifyer) → {Promise.<{UIDs: Array.<Buffer>, isType: boolean}>}
Resolves the UIDs to rebuild based on the identifier.
If the identifier is a known object type, fetches all UIDs of that type ordered by hierarchy.
If it's a UUID, converts it to hex. Returns an empty array if invalid.
Parameters:
| Name | Type | Description |
|---|---|---|
identifyer |
string | Object type ('person','group','extern','event','job') or a UUID string |
Returns:
- Type
- Promise.<{UIDs: Array.<Buffer>, isType: boolean}>
resumeAfterReconnect(socketID)
Nach einem Reconnect: verpasste Nachrichten ausliefern und auf
Direktzustellung umschalten.
Wird beim ersten `monitor`/`monitorObject` aufgerufen — also genau dann, wenn
der Client seine Abos neu registriert. Eine frische Verbindung ist bereits
`resynced` und ist hier ein No-Op.
Parameters:
| Name | Type | Description |
|---|---|---|
socketID |
string |
- Source:
same(a, b) → {boolean}
Parameters:
| Name | Type | Description |
|---|---|---|
a |
unknown | |
b |
unknown |
Returns:
- Type
- boolean
sanitizeGraphvizColor(color) → {string|null}
Validates a Graphviz color value.
Parameters:
| Name | Type | Description |
|---|---|---|
color |
any |
- Source:
Returns:
- Type
- string | null
sanitizeTextForEmbedding(text, options) → {string}
Sanitize text by replacing sensitive information with hashed placeholders
Preserves semantic structure while protecting PII
Parameters:
| Name | Type | Description | |||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
text |
string | Text to sanitize | |||||||||||||||||||||||||||||||||||
options |
Object | Sanitization options
Properties
|
- Source:
Returns:
- Sanitized text with hashed placeholders
- Type
- string
saveAppRelease()
Legt ein Release an oder schreibt es fort (Schlüssel `AppKey`+`Version`).
`current: true` macht den Zeiger in **einer** Transaktion um: erst alle
anderen Zeilen der App auf 0, dann diese auf 1. Sonst könnte bei einem Fehler
dazwischen kein oder — schlimmer — _jeder_ Zeiger gesetzt sein, und die
Auflösung oben (`Current = 1 LIMIT 1`) würde zufällig wählen.
Parameters:
| Type | Description |
|---|---|
saveApps(orgId, apps)
Persist the app registry for one organisation.
Parameters:
| Name | Type | Description |
|---|---|---|
orgId |
string | |
apps |
Record.<string, AppEntry> |
- Source:
saveDomains(orgId, domains)
Persist domain settings for one organisation.
Parameters:
| Name | Type | Description |
|---|---|---|
orgId |
string | |
domains |
Record.<string, string> |
- Source:
saveMailSettings(orgId, settings)
Persist mail/SMTP settings for one organisation.
If the password is the mask sentinel, the existing password is preserved.
Parameters:
| Name | Type | Description |
|---|---|---|
orgId |
string | |
settings |
object |
- Source:
saveOrgApps(orgId, apps)
Ersetzt den App-Bestand einer Organisation (die Map kommt als Ganzes).
Bestehende Apps behalten ihre UID — siehe Dateikopf.
Der Host einer App steht als Feld `domain` **im App-Objekt selbst**; es gibt
bewusst kein Domain-Objekt und keinen Link dafür. Eine frühere Fassung legte
für jeden App-Host automatisch ein `appDomain`-Objekt an und verlinkte es —
dadurch enthielt die Domain-Liste einer Organisation anschließend jeden
App-Host, und App-Hosts (Routing) und Mandanten-Domains (CORS, Cookie-Umfang,
Basis-Domain) waren nicht mehr unterscheidbar. `resolveDomain()` liest das
Feld deshalb direkt; bei 4–14 Apps je Organisation braucht es dafür keinen
Index.
Parameters:
| Name | Type | Description |
|---|---|---|
orgId |
string | |
apps |
Record.<string, object> |
saveOrgDomains(orgId, domains)
Ersetzt den Domain-Bestand einer Organisation.
Angenommen wird `{ [domain]: { type, status } }` **und** die Altform
(`"internal"`/`"external"`/`"verified"`) — eine Fassung, die nur die neue Form
verstünde, würde den vorhandenen Bestand beim ersten Speichern leeren.
Bestehende Domains behalten ihre UID: an ihnen hängen die Basis-Domain-
Erkennung (`shared-auth` prüft Kunden-Domains gegen die verifizierten) und der
Cookie-Umfang. Löschen und Neuanlegen würde diesen Anker bei jedem Speichern
austauschen.
Parameters:
| Name | Type | Description |
|---|---|---|
orgId |
string | |
domains |
Record.<string, unknown> |
(async) saveOrgSection(orgId, section, data)
Persist a named section for one organisation in Vault.
Parameters:
| Name | Type | Description |
|---|---|---|
orgId |
string | |
section |
string | |
data |
object |
- Source:
saveSecretsToVault(secretPath, data) → {Promise.<void>}
Write (or update) a secret in Vault KV v2.
Merges the new data on top of any existing secret (patch semantics via POST).
Parameters:
| Name | Type | Description |
|---|---|---|
secretPath |
string | Vault KV v2 path including the "data" segment, e.g. "orgas/data/UUID-xxxx/domains" |
data |
Record.<string, unknown> | Key/value data to store |
- Source:
Returns:
- Type
- Promise.<void>
scalarEntry()
Altwert-Skalar -> Eintrag, falls ein Feld noch als einfacher Wert gespeichert ist.
- Source:
scopeKey(row) → {string}
Der Schluessel einer Zeile fuer den Vergleich: `(UID, UIDUser, Level)` in
kanonischer Form.
Bewusst **einmal pro Zeile** berechnet. Vorher stand `asUuid` im innersten
Praedikat des Diffs, damit rechnete **jedes** `(a, b)`-Paar beide Seiten neu
aus — Regex und `HEX2uuid` inklusive. Das war O(n*m) mit teurer Konstante: auf
der Dev-Datenbank (`Visible`: 1.085.068 Zeilen) hat ein **einziger** Aufruf den
Server stundenlang auf 100 % CPU gehalten und die Event-Loop blockiert.
`JSON.stringify` statt einer Verkettung mit Trennzeichen, damit der Schluessel
auch fuer nicht deutbare Rohwerte kollisionsfrei bleibt — der Vergleich muss
exakt derselbe bleiben wie vorher.
Parameters:
| Name | Type | Description |
|---|---|---|
row |
VisibleScope |
- Source:
Returns:
- Type
- string
searchSimilarEntities(searchText, options) → {Promise.<Array>}
Search for similar entities using vector similarity
Parameters:
| Name | Type | Description | ||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
searchText |
string | Text to search for | ||||||||||||||||||||||||||||||||||||||||
options |
Object | Search options
Properties
|
- Source:
Returns:
- Array of matching entities with similarity scores
- Type
- Promise.<Array>
sendErrorMail(cfg, label, errors) → {void}
Verschickt eine Fehler-Mail — fire-and-forget.
Ein Fehler im Mailer darf die Fehlerbehandlung nicht aufhalten oder gar
werfen, deshalb ist alles umschlossen und der Versand wird nicht abgewartet.
Parameters:
| Name | Type | Description |
|---|---|---|
cfg |
Object | |
label |
string | `FATAL` (nur hierfuer wird gemailt) |
errors |
Array.<unknown> |
- Source:
Returns:
- Type
- void
serializeErrors(errors) → {string}
Macht Fehler mailbar.
`JSON.stringify(new Error('x'))` ergibt `{}` — `message` und `stack` sind
nicht-enumerable und fielen damit aus dem Mailtext heraus. Die Fehler-Mail
enthielt deshalb nur `[{}]` und war als Meldung wertlos.
Parameters:
| Name | Type | Description |
|---|---|---|
errors |
Array.<unknown> |
- Source:
Returns:
- Type
- string
setCurrentRelease(appKey, version)
Setzt den Zeiger auf eine vorhandene Version.
Ein Deploy ist damit ein Config-Update und ein Rollback das Zurücksetzen
desselben Zeigers — es startet nichts neu.
Parameters:
| Name | Type | Description |
|---|---|---|
appKey |
string | |
version |
string |
setOrgReleaseOverride(appKey, orgId, version)
Stellt eine Organisation auf ein bestimmtes Release (Canary).
Parameters:
| Name | Type | Description |
|---|---|---|
appKey |
string | |
orgId |
string | UID in `UUID-`-Form |
version |
string |
setPublicReadPolicy()
Set an anonymous read policy on a bucket (MinIO / S3).
Uses publicMinioClient if available, otherwise falls back to myMinioClient.
- Source:
shareToSnapshot(row, projectUid)
Parse a snapshot share row into the contract share shape. The share belongs
to the org, so project_uid comes from the snapshot context, not UIDBelongsTo.
Parameters:
| Name | Type | Description |
|---|---|---|
row |
any | |
projectUid |
string |
(async) slugTaken(slug, orgHex, excludeHexopt)
Slug uniqueness within the org (two teams must not collide on the same
headscale user / MagicDNS subdomain).
Parameters:
| Name | Type | Attributes | Description |
|---|---|---|---|
slug |
string | ||
orgHex |
string | ||
excludeHex |
string |
<optional> |
- Source:
(async) softDeleteVaultSecret(path) → {Promise.<boolean>}
Soft-delete the latest version of a KV2 secret via the data API.
Some tokens only get `delete` on the data endpoint (not the metadata one).
A soft delete makes the current version unreadable (read returns 404) —
good enough for a credential to disappear from the API, though version
history remains in Vault.
Parameters:
| Name | Type | Description |
|---|---|---|
path |
string | KV2 data path |
Returns:
true when the soft delete succeeded
- Type
- Promise.<boolean>
squeeze(rows, entityKeySet, allColumns) → {Array.<Object>}
Squeeze consecutive duplicate history rows for a single entity.
Keeps the first occurrence, then only rows whose hash differs from the previous,
plus the current (latest) row.
Parameters:
| Name | Type | Description |
|---|---|---|
rows |
Array.<Object> | All history rows for one entity, sorted by ValidFrom |
entityKeySet |
Set.<string> | |
allColumns |
Array.<{Field: string, isVirtual: boolean}> |
Returns:
- Type
- Array.<Object>
syncAccountingForFunction(functionUID) → {Promise.<void>}
Synchronizes Visible.Accounting for ALL jobs linked to a function template.
Called when a function template's financialMaster flag or accounting rules change.
Parameters:
| Name | Type | Description |
|---|---|---|
functionUID |
Buffer | string | UID of the function template |
Returns:
- Type
- Promise.<void>
syncAccountingVisibility(jobUID, optsopt) → {Promise.<void>}
Synchronizes `Visible.Accounting` for a single job.
- If financialMaster=true: sets Accounting='admin' on the job's group AND recursively
on all sub-groups of that group using a recursive CTE.
- If financialMaster=false: removes Accounting rows for this person+group.
Does NOT remove if the person has another job with financialMaster in same group.
Parameters:
| Name | Type | Attributes | Description | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
jobUID |
Buffer | string | UID of the job (hex buffer or UUID string) | |||||||||||||||||
opts |
Object |
<optional> |
Options
Properties
|
- Source:
Returns:
- Type
- Promise.<void>
(async) tailnetInOrg(uidHex, orgHex)
Parameters:
| Name | Type | Description |
|---|---|---|
uidHex |
string | |
orgHex |
string |
- Source:
(async) tailnetInOrg(uidHex, orgHex)
Parameters:
| Name | Type | Description |
|---|---|---|
uidHex |
Buffer | |
orgHex |
Buffer |
testSanitization()
Test sanitization with sample data (for debugging)
- Source:
timeOf(value) → {number}
`ValidFrom` is a `timestamp(6)`; the driver hands it over as a `Date`. The
comparison has to use the microseconds, not the rendered date.
Parameters:
| Name | Type | Description |
|---|---|---|
value |
unknown |
Returns:
- Type
- number
toCredential(ref, orgId, data)
Map a Vault secret payload + ref to the wire object (never the private key).
Parameters:
| Name | Type | Description |
|---|---|---|
ref |
string | {UID}/{name} |
orgId |
string | organization UID (to derive scope) |
data |
Object | Vault payload |
toListingInput(req)
Parameters:
| Name | Type | Description |
|---|---|---|
req |
ExpressRequestAuthorized |
- Source:
toOpenSshPublicKey(derPub) → {string}
Encode an ed25519 public key in DER (SPKI) form as the OpenSSH wire format
used by Git hosts ("ssh-ed25519 AAAA..."):
string "ssh-ed25519"
string raw 32-byte public key
The SPKI DER for ed25519 is exactly 44 bytes: 12 byte header + 32 byte key.
Parameters:
| Name | Type | Description |
|---|---|---|
derPub |
Buffer |
Returns:
- Type
- string
toReport(person) → {any}
Parameters:
| Name | Type | Description |
|---|---|---|
person |
any |
Returns:
- Type
- any
toStringUuid(uuid) → {string}
Helper function to ensure UUIDs are converted to strings
Parameters:
| Name | Type | Description |
|---|---|---|
uuid |
string | Buffer | UUID that might be Buffer or string |
- Source:
Returns:
- UUID as string
- Type
- string
(async) traverseGroupGraphRecursive()
Recursively traverses group links level-by-level.
Keeps route logic readable while preserving the existing batched SQL behavior.
Parameters:
| Type | Description |
|---|---|
- Source:
(async) triggerQueue(rootGroup) → {Promise.<void>}
Triggers the processing of the tree queue for a specific root organization.
Processes all queued actions in order and updates the queue status.
Parameters:
| Name | Type | Description |
|---|---|---|
rootGroup |
Buffer | string | The UID of the root organization group (Buffer or hex string) |
- Source:
Returns:
- Type
- Promise.<void>
(async) triggerQueues() → {Promise.<void>}
Initializes action handlers and triggers queue processing for all organizations.
This function imports the action modules and starts queue processing for all root organizations.
- Source:
Returns:
- Type
- Promise.<void>
(async) unExclude(entry, UIDOrga)
Removes the exclusion link for an entry and publishes add events to notify about the re-inclusion.
This function is called when an entry that was previously excluded from a list should now be included.
Parameters:
| Name | Type | Description |
|---|---|---|
entry |
any | Entry object containing UIDEntry, UIDList, UIDBelongsTo |
UIDOrga |
string | The unique identifier of the organization (required) |
Throws:
Will log an error if any operation fails during execution.
(async) updateAchievement(object, achievementUID, templateUID, session, connection) → {Promise.<void>}
Updates an existing achievement in the database.
Parameters:
| Name | Type | Description |
|---|---|---|
object |
Object | Achievement object with updated data |
achievementUID |
string | Buffer | UUID (hex string or Buffer) of the achievement |
templateUID |
string | Buffer | UUID (hex string or Buffer) of the template |
session |
Object | User session data |
connection |
Object | Database connection for transaction |
Returns:
- Type
- Promise.<void>
updateEntityEmbedding(entityUID, data, entityType, organizationUID, optionsopt) → {Promise.<void>}
Updates or creates AI embeddings for any entity type asynchronously
Parameters:
| Name | Type | Attributes | Description | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
entityUID |
Buffer | Entity UID as buffer | ||||||||||||||||
data |
Object | string | Entity data object or pre-prepared text | ||||||||||||||||
entityType |
string | Entity type ('person', 'extern', 'group', etc.) | ||||||||||||||||
organizationUID |
Buffer | Organization UID as buffer | ||||||||||||||||
options |
Object |
<optional> |
Optional configuration
Properties
|
- Source:
Returns:
- Type
- Promise.<void>
(async) updateJob(req, res) → {Promise.<void>}
Updates a job after validating user permissions.
The function performs these steps:
1. Converts the job UID parameter to hexadecimal format
2. Checks if the user has admin permissions for the job
3. Validates the job UID exists
4. Retrieves the existing job data from the database
5. Merges the existing job data with the provided updated data
6. Updates the job record in the database
7. Adds the job to an update list for processing
Parameters:
| Name | Type | Description | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
req |
Object | Express request object
Properties
|
|||||||||||||||
res |
Object | Express response object |
- Source:
Throws:
-
- Logs error through errorLoggerUpdate if operation fails
- Type
- Error
Returns:
- Sends JSON response with operation success status
- Type
- Promise.<void>
uploadAppIcon(orgId, appId, fileStream, mimeType) → {Promise.<string>}
Upload an app icon:
1. Stores the original in the public bucket (URL saved in Vault apps[appId].icon).
2. Uses sharp to generate all standard PWA sizes and writes them to the data
bucket at `{orgId}/manifests/{appId}/{iconName}` — the exact paths the
static server looks up, so no redirect is needed after this.
Parameters:
| Name | Type | Description |
|---|---|---|
orgId |
string | |
appId |
string | |
fileStream |
||
mimeType |
string |
- Source:
Returns:
The public icon URL
- Type
- Promise.<string>
uploadOrgAppIcon(orgId, appId, fileStream, mimeType) → {Promise.<string>}
Lädt ein App-Icon hoch (Original + PWA-Varianten) und legt ein `appAsset` an.
Die S3-Pfade sind **dieselben** wie im Vault-basierten `service.js` — der
Static-Server findet sie also unverändert, ohne dass dort etwas umgestellt
werden muss.
Parameters:
| Name | Type | Description |
|---|---|---|
orgId |
string | |
appId |
string | |
fileStream |
||
mimeType |
string |
Returns:
öffentliche Icon-URL
- Type
- Promise.<string>
validateDomains(newDomains, currentOrgId)
Prüft einen vorgeschlagenen Domain-Bestand.
Gehört hierher und nicht in den Vault-Adapter: geprüft wird gegen **den
Bestand, in den geschrieben wird**. Als die Prüfung noch aus Vault las, hat
sie den eigenen, inzwischen in der Datenbank liegenden Bestand nicht gesehen —
eine Domain ließ sich damit zweimal vergeben.
Parameters:
| Name | Type | Description |
|---|---|---|
newDomains |
Record.<string, unknown> | |
currentOrgId |
string |
Returns:
validateEmailArray() → {Object}
Validate an array of email entries, rejecting any that contain
display-name formatting like "Name ".
Only bare email addresses are accepted.
Parameters:
| Type | Description |
|---|---|
- Source:
Returns:
- Type
- Object
validateLoggerConfig()
Validates logger configuration
- Source:
Throws:
-
If configuration is invalid
- Type
- Error
validateRef()
Validate that `:ref` looks like "{UID}/{name}" (the service re-validates).
- Source:
vaultPathFor()
KV2 secret path for one credential (ref = {UID}/{name}).
vectorToString(embedding) → {string}
Convert Float32Array to MariaDB VECTOR text format like "[0.1,0.2,...]"
Parameters:
| Name | Type | Description |
|---|---|---|
embedding |
Float32Array | Embedding vector |
- Source:
Returns:
- Vector text representation for VEC_FromText()
- Type
- string
wrapLabel(value, maxLineLength) → {string}
Wraps text into short lines to keep Graphviz labels compact.
Parameters:
| Name | Type | Default | Description |
|---|---|---|---|
value |
string | ||
maxLineLength |
number | 16 |
- Source:
Returns:
- Type
- string
writesVault()
Schreibt zusätzlich nach Vault? (`vault` schreibt nur dorthin)
Type Definitions
AccountItem
Type:
- Object
Properties:
| Name | Type | Attributes | Description |
|---|---|---|---|
type |
string | Account type (e.g., 'family', 'familyFees', 'personal') | |
IBAN |
string |
<optional> |
Bank account IBAN |
- Source:
AddressItem
Type:
- Object
Properties:
| Name | Type | Attributes | Description |
|---|---|---|---|
type |
string | Address type (e.g., 'family', 'personal', 'secondary') | |
road |
string |
<optional> |
Street name |
houseNumber |
string |
<optional> |
House number |
postcode |
string |
<optional> |
Postal code |
countryCode |
string |
<optional> |
Country code |
- Source:
AppEntry
Type:
- Object
Properties:
| Name | Type | Attributes | Description |
|---|---|---|---|
domain |
string | The app's URL or hostname | |
roles |
Array.<string> | Keycloak roles that grant access | |
title |
string | Human-readable app name | |
description |
string |
<optional> |
|
port |
number |
<optional> |
- Source:
ClientData
Per-connection state stored in clientDataStore.
Type:
- Object
Properties:
| Name | Type | Attributes | Description |
|---|---|---|---|
updateBuffer |
Object | null | Pending list-update message to flush on reconnect | |
updateAbo |
Set.<Buffer> | Set of hex-encoded list UIDs this client monitors (via `monitor` events) | |
objectAbo |
Set.<Buffer> | Set of hex-encoded object UIDs this client monitors (via `monitorObject` events) | |
messageBuffer |
Array | General message buffer (reserved for future use) | |
UIDuser |
string | userUID of the connected user | |
UIDroot |
string | Organisation UID active for this connection | |
orgSource |
string | How orgUID was determined: 'header' | 'session' | 'user-default' | 'dynamic' | |
connectedAt |
number | Unix ms timestamp of initial connect | |
queueStatus |
any |
<optional> |
Last known queue status (cached for reference) |
- Source:
CollectionsListingInput
Type:
- Object
Properties:
| Name | Type | Description |
|---|---|---|
session |
any | |
query |
Record.<string, any> | |
body |
any | |
params |
Record.<string, any> |
- Source:
ControllerFunction()
DataFieldSpec
Type:
- Object
Properties:
| Name | Type | Attributes | Description |
|---|---|---|---|
path |
string | JSON path expression | |
alias |
string | Column alias name | |
query |
boolean |
<optional> |
Use JSON_QUERY instead of JSON_VALUE |
- Source:
DatabaseObject
Type:
- Object
Properties:
| Name | Type | Attributes | Description |
|---|---|---|---|
UID |
Buffer | The unique identifier as a Buffer | |
UIDBelongsTo |
Buffer | The parent object identifier as a Buffer | |
Type |
string | The object type | |
Title |
string |
<optional> |
Optional title for the object |
DatabaseQueryOptions
Type:
- Object
Properties:
| Name | Type | Attributes | Description |
|---|---|---|---|
log |
boolean |
<optional> |
Whether to log the query |
cast |
Array.<string> |
<optional> |
Fields to cast to JSON |
batch |
boolean |
<optional> |
Whether this is a batch operation |
group |
function |
<optional> |
Grouping function for results |
EmailItem
Type:
- Object
Properties:
| Name | Type | Attributes | Description |
|---|---|---|---|
type |
string | Email type (e.g., 'family', 'personal', 'secondary') | |
email |
string |
<optional> |
Email address |
- Source:
EntryObject
Type:
- Object
Properties:
| Name | Type | Description |
|---|---|---|
UID |
Buffer | Entry unique identifier |
UIDBelongsTo |
Buffer | Parent object identifier |
UIDList |
Buffer | List identifier |
UIDEntry |
Buffer | Entry identifier |
Type |
string | Object type |
Data |
Object | Entry data |
- Source:
EventLogConnection
A database connection capable of executing statements (either the transaction
connection wrapper from @commtool/sql-query or the default pool).
Type:
- Object
Properties:
| Name | Type | Description |
|---|---|---|
query |
function |
- Source:
ExpressRequest
Type:
- Object
Properties:
| Name | Type | Attributes | Description |
|---|---|---|---|
params |
any |
<optional> |
Route parameters |
query |
any |
<optional> |
Query string parameters |
body |
any |
<optional> |
Request body |
method |
string |
<optional> |
HTTP method |
url |
string |
<optional> |
Request URL |
headers |
any |
<optional> |
Request headers |
user |
any |
<optional> |
Authenticated user object |
session |
SessionData |
<optional> |
Session data |
ExpressRequestAuthorized
Type:
- Object
Properties:
| Name | Type | Attributes | Description | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
params |
any |
<optional> |
Route parameters | ||||||||||||||||||||||||||||||||||||
query |
any |
<optional> |
Query string parameters | ||||||||||||||||||||||||||||||||||||
body |
any |
<optional> |
Request body | ||||||||||||||||||||||||||||||||||||
method |
string |
<optional> |
HTTP method | ||||||||||||||||||||||||||||||||||||
url |
string |
<optional> |
Request URL | ||||||||||||||||||||||||||||||||||||
headers |
any |
<optional> |
Request headers | ||||||||||||||||||||||||||||||||||||
user |
Object |
<optional> |
Authenticated user object | ||||||||||||||||||||||||||||||||||||
session |
Object | Session data (guaranteed to exist for authorized requests)
Properties
|
ExpressResponse
Type:
- Object
Properties:
| Name | Type | Description |
|---|---|---|
status |
function | Set status code |
json |
function | Send JSON response |
send |
function | Send response |
set |
function | Set header |
end |
function | End response |
FamilyMemberObject
Type:
- Object
Properties:
| Name | Type | Attributes | Description | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
UID |
Buffer | Member unique identifier | |||||||||||||||||||||
Type |
'person' | 'extern' | 'family' | Object type (required) | |||||||||||||||||||||
Data |
Object | Member data object (required)
Properties
|
|||||||||||||||||||||
Touched |
Array.<string> |
<optional> |
Names of the shared fields the caller actually supplied (from the write patch). Only those are reconciled with removal — see `adjustMemberData`. Omit for full-database sources (then nothing is removed). |
- Source:
FilterAction
Type:
- 'visible' | 'changeable' | 'include' | 'exclude' | 'intersect'
FilterData
Type:
- Object
Properties:
| Name | Type | Attributes | Description |
|---|---|---|---|
UID |
string | Filter unique identifier | |
Type |
string | Filter type (include, exclude, intersect) | |
Data |
any | Filter configuration | |
SourceType |
string |
<optional> |
Source type (group, list, etc.) |
listUID |
string |
<optional> |
Associated list UID |
FilterObject
Type:
- Object
Properties:
| Name | Type | Description |
|---|---|---|
UID |
Buffer | The filter unique identifier |
Type |
string | Filter type ('include', 'exclude', 'intersect') |
Data |
Object | Filter configuration data |
FilterOptions
Type:
- Object
Properties:
| Name | Type | Attributes | Default | Description |
|---|---|---|---|---|
virtual |
boolean |
<optional> |
false | Whether this is a virtual operation (no permanent changes) |
connection |
any |
<optional> |
null | Database connection for transactions |
LinkDecision
Type:
- Object
Properties:
| Name | Type | Description |
|---|---|---|
keep |
Array.<any> | the links that survive (at most one, none on a skip) |
remove |
Array.<any> | the links to delete |
skipReason |
string | null | set when a human has to decide |
LoggerOptions
Type:
- Object
Properties:
| Name | Type | Attributes | Description |
|---|---|---|---|
maxSize |
string |
<optional> |
Maximum size per log file (e.g., '20M') |
interval |
string |
<optional> |
Time interval for rotation (e.g., '1d', '1h') |
path |
string |
<optional> |
Directory path for logs |
maxFiles |
number |
<optional> |
Number of files to retain |
- Source:
MemberData
Type:
- Object
Properties:
| Name | Type | Attributes | Description |
|---|---|---|---|
UID |
Buffer | string | Member unique identifier | |
UIDBelongsTo |
Buffer | string | Parent object identifier | |
Type |
string | Object type | |
Data |
any | Member data | |
Title |
string |
<optional> |
Optional title |
new |
boolean |
<optional> |
Whether this is a new entry |
OrganizationData
Type:
- Object
Properties:
| Name | Type | Attributes | Description |
|---|---|---|---|
UID |
string | Organization unique identifier | |
name |
string | Organization name | |
data |
any |
<optional> |
Additional organization data |
PersonListItem
Type:
- Object
Properties:
| Name | Type | Attributes | Description |
|---|---|---|---|
UID |
string | Object UID (UUID string) | |
Type |
PersonType | Object type | |
UIDBelongsTo |
string | Parent person UID | |
Title |
string | null | Title / role label | |
Display |
string | Member display name | |
SortName |
string | Sort key | |
UIDgroup |
string | null | Primary group UID | |
pGroup |
string | null | Primary group display label | |
hierarchie |
number | null | Hierarchy level | |
stage |
number | null | Stage / level | |
gender |
string | null | Gender | |
dindex |
number | null | Display index | |
visibility |
'visible' | 'changeable' |
<optional> |
Visibility type |
- Source:
PersonType
Type:
- 'person' | 'guest' | 'extern' | 'family' | 'job' | 'entry' | 'eventJob'
- Source:
PhoneItem
Type:
- Object
Properties:
| Name | Type | Attributes | Description |
|---|---|---|---|
type |
string | Phone type (e.g., 'family', 'personal', 'secondary') | |
number |
string |
<optional> |
Phone number |
- Source:
PublishEventOptions
Type:
- Object
Properties:
| Name | Type | Attributes | Default | Description |
|---|---|---|---|---|
organization |
string | The organization UUID for multi-tenant scoping | ||
data |
Array.<any> | any | The event data payload | ||
backDate |
number |
<optional> |
Unix timestamp for backdated events | |
eventLog |
boolean |
<optional> |
true | Whether to also persist the event to eventLog |
- Source:
ReleaseSource
Type:
- 'override' | 'current'
ServiceResult
Type:
- Object
Properties:
| Name | Type | Attributes | Description |
|---|---|---|---|
success |
boolean | ||
result |
any |
<optional> |
|
message |
string |
<optional> |
|
status |
number |
<optional> |
HTTP status to respond with (default 200) |
- Source:
ServiceResult
Type:
- Object
Properties:
| Name | Type | Attributes | Description |
|---|---|---|---|
success |
boolean | Whether the operation was successful | |
result |
any |
<optional> |
Result data if successful |
error |
any |
<optional> |
Error message if unsuccessful |
UIDaction |
string |
<optional> |
Action UID for some operations |
SessionData
Type:
- Object
Properties:
| Name | Type | Attributes | Description |
|---|---|---|---|
root |
string |
<optional> |
The root organization UUID |
user |
string |
<optional> |
The database member UUID |
baseUser |
string |
<optional> |
The base user UUID |
loginOrga |
string |
<optional> |
The login organization UUID from token |
sysUser |
string |
<optional> |
System user UID (often used as organization/system admin) |
authUser |
any |
<optional> |
Authenticated user from bearer token (groups, orgRoles, etc.) |
save |
function | Save session data | |
destroy |
function | Destroy session | |
regenerate |
function | Regenerate session ID | |
reload |
function | Reload session data |
ShareType
Type:
- 'repositoryShare' | 'directoryShare'
- Source:
SocketIOServer
Socket.IO server instance used to register connection handlers and emit to rooms.
Type:
- Object
- Source:
SocketIOSocket
Represents a single connected Socket.IO client.
Type:
- Object
Properties:
| Name | Type | Description | |||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
request |
Object | Underlying HTTP upgrade request (contains session, headers) | |||||||||||||||||||||
data |
Object | Per-socket mutable storage set by connection handler
Properties
|
|||||||||||||||||||||
id |
string | Internal Socket.IO connection ID | |||||||||||||||||||||
connected |
boolean | Whether the socket is currently connected | |||||||||||||||||||||
emit |
function | Emit an event to this client | |||||||||||||||||||||
join |
function | Join a Socket.IO room | |||||||||||||||||||||
disconnect |
function | Forcefully close the connection | |||||||||||||||||||||
handshake |
Object | Handshake information (headers, query, auth) |
- Source:
VisibleScope
Eine Sichtbarkeits-Zeile mit dem Typ des Objekts, an dem sie haengt.
`UID` und `UIDUser` sind **kanonische Strings** (`UUID-…`), keine Rohbuffer:
die Normalisierung passiert einmal an der Grenze, in loadVisibleScopes
(`cast: ['UUID']`). Wer die Zeilen von Hand baut (die Rebuild-Pfade in
`rebuildList.js`), muss dieselbe Form liefern — `asUuid` toleriert Buffer
weiterhin, damit ein einzelner Aufrufer das nicht erzwingt.
Type:
- Object
Properties:
| Name | Type | Description |
|---|---|---|
UID |
string | das Objekt, an dem das Recht haengt |
Type |
string | das Level: `visible` | `changeable` | `admin` |
UIDUser |
string | die Person, die das Recht hat |
ObjectType |
string | `ObjectBase.Type` von `UID` |
- Source:
treeAction
The action object containing details about the operation to be performed.
Type:
- Object