# API

This page documents the currently implemented DLC Manager API.

The API is available on both:
- client
- dedicated server

There are now two access modes:
- Java API: in-process calls through `DlcApiService`
- HTTP API: external calls through `/api/...`

## Enable API

Configure the `[api]` section in `config.dc` or `config-server.dc`:

```dc
[api]
apiEnabled = true
apiPort = 10086
apiToken = "your_token_here"
```

Fields:
- `apiEnabled`: total API switch. When `false`, both Java API and HTTP API are disabled
- `apiPort`: HTTP API port. Default `10086`. Set to `0` to disable only the HTTP API while keeping the Java API enabled
- `apiToken`: bearer token used for HTTP authentication

Effective behavior:
- `apiEnabled = false`: Java API disabled, HTTP API disabled
- `apiEnabled = true` and `apiPort = 0`: Java API enabled, HTTP API disabled
- `apiEnabled = true` and `apiPort > 0`: Java API enabled, HTTP API enabled

## Authentication

All HTTP routes require:

```http
Authorization: Bearer <apiToken>
```

If `apiToken` is empty, all HTTP requests are rejected.

The Java API does not use HTTP bearer authentication.

## Java API

The in-process API entry is:

- `com.cozooo.dlc_manager.server.DlcApiService`

Current direct Java methods include:
- `ping()`
- `version()`
- `state()`
- `listDlc()`
- `listPendingDlc()`
- `setDlcEnabled(JsonObject request, boolean enabled)`
- `applyDlc(JsonObject request)`

Each Java method returns:

```java
DlcApiService.ApiResult
```

The result structure matches the HTTP response model:
- `status`
- `body`
- `ok()`
- `data()`
- `error()`

## HTTP API Rules

- Base path: `/api`
- Response format:

Success:

```json
{
  "ok": true,
  "data": {}
}
```

Failure:

```json
{
  "ok": false,
  "error": {
    "code": "not_found",
    "message": "Unknown API route."
  }
}
```

- `OPTIONS` is supported for CORS preflight
- CORS is enabled with `Access-Control-Allow-Origin: *`

## Basic Endpoints

### `GET /api/ping`

Health check.

Response fields:
- `service`
- `running`
- `httpRunning`
- `mode`

### `GET /api/version`

Returns current version data from `DLC/info/version.dc`.

Response fields:
- `base`
- `dlc`
- `rollback_from`
- `changelog_shown`
- `label`

### `GET /api/state`

Returns API/runtime summary.

Response fields:
- `serverRunning`
- `dedicatedServer`
- `totalItems`
- `enabledItems`
- `counts`

### `GET /api/dlc/list`

Returns grouped DLC items.

Each item may include:
- `id`
- `identifier`
- `name`
- `type`
- `enabled`
- `baselineEnabled`
- `pending`
- `hidden`
- `configType`
- `alarm`
- `allowEnableByAll`
- `appliedTarget`
- `sourcePath`
- `sourcePaths`
- `displayNames`
- `tips`
- `dependencies`
- `conflicts`

### `GET /api/dlc/pending`

Returns pending DLC changes.

Response fields:
- `count`
- `items`
  - `id`
  - `identifier`
  - `name`
  - `type`
  - `enabled`
  - `baselineEnabled`
  - `pending`

## DLC Root / Config File Endpoints

### `GET /api/dlc/root/list`

Lists editable `.dc` files under DLC roots.

Returned files are limited to:
- `<DLC>/info/**/*.dc`
- `<DLC>/remote/**/*.dc`
- `<DLC>/required/**/*.dc`
- `<DLC>/preset/**/*.dc`
- `<DLC>/update/**/*.dc`
- `<DLC>/notice.dc`

Response fields:
- `rootCount`
- `roots`
  - `rootIndex`
  - `rootPath`
  - `files`
    - `name`
    - `path`
    - `type`
    - `supported`

### `POST /api/dc/detect-type`

Detect config type from path/name/content.

Request body:

```json
{
  "name": "example.dc",
  "path": "remote/example.dc",
  "content": "..."
}
```

Response fields:
- `type`
- `supported`

### `POST /api/dc/validate`

Validate a dc document.

Request body:

```json
{
  "type": "remote",
  "content": "..."
}
```

Response fields:
- `type`
- `supported`
- `valid`
- `issues`

Issue fields:
- `severity`
- `source`
- `field`
- `message`

### `POST /api/dc/read`

Read a dc file from the DLC workspace.

Request body:

```json
{
  "path": "remote/example.dc",
  "rootIndex": 0
}
```

Response fields:
- `name`
- `path`
- `rootIndex`
- `type`
- `supported`
- `content`

### `POST /api/dc/write`

Write a dc file into the DLC workspace.

Request body:

```json
{
  "path": "remote/example.dc",
  "rootIndex": 0,
  "type": "remote",
  "content": "..."
}
```

Notes:
- For supported config types, the server parses content before writing.
- Paths outside the DLC workspace are rejected.

Response fields:
- `name`
- `path`
- `rootIndex`
- `type`
- `supported`
- `written`

### `POST /api/notice/content`

Request body:

```json
{
  "url": "https://example.com/notice.md"
}
```

Response fields:
- `url`
- `content`

## File Endpoints

### `POST /api/file/add`

Request body:

```json
{
  "path": "mods/example.jar",
  "content": "..."
}
```

Or:

```json
{
  "path": "config/example.txt",
  "contentBase64": "SGVsbG8="
}
```

Or:

```json
{
  "path": "mods/example.jar",
  "source": "downloads/example.jar"
}
```

