server admin panel is now supports multiple operators profiles

This commit is contained in:
onysd 2026-09-08 01:01:35 +03:00
parent e48160ac3a
commit 280321b902
25 changed files with 2197 additions and 128 deletions

View file

@ -17,6 +17,22 @@ export const permissionBotVerificationManage = "botverification.manage";
// Server Settings: identity, .env, restart/update. One right, not
// review/manage -- see the constant's doc comment in security.go.
export const permissionServerManage = "server.manage";
// Operator accounts. The one right that can hand out every other right, so it
// is never implied by anything else -- see the constant's doc comment in
// security.go.
export const permissionAdminsManage = "admins.manage";
// Section rights, in read/manage pairs following the sidebar -- see the const
// block in security.go, which these must match exactly.
export const permissionAccountsRead = "accounts.read";
export const permissionChannelsRead = "channels.read";
export const permissionBotsRead = "bots.read";
export const permissionMessagesRead = "messages.read";
export const permissionModerationReview = "moderation.review";
export const permissionBroadcastsRead = "broadcasts.read";
export const permissionStorageRead = "storage.read";
export const permissionContentRead = "content.read";
export const permissionUsernamesRead = "usernames.read";
export const permissionDashboardRead = "dashboard.read";
// GET /api/session is read once at boot; the panel keeps the answer here so a
// section the session may not use is hidden instead of rendered into a 403. This
@ -117,3 +133,117 @@ export function ThirdPartyVerificationHiddenGate({ children }: { children: React
</PageFrame>
);
}
// Human-readable names for the permission strings. The raw value is what the
// backend stores and checks, but "content.manage" is a machine's word for it --
// an operator ticking boxes should read what the right actually lets someone do.
//
// Anything missing from this map falls back to the raw string rather than being
// hidden, so a right added on the server still appears (just untranslated)
// instead of silently vanishing from the editor.
const permissionLabels: Record<string, { title: string; hint: string }> = {
"accounts.read": { title: "View accounts", hint: "Browse users, their profiles and sessions" },
"accounts.manage": { title: "Edit accounts", hint: "Change profiles, usernames, freeze and revoke sessions" },
"channels.read": { title: "View groups and channels", hint: "Browse supergroups and channels" },
"channels.manage": { title: "Edit groups and channels", hint: "Change settings, usernames and avatars" },
"bots.read": { title: "View bots", hint: "Browse the bot list and their details" },
"bots.manage": { title: "Create and delete bots", hint: "Add new bots and remove existing ones" },
"bots.token.read": { title: "Reveal bot tokens", hint: "Export a bot's live credential" },
"messages.read": { title: "View messages", hint: "Read private and group message history" },
"messages.manage": { title: "Delete messages", hint: "Remove messages and clear history" },
"moderation.review": { title: "Handle reports", hint: "Work the moderation queue and decide cases" },
"broadcasts.read": { title: "View broadcasts", hint: "See past and scheduled broadcasts" },
"broadcasts.send": { title: "Send broadcasts", hint: "Deliver a message to many users at once" },
"content.read": { title: "View stickers, emoji and GIFs", hint: "Browse the packs and the GIF catalogue" },
"content.manage": { title: "Edit stickers, emoji and GIFs", hint: "Create, rename and remove packs and catalogue entries" },
"usernames.read": { title: "View NFT usernames", hint: "Browse collectible usernames" },
"usernames.manage": { title: "Manage NFT usernames", hint: "Mint, transfer and revoke collectible usernames" },
"storage.read": { title: "View storage", hint: "See media usage per account" },
"storage.manage": { title: "Purge storage", hint: "Manually delete stored media" },
"dashboard.read": { title: "View the dashboard", hint: "See the overview counters and server health" },
"premium.manage": { title: "Manage Premium", hint: "Grant, revoke and refund Premium" },
"verification.review": { title: "Verify accounts", hint: "Work the verification queue and grant badges" },
"verification.revoke": { title: "Remove verification", hint: "Take a granted badge away (needs the right above too)" },
"botverification.review": { title: "Handle third-party marks", hint: "Work the third-party verification queue" },
"botverification.manage": { title: "Appoint verifiers", hint: "Grant verifier status and curate mark icons" },
"server.manage": { title: "Server settings", hint: "Identity, .env editing, restart and update" },
"admins.manage": { title: "Manage operators", hint: "Create operators and decide what everyone can do" },
"*": { title: "Full access", hint: "Every right, including future ones" }
};
export function permissionTitle(permission: string): string {
return permissionLabels[permission]?.title ?? permission;
}
export function permissionHint(permission: string): string {
return permissionLabels[permission]?.hint ?? "";
}
// Rights grouped by the part of the console they govern, so the editor reads as
// a few short decisions instead of one wall of twenty-six checkboxes.
//
// The order is roughly "everyday work first, keys to the building last": an
// operator scanning down the list meets the routine rights before the ones that
// can undo the deployment.
export const permissionGroups: { title: string; hint: string; permissions: string[] }[] = [
{
title: "People and chats",
hint: "Users, groups and their message history",
permissions: ["accounts.read", "accounts.manage", "channels.read", "channels.manage", "messages.read", "messages.manage"]
},
{
title: "Moderation and verification",
hint: "Reports, badges and third-party marks",
permissions: ["moderation.review", "verification.review", "verification.revoke", "botverification.review", "botverification.manage"]
},
{
title: "Content",
hint: "Sticker packs, emoji, GIFs and collectible usernames",
permissions: ["content.read", "content.manage", "usernames.read", "usernames.manage"]
},
{
title: "Bots",
hint: "The bot roster and its credentials",
permissions: ["bots.read", "bots.manage", "bots.token.read"]
},
{
title: "Broadcasting",
hint: "Messages sent to many users at once",
permissions: ["broadcasts.read", "broadcasts.send"]
},
{
title: "Storage and overview",
hint: "Media usage and the dashboard",
permissions: ["storage.read", "storage.manage", "dashboard.read"]
},
{
title: "Billing",
hint: "Premium grants and refunds",
permissions: ["premium.manage"]
},
{
title: "The console itself",
hint: "The two rights that can change the deployment or hand out every other right",
permissions: ["server.manage", "admins.manage"]
}
];
// groupPermissions arranges the server's list into the groups above. Anything
// the server offers that no group claims is collected at the end rather than
// dropped, so a right added on the backend still appears here without this file
// having to be edited first.
export function groupPermissions(available: string[]): { title: string; hint: string; permissions: string[] }[] {
const remaining = new Set(available);
const out: { title: string; hint: string; permissions: string[] }[] = [];
for (const group of permissionGroups) {
const present = group.permissions.filter((p) => remaining.has(p));
present.forEach((p) => remaining.delete(p));
if (present.length > 0) {
out.push({ title: group.title, hint: group.hint, permissions: present });
}
}
if (remaining.size > 0) {
out.push({ title: "Other", hint: "Rights this console version does not have a group for", permissions: [...remaining] });
}
return out;
}