Skip to content

User Panel Module

The User Panel Module (@zumito-team/user-panel-module) provides a web dashboard where Discord users can manage their servers, view account info, and access module-specific pages (like music queues). It features Discord OAuth2 authentication, multi-language support, and a dynamic sidebar that other modules can extend.

Terminal window
npm install @zumito-team/user-panel-module

Add to your zumito.config.ts:

bundles: ['@zumito-team/user-panel-module']

A full web interface at /panel showing:

  • Server list — all guilds where the user has Administrator, ManageGuild, or owner permissions.
  • Guild-specific dashboard at /panel/:guildId with extensible sidebar sections.
  • Multi-language support with automatic detection via cookies or Accept-Language headers.

Discord OAuth2-based login flow via the shared discord-auth module:

  1. User visits /panel/login → redirected to Discord authorization (identify scope).
  2. Discord redirects back to /panel/login/callback.
  3. A JWT (panel_token, purpose: 'panel') is issued with the full Discord user profile, stored in an httpOnly cookie with 30-day expiry.
  4. All panel routes validate this token via UserPanelAuthService (which extends DiscordAuthService).
  5. Tokens issued for other modules (e.g., Admin) are rejected thanks to the purpose claim.

| Service | Purpose | |---|---|---| | UserPanelNavigationService | Register sidebar items in the user panel. | | UserPanelViewService | Render pages within the panel layout (EJS templates). | | UserPanelAuthService | Extends DiscordAuthService. Validates JWT tokens with purpose: 'panel' and extracts Discord user data. | | UserPanelLanguageManager | Resolve language from cookies or headers, provide translation functions. | | UserPanelColorsService | Manage the panel color palette. |

MethodPathDescription
GET/panelMain dashboard with server list.
GET/panel/:guildIdGuild-specific dashboard.
GET/panel/loginRedirect to Discord OAuth2.
GET/panel/login/callbackOAuth2 callback handler.
GET/panel/logoutClear auth cookie and redirect to /panel.

The module registers these initial entries:

  • Back — Link to /panel.
  • Dashboard — Guild overview at /panel/:guildId with a general section.
VariableRequiredDescription
DISCORD_CLIENT_IDYesDiscord application client ID.
DISCORD_CLIENT_SECRETYesDiscord application client secret.
SECRET_KEYYesSecret key for JWT signing.
HOSTYesBot host URL.
FRONTEND_URLYesFrontend URL for redirects.
USER_PANEL_COLORS_FILENoPath to a JSON file with color overrides.
USER_PANEL_COLORSNoJSON string with color overrides.

Same structure as the admin module. Configurable via USER_PANEL_COLORS env var or JSON file:

{
"primary": "#5865f2",
"accent": "#5865f2",
"success": "#57f287",
"warning": "#fee75c",
"danger": "#ed4245",
"foreground": "#ffffff",
"dark": {
"100": "#36393f",
"200": "#2f3136",
"300": "#292b2f",
"400": "#202225"
}
}

Other modules can register sidebar items by consuming UserPanelNavigationService:

import { ServiceContainer } from 'zumito-framework';
const nav = ServiceContainer.getService('UserPanelNavigationService');
// Register a top-level nav item
nav.registerItem({
id: 'my-feature',
label: 'My Feature',
icon: 'star',
link: '/panel/my-feature'
});
// Register sub-items under an existing parent's section
nav.registerSubItems('dashboard', 'general', [
{
id: 'my-sub-page',
label: 'My Sub Page',
icon: 'settings',
link: '/panel/:guildId/my-page'
}
]);

Create your own route classes that use UserPanelViewService.render():

import { Route, RouteMethod, ServiceContainer } from 'zumito-framework';
class MyPanelRoute extends Route {
method = RouteMethod.get;
path = '/panel/:guildId/my-page';
async execute(req, res) {
const auth = ServiceContainer.getService('UserPanelAuthService');
const { isValid, data } = await auth.isLoginValid(req);
if (!isValid) return res.redirect('/panel/login');
const view = ServiceContainer.getService('UserPanelViewService');
const html = await view.render({
content: '<h1>My Custom Page</h1>',
reqPath: req.path,
req,
res
});
res.send(html);
}
}

Your module must declare the dependency when extending the panel:

class MyModule extends Module {
requirements = {
services: ['UserPanelNavigationService']
};
async initialize() {
const nav = ServiceContainer.getService('UserPanelNavigationService');
nav.registerSubItems('dashboard', 'general', [
{ id: 'my-page', label: 'My Page', link: '/panel/:guildId/my-page' }
]);
}
}

The panel automatically detects the user’s language. Use the UserPanelLanguageManager to access translations:

const langManager = ServiceContainer.getService('UserPanelLanguageManager');
const { t, lang } = langManager.getLanguageVariables(req, res);
const greeting = t('welcome_message'); // Returns translated string
const availableLanguages = langManager.getAvailableLanguages(); // ['en', 'es', ...]

Both modules share the same auth foundation via discord-auth, with different configurations:

FeatureAdminUser Panel
Auth serviceAdminAuthServiceDiscordAuthServiceUserPanelAuthServiceDiscordAuthService
View serviceAdminViewServiceUserPanelViewService
Nav serviceNavigationServiceUserPanelNavigationService
Colors serviceAdminColorsServiceUserPanelColorsService
Cookie nameadmin_tokenpanel_token
JWT purpose'admin''panel'
Sub-item keysectionLabelsectionId
  • discord-auth — Shared Discord OAuth2 authentication service.
  • ejs — Template rendering for panel pages.
  • jose — JWT verification and signing.
  • zumito-framework
  • DisTube Module — Registers a music queue page in the user panel sidebar.
  • Admin Module — Similar architecture for admin-level dashboards.