Guild Settings - Usage Guide
Overview
Guild settings are now editable through a new Settings tab in the guild page, restricted to wowRank 0-1 (Guild Master and top officers only).
Permission Level
- wowRank 0 (Guild Master): Full access to all settings ✓
- wowRank 1 (Top Officer): Full access to all settings ✓
- wowRank 2 (Officer): Can manage members/events, cannot edit guild settings ✗
- wowRank 3+ (Members): Read-only access ✗
This ensures only the most trusted leadership can modify important guild configuration.
Available Settings
1. Guild Logo Management
Upload and manage custom guild logos with professional image editing, or toggle between synced and custom logos for Battle.net guilds.
Features:
- Advanced Image Editor: Professional crop, zoom, and rotate interface
- Drag to Reposition: Click and drag to position your logo
- Zoom Control: 1x-3x zoom with slider or scroll/pinch gestures
- Rotation Support: 0-360° rotation for perfect alignment
- Visual Crop Preview: Grid overlay shows exactly what will be saved
- WYSIWYG Output: What you see in the preview is what gets saved (512x512 PNG)
- Logo Selection (Blizzard guilds): Choose between Battle.net crest or custom upload via radio cards
- Touch Support: Optimized for mobile and tablet devices
- File Validation: JPEG, PNG, GIF, WebP (max 2MB) with client-side validation
- Instant Updates: Logo changes visible immediately across all pages
- Cache Management: Automatic cache invalidation on upload
API Endpoints:
# Upload custom logo
POST /api/v1/guilds/:guildId/logo
Authorization: Bearer <token>
Content-Type: multipart/form-data
Form Data:
logo: <image file>
# Toggle between synced/custom logo (Blizzard guilds)
PATCH /api/v1/guilds/:guildId/logo/toggle
Authorization: Bearer <token>
{
"useCustomLogo": true
}
# Get logo information
GET /api/v1/guilds/:guildId/logo
Response Format:
{
"currentLogo": "/uploads/guild-logos/xyz-123456789.png",
"syncedLogo": "/api/v1/blizzard/guild-logo/...",
"customLogo": "/uploads/guild-logos/xyz-123456789.png",
"emblemData": {
"icon": 12,
"iconColor": "rgba(255,255,255,1)",
"border": 5,
"borderColor": "rgba(30,30,30,1)",
"backgroundColor": "rgba(200,50,50,1)"
},
"useCustomLogo": true,
"syncSource": "blizzard",
"hasCustomLogo": true,
"hasSyncedLogo": true
}
Note: Battle.net guilds may have emblemData (SVG-like crest structure) instead of syncedLogo URL. The frontend GuildCrest component renders this structured data.
Cache Management: Logo updates trigger comprehensive cache clearing:
- Backend Redis cache (guild detail + list endpoints)
- Frontend TanStack Query cache
- Both authenticated user and guest caches
- Pattern-based clearing ensures all query param variations are cleared
2. Roster Visibility
Control who can view your guild roster:
- 🌍 Public - Anyone can view the roster (great for recruiting)
- 👥 Members Only - Only guild members can view (default, balanced)
- 🔒 Officers Only - Only ranks 0-2 can view (maximum privacy)
API Endpoint:
PATCH /api/v1/guilds/:guildId/settings
Authorization: Bearer <token>
{
"rosterPrivacy": "officers"
}
3. Public Listing
Control whether your guild appears in the public guild directory for unauthenticated users. When enabled, basic guild info (name, realm, faction, logo) is visible to anyone, allowing potential recruits to discover and apply without logging in first.
This is separate from roster privacy — a guild can be publicly listed while keeping its roster restricted to members or officers.
{
"publicListing": true
}
Default: true (guilds are publicly listed)
4. Show Character Details
Toggle display of character level, class, and gear info in roster.
{
"showCharacterDetails": true
}
5. Allow Guild Applications
Enable/disable the ability for users to submit applications to join.
{
"allowApplications": false
}
How It Works
Backend: CASL Enforces Restrictions
The AbilityFactory checks wowRank and only grants Action.Update on Organization to ranks 0-1:
// Only ranks 0-1 can update guild settings
if (wowRank !== null && wowRank <= 1) {
can([Action.Read, Action.Update], 'Organization', { id: membership.guildId });
}
// Rank 2 can manage members/events but NOT guild settings
else if (wowRank === 2) {
can(Action.Read, 'Organization', { id: membership.guildId });
// Note: can NOT update Organization
}
Frontend: Settings Link Auto-Hidden
The Settings sidebar link and its sub-pages only appear for users with canManageGuild permission. The settings are split across dedicated routes:
| Route | Page |
|---|---|
/guilds/:id/settings/basic | Name, alias (URL slug) |
/guilds/:id/settings/info | MOTD and guild description |
/guilds/:id/settings/privacy | Public listing, roster visibility, feature toggles |
/guilds/:id/settings/appearance | Logo management |
/guilds/:id/settings/integrations | Discord server connection |
/guilds/:id/settings/roster | Battle.net sync (Blizzard guilds only) |
/guilds/:id/settings/advanced | Archive / delete (standalone guilds only) |
Navigation for all these pages is driven by a single shared list in -settingsLinks.ts (see Adding a Settings Page below), so both the desktop sidebar and the mobile tab bar always stay in sync.
UI Features
Layout
- Two-Column Responsive Design: Settings organized in main content (left) and sidebar (right)
- Privacy & Access Section: Roster visibility and feature toggles in main column
- Appearance Section: Logo management in sidebar
- Roster Sync Section: Battle.net sync tools for connected guilds
- Advanced Section: Danger zone actions (archive/delete)
- Mobile Responsive: Stacks vertically on small screens
Interactions
- Real-time updates: Settings apply instantly
- Auto-save: Changes save automatically when toggled
- Success/Error feedback: Visual confirmation of setting changes
- Helpful descriptions: Each setting explains what it does
- Permission notice: Warning that only ranks 0-1 can edit
- Logo Radio Cards: Visual selection between Battle.net and custom logos
- Professional Upload Modal: Industry-standard crop/zoom/rotate interface
Testing the Feature
As Guild Master (rank 0)
- Navigate to your guild page
- See the "Settings" tab appear
- Click Settings tab
- Change roster privacy setting
- See success message
- Setting is now enforced guild-wide
As Rank 2 Officer
- Navigate to guild page
- Settings tab does NOT appear
- Cannot access
/guilds/:id/settingsendpoint - API returns 403 Forbidden if attempted
As Regular Member
- Navigate to guild page
- No Settings tab visible
- Cannot modify guild configuration
Privacy Enforcement Example
Guild A sets rosterPrivacy: "officers"
- Guild Master (rank 0): ✓ Can see full roster
- Top Officer (rank 1): ✓ Can see full roster
- Officer (rank 2): ✓ Can see full roster
- Regular Member (rank 4): ✗ Cannot see roster (CASL blocks it)
- Guest (not logged in): ✗ Cannot see roster
Guild B sets rosterPrivacy: "public"
- Everyone: ✓ Can see roster (including guests)
The AbilityFactory reads the guild's settings.rosterPrivacy and dynamically generates abilities accordingly.
Adding a Settings Page
To add a new settings section:
-
Register the route — create
apps/web/src/routes/guilds/$guildId/settings.<name>.tsxfollowing the pattern of the existing settings routes. -
Add to the shared nav list — open
apps/web/src/routes/guilds/$guildId/-settingsLinks.tsand append an entry:{ segment: 'my-section', label: 'My Section' },
// or conditionally:
{ segment: 'my-section', label: 'My Section', condition: (s) => s === 'standalone' },This single change updates both the desktop sidebar sub-links (
OrganizationLayout) and the mobile horizontal tab bar (settings.tsx) simultaneously. -
Update the backend DTO if new persisted settings are needed (
update-guild-settings.dto.ts):@IsOptional()
@IsBoolean()
newSetting?: boolean; -
(Optional) Add CASL logic (
ability.factory.ts):if (guildSettings.newSetting) {
can(Action.SomeAction, 'SomeSubject');
}
API Response Format
When fetching a guild with settings:
{
"id": "guild-123",
"name": "My Guild",
"realm": "Area 52",
"faction": "horde",
"settings": {
"publicListing": true,
"rosterPrivacy": "members",
"showCharacterDetails": true,
"allowApplications": false
},
"userPermissions": {
"abilityRules": [...] // Includes privacy enforcement rules
}
}
The abilityRules are automatically generated based on the guild's settings and the user's rank.
Public Access (Unauthenticated Users)
When publicListing is enabled, the guild appears in the public directory and its overview page is accessible without login. Unauthenticated users can:
- ✓ See guild name, realm, faction, logo
- ✓ View published/active/completed events
- ✓ Submit an application (if applications are enabled)
- ✗ Cannot see draft events (organizer-only)
- ✗ Cannot see the roster (controlled by
rosterPrivacy)
The @OptionalAuth() decorator on the organization endpoints allows both authenticated and unauthenticated requests. The backend filters results based on publicListing for guests.
Security
✅ Backend-enforced: Even if frontend is bypassed, AbilitiesGuard checks CASL rules
✅ Rank-based: Only wowRank 0-1 can update
✅ Per-guild: Each guild has independent settings
✅ Type-safe: Settings validated with class-validator DTOs
✅ Audit trail: All setting changes go through the API (loggable)
Migration
If adding default settings to existing guilds:
-- Set default roster privacy for guilds without it
UPDATE guilds
SET settings = jsonb_set(
COALESCE(settings, '{}'::jsonb),
'{rosterPrivacy}',
'"members"'
)
WHERE settings IS NULL
OR settings->>'rosterPrivacy' IS NULL;