Admin > Editor > Viewer
β β β
Full Create/ View
Access Edit Only
- Go to Dashboard Settings β Share Links tab
- Select role: Viewer, Editor, or Admin
- Optionally set expiration (days)
- Click "Generate Link"
- Link appears in "Active Share Links" list
- Click link field to copy to clipboard
http://localhost:3000/accept-share?token=<32-byte-hex>
- Change Permission: Use role dropdown
- Revoke: Click "Revoke" button
- Copy Link: Click the link field
- View Usage: See access count
- User accesses Dashboard Settings
- User has "Viewer" role on that dashboard
π You are in viewing mode
You are unable to make changes to this document.
- Dashboard name update
- Visibility settings
- Transfer admin role
- Delete dashboard
- Add collaborators
- Create/manage share tokens
// Get user's role for dashboard
const role = await dashboardModel.getUserRole(userId, dashboardId);
// Returns: "Admin" | "Editor" | "Viewer" | null// Only allow Admin and Editor to edit
router.put("/tasks/:id",
checkTaskPermission(['Admin', 'Editor']), // Middleware checks role
taskController.updateTask
);// Hide buttons for non-admins
if (currentUserRole !== 'Admin') {
addPeopleBtn.style.display = 'none';
}β Create/View/Edit/Delete dashboards β Create/View/Edit/Delete boards β Create/View/Edit/Delete tasks β Invite users (any role) β Change user roles β Remove collaborators β Create share tokens β Modify share tokens β Revoke tokens β Change visibility (private/public) β Transfer admin role
β View dashboards β View/Edit/Delete own boards β Create/View/Edit/Delete tasks β View collaborators list β Cannot invite users β Cannot change roles β Cannot create share tokens β Cannot modify settings
β
View dashboards
β
View boards
β
View tasks
β Cannot create/edit content
β Cannot manage collaborators
β Cannot create tokens
β Cannot modify settings
POST /api/dashboards/1/share-tokens
Content-Type: application/json
Authorization: Bearer <token>
{
"role": "Editor",
"expirationDays": 7
}
Response:
{
"ShareTokenId": 1,
"Token": "abc123...",
"Role": "Editor",
"CreatedAt": "2024-01-30T...",
"ExpiresAt": "2024-02-06T..."
}GET /api/dashboards/1/share-tokens
Authorization: Bearer <token>
Response:
[
{
"ShareTokenId": 1,
"Token": "abc123...",
"Role": "Editor",
"ExpiresAt": "2024-02-06T...",
"IsActive": 1,
"AccessCount": 5
}
]PUT /api/dashboards/share-tokens/1/role
Content-Type: application/json
Authorization: Bearer <token>
{
"newRole": "Viewer"
}DELETE /api/dashboards/share-tokens/1/revoke
Authorization: Bearer <token>Solution: Check that user has role assigned in UserDashboards table
Solution: Check expiration date and IsActive status in ShareTokens table
Solution: Check frontend role comparison (case-sensitive: 'Admin' not 'admin')
Solution: Ensure collaborators are loaded before checking role
SELECT Role FROM UserDashboards
WHERE UserId = 1 AND DashboardId = 1;SELECT u.FullName, u.Email
FROM UserDashboards ud
JOIN Users u ON ud.UserId = u.UserId
WHERE ud.DashboardId = 1 AND ud.Role = 'Admin';SELECT * FROM ShareTokens
WHERE DashboardId = 1
AND IsActive = 1
AND (ExpiresAt IS NULL OR ExpiresAt > GETDATE());UPDATE ShareTokens
SET IsActive = 0, RevokedAt = GETDATE()
WHERE DashboardId = 1 AND IsActive = 1;- Only Admins can create share tokens
- Tokens are 256-bit (32 bytes) random
- Tokens cannot be guessed
- Tokens can expire
- Tokens can be revoked
- Access is logged (count)
- Viewers cannot modify content
- Role checks at middleware + backend
- Frontend shows appropriate UI per role
- Database enforces role constraints
Before deploying:
- Backup existing database
- Run schema.sql migration
- Run seed.sql with new role values
- Verify all "Owner" role values changed to "Admin"
- Test admin user can create tokens
- Test viewer sees protection banner
- Test editor cannot manage tokens
- Test share token access works
- Clear browser cache (old role values)
- Test in incognito mode
// In dashboardModel.js, line ~207
const expiresAt = new Date(Date.now() + expirationDays * 24 * 60 * 60 * 1000);Default: 7 days if expirationDays is null
/* dashboard-settings.css */
.viewing-mode-banner {
background: #fef3c7; /* Light yellow */
border: 1px solid #f59e0b; /* Amber */
}- Database: 'Admin', 'Editor', 'Viewer' (capital first letter)
- JavaScript: 'admin', 'editor', 'viewer' (lowercase - watch for inconsistencies!)
- Middleware: 'Admin', 'Editor', 'Viewer' (use capital)
// Run in browser console
fetch('/api/dashboards/1/share-tokens', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${localStorage.getItem('token')}`
},
body: JSON.stringify({
role: 'Editor',
expirationDays: 7
})
}).then(r => r.json()).then(console.log);// In dashboard-settings.js console
console.log('Current collaborators:', collaborators);
console.log('Current role:', collaborators.find(c => c.email === getUserInfoFromToken().email)?.role);- Share tokens are validated on each use (AccessCount incremented)
- Consider caching token info if high-volume access expected
- Index on ShareTokens.Token for fast lookups recommended
- Expiration check happens at database level for efficiency