PermisosQR uses a two-role authorization model designed to cleanly separate administrative control from daily operational tasks. The Super Admin (Documentation Index
Fetch the complete documentation index at: https://mintlify.com/edgar2420/QrPermision/llms.txt
Use this file to discover all available pages before exploring further.
super_admin) has full system access — managing users, generating and deleting QR codes, viewing all permission history, and accessing reports. The Admin Operator (admin_operator) is scoped to day-to-day work: enabling and returning permissions against QR codes and viewing only their own permission history. This separation ensures that operators can perform their jobs without accessing sensitive configuration or other users’ activity data.
Role Comparison
The table below shows which actions each role can perform across every area of the system.| Action | super_admin | admin_operator |
|---|---|---|
| Generate QR codes | ✅ | ❌ |
| Delete QR codes | ✅ | ❌ |
| Disable / reactivate QR codes | ✅ | ❌ |
| Enable permissions (grant exit) | ✅ | ✅ |
| Return permissions (log re-entry) | ✅ | ✅ |
| View permission history — all records | ✅ | ❌ |
| View permission history — own records only | ✅ | ✅ |
| Create / update / delete users | ✅ | ❌ |
| View reports and dashboard | ✅ | ❌ |
| Delete permission records | ✅ | ❌ |
| Change own password | ✅ | ✅ |
Role Enforcement
Every API route in PermisosQR is protected by two Express middlewares defined inauth.middleware.js.
authenticate runs first on all protected routes. It extracts the Bearer token from the Authorization header, verifies it against JWT_SECRET, and attaches the decoded payload to req.user. If the token is missing, malformed, or expired the request is rejected with HTTP 401.
requireSuperAdmin is applied on top of authenticate for routes that only Super Admins may call. It checks req.user.role and rejects with HTTP 403 if the caller is not a super_admin:
GET /api/users/:id, PATCH /api/users/:id/password) apply only the authenticate middleware and omit requireSuperAdmin.
User Schema
Theusers table in PostgreSQL defines the following columns:
| Column | Type | Notes |
|---|---|---|
id | SERIAL | Primary key, auto-incremented |
name | VARCHAR(100) | Full display name, required |
email | VARCHAR(100) | Unique login email, required |
password_hash | VARCHAR(255) | bcrypt hash (cost factor 10), never returned by the API |
role | VARCHAR(20) | 'super_admin' or 'admin_operator', defaults to 'admin_operator' |
is_active | BOOLEAN | Defaults to true; inactive users cannot log in |
created_at | TIMESTAMPTZ | Set automatically on insert |
updated_at | TIMESTAMPTZ | Updated automatically via trigger on every UPDATE |
role is enforced at the database level:
Initial Seeded Users
Theseed.sql file inserts two ready-to-use accounts when the database is first initialized. Both share the same bcrypt-hashed default password.
| Name | Role | |
|---|---|---|
| Edgar Rojas Apaza | admin@permisosqr.com | super_admin |
| Operador Principal | operador@permisosqr.com | admin_operator |
ON CONFLICT (email) DO NOTHING, so re-running the seed is safe.
Operator-scoped history is enforced server-side, not just in the UI. When a request comes from an
admin_operator, the permissions query automatically appends a WHERE enabled_by = <req.user.id> filter so operators can never retrieve another user’s permission records — even with a direct API call.