# @hiyve/admin

Server-only admin client for the Hiyve Cloud API. Manages user profiles, organizations, and API keys.

> **Warning:** This package is for **server-side (Node.js) use only**. Secret keys must never be exposed to the browser. Three layers of enforcement prevent accidental client-side usage.

## Installation

```bash
npm install @hiyve/admin
```

## Quick Start

```typescript
import { AdminClient } from '@hiyve/admin';

const admin = new AdminClient({
  secretKey: process.env.HIYVE_SECRET_KEY,
  environment: 'production',
});

// List profiles
const result = await admin.listProfiles('org_abc123', { limit: 20 });

// Get a single profile
const profile = await admin.getProfile('profile_123', 'org_abc123');

// Update a profile
const updated = await admin.updateProfile('profile_123', 'org_abc123', {
  name: 'John Smith',
  organization: 'Acme Corp',
});

// Deactivate a profile
await admin.deactivateProfile('profile_123', 'org_abc123');
```

## Constructor

```typescript
new AdminClient(config: AdminConfig)
```

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `secretKey` | `string` | — | Secret API key (from your Hiyve dashboard) — **required** |
| `environment` | `'production' \| 'development'` | `'production'` | Target environment |
| `baseUrl` | `string` | — | Custom API URL (overrides environment) |
| `timeout` | `number` | `30000` | Request timeout in milliseconds |

## API Reference

### `listProfiles(brandOrgId?, options?)`

List user profiles for an organization. Returns a paginated response.

