feat(admin): bot management (list, verify, create, delete)

- Add a Bots admin tab: list/search bots with a dedicated read query
  (users.is_bot, excluded from the accounts list), showing owner and
  system-vs-user type
- Create system bots from the admin via a new bot.create command that
  reuses the existing bot provisioning flow; the token is shown once
- Delete user-created bots via a new bot.delete command backed by a
  dedicated Postgres DeleteBotAccount (revokes sessions, purges private
  state, releases username, drops the bots row, tombstones the user);
  system service bots are rejected
- Verified badge toggling reuses the existing set-verified command
- All write paths go through the dry-run/confirm + audit command pipeline
- Rebuild dist bundle
This commit is contained in:
epilepticseizureee 2026-07-23 00:34:05 +03:00
parent ad9d535edc
commit 9e45da69ef
22 changed files with 985 additions and 12 deletions

View file

@ -19,6 +19,7 @@ export function currentRoute(): RouteState {
export function routeTitle(pathname: string, t: TFunction): string {
if (pathname.startsWith("/accounts")) return t("route.accounts");
if (pathname.startsWith("/channels")) return t("route.channels");
if (pathname.startsWith("/bots")) return t("route.bots");
if (pathname.startsWith("/messages")) return t("route.messages");
if (pathname.startsWith("/gifts")) return t("route.gifts");
return t("route.dashboard");
@ -27,6 +28,7 @@ export function routeTitle(pathname: string, t: TFunction): string {
export function routeSubtitle(pathname: string, t: TFunction): string {
if (pathname.startsWith("/accounts")) return t("route.accountsSubtitle");
if (pathname.startsWith("/channels")) return t("route.channelsSubtitle");
if (pathname.startsWith("/bots")) return t("route.botsSubtitle");
if (pathname.startsWith("/messages")) return t("route.messagesSubtitle");
if (pathname.startsWith("/gifts")) return t("route.giftsSubtitle");
return t("route.dashboardSubtitle");