Or create a directory:

```json
{
  "path": "tmp/example",
  "directory": true
}
```

Response fields:
- `path`
- `created`
- `directory`

### `POST /api/file/del`

Request body:

```json
{
  "path": "mods/example.jar"
}
```

Response fields:
- `path`
- `deleted`

### `POST /api/file/mv`

Request body:

```json
{
  "from": "mods/example.jar",
  "to": "mods/example-disabled.jar"
}
```

Response fields:
- `from`
- `to`
- `moved`

### `POST /api/file/rename`

Request body:

```json
{
  "path": "mods/example.jar",
  "name": "example-disabled.jar"
}
```

Response fields:
- `from`
- `to`
- `renamed`

### `POST /api/file/upload`

Upload raw binary content to a file path inside the game workspace.

Parameters:
- `path`: target save path inside the game directory, for example `mods/example.jar` or `config/example.txt`

Request body:
- raw binary body

Supported target path sources:
- query param `path`
- request header `X-Dlc-Path`

Response fields:
- `path`
- `uploaded`
- `size`

## DLC Apply Endpoints

### `POST /api/dlc/enable`

Request body:

```json
{
  "id": "mod:...|a.jar"
}
```

Or:

```json
{
  "ids": ["mod:...|a.jar", "unknown:...|b.zip"]
}
```

Or use identifiers:

```json
{
  "identifier": "example_mod"
}
```

```json
{
  "identifiers": ["example_mod", "example_pack"]
}
```

Response fields:
- `enabled`
- `changes`
- `linkedChanges`
- `unknownIds`
- `unknownIdentifiers`

### `POST /api/dlc/disable`

Request body:

```json
{
  "id": "mod:...|a.jar"
}
```

Or:

```json
{
  "ids": ["mod:...|a.jar", "unknown:...|b.zip"]
}
```

Or use identifiers:

```json
{
  "identifier": "example_mod"
}
```

```json
{
  "identifiers": ["example_mod", "example_pack"]
}
```

Response fields:
- `enabled`
- `changes`
- `linkedChanges`
- `unknownIds`
- `unknownIdentifiers`

### `POST /api/dlc/apply`

Apply current pending DLC changes only.

Request body:

```json
{
  "mode": "restart"
}
```

Supported `mode`:
- `restart`
- `reload`

Response fields:
- `mode`
- `changedCount`
- `requiresRestart`
- `hotReloaded`
- `appliedChanges`
- `messages`

## Remote Endpoints

### `GET /api/remote/list`

Returns missing remote items.

Optional header:

```http
X-Dlc-Lang: zh_cn
```

### `POST /api/remote/download`

Downloads all currently missing remote items immediately.

Response fields:
- `success`
- `downloaded`
- `error`

### `POST /api/remote/download/start`

Starts remote download task for selected keys.

Request body:

```json
{
  "keys": ["remote:key1", "remote:key2"],
  "lang": "zh_cn"
}
```

Response fields:
- `status`

### `GET /api/remote/download/status`

Returns remote download task status.

Optional header:

```http
X-Dlc-Lang: zh_cn
```

## Required Endpoints

### `GET /api/required/list`

Returns required resource items.

Optional header:

```http
X-Dlc-Lang: zh_cn
```

### `POST /api/required/download/start`

Starts required download task for selected ids.

Request body:

```json
{
  "ids": ["id_a", "id_b"],
  "lang": "zh_cn"
}
```

Response fields:
- `status`

### `GET /api/required/download/status`

Returns required download task status.

Optional header:

```http
X-Dlc-Lang: zh_cn
```

### `POST /api/required/download/cancel`

Cancels current required download task.

Response fields:
- `canceled`

### `POST /api/required/uninstall`

Uninstalls selected required resources.

Request body:

```json
{
  "ids": ["id_a", "id_b"]
}
```

Response fields:
- `removed`

### `POST /api/required/apply`

Applies selected required resources by id.

Request body:

```json
{
  "ids": ["id_a", "id_b"]
}
```

Response fields:
- `applied`

## Update Endpoints

### `GET /api/update/state`

Returns current update state and rollback history.

Response fields:
- `rollbackFrom`
- `rolledBack`
- `history`

### `GET /api/update/list`

Returns current applicable update packages.

Response fields:
- `hasUpdates`
- `updates`

Each update row may include:
- `name`
- `platform`
- `base`
- `base_new`
- `dlc`
- `dlc_new`
- `hidden`
- `changelog`

### `POST /api/update/apply`

Apply updates.

If no version pair is provided, applies the currently detected update chain.

Request body:

```json
{
  "baseOld": "1.0.0",
  "dlcOld": "1.0.0",
  "baseNew": "1.0.1",
  "dlcNew": "1.0.2"
}
```

Response fields:
- `success`
- `missing`
- `message`
- `missingPaths`
- `addedRequired`
- `addedRemote`

### `POST /api/update/rollback`

Rollback to target version.

Request body:

```json
{
  "target": "1.0.0+1.0.0"
}
```

Response fields:
- `success`
- `message`
- `version`

### `POST /api/update/import`

Import an existing update package from inside the game directory.

Request body:

```json
{
  "path": "downloads/example-update.zip"
}
```

Response fields:
- `success`
- `path`

### `POST /api/update/import-url`

Download an update package from URL, then import it.

Request body:

```json
{
  "url": "https://example.com/update.zip",
  "fileName": "update.zip"
}
```

Response fields:
- `success`
- `message`
- `downloadedPath`