```typescript
const result = await admin.listProfiles('org_abc123', {
  page: 1,
  limit: 20,
  search: 'john',
  metadata: { role: 'admin' },
});
console.log(result.data);   // UserProfile[]
console.log(result.total);  // total matching profiles
console.log(result.pages);  // total pages
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `brandOrgId` | `string?` | Organization ID. If omitted, defaults to the org associated with the API key |
| `options.page` | `number` | Page number (default: 1) |
| `options.limit` | `number` | Items per page (default: 20, max: 100) |
| `options.search` | `string` | Search by name or email |
| `options.metadata` | `Record<string, unknown>` | Filter by metadata fields (e.g. `{ role: 'admin' }`) |

**Returns:** `Promise<PaginatedResponse<UserProfile>>`

### `getProfile(profileId, brandOrgId)`

Get a single user profile by ID.

```typescript
const profile = await admin.getProfile('profile_123', 'org_abc123');
console.log(profile.name, profile.email);
```

**Returns:** `Promise<UserProfile>`

### `updateProfile(profileId, brandOrgId, updates)`

Update a user profile's fields.

```typescript
const updated = await admin.updateProfile('profile_123', 'org_abc123', {
  name: 'John Smith',
  organization: 'Acme Corp',
  metadata: { department: 'Engineering' },
});
```

| Field | Type | Description |
|-------|------|-------------|
| `name` | `string` | Display name |
| `picture` | `string` | Profile picture URL |
| `phone` | `string` | Phone number |
| `organization` | `string` | Organization name |
| `metadata` | `Record<string, unknown>` | Custom key-value data |
| `roles` | `string[]` | Assigned roles |

**Returns:** `Promise<UserProfile>`

### `deactivateProfile(profileId, brandOrgId)`

Deactivate (soft-delete) a user profile. The profile is not permanently deleted.

```typescript
const result = await admin.deactivateProfile('profile_123', 'org_abc123');
console.log(result.message); // 'Profile deactivated'
```

**Returns:** `Promise<{ message: string; profile: UserProfile }>`

### `listAllowedOrigins(orgId)`

List the CORS allowed origins for an organization.

```typescript
const result = await admin.listAllowedOrigins('org_abc123');
console.log(result.origins); // ['https://myapp.com', 'https://staging.myapp.com']
```

**Returns:** `Promise<AllowedOriginsResponse>`

### `setAllowedOrigins(orgId, origins)`

Replace all allowed origins for an organization.

```typescript
await admin.setAllowedOrigins('org_abc123', [
  'https://myapp.com',
  'https://staging.myapp.com',
]);
```

**Returns:** `Promise<AllowedOriginsResponse>`

### `addAllowedOrigins(orgId, origins)`

Add origin(s) to the allowed list without removing existing ones.

```typescript
await admin.addAllowedOrigins('org_abc123', ['https://new-app.com']);
```

**Returns:** `Promise<AllowedOriginsResponse>`

### `removeAllowedOrigins(orgId, origins)`

Remove specific origin(s) from the allowed list.

```typescript
await admin.removeAllowedOrigins('org_abc123', ['https://old-app.com']);
```

**Returns:** `Promise<AllowedOriginsResponse>`

## Types

### `UserProfile`

| Field | Type | Description |
|-------|------|-------------|
| `id` | `string` | Profile ID |
| `name` | `string` | Display name |
| `email` | `string` | Email address |
| `picture` | `string?` | Profile picture URL |
| `phone` | `string?` | Phone number |
| `organization` | `string?` | Organization name |
| `metadata` | `Record<string, unknown>?` | Custom key-value data |
| `roles` | `string[]?` | Assigned roles |
| `active` | `boolean` | Whether the profile is active |
| `brandOrgId` | `string` | Organization ID |
| `onboardingCompleted` | `boolean?` | Onboarding status |
| `created` | `string?` | Creation timestamp (legacy) |
| `createdAt` | `string?` | Creation timestamp |
| `updatedAt` | `string?` | Last update timestamp |

### `AllowedOriginsResponse`

| Field | Type | Description |
|-------|------|-------------|
| `origins` | `string[]` | Allowed origins for the organization |

### `HiyveAuthUser`

Authenticated user attached to `req.hiyveUser` by `createAuthMiddleware`.

| Field | Type | Description |
|-------|------|-------------|
| `id` | `string` | User ID |
| `email` | `string` | Email address |
| `name` | `string?` | Display name |
| `emailVerified` | `boolean?` | Whether the email is verified |
| `metadata` | `Record<string, unknown>?` | Custom metadata |
| `createdAt` | `string?` | Account creation timestamp |

### Other Exported Types

| Type | Description |
|------|-------------|
| `PaginatedResponse<T>` | Paginated list response with `data`, `total`, `page`, `limit`, `pages` |
| `ListProfilesOptions` | Options for `listProfiles()` — `page`, `limit`, `search`, `metadata` |
| `UpdateProfileData` | Fields for `updateProfile()` — `name`, `picture`, `phone`, `organization`, `metadata`, `roles` |
| `AdminConfig` | Constructor config — `secretKey`, `environment`, `baseUrl`, `timeout` |
| `CloudEnvironment` | `'production' \| 'development'` |
| `HiyveServerConfig` | Server middleware config |
| `HiyveRequestHandler` | Express-compatible request handler type |
| `HiyveHandlers` | Object containing all route handlers |
| `HiyveAuthMiddlewareOptions` | Options for `createAuthMiddleware` — `optional`, `cacheTtlMs`, `cacheMaxSize` |

### Exported Constants

| Constant | Value | Description |
|----------|-------|-------------|
| `DEFAULT_BASE_URL` | `'https://api.hiyve.dev'` | Production API URL |
| `DEFAULT_TIMEOUT` | `30000` | Default request timeout (ms) |
| `ENVIRONMENT_URLS` | `{ production, development }` | URL map for each environment |

## Server Middleware

Express-compatible route handlers for room tokens, cloud tokens, join tokens, AI note generation, health checks, and identity proxy. Mount all routes with a single call or use individual handlers.

### Quick Start

```typescript
import express from 'express';
import { mountHiyveRoutes, loadHiyveConfig } from '@hiyve/admin';

const app = express();
app.use(express.json());

const apiRouter = express.Router();
mountHiyveRoutes(apiRouter, loadHiyveConfig());
app.use('/api', apiRouter);

