Skip to content

Add admin dashboard foundation with RBAC and security PIN (#64)#82

Merged
hrx01-dev merged 4 commits into
hrx01-dev:mainfrom
Harsh-goswami-103:feat/admin-dashboard-rbac
Jun 20, 2026
Merged

Add admin dashboard foundation with RBAC and security PIN (#64)#82
hrx01-dev merged 4 commits into
hrx01-dev:mainfrom
Harsh-goswami-103:feat/admin-dashboard-rbac

Conversation

@Harsh-goswami-103

@Harsh-goswami-103 Harsh-goswami-103 commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Summary

Implements the admin dashboard foundation (issue #64): an internal /admin portal built on Firebase Authentication + Firestore, with role-based access control, dynamic navigation, route protection, a security-PIN layer for sensitive actions, and append-only audit logging.

Closes #64

What's included

  • RBACsuper_admin / frontend_dev / backend_dev / qa_delivery with a fine-grained permission matrix, enforced client-side (route guards + can()) and server-side in firestore.rules.
  • Auth & route protection — admin login, ProtectedAdminRoute, RequirePermission, real-time admins/{uid} role loading, and an unauthorized screen.
  • Dynamic, role-filtered sidebar + responsive admin layout.
  • Pages — dashboard, projects, clients, messages, audit, settings.
  • Security PIN — PBKDF2 / Web Crypto, session-based verification with first-time setup + re-verify, gating sensitive actions (delete project, change roles, disable admin, reveal sensitive metric).
  • Typed Firestore data layer (parsers + realtime hooks) and unforgeable, append-only audit logs.
  • Firestore Security Rules — RBAC enforcement, hardened public messages create rule, owner-scoped admin self-updates, immutable audit_logs.
  • docs/ADMIN.md — architecture, Firestore schema, setup, and security model.

Quality

  • npm run typecheck, npm run lint, and npm run build all pass.
  • Underwent a multi-dimension adversarial review (RBAC, Firestore rules, PIN/crypto, router/state, pages/a11y); all confirmed findings were fixed — rules hardening, a last-super-admin lockout guard, audit-route reachability for backend_dev, PIN edge cases, write-failure feedback, and scoping the landing splash so it no longer blocks /admin and auth routes.

Setup required before use (Firebase console, project servio-0)

  1. Authentication → enable Email/Password.
  2. Firestore → create the database.
  3. Bootstrap the first super admin — create the auth user, then an admins/{uid} document with role: "super_admin", disabled: false, email, displayName (rules only let an existing super admin create others). See docs/ADMIN.md §5.3.
  4. Deploy rulesfirebase deploy --only firestore:rules.

Firebase web config is read from .env.local (git-ignored); see .env.example.

Notes / follow-ups

  • PIN verification is client-side (documented limitation; hash material is readable by super admins). Hardening path: move PIN fields to an owner-only subcollection and/or verify in a Cloud Function. See docs/ADMIN.md §8.

Summary by CodeRabbit

Release Notes

  • New Features
    • Added a dedicated Admin Portal at /admin with login, dashboard, projects, clients, messages, audit log, and settings pages.
    • Enabled role-based access control with permission-gated navigation and actions.
    • Introduced Security-PIN protection for sensitive operations, plus a session verification window.
    • Added an Audit Log view to track sensitive admin activity.
  • Security / Backend Rules
    • Added Firestore security rules and index configuration to enforce admin access control.
  • Documentation
    • Added Admin Portal setup and security-PIN/RBAC documentation.
  • Configuration
    • Updated the environment template with complete client-side Firebase web settings.

some of the glimpse

image Screenshot 2026-06-21 023553 Screenshot 2026-06-21 023355

Introduce an internal /admin portal built on Firebase Auth + Firestore.

- RBAC across super_admin / frontend_dev / backend_dev / qa_delivery with a
  fine-grained permission matrix, enforced both client-side (route guards,
  can()) and server-side in firestore.rules.
- Dynamic, role-filtered sidebar and a responsive admin layout.
- Pages: login, dashboard, projects, clients, messages, audit, settings,
  plus an unauthorized screen.
- Security PIN (PBKDF2 / Web Crypto) gating sensitive actions, with
  session-based verification plus first-time setup and re-verify flows.
- Typed Firestore data layer (parsers + realtime hooks) and append-only,
  unforgeable audit logging.
- firestore.rules: RBAC enforcement, hardened public messages create rule,
  owner-scoped admin self-updates, immutable audit_logs.
- docs/ADMIN.md: architecture, schema, setup, and security model.
- Wire Firestore (db) into firebase.ts and scope the landing splash so it no
  longer blocks /admin and auth routes.
@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 953b6c29-1167-40a5-b4c8-68445367978f

📥 Commits

Reviewing files that changed from the base of the PR and between 5f3b67a and 635b7e6.

📒 Files selected for processing (5)
  • .env.example
  • docs/ADMIN.md
  • src/admin/context/AdminContext.tsx
  • src/admin/hooks/useAdminData.ts
  • src/admin/lib/devMock.ts

📝 Walkthrough

Walkthrough

Introduces a complete /admin SPA for the Servio platform: domain types, a four-role RBAC model, Firestore security rules, a client-side PBKDF2-SHA256 PIN utility with session gating, Firebase Auth and admin context providers, shared UI components and route guards, real-time collection hooks with development mocks, eight protected admin pages, app routing integration, and admin documentation.

Changes

Admin Portal Foundation

Layer / File(s) Summary
Domain types and RBAC model
src/admin/types.ts, src/admin/rbac/roles.ts, src/admin/rbac/permissions.ts, src/admin/rbac/navigation.ts
Defines all Firestore-backed domain types (AdminProfile, Project, Client, ContactMessage, AuditLogEntry), four AdminRole values with display metadata, resource:action Permission union, role-to-permission matrix, sensitive permission subset, and the sidebar navigation registry.
Firestore collections, parsers, and security rules
firebase.json, .env.example, firestore.rules, src/admin/lib/collections.ts
Adds Firestore config block, environment variable template with Firebase and bootstrap-email keys, typed collection handles and document parsers, and complete Firestore security rules with auth helpers, role→permission capability checks, collection-level CRUD gating, schema-validated message creation, and append-only audit log enforcement.
Client-side PIN cryptography
src/admin/lib/pin.ts
Implements PBKDF2-SHA256 PIN hashing via Web Crypto, random salt generation, constant-time equality, PinCredential creation, and PIN verification.
Admin and PIN context providers and hooks
src/admin/context/AdminContextObject.ts, src/admin/context/AdminContext.tsx, src/admin/context/PinContextObject.ts, src/admin/context/PinProvider.tsx, src/admin/context/useAdmin.ts, src/admin/context/usePinGate.ts, src/admin/hooks/useSensitiveAction.ts
Defines admin and PIN context contracts; AdminProvider subscribes to admins/{uid}, handles bootstrap provisioning, and exposes can(permission); PinGateProvider manages session TTL, dialog challenge lifecycle, and promise-based verification; strict hooks and a useSensitiveAction wrapper gate actions behind PIN verification.
Shared admin UI components and route guards
src/admin/components/...
Adds loading spinner, empty state, page header, stat card, role badge, PIN setup/verify dialog (PBKDF2 Firestore write), permission-filtered sidebar with sign-out, responsive layout shell with mobile slide-over, and ProtectedAdminRoute/RequirePermission guards.
Real-time collection hooks, dev mocks, audit helper, and formatting utilities
src/admin/hooks/useAdminData.ts, src/admin/lib/devMock.ts, src/admin/lib/audit.ts, src/admin/lib/authError.ts, src/admin/lib/format.ts
Generic useCollectionData hook with real-time Firestore subscription and dev-mode mock fallback; collection-specific typed hooks; development mock data for MOCK_USER, MOCK_ADMIN, MOCK_PROJECTS, MOCK_CLIENTS, MOCK_MESSAGES, MOCK_AUDIT; writeAuditLog with best-effort semantics; Firebase auth error mapper; date/relative/currency/initials formatters.
Admin pages
src/admin/pages/...
Eight pages: AdminLogin with redirect flow, Unauthorized with denial-reason display, Dashboard with RBAC stat cards and PIN-gated revenue reveal, Projects with creation/status-change/PIN-gated delete, Clients with CRUD dialog, Messages with filter/expand/status-update, Audit with 50-entry table, and Settings with PIN status and admin role/disable management.
App routing integration and documentation
src/app/App.tsx, src/admin/AdminApp.tsx, docs/ADMIN.md
AdminApp defines the /admin/* route tree with provider wrapping and per-page RequirePermission gates; App adds the /admin/* route and scopes splash/inert logic to LandingShell for the landing page; docs/ADMIN.md documents architecture, RBAC model, Firestore schema, PIN design, setup steps, and known limitations.

Sequence Diagram(s)

sequenceDiagram
  participant Browser
  participant AdminApp
  participant AdminProvider
  participant Firestore
  participant PinGateProvider
  participant PinDialog

  Browser->>AdminApp: navigate /admin/*
  AdminApp->>AdminProvider: wrap routes
  AdminProvider->>Firestore: onSnapshot admins/{uid}
  Firestore-->>AdminProvider: AdminProfile (or missing → bootstrap provision)
  AdminProvider-->>AdminApp: isAdmin, can(permission)
  AdminApp->>PinGateProvider: wrap protected routes
  Note over AdminApp: ProtectedAdminRoute checks isAdmin
  Note over AdminApp: RequirePermission checks can(permission)
  Browser->>PinGateProvider: sensitive action triggered
  PinGateProvider->>PinDialog: open PIN challenge
  PinDialog-->>PinGateProvider: onSuccess / onCancel
  PinGateProvider-->>Browser: execute or abort action
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related issues

  • #64 (Build Admin Dashboard Foundation with Role-Based Access Control (RBAC) and Security PIN Support): This PR implements all acceptance criteria from that issue: Firebase admin auth, /admin route tree, dashboard layout, four-role RBAC with Firestore enforcement, protected routes, dynamic sidebar, PIN architecture with session TTL, Firestore collections designed and documented, and unauthorized access handling.

Possibly related PRs

  • hrx01-dev/Servio#41: Both PRs modify src/app/App.tsx routing—this PR adds the admin /admin/* subtree and refactors landing-page splash gating into LandingShell, while the related PR also updates app routing and landing behavior.
  • hrx01-dev/Servio#48: Both PRs modify src/app/App.tsx to implement/reshape the app's landing "splash/loading" gating via useAppLoading, inert scroll, and focus handoff, so the changes overlap at the same integration point.

Suggested reviewers

  • hrx01-dev

Poem

🐰 Hop hop, the admin door is sealed tight,
Four roles and a PIN guard the night,
PBKDF2 hashes each secret away,
Firestore rules keep intruders at bay,
The bunny reviews and the portal shines bright! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 23.68% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main addition: an admin dashboard with RBAC and security PIN. It accurately reflects the primary changes across all modified/new files.
Linked Issues check ✅ Passed The PR comprehensively implements all coding-related requirements from issue #64: Firebase Authentication, dedicated /admin routes with pages, RBAC with four roles, route protection via guards, dynamic sidebar, Firestore collections, security PIN with hashing, and append-only audit logging. All acceptance criteria are met.
Out of Scope Changes check ✅ Passed All changes directly support the admin dashboard foundation objectives. Documentation (ADMIN.md), configuration (firebase.json, .env.example), types, RBAC, PIN utilities, context providers, components, pages, and Firestore rules all align with issue #64 requirements. Minor modifications to App.tsx integrate the admin routes as expected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

…d-rbac

# Conflicts:
#	src/Firebase/firebase.ts
#	src/app/App.tsx

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 15

🧹 Nitpick comments (1)
src/admin/pages/Settings.tsx (1)

34-39: ⚡ Quick win

Mirror UI lock invariants inside mutation handlers.

Self-protection and “last enabled super admin” safeguards are expressed in UI state (locked), but should also be enforced in handlers so future UI changes can’t bypass these constraints accidentally.

Suggested refactor
   async function handleRoleChange(row: AdminProfile, newRole: AdminRole) {
+    if (row.uid === currentAdmin.uid) {
+      toast.error("You can't change your own role.");
+      return;
+    }
     if (newRole === row.role) return;
     if (row.role === "super_admin" && !row.disabled && enabledSuperCount <= 1) {
       toast.error("You can't change the role of the last enabled super admin.");
       return;
     }
   async function handleToggleDisabled(row: AdminProfile) {
+    if (row.uid === currentAdmin.uid) {
+      toast.error("You can't change your own status.");
+      return;
+    }
     if (row.role === "super_admin" && !row.disabled && enabledSuperCount <= 1) {
       toast.error("You can't disable the last enabled super admin.");
       return;
     }

Also applies to: 62-66, 154-164

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/admin/pages/Settings.tsx` around lines 34 - 39, The validation checks in
handleRoleChange for preventing role changes to the last enabled super admin
should be enforced in the actual mutation handler that performs the backend
update, not just in the UI event handler. Move the constraint validation logic
(checking if newRole equals current role, if it's the last enabled super admin,
and if the admin is locked) from handleRoleChange into the underlying mutation
function or API call to ensure these safeguards remain enforced even if the UI
implementation changes. Apply the same refactoring pattern to all similar role
and status change handlers throughout the Settings component.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/ADMIN.md`:
- Around line 243-246: Update the documentation entry for the `/admin` route to
accurately reflect all possible redirect behaviors based on user authentication
and authorization status, rather than only showing the happy path redirect to
`/admin/dashboard`. The entry should clarify that unauthenticated users are
redirected to `/admin/login`, non-admin authenticated users are redirected to
`/admin/unauthorized`, and only authenticated admin users are redirected to
`/admin/dashboard`. Replace the current single-line description with a more
detailed explanation or conditional behavior specification that covers all three
scenarios.
- Around line 17-33: The fenced code block that starts with "Browser ──▶
AuthProvider (Firebase Auth)" is missing a language specifier, which violates
markdownlint rule MD040. Add the language identifier "text" to the opening fence
of this code block so it reads as three backticks followed by "text" instead of
just three backticks alone. This will properly declare the code block language
and satisfy the linter requirement.
- Around line 220-226: Add required blank lines around the admin configuration
table in the ADMIN.md file. Insert a blank line before the table that begins
with the header row containing Field, Type, and Value columns, and insert
another blank line after the table ends at the `createdAt` timestamp row. This
addresses the markdown linting rule MD058 which requires proper spacing around
tables for readability.

In `@firestore.rules`:
- Line 137: The current `allow update: if can('messages:reply');` rule in the
firestore.rules file permits full-document rewrites, allowing moderators to
modify immutable fields such as name, email, body, and createdAt. To fix this,
modify the update rule to include field-level validation that ensures only
mutable fields (such as reply status or moderator notes) can be updated while
protecting immutable fields like name, email, body, and createdAt from being
altered. Use Firestore security rule syntax to check the difference between the
original resource.data and the incoming request.resource.data, allowing only
changes to approved mutable fields.

In `@src/admin/components/AdminLayout.tsx`:
- Around line 19-25: The mobile sidebar overlay div in AdminLayout lacks
keyboard accessibility and has no Escape key handler to close the modal. Add an
onKeyDown event handler to the overlay div (the one with className using cn()
and aria-hidden={!mobileOpen}) that listens for the Escape key and closes the
sidebar by setting mobileOpen to false. Additionally, ensure the overlay div has
appropriate ARIA attributes (such as role="dialog" or similar) to properly
convey its modal nature to assistive technologies. Apply these keyboard and
accessibility improvements to all mobile sidebar overlay divs referenced in the
comment (around lines 19-25, 34-43, and 48-53).

In `@src/admin/components/AdminSidebar.tsx`:
- Around line 22-26: The handleSignOut function lacks error handling for the
signOut(auth) call, which means if it rejects, the function throws unhandled and
skips the navigate call, leaving the UI in a broken state without user feedback.
Wrap the signOut(auth) call in a try-catch block to explicitly handle failures,
log the error appropriately, and provide user feedback (such as an error message
or toast notification) while ensuring the navigation or error state is handled
gracefully.

In `@src/admin/context/AdminContext.tsx`:
- Around line 33-35: When the currentUser changes, the admin state should be
reset immediately to prevent stale role data from being exposed briefly. In the
AdminContext, after setting setDocLoading(true) and setError(null) at the
beginning of the effect that listens to currentUser changes, add a call to reset
the admin state (setAdmin) to null or an initial/empty value. This ensures that
role and can() methods will not reflect the previous user's permissions while
the new user's admin data is being fetched. Apply this fix at both locations
mentioned: around line 33-35 and also at lines 56-62.

In `@src/admin/context/PinProvider.tsx`:
- Around line 42-49: The PinProvider component does not properly settle the
pending promise created by challenge() when it unmounts, which can leave callers
hanging indefinitely. Add a cleanup function to the useEffect hooks mentioned
(including the one with admin?.uid dependency and the one referenced in lines
53-59) that settles the promise stored in resolverRef.current by calling it with
an appropriate value (such as false) if it exists before the component is
removed from the DOM. This ensures that any in-flight challenge() calls receive
a resolution and do not hang.
- Around line 70-74: The isVerified value on line 70 becomes stale after the TTL
expires because it only updates when the component re-renders, and Date.now() is
only evaluated at render time. To fix this, add a useEffect hook that calculates
the remaining time until verifiedUntil expires and sets up a timeout to trigger
a state update (like setting a dummy state or using a date ticker) when that
timeout completes, ensuring isVerified automatically reflects the current
verification status without requiring external state changes to force a
re-render.

In `@src/admin/lib/collections.ts`:
- Around line 73-74: The pinIterations field in the collections parser accepts
any number value without validation, which allows invalid values like NaN,
Infinity, and negative numbers to pass through to PIN verification flows that
use this as a PBKDF2 work factor. Add validation logic in the pinIterations
assignment to ensure the value is a valid positive integer (or reasonable
positive number) and reject invalid values by returning undefined or throwing an
error. The validation should check for NaN, Infinity, negative values, and
optionally enforce an upper bound to prevent performance issues.
- Line 94: The budget value assignment currently uses typeof check which allows
NaN and Infinity values to pass through since they are technically of type
"number" in JavaScript. Replace the typeof check with Number.isFinite() to
ensure the budget value is a valid finite number, filtering out both NaN and
Infinity values before assigning them to the budget property.

In `@src/admin/pages/AdminLogin.tsx`:
- Around line 47-51: The handleSignOut function does not handle errors from the
signOut call, which can leave unhandled promise rejections and provide no user
feedback on failure. Wrap the await signOut(auth) call in a try-catch block,
catching any errors and setting the error state with an appropriate error
message before setting busy to false. This fix should be applied to both the
handleSignOut function around line 47 and the similar sign-out handler around
line 90-95.

In `@src/admin/pages/Projects.tsx`:
- Around line 104-111: The budget validation in the handleCreate function only
checks if the parsed budget is a finite number using
Number.isFinite(parsedBudget), but does not reject negative values. While the
input field has a min="0" attribute for UI-level validation, the backend logic
must also validate that the budget is non-negative. Add an additional check to
ensure parsedBudget is greater than or equal to 0 in the conditional expression
that determines whether to include the budget field when adding the document to
projectsCollection. Apply this same validation fix to all locations where budget
is being persisted (including the second location mentioned at lines 391-396).

In `@src/admin/pages/Unauthorized.tsx`:
- Around line 12-15: The handleSignOut function lacks error handling around the
signOut(auth) call, which can throw an error and leave the UI in a broken state
without user feedback. Wrap the signOut(auth) call in a try-catch block, and in
the catch block, handle the error gracefully by logging it and providing user
feedback through a toast notification or error message before proceeding with
the navigation to the login page, ensuring the user is always aware of what
happened.

In `@src/admin/rbac/permissions.ts`:
- Around line 82-91: The SENSITIVE_PERMISSIONS array is missing settings:view,
which means settings access is not being gated behind PIN verification as
required by issue `#64`. Add settings:view to the SENSITIVE_PERMISSIONS array to
mark it as a PIN-sensitive capability, and update the settings route guard in
src/admin/AdminApp.tsx to verify an active PIN session in addition to checking
permissions, ensuring that accessing /admin/settings requires PIN verification
rather than just permission-based access.

---

Nitpick comments:
In `@src/admin/pages/Settings.tsx`:
- Around line 34-39: The validation checks in handleRoleChange for preventing
role changes to the last enabled super admin should be enforced in the actual
mutation handler that performs the backend update, not just in the UI event
handler. Move the constraint validation logic (checking if newRole equals
current role, if it's the last enabled super admin, and if the admin is locked)
from handleRoleChange into the underlying mutation function or API call to
ensure these safeguards remain enforced even if the UI implementation changes.
Apply the same refactoring pattern to all similar role and status change
handlers throughout the Settings component.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 15dd739d-913b-45c5-99ee-a340c02f4606

📥 Commits

Reviewing files that changed from the base of the PR and between 92f57e1 and 67edfb2.

📒 Files selected for processing (43)
  • .env.example
  • docs/ADMIN.md
  • firebase.json
  • firestore.indexes.json
  • firestore.rules
  • src/Firebase/firebase.ts
  • src/admin/AdminApp.tsx
  • src/admin/components/AdminLayout.tsx
  • src/admin/components/AdminLoading.tsx
  • src/admin/components/AdminSidebar.tsx
  • src/admin/components/EmptyState.tsx
  • src/admin/components/PageHeader.tsx
  • src/admin/components/PinDialog.tsx
  • src/admin/components/RoleBadge.tsx
  • src/admin/components/StatCard.tsx
  • src/admin/components/guards/ProtectedAdminRoute.tsx
  • src/admin/components/guards/RequirePermission.tsx
  • src/admin/context/AdminContext.tsx
  • src/admin/context/AdminContextObject.ts
  • src/admin/context/PinContextObject.ts
  • src/admin/context/PinProvider.tsx
  • src/admin/context/useAdmin.ts
  • src/admin/context/usePinGate.ts
  • src/admin/hooks/useAdminData.ts
  • src/admin/hooks/useSensitiveAction.ts
  • src/admin/lib/audit.ts
  • src/admin/lib/authError.ts
  • src/admin/lib/collections.ts
  • src/admin/lib/format.ts
  • src/admin/lib/pin.ts
  • src/admin/pages/AdminLogin.tsx
  • src/admin/pages/Audit.tsx
  • src/admin/pages/Clients.tsx
  • src/admin/pages/Dashboard.tsx
  • src/admin/pages/Messages.tsx
  • src/admin/pages/Projects.tsx
  • src/admin/pages/Settings.tsx
  • src/admin/pages/Unauthorized.tsx
  • src/admin/rbac/navigation.ts
  • src/admin/rbac/permissions.ts
  • src/admin/rbac/roles.ts
  • src/admin/types.ts
  • src/app/App.tsx

Comment thread docs/ADMIN.md
Comment thread docs/ADMIN.md
Comment on lines +220 to +226
| Field | Type | Value |
| --- | --- | --- |
| `email` | string | the admin's email |
| `displayName` | string | e.g. `Harsh Goswami` |
| `role` | string | `super_admin` |
| `disabled` | boolean | `false` |
| `createdAt` | timestamp | now |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add required blank lines around the bootstrap table.

The table beginning at Line 220 is not surrounded by blank lines (MD058).

Suggested patch
 2. **Firestore → Start collection** `admins` → **Document ID = that UID**, fields:
+
    | Field | Type | Value |
    | --- | --- | --- |
    | `email` | string | the admin's email |
    | `displayName` | string | e.g. `Harsh Goswami` |
    | `role` | string | `super_admin` |
    | `disabled` | boolean | `false` |
    | `createdAt` | timestamp | now |
+
 3. Visit `/admin/login`, sign in — you now have full access and can add the rest
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 220-220: Tables should be surrounded by blank lines

(MD058, blanks-around-tables)


[warning] 226-226: Tables should be surrounded by blank lines

(MD058, blanks-around-tables)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/ADMIN.md` around lines 220 - 226, Add required blank lines around the
admin configuration table in the ADMIN.md file. Insert a blank line before the
table that begins with the header row containing Field, Type, and Value columns,
and insert another blank line after the table ends at the `createdAt` timestamp
row. This addresses the markdown linting rule MD058 which requires proper
spacing around tables for readability.

Source: Linters/SAST tools

Comment thread docs/ADMIN.md
Comment thread firestore.rules
);

allow read: if can('messages:view');
allow update: if can('messages:reply');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Lock message updates to mutable fields only.

allow update: if can('messages:reply') currently permits full-document rewrites, so moderators can alter immutable inbound fields (name, email, body, createdAt) and overwrite original submissions.

Suggested rule tightening
 match /messages/{id} {
@@
-  allow update: if can('messages:reply');
+  allow update: if can('messages:reply')
+    && request.resource.data.diff(resource.data).affectedKeys().hasOnly([
+         'status'
+       ]);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
allow update: if can('messages:reply');
allow update: if can('messages:reply')
&& request.resource.data.diff(resource.data).affectedKeys().hasOnly([
'status'
]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@firestore.rules` at line 137, The current `allow update: if
can('messages:reply');` rule in the firestore.rules file permits full-document
rewrites, allowing moderators to modify immutable fields such as name, email,
body, and createdAt. To fix this, modify the update rule to include field-level
validation that ensures only mutable fields (such as reply status or moderator
notes) can be updated while protecting immutable fields like name, email, body,
and createdAt from being altered. Use Firestore security rule syntax to check
the difference between the original resource.data and the incoming
request.resource.data, allowing only changes to approved mutable fields.

Comment thread src/admin/components/AdminLayout.tsx
Comment thread src/admin/lib/collections.ts
Comment thread src/admin/pages/AdminLogin.tsx
Comment thread src/admin/pages/Projects.tsx
Comment thread src/admin/pages/Unauthorized.tsx
Comment thread src/admin/rbac/permissions.ts
@hrx01-dev hrx01-dev self-assigned this Jun 20, 2026
Allow viewing /admin without a Firebase backend during local development.

- VITE_ADMIN_DEV_MOCK (.env.local) gates a fake super_admin + demo data,
  hard-gated on import.meta.env.DEV so it can never activate in a production
  build. The mock args are gated too, so the demo data and fake credentials
  are tree-shaken out of the prod bundle (verified: clean dist).
- AdminContext injects the mock admin synchronously when enabled; behavior is
  unchanged when disabled. Data hooks serve demo collections in mock mode.
- Documented in .env.example and docs/ADMIN.md (5.5). Off by default — the
  toggle lives only in the git-ignored .env.local.
@hrx01-dev hrx01-dev merged commit ff69e01 into hrx01-dev:main Jun 20, 2026
2 of 3 checks passed
kumudranjan6127-debug added a commit to kumudranjan6127-debug/Servio that referenced this pull request Jun 20, 2026
…d @google/generative-ai

upstream/main added AI estimation (PR hrx01-dev#83) and admin RBAC (PR hrx01-dev#82) and
reverted the broken vite-plugin-ssg prerender step (PR hrx01-dev#87).

DashboardLayout conflict resolved: keep our Moon/Sun theme toggle imports
alongside upstream's Sparkles/Settings2 for the new nav items.

Install @google/generative-ai@^0.24.1 required by the new estimationService.

Closes hrx01-dev#71 — theme toggle is already implemented in both the sidebar and
mobile header of DashboardLayout; this merge makes it available to all
authenticated users on the latest codebase.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Build Admin Dashboard Foundation with Role-Based Access Control (RBAC) and Security PIN Support

2 participants