Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Renzo717/Aula-Virtual-Universidad-Radiolocucion/llms.txt

Use this file to discover all available pages before exploring further.

NuestraVoz — Campus Virtual enforces a role-based access control (RBAC) model across every API route and dashboard view. When a user registers or is created by an administrator, a UserRole is stored in the profiles table. Every subsequent request carries a signed JWT cookie that the server verifies before checking whether the caller’s role is authorised to proceed. Understanding what each role can and cannot do is essential for both platform operators and integrators building on top of the API.

Roles at a Glance

There are four roles in the system, each matching a value of the shared UserRole type:
// packages/shared/src/types.ts
export type UserRole = 'admin' | 'docente' | 'estudiante' | 'preceptor';

Admin

Full platform management: users, careers, subjects, enrolments, grade book, and audit logs. The only role that can delete records or approve pending docente accounts.

Docente (Teacher)

Manages their own assigned subjects only. Creates activities, grades submissions, posts forum threads, and enters final grades. Account requires admin approval before first login.

Estudiante (Student)

Accesses enrolled subjects, submits assignments, participates in forums, and views their grade history and bulletin PDF. Account is immediately active after registration.

Preceptor

Administrative role focused on enrolment management and grade-book operations. Can assign students to careers or subjects, update subject metadata, and enter final grades for any subject.

Account Status

Every profile also carries a UserStatus that gates platform access independently from role:
// packages/shared/src/types.ts
export type UserStatus = 'activo' | 'pendiente' | 'inactivo';
StatusMeaning
activoAccount is fully operational. User can log in normally.
pendienteAccount exists but is awaiting approval. Login is blocked with a 403 response. Automatically assigned to new docente self-registrations.
inactivoAccount has been deactivated by an admin. requireAuth rejects every request with 403.
Only estudiante accounts self-register as activo. All docente self-registrations start as pendiente and must be explicitly approved by an admin via PATCH /api/users/:id with { "status": "activo" }. The preceptor and admin roles cannot be self-registered — they must be created directly by an admin.

How the requireRole Middleware Works

Authentication and authorisation are split into two composable Express middleware functions defined in apps/api/src/middleware/auth.ts.

Step 1 — requireAuth

// apps/api/src/middleware/auth.ts
export async function requireAuth(req: AuthRequest, res: Response, next: NextFunction) {
  const token = req.cookies?.[config.sessionCookieName];
  if (!token) {
    return res.status(401).json({ error: 'No autenticado' });
  }

  const { data: { user }, error } = await supabaseAdmin.auth.getUser(token);
  if (error || !user) {
    return res.status(401).json({ error: 'Sesión inválida' });
  }

  const { data: profile } = await supabaseAdmin
    .from('profiles')
    .select('rol, status')
    .eq('id', user.id)
    .single();

  if (!profile || profile.status === 'inactivo') {
    return res.status(403).json({ error: 'Cuenta inactiva' });
  }

  req.user = {
    id: user.id,
    email: user.email!,
    rol: profile.rol,
    status: profile.status,
  };

  next();
}
requireAuth reads the session token from an httpOnly cookie, verifies it with Supabase Auth, fetches the matching profiles row, and rejects inactive accounts. On success it attaches req.user — containing id, email, rol, and status — so downstream middleware can use it without a second database round-trip.

Step 2 — requireRole

// apps/api/src/middleware/auth.ts
export function requireRole(...roles: UserRole[]) {
  return (req: AuthRequest, res: Response, next: NextFunction) => {
    if (!req.user) return res.status(401).json({ error: 'No autenticado' });
    if (!roles.includes(req.user.rol)) {
      return res.status(403).json({ error: 'Sin permisos' });
    }
    next();
  };
}
requireRole is a factory that accepts one or more UserRole values and returns a middleware function. It reads req.user.rol (set by requireAuth) and returns 403 Sin permisos if the caller’s role is not in the allowed list. Routes combine the two:
// Example: only admin can create a career
router.post('/carreras', requireRole('admin'), handler);

// Example: admin and preceptor can update a subject
router.patch('/carreras/materias/:id', requireRole('admin', 'preceptor'), handler);

// Example: admin, docente, and preceptor can access finals
router.use(requireAuth, requireRole('admin', 'docente', 'preceptor'));
Always apply requireAuth before requireRole. Calling requireRole on a route where requireAuth has not run means req.user is undefined and the middleware will respond with 401 regardless of the token.

Role Capability Comparison

CapabilityAdminDocenteEstudiantePreceptor
Create / edit / delete users
Approve pending docente accounts
Create / delete careers
Create subjects
Edit subject metadata (name, teacher, schedule)
Manage enrolments (inscripciones)
Access assigned subjects only
Access enrolled subjects
Create activities & grade submissions
Enter final grades✅ (own subjects)✅ (any subject)
Download grade bulletins✅ (own)
View audit log / dashboard stats
Forum participation
Update own profile & password

Admin Role

Full platform management, dashboard stats, audit log, and user approval workflow.

Teacher Role

Pending approval flow, subject management, activity grading, and final grade entry.

Student Role

Enrolment model, activity submissions, forum participation, and bulletin download.

Preceptor Role

Enrolment administration, subject editing, and grade-book management.

Build docs developers (and LLMs) love