app.listen(3000);
```

This registers the following routes on the router:
- `POST /generate-room-token` — Generate a room token for WebRTC
- `POST /generate-cloud-token` — Generate a cloud token for AI services
- `POST /generate-note` — Generate AI-powered meeting notes
- `POST /create-join-token` — Generate a server-side join token
- `GET /health` — Health check
- `ALL /hiyve/identity/*` — Proxy to Hiyve Cloud identity endpoints

### `loadHiyveConfig(env?): HiyveServerConfig`

Load configuration from environment variables. Reads `APIKEY`, `CLIENT_SECRET`, `SERVER_REGION`, `SERVER_REGION_URL`, and `ENVIRONMENT`.

```typescript
const config = loadHiyveConfig(); // reads from process.env
const config = loadHiyveConfig(customEnv); // or pass a custom env object
```

### `mountHiyveRoutes(router, config): router`

Mount all Hiyve routes on an Express-compatible router. Returns the router for chaining.

### `createHiyveHandlers(config): HiyveHandlers`

Create individual handlers without mounting them. Use this when you need custom routing.

```typescript
const handlers = createHiyveHandlers(loadHiyveConfig());
app.post('/api/room-token', handlers.roomToken);
app.post('/api/cloud-token', handlers.cloudToken);
app.post('/api/note', handlers.generateNote);
app.post('/api/join-token', handlers.joinToken);
app.get('/api/health', handlers.health);
app.all('/api/hiyve/identity/*', handlers.identityProxy);
```

### Individual Handler Factories

| Factory | Description |
|---------|-------------|
| `createRoomTokenHandler(config)` | Returns handler for room token generation |
| `createCloudTokenHandler(config)` | Returns handler for cloud token generation |
| `createNoteHandler(config)` | Returns handler for AI note generation |
| `createHealthHandler(config)` | Returns handler for health checks |

> **Note:** `joinToken` and `identityProxy` handlers are available via `createHiyveHandlers()` or `mountHiyveRoutes()` but are not exported as individual factory functions.

### `HiyveServerConfig`

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `apiKey` | `string` | — | API key for signaling server and cloud API |
| `clientSecret` | `string` | — | Client secret for signaling server |
| `region` | `string` | `'us-west-2'` | Server region |
| `regionUrl` | `string` | Resolved automatically | Region URL suffix |
| `environment` | `string` | `'development'` | `'production'` or `'development'` |
| `cloudApiUrl` | `string` | — | Override cloud API URL (auto-derived from environment) |

### `HiyveHandlers`

| Property | Route | Description |
|----------|-------|-------------|
| `roomToken` | `POST` | Room token generation |
| `cloudToken` | `POST` | Cloud token generation |
| `generateNote` | `POST` | AI note generation |
| `joinToken` | `POST` | Server-side join token creation |
| `health` | `GET` | Health check |
| `identityProxy` | `ALL` | Identity proxy to Hiyve Cloud |

## Authentication Middleware

Verify end-user access tokens against Hiyve Cloud. Attach this middleware to routes that require a logged-in user.

```typescript
import express from 'express';
import { createAuthMiddleware, loadHiyveConfig } from '@hiyve/admin';

const app = express();
const config = loadHiyveConfig();

// Protect routes — verified user available as req.hiyveUser
app.get('/api/me', createAuthMiddleware(config), (req, res) => {
  res.json({ user: req.hiyveUser });
});

// Optional auth — req.hiyveUser is null if no token provided
app.get('/api/public', createAuthMiddleware(config, { optional: true }), (req, res) => {
  res.json({ user: req.hiyveUser });
});
```

### `createAuthMiddleware(config, options?)`

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `optional` | `boolean` | `false` | Allow requests without a token (`req.hiyveUser` is `null`) |
| `cacheTtlMs` | `number` | `300000` | Token verification cache TTL (5 minutes) |
| `cacheMaxSize` | `number` | `1000` | Maximum number of cached tokens |

The middleware extracts the `Bearer` token from the `Authorization` header, verifies it against Hiyve Cloud, and attaches the authenticated user to `req.hiyveUser` (see `HiyveAuthUser` type above).

## Server-Only Enforcement

This package is restricted to server-side (Node.js) environments. If used in a browser context, it will throw an error at both build time and runtime.

## Migration from @hiyve/cloud

If you were previously importing `AdminClient` from `@hiyve/cloud`:

```diff
- import { AdminClient } from '@hiyve/cloud';
+ import { AdminClient } from '@hiyve/admin';
```

All types (`UserProfile`, `AdminConfig`, etc.) are also exported from `@hiyve/admin`.

## License

Proprietary - Hiyve SDK
