Privileges
The admin permission model. Works the same on both data providers: json stores helper admins and their privileges just like sql does.
Two roles
Super admin — credentials from ADMIN_LOGIN and ADMIN_PASS in server/.env. Passes any privilege check regardless of the list.
Helper admins — created by the super admin. They start unauthorized and activate themselves with a temporary password, and hold only the privileges explicitly granted. Changes take effect without logging in again.
How they are checked
In HTTP routes the privilege sits as middleware right after authentication:
router.post('/switch-mode',
requireAdmin,
requirePrivilege(PRIVILEGES.MODE_SWITCH),
handler);Some checks live at the Socket.io layer (socket/context.js) — that is how RADIO_HOST and RADIO_MODERATOR work, which is why the table below shows zero HTTP endpoints for them.
Privileges: 14
| Privilege | Value | Endpoints guarded |
|---|---|---|
QUEUE_MANAGE | queue_manage | 1 |
ARTIST_ARTS | artist_arts | 4 |
UPLOAD_SONGS | upload_songs | 10 |
EDITOR_LYRICS | editor_lyrics | 9 |
EDITOR_META | editor_meta | 14 |
SETTINGS_BRANDING | settings_branding | 0 |
SETTINGS_GROUPS | settings_groups | 4 |
SETTINGS_ALGORITHM | settings_algorithm | 0 |
STATS | stats | 2 |
MODE_SWITCH | mode_switch | 1 |
JINGLES_UPLOADER | jingles_uploader | 21 |
RADIO_HOST | radio_host | 0 |
RADIO_MODERATOR | radio_moderator | 0 |
DONATIONS_MANAGE | donations_manage | 3 |
The table counts HTTP endpoints only. Some privileges — notably
RADIO_HOSTandRADIO_MODERATOR— are also checked at the Socket.io layer (socket/context.js), so a zero does not mean the privilege is unused.
Per-section checking on POST /api/admin/settings
One endpoint writes three groups of settings governed by different privileges:
| Body section | Privilege | Contents |
|---|---|---|
branding | SETTINGS_BRANDING | radio names, links |
generation | SETTINGS_ALGORITHM | durations, mode, genre groups |
radioHosts | RADIO_MODERATOR | guest and special-guest limits |
Authorization is based on what actually changes, not on what was submitted. The admin panel always posts every section, including ones the current admin did not edit, so the mere presence of a section means nothing.
The server runs both the incoming data and the current state through the same validation pipeline and compares the results: if a section matches after normalization, no privilege is needed for it. Unit conversion, whitespace trimming or a different key order therefore never look like an edit.
If a section the admin may not touch does change, the whole request is rejected with 403 — there is no partial save. Changed sections are recorded in the audit log.
For authors of alternative admin panels
songGroups is ignored in that request: song groups are edited through the separate /api/admin/song-groups endpoints under SETTINGS_GROUPS.