npm package: @ortha/server-users
Description: Users backend for the Ortha server — query, mutation, filtering, and pagination for user management. Supports cursor-based pagination, relation-based filtering (roles, projects, invite status), and bulk project assignment operations.
Latest version: 0.0.4

# @ortha/server-users

Users backend for the Ortha server — query, mutation, filtering, and pagination for user management. Supports cursor-based pagination, relation-based filtering (roles, projects, invite status), and bulk project assignment operations.

## Installation

Internal monorepo dependency — import directly:

```ts
import { usersServerPlugin } from '@ortha/server-users';
import type { UserResponse, PaginatedResponse } from '@ortha/server-users';
```

## Usage

### Registering the plugin

```ts
import { bootstrap } from '@ortha/server-platform-bootstrap';
import { databasePlugin } from '@ortha/server-platform-database';
import { identitySchema, identityServerPlugin } from '@ortha/server-identity';
import { usersServerPlugin } from '@ortha/server-users';

await bootstrap({
    config: bootstrapConfig,
    plugins: [
        databasePlugin({ ...dbConfig, schemas: [identitySchema] }),
        identityServerPlugin(jwtConfig),
        usersServerPlugin()
    ]
});
```

## Routes

| Method | Path                                 | Auth | Permissions         | Description                   |
| ------ | ------------------------------------ | ---- | ------------------- | ----------------------------- |
| GET    | `/api/users/stats`                   | Yes  | —                   | Aggregated user statistics    |
| GET    | `/api/users`                         | Yes  | —                   | List users (paginated)        |
| GET    | `/api/users/:id`                     | Yes  | `read:admin:user`   | Get user by ID                |
| PATCH  | `/api/users/:id`                     | Yes  | `update:admin:user` | Update user info              |
| PATCH  | `/api/users/:id/status`              | Yes  | `update:admin:user` | Toggle user active status     |
| PATCH  | `/api/users/:id/password`            | Yes  | `update:admin:user` | Reset user password           |
| PATCH  | `/api/users/:id/projects`            | Yes  | `update:admin:user` | Bulk assign/unassign projects |
| POST   | `/api/users/:id/projects`            | Yes  | `update:admin:user` | Assign single project         |
| DELETE | `/api/users/:id/projects/:projectId` | Yes  | `delete:admin:user` | Unassign project              |

## API Reference

### Plugin

| Export                | Kind    | Description                            |
| --------------------- | ------- | -------------------------------------- |
| `usersServerPlugin()` | factory | Fastify plugin — registers user routes |

### Types

| Export                  | Kind | Description                         |
| ----------------------- | ---- | ----------------------------------- |
| `UserResponse`          | type | Serialized user response            |
| `PaginatedResponse`     | type | Cursor-paginated response wrapper   |
| `ListUsersQuery`        | type | Query parameters for user listing   |
| `UpdateUserBody`        | type | Update user request body            |
| `UpdateUserStatusBody`  | type | Update user status request body     |
| `ResetUserPasswordBody` | type | Reset password request body         |
| `RawUser`               | type | Raw Sequelize user model type       |
| `RoleResponse`          | type | Serialized role in user response    |
| `ProjectResponse`       | type | Serialized project in user response |
| `UsersStatsResponse`    | type | Aggregated user statistics          |

### Re-exported from @ortha/server-utils

| Export                | Kind | Description                             |
| --------------------- | ---- | --------------------------------------- |
| `FilterService`       | type | Service interface for filter parsing    |
| `ParsedFilter`        | type | Parsed filter result (where + includes) |
| `ParsedInclude`       | type | Single relation-based filter entry      |
| `IncludeFilterConfig` | type | Config for allowed relation filters     |
| `AllowedIncludes`     | type | Map of relation names to filter configs |

## Filtering

The users endpoint supports advanced server-side filtering via `filterService` from `@ortha/server-utils`:

- **Allowed fields**: standard user fields (email, name, etc.)
- **Status filtering**: `$exists` on `InviteCode` relation (for pending status)
- **Project filtering**: `$exists` on `UserProject` relation with `projectId` conditions
- User responses eager-load role, projects, and invite code status via `userIncludes`

## Internal Structure

```
src/lib/
├── types/index.ts                          # Request/response types
├── usersServerPlugin/index.ts              # Fastify plugin factory
├── controllers/                            # Route handlers (one per folder)
└── services/
    ├── index.ts                            # Barrel
    ├── utils/
    │   ├── userIncludes/index.ts           # Sequelize include config for queries
    │   ├── userAttributes/index.ts         # Shared USER_ATTRIBUTES constant
    │   └── refetchAndSerialize/index.ts    # Re-fetch + serialize after mutation
    ├── listUsers/index.ts                  # Paginated user listing with filters
    ├── getUserById/index.ts                # Single user by ID
    ├── getUserStats/index.ts               # Aggregated statistics
    ├── updateUser/index.ts                 # Update user info
    ├── updateUserStatus/index.ts           # Toggle active status
    ├── assignProject/index.ts              # Assign user to project
    ├── unassignProject/index.ts            # Unassign user from project
    ├── bulkAssignProjects/index.ts         # Bulk assign projects
    ├── bulkUnassignProjects/index.ts       # Bulk unassign projects
    ├── resetUserPassword/index.ts          # Admin password reset
    ├── serializeUser/index.ts              # Serialize single user
    └── serializeUsers/index.ts             # Serialize user array
```

## Key Patterns

- Each service is a plain async function: `serviceName(deps)(args)`.
- Filter service from `@ortha/server-utils` with user-specific `allowedFields`.
- `opToSql` from `@ortha/server-utils` for building EXISTS subqueries.
- Routes protected with `authHook` and `permissionHook` from `@ortha/server-identity`.
- User responses include eager-loaded role, projects, and invite code status.
- Cursor-based pagination via helpers from `@ortha/server-utils`.

## Dependencies

- `@ortha/server-platform-database` — model registry
- `@ortha/server-identity` — `authHook`, `permissionHook`, `hashPassword`
- `@ortha/server-utils` — error builders, pagination, filter service, SQL helpers

## Building

```bash
nx build server-users
```