# Bigscreen API Client

A TypeScript API client for interacting with the Bigscreen API servers. This client handles authentication, token management, and provides methods for making HTTP requests to both the main API and Cloud API.

## Files Required

When copying this client to your project, you will need:

- `api.ts` - The main API client class
- `config.ts` - Configuration for server URLs and API key

## Dependencies

Install the required npm packages:

```bash
npm install lodash superagent
npm install --save-dev @types/lodash @types/superagent
```

## Configuration

The client requires the following configuration values:

```typescript
export type BigApiConfig = {
    apiServerUrl: string,      // Main API server URL (e.g., "https://api.bigscreenvr.com")
    cloudApiServerUrl: string, // Cloud API server URL
    apiKey: string,            // Your application API key
    rdcServerHost: string      // RDC server host
}
```

### Option 1: Environment Variables

Create a `config.ts` file that reads from environment variables:

```typescript
const CloudConfig = {
    apiServerUrl: process.env.API_SERVER_URL,
    cloudApiServerUrl: process.env.CLOUD_API_SERVER_URL,
    apiKey: process.env.API_KEY,
    rdcServerHost: process.env.RDC_SERVER_HOST
}

export default CloudConfig;
```

### Option 2: Override Config on Init

Pass configuration directly when initializing:

```typescript
import { BigApi } from "./api";

await BigApi.init({
    apiServerUrl: "https://api.bigscreenvr.com",
    cloudApiServerUrl: "https://cloud-api.bigscreenvr.com",
    apiKey: "your-api-key",
    rdcServerHost: "https://rdc.bigscreenvr.com"
});
```

## Usage

### Initialization

Always initialize the client before making API calls:

```typescript
import { BigApi } from "./api";

// Initialize with default config (reads from config.ts)
const accountProfile = await BigApi.init();

// Or initialize with custom config
const accountProfile = await BigApi.init({
    apiServerUrl: "https://api.bigscreenvr.com",
    cloudApiServerUrl: "https://cloud-api.bigscreenvr.com",
    apiKey: "your-api-key",
    rdcServerHost: "https://rdc.bigscreenvr.com"
});

// Returns the account profile if user is already logged in, null otherwise
```

### Authentication

#### Login

```typescript
import { BigApi } from "./api";

try {
    const accountProfile = await BigApi.login({
        email: "user@example.com",
        password: "password123"
    });
    console.log("Logged in as:", accountProfile);
} catch (error) {
    console.error("Login failed:", error.message);
}
```

#### Create Account

```typescript
import { BigApi } from "./api";

try {
    const accountProfile = await BigApi.createAccount({
        email: "newuser@example.com",
        username: "newuser",
        password: "password123"
    });
    console.log("Account created:", accountProfile);
} catch (error) {
    console.error("Account creation failed:", error.message);
}
```

#### Logout

```typescript
await BigApi.logout();
```

#### Check Authentication Status

```typescript
const isLoggedIn = await BigApi.isAuthenticated();
```

### Making API Requests

The client provides HTTP methods for both the main API and Cloud API:

#### Main API Requests

```typescript
import { BigApi, BigApiAuthenticationGuard } from "./api";

// GET request (requires authentication by default)
const response = await BigApi.get("/users/profile");
console.log(response.body);

// GET request without authentication
const publicData = await BigApi.get(
    "/public/data",
    BigApiAuthenticationGuard.SkipAuthentication
);

// POST request
const createResponse = await BigApi.post("/items", {
    name: "New Item",
    description: "Item description"
});

// PUT request
const updateResponse = await BigApi.put("/items/123", {
    name: "Updated Item"
});

// DELETE request
const deleteResponse = await BigApi.delete("/items/123");
```

#### Cloud API Requests

```typescript
// GET from Cloud API
const cloudData = await BigApi.cloudApiGet("/rooms/active");

// POST to Cloud API
const cloudResponse = await BigApi.cloudApiPost("/rooms", {
    name: "My Room"
});

// PUT to Cloud API
await BigApi.cloudApiPut("/rooms/123", { name: "Updated Room" });

// DELETE from Cloud API
await BigApi.cloudApiDelete("/rooms/123");
```

### Error Handling

The client throws `BigApiError` for failed requests:

```typescript
import { BigApi, BigApiError } from "./api";

try {
    const response = await BigApi.get("/some/endpoint");
} catch (error) {
    if (error instanceof BigApiError) {
        console.error("Status:", error.status);
        console.error("Message:", error.message);
        console.error("Error code:", error.code);
        console.error("Validation errors:", error.errors);
    }
}
```

### Authentication Guards

Control authentication behavior per request:

```typescript
import { BigApiAuthenticationGuard } from "./api";

// Skip authentication entirely (for public endpoints)
BigApiAuthenticationGuard.SkipAuthentication

// Authentication is required (default)
BigApiAuthenticationGuard.AuthenticationRequired
```

## Server-Side Usage Notes

The current implementation stores tokens using browser cookies (`document.cookie`) or `localStorage`. For server-side applications, you will need to modify the token storage methods:

1. Replace `setCookie`, `getCookie`, and `deleteCookie` with your preferred storage mechanism
2. Replace `localStorage` calls with server-appropriate storage (database, Redis, file system, etc.)
3. The `getSystemInfo()` method uses `window.navigator` which is not available in Node.js - provide alternative device identification

Example server-side token storage modification:

```typescript
// In-memory storage example (replace with database/Redis for production)
const tokenStorage = new Map<string, string>();

private static setRefreshToken(refreshToken: string) {
    tokenStorage.set(BigApi.REFRESH_TOKEN_KEY, refreshToken);
}

private static getRefreshToken() {
    return tokenStorage.get(BigApi.REFRESH_TOKEN_KEY) || null;
}

private static setAccessToken(accessToken: string) {
    tokenStorage.set(BigApi.ACCESS_TOKEN_KEY, accessToken);
}

private static getAccessToken() {
    return tokenStorage.get(BigApi.ACCESS_TOKEN_KEY) || null;
}

private static deleteTokens() {
    tokenStorage.delete(BigApi.REFRESH_TOKEN_KEY);
    tokenStorage.delete(BigApi.ACCESS_TOKEN_KEY);
}
```

## Authentication Error Codes

The API may return the following authentication error codes:

| Code | Name | Description |
|------|------|-------------|
| 0 | Unknown | Unknown error |
| 1 | BadAPIKey | Invalid API key |
| 2 | MissingAuthenticationHeaders | Required headers not provided |
| 3 | RefreshTokenNeedsRenewal | Refresh token expired |
| 4 | AccessTokenNeedsRenewal | Access token expired (auto-renewed) |
| 5 | RenewalNonceExpired | Token renewal nonce expired |
| 6 | TokenParserError | Failed to parse token |
| 7 | InvalidLoginCredentials | Wrong email/password |
| 8 | BannedAccount | Account is banned |
| 9 | BannedIP | IP address is banned |
| 10 | BannedDevice | Device is banned |
| 11 | RequiresVerifiedEmailAddress | Email verification required |

## Token Renewal

The client automatically handles access token renewal. When a request fails with `AccessTokenNeedsRenewal`, the client will:

1. Receive a nonce from the server
2. Send a renewal request with the refresh token
3. Store the new access token
4. The original request can then be retried

This happens transparently when using `checkAccessTokenStatus()` before requests.
