Add paginated users/roles, user status, frontend permissions, profile pictures, identifier file storage

- Users page: paginated, searchable, sortable with inline roles (no N+1)
- Roles page: paginated, searchable, sortable + /roles/all for dropdowns
- User is_active field with migration, PATCH toggle, auth check (disabled=401)
- Frontend permission checks: auth store loads permissions, sidebar/buttons conditional
- Profile pictures via file storage for users and members, avatar component
- Identifier images use file storage API instead of base64
- Fix TypeScript errors across admin UI
- 64 API tests passing (10 new)
This commit is contained in:
Ryan Moon
2026-03-29 08:16:34 -05:00
parent 92371ff228
commit b9f78639e2
48 changed files with 1689 additions and 643 deletions

View File

@@ -1,5 +1,7 @@
import fp from 'fastify-plugin'
import fjwt from '@fastify/jwt'
import { eq } from 'drizzle-orm'
import { users } from '../db/schema/users.js'
import { RbacService } from '../services/rbac.service.js'
declare module 'fastify' {
@@ -72,6 +74,18 @@ export const authPlugin = fp(async (app) => {
await request.jwtVerify()
request.companyId = request.user.companyId
// Check if user account is active
const [dbUser] = await app.db
.select({ isActive: users.isActive })
.from(users)
.where(eq(users.id, request.user.id))
.limit(1)
if (!dbUser || !dbUser.isActive) {
reply.status(401).send({ error: { message: 'Account disabled', statusCode: 401 } })
return
}
// Load permissions from DB and expand with inheritance
const permSlugs = await RbacService.getUserPermissions(app.db, request.user.id)
request.permissions = expandPermissions(permSlugs)