mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-06 15:46:37 +08:00
[优化] 更新文档
This commit is contained in:
+120
-13
@@ -1,12 +1,12 @@
|
||||
# API Conventions
|
||||
|
||||
You will learn: The response structure, path conventions, authentication methods, and Swagger entry point for OpenFlare management and Agent APIs.
|
||||
You will learn: The response structure, path conventions, authentication methods, and Swagger entrance for the OpenFlare Admin API and Agent API.
|
||||
|
||||
Both the OpenFlare management APIs and Agent APIs use JSON.
|
||||
Both the OpenFlare Admin API and Agent API communicate using JSON.
|
||||
|
||||
## Response Structure
|
||||
|
||||
Both success and failure should return a clear `message`:
|
||||
Both successful and failed API responses must return a clear `message`:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -18,31 +18,138 @@ Both success and failure should return a clear `message`:
|
||||
|
||||
## Path Conventions
|
||||
|
||||
| Type | Convention |
|
||||
| Category | Convention |
|
||||
| --- | --- |
|
||||
| Management API | Authenticated by management console Session |
|
||||
| Agent API | Fixed under `/api/agent/*` |
|
||||
| Read-only API | Use `GET` |
|
||||
| Mutation-type API | Use `POST` |
|
||||
| Admin API | Authenticated via the Admin Session |
|
||||
| Agent API | Located strictly under `/api/agent/*` |
|
||||
| Relay API | Located strictly under `/api/relay/*`, authenticated via `X-Agent-Token` (reusing the Agent's token) |
|
||||
| OpenFlared API | Located strictly under `/api/flared/*`, authenticated via `X-Tunnel-Token` (dedicated tunnel_token) |
|
||||
| Read-only APIs | Use the `GET` method |
|
||||
| Mutating APIs | Use the `POST` method |
|
||||
|
||||
## WAF IP Group APIs
|
||||
|
||||
The Admin WAF IP Group APIs require Admin Session authentication:
|
||||
|
||||
| Method | Path | Description |
|
||||
| --- | --- | --- |
|
||||
| `GET` | `/api/waf/ip-groups` | Query IP groups list |
|
||||
| `GET` | `/api/waf/ip-groups/:id` | Query a single IP group |
|
||||
| `POST` | `/api/waf/ip-groups` | Create a new IP group |
|
||||
| `POST` | `/api/waf/ip-groups/test` | Test automatic IP group Expr rules; returns matching IPs in the lookback window without persisting the config |
|
||||
| `POST` | `/api/waf/ip-groups/:id/update` | Update an existing IP group |
|
||||
| `POST` | `/api/waf/ip-groups/:id/delete` | Delete an IP group; denied if currently referenced by any rule group |
|
||||
| `POST` | `/api/waf/ip-groups/:id/sync` | Manually sync subscription IP groups or execute automatic IP group aggregation |
|
||||
|
||||
The IP group `type` supports `manual`, `automatic`, and `subscription`. The `auto_config` parameter for automatic IP groups is a JSON object:
|
||||
|
||||
```json
|
||||
{
|
||||
"lookback_minutes": 60,
|
||||
"rules": [
|
||||
{
|
||||
"name": "Single IP High-Frequency 404 Scanning",
|
||||
"expr": "request_count > 100 && status_404_ratio >= 0.8"
|
||||
},
|
||||
{
|
||||
"name": "Single IP Direct IP Access Mismatch",
|
||||
"expr": "ip_host_count > 50 && ip_host_ratio > 0.5"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Automatic rules evaluate Expr boolean expressions against metrics aggregated on a per-client-IP basis. The available metrics include `ip`, `request_count`, `status_404_count`, `status_404_ratio`, `ip_host_count`, `ip_host_ratio`, `client_error_count`, `server_error_count`, and `last_seen_unix`. The full syntax is detailed in [WAF Auto IP Group Expressions](../guide/waf-ip-group-expr.md).
|
||||
|
||||
Subscription formats support `text` and `json`: plain text parsing resolves one IP or CIDR per line, ignoring empty lines and comments starting with `#`; JSON parsing decodes arrays, reading the root array by default.
|
||||
|
||||
## Authentication
|
||||
|
||||
The management console continues to reuse the existing login, role, and Session system.
|
||||
The Admin panel continues to reuse the existing login, role, and Session validation.
|
||||
|
||||
Official Agent requests uniformly use the node-exclusive `agent_token`; the first access can use the global `discovery_token`. The Agent request header is fixed as:
|
||||
Agent requests must carry the node-specific `agent_token` (except for first-time registration, which can use the global `discovery_token`). The header is formatted as:
|
||||
|
||||
```http
|
||||
X-Agent-Token: <token>
|
||||
```
|
||||
|
||||
Full Tokens must not be printed in the logs.
|
||||
### Agent WAF IP Group Synchronization
|
||||
|
||||
The Agent heartbeat payload can carry local WAF IP group checksums:
|
||||
|
||||
```json
|
||||
{
|
||||
"waf_ip_group_checksums": {
|
||||
"1": "sha256..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The Server evaluates the checksums against active configurations, returning mismatched IP groups in the heartbeat response:
|
||||
|
||||
```json
|
||||
{
|
||||
"waf_ip_groups": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Auto Blacklist",
|
||||
"type": "automatic",
|
||||
"enabled": true,
|
||||
"ip_list": ["203.0.113.10"],
|
||||
"checksum": "sha256..."
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Alternatively, the Agent can proactively request differential updates upon applying a new configuration version:
|
||||
|
||||
| Method | Path | Description |
|
||||
| --- | --- | --- |
|
||||
| `POST` | `/api/agent/waf/ip-groups/sync` | Returns mismatched WAF IP groups based on Agent-supplied `ids` and `checksums` |
|
||||
|
||||
When an IP group is updated on the Server, connected Agents receive a WebSocket push containing `type = "waf_ip_groups"` with the changed IP groups array as payload. The Agent updates only the changed groups incrementally.
|
||||
|
||||
## OpenFlared API
|
||||
|
||||
The OpenFlared client communicates with the Server via a dedicated `tunnel_token`, completely decoupled from the Agent authentication system. All endpoints require `X-Tunnel-Token` authentication; requests are denied with `403` if the token is invalid.
|
||||
|
||||
| Method | Path | Description |
|
||||
| --- | --- | --- |
|
||||
| `POST` | `/api/flared/heartbeat` | Client heartbeat, updates online status and retrieves active tunnel config version summaries |
|
||||
| `GET` | `/api/flared/config/active` | Pulls the complete tunnel routing configuration (relay list + frpc proxy definitions) |
|
||||
| `POST` | `/api/flared/apply-log` | Reports configuration application results (success / warning / failed) |
|
||||
| `GET` | `/api/flared/ws` | Upgrades to a WebSocket connection for real-time `active_config` pushes |
|
||||
|
||||
Heartbeat request example:
|
||||
|
||||
```http
|
||||
POST /api/flared/heartbeat
|
||||
X-Tunnel-Token: <tunnel_token>
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"client_version": "v0.2.0",
|
||||
"frp_version": "0.61.0",
|
||||
"tunnel_status": "running",
|
||||
"connected_relays": [
|
||||
{ "relay_node_id": "node-relay-1", "status": "healthy", "proxy_count": 3 }
|
||||
],
|
||||
"current_version": "v1",
|
||||
"current_checksum": "sha256..."
|
||||
}
|
||||
```
|
||||
|
||||
The heartbeat response returns the `active_config` summary and `tunnel_settings` (containing runtime settings like heartbeat intervals and WebSocket upgrade switches). When a new configuration version is published, the Server broadcasts a message `type = "active_config"` with the version summary as payload to all connected Clients over WebSockets, prompting them to fetch and apply the config immediately.
|
||||
|
||||
Full tokens must never be logged.
|
||||
|
||||
## Swagger
|
||||
|
||||
Accessible after logging into the management console:
|
||||
Once logged into the management console, the Swagger page is accessible at:
|
||||
|
||||
```text
|
||||
/swagger/index.html
|
||||
```
|
||||
|
||||
The Swagger files are located in `openflare_server/docs`, generated by `swag init`.
|
||||
The Swagger definition file is stored in `openflare_server/docs`, generated by `swag init`.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Commands and Scripts
|
||||
# CLI Commands
|
||||
|
||||
You will learn: Common commands for starting, building, testing, installing, and uninstalling the OpenFlare Server, management console frontend, Agent, Swagger, and documentation site.
|
||||
You will learn: Common commands for starting, building, testing, installing, and uninstalling the OpenFlare Server, Admin Frontend, Agent, Swagger, and Documentation site.
|
||||
|
||||
## Server
|
||||
|
||||
@@ -14,13 +14,13 @@ export LOG_LEVEL='info'
|
||||
go run .
|
||||
```
|
||||
|
||||
Specify listening port and log directory:
|
||||
Specify listening port and logging directory:
|
||||
|
||||
```bash
|
||||
go run . --port 3000 --log-dir ./logs
|
||||
```
|
||||
|
||||
Test:
|
||||
Run tests:
|
||||
|
||||
```bash
|
||||
cd openflare_server
|
||||
@@ -37,14 +37,14 @@ pnpm install
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
Build static artifacts:
|
||||
Build static assets:
|
||||
|
||||
```bash
|
||||
cd openflare_server/web
|
||||
pnpm build
|
||||
```
|
||||
|
||||
Checks:
|
||||
Linting and testing checks:
|
||||
|
||||
```bash
|
||||
cd openflare_server/web
|
||||
@@ -69,13 +69,45 @@ cd openflare_agent
|
||||
go build -o openflare-agent ./cmd/agent
|
||||
```
|
||||
|
||||
Test:
|
||||
Run tests:
|
||||
|
||||
```bash
|
||||
cd openflare_agent
|
||||
GOCACHE=/tmp/openflare-go-cache go test ./...
|
||||
```
|
||||
|
||||
## Relay (Server-side)
|
||||
|
||||
Run from source:
|
||||
|
||||
```bash
|
||||
cd openflare_relay
|
||||
go run ./cmd -config /path/to/relay.json
|
||||
```
|
||||
|
||||
Compile:
|
||||
|
||||
```bash
|
||||
cd openflare_relay
|
||||
go build -o openflare-relay ./cmd
|
||||
```
|
||||
|
||||
## OpenFlared (Client-side)
|
||||
|
||||
Run from source:
|
||||
|
||||
```bash
|
||||
cd openflared
|
||||
go run ./cmd -config /path/to/flared.json
|
||||
```
|
||||
|
||||
Compile:
|
||||
|
||||
```bash
|
||||
cd openflared
|
||||
go build -o openflared ./cmd
|
||||
```
|
||||
|
||||
## Install Agent
|
||||
|
||||
```bash
|
||||
|
||||
+236
-125
@@ -1,98 +1,114 @@
|
||||
# Configuration Reference
|
||||
# Configuration Options
|
||||
|
||||
You will learn: What configuration sources are supported by OpenFlare Server, frontend builds, and Agents, what the default values of configuration items are, and how common deployment combinations should be configured.
|
||||
You will learn: What configuration sources are supported by OpenFlare Server, frontend builds, and Agents; what the default configuration values are; and how to configure common deployment combinations.
|
||||
|
||||
This document summarizes the Server and Agent configuration items supported by OpenFlare `1.0.0`, retaining only the startup, deployment, and runtime parameters that remain valid.
|
||||
This document aggregates the currently supported configuration options for OpenFlare Server and Agent in version `1.0.0`, keeping only running parameters that are currently active.
|
||||
|
||||
## Configuration Sources
|
||||
|
||||
The Server supports three types of configuration sources:
|
||||
|
||||
1. Command-line parameters.
|
||||
1. CLI arguments.
|
||||
2. Environment variables.
|
||||
3. Runtime configurations in the database `Option` table.
|
||||
3. Runtime configurations in the database `options` table.
|
||||
|
||||
The Agent supports:
|
||||
|
||||
1. `-config` command-line parameter.
|
||||
2. `agent.json` configuration file.
|
||||
3. A few log-related environment variables.
|
||||
1. The `-config` CLI argument.
|
||||
2. The `agent.json` configuration file.
|
||||
3. A small set of environment variables for overriding logs and settings.
|
||||
|
||||
The Relay (Server-side) supports:
|
||||
|
||||
1. The `-config` CLI argument.
|
||||
2. The `relay.json` configuration file.
|
||||
3. Persistent environment variables for overriding runtime flags.
|
||||
|
||||
The Client (Intranet Client) supports:
|
||||
|
||||
1. The `-config` CLI argument.
|
||||
2. The `flared.json` configuration file.
|
||||
3. Startup overrides and logging environment variables.
|
||||
|
||||
## Configuration File Locations
|
||||
|
||||
| Component | Default Location | Description |
|
||||
| --- | --- | --- |
|
||||
| Server SQLite | `openflare.db` | Can be modified via `SQLITE_PATH` |
|
||||
| Agent Configuration File | `./agent.json` | Can be specified via `-config` |
|
||||
| One-click Install Agent Config | `/opt/openflare-agent/agent.json` | Default generated by the installation script |
|
||||
| Agent Data Directory | `data` under the config directory | Can be modified via `data_dir` |
|
||||
| Server SQLite | `openflare.db` | Can be customized via `SQLITE_PATH` |
|
||||
| Agent Config | `./agent.json` | Can be specified via `-config` |
|
||||
| One-Click Agent | `/opt/openflare-agent/agent.json` | Generated by the installation script by default |
|
||||
| Agent Data Dir | `data` in the config folder | Can be customized via `data_dir` |
|
||||
| Relay Config | `./relay.json` | Can be specified via `-config` |
|
||||
| One-Click Relay | `/opt/openflare-relay/relay.json` | Generated by the installation script by default |
|
||||
| Client Config | `./flared.json` | Can be specified via `-config` |
|
||||
| One-Click Client | `/opt/openflared/flared.json` | Generated by the installation script by default |
|
||||
|
||||
## Server CLI Flags
|
||||
## Server CLI Arguments
|
||||
|
||||
```bash
|
||||
cd openflare_server
|
||||
go run . --port 3000 --log-dir ./logs
|
||||
```
|
||||
|
||||
| Flag | Purpose | Default |
|
||||
| Argument | Description | Default Value |
|
||||
| --- | --- | --- |
|
||||
| `--port` | Specify the port the Server listens on | `3000` |
|
||||
| `--log-dir` | Specify the log directory | empty |
|
||||
| `--version` | Print the current version and exit | `false` |
|
||||
| `--help` | Print help information and exit | `false` |
|
||||
| `--port` | Port the Server listens on | `3000` |
|
||||
| `--log-dir` | Directory to output logs | Empty (stdout) |
|
||||
| `--version` | Outputs current version and exits | `false` |
|
||||
| `--help` | Outputs help information and exits | `false` |
|
||||
|
||||
## Server Environment Variables
|
||||
|
||||
| Variable | Purpose | Default |
|
||||
| Environment Variable | Description | Default Value |
|
||||
| --- | --- | --- |
|
||||
| `PORT` | Server listen port | `3000` |
|
||||
| `GIN_MODE` | Gin execution mode | `release` unless `debug` |
|
||||
| `LOG_LEVEL` | Log level | `info` |
|
||||
| `SESSION_SECRET` | Session signing secret | randomly generated on startup |
|
||||
| `PORT` | Port the Server listens on | `3000` |
|
||||
| `GIN_MODE` | Gin framework running mode | Defaults to release unless `debug` |
|
||||
| `LOG_LEVEL` | Logging level | `info` |
|
||||
| `SESSION_SECRET` | Session signing key | Randomly generated on startup |
|
||||
| `SQLITE_PATH` | SQLite database file path | `openflare.db` |
|
||||
| `DSN` | PostgreSQL DSN, preferred over SQLite when set | empty |
|
||||
| `SQL_DSN` | Legacy PostgreSQL DSN, lower priority than `DSN` | empty |
|
||||
| `REDIS_CONN_STRING` | Redis connection string | empty |
|
||||
| `AGENT_TOKEN` | Legacy global Agent token | empty |
|
||||
| `DSN` | PostgreSQL DSN (takes precedence over SQLite) | Empty |
|
||||
| `SQL_DSN` | Legacy PostgreSQL DSN (lower priority than `DSN`) | Empty |
|
||||
| `REDIS_CONN_STRING` | Redis connection string | Empty |
|
||||
| `AGENT_TOKEN` | Legacy global Agent Token | Empty |
|
||||
|
||||
Description:
|
||||
Notes:
|
||||
|
||||
* When both `DSN` and `SQL_DSN` exist, `DSN` takes precedence.
|
||||
* When `DSN`/`SQL_DSN` and `SQLITE_PATH` exist simultaneously, PostgreSQL takes precedence.
|
||||
* When the target PostgreSQL database is empty and a local SQLite file exists at `SQLITE_PATH`, the Server automatically migrates SQLite data at startup and prints table-by-table migration progress in the logs.
|
||||
* If both `DSN` and `SQL_DSN` exist, `DSN` is prioritized.
|
||||
* If either `DSN` or `SQL_DSN` coexist with `SQLITE_PATH`, PostgreSQL is prioritized.
|
||||
* If the target PostgreSQL database is empty and a local SQLite file exists at `SQLITE_PATH`, the Server automatically migrates SQLite data table-by-table on startup.
|
||||
* `SESSION_SECRET` must be explicitly configured in production.
|
||||
* When `REDIS_CONN_STRING` is not configured, related capabilities fall back to in-process implementations.
|
||||
* If `REDIS_CONN_STRING` is unconfigured, co-located features fall back to in-memory implementations.
|
||||
|
||||
## Runtime Options
|
||||
|
||||
The following options are maintained on the settings page of the management console and can be hot-updated:
|
||||
The following options are maintained in the admin settings page and support hot reloading:
|
||||
|
||||
| Option | Purpose | Default |
|
||||
| Parameter | Description | Default Value |
|
||||
| --- | --- | --- |
|
||||
| `AgentHeartbeatInterval` | Agent heartbeat interval (milliseconds) | `10000` |
|
||||
| `AgentWebsocketUpgradeEnabled` | Whether to allow Agents to upgrade to WebSockets after successful HTTP heartbeats | `true` |
|
||||
| `NodeOfflineThreshold` | Node offline threshold (milliseconds) | `120000` |
|
||||
| `AgentUpdateRepo` | Agent self-update repository | `Rain-kl/OpenFlare` |
|
||||
| `GeoIPProvider` | Node/IP region lookup provider | `ipinfo` |
|
||||
| `DatabaseAutoCleanupEnabled` | Whether to enable daily automatic cleanup of observability data | `false` |
|
||||
| `DatabaseAutoCleanupRetentionDays` | In-database retention days, at least 1 day | `30` |
|
||||
| `AgentHeartbeatInterval` | Heartbeat interval for Agents (ms) | `10000` |
|
||||
| `AgentWebsocketUpgradeEnabled` | Toggles WebSocket upgrades after successful HTTP heartbeat | `true` |
|
||||
| `NodeOfflineThreshold` | Threshold duration to mark a node offline (ms) | `120000` |
|
||||
| `AgentUpdateRepo` | GitHub repository for Agent self-updates | `Rain-kl/OpenFlare` |
|
||||
| `GeoIPProvider` | Geolocation resolution provider | `ipinfo` |
|
||||
| `DatabaseAutoCleanupEnabled` | Toggles daily automatic cleanup of observability logs | `false` |
|
||||
| `DatabaseAutoCleanupRetentionDays` | Data retention duration in days, minimum 1 day | `30` |
|
||||
| `GlobalApiRateLimitNum` / `GlobalApiRateLimitDuration` | Global API rate limit count / window | `300` / `180` |
|
||||
| `GlobalWebRateLimitNum` / `GlobalWebRateLimitDuration` | Global Web rate limit count / window | `300` / `180` |
|
||||
| `CriticalRateLimitNum` / `CriticalRateLimitDuration` | Sensitive API rate limit count / window | `100` / `1200` |
|
||||
|
||||
Description:
|
||||
Notes:
|
||||
|
||||
* When `DatabaseAutoCleanupEnabled` is enabled, the Server automatically cleans up three types of observability data (`node_access_logs`, `node_metric_snapshots`, `node_request_reports`) at 3:00 AM every day.
|
||||
* `DatabaseAutoCleanupRetentionDays` is the unified retention count and must be greater than or equal to 1.
|
||||
* The management console supports leaving the retention days blank during manual cleanup to directly delete all history of the corresponding datasets.
|
||||
* The GitHub Release pointed to by `AgentUpdateRepo` must provide a matching `.sha256` checksum file for each Agent binary, such as `openflare-agent-linux-amd64.sha256`; the self-update validates this SHA-256 digest before replacing the executable.
|
||||
* Third-party logins no longer use `GitHubOAuthEnabled`, `GitHubClientId`, or `GitHubClientSecret` as primary configuration entries; these legacy options are only used for migration to the default GitHub authentication source during upgrades.
|
||||
* Legacy options for WeChat login are retained for compatibility, but the management console no longer provides WeChat login configuration entries.
|
||||
* Legacy options for Turnstile and backend verification remain, and existing configurations will continue to take effect.
|
||||
* When `DatabaseAutoCleanupEnabled` is enabled, the Server deletes `node_access_logs`, `node_metric_snapshots`, and `node_request_reports` daily at 3:00 AM.
|
||||
* `DatabaseAutoCleanupRetentionDays` must be greater than or equal to 1.
|
||||
* Leaving retention days blank during a manual trigger in the console deletes all historic logs instantly.
|
||||
* The GitHub Release in `AgentUpdateRepo` must contain a matching `.sha256` checksum file for every Agent binary (e.g., `openflare-agent-linux-amd64.sha256`); the Agent validates this checksum before replacing the local executable.
|
||||
* Third-party logins no longer use `GitHubOAuthEnabled`, `GitHubClientId`, and `GitHubClientSecret` as main configuration entrypoints; these legacy options are used only for migrating default GitHub credentials during upgrades.
|
||||
* The legacy WeChat login options are kept for backward compatibility, but the option page no longer edits them.
|
||||
* Legacy Cloudflare Turnstile options and validation logic are retained and will work normally.
|
||||
|
||||
## OpenResty Parameters
|
||||
|
||||
OpenResty performance and caching parameters continue to be stored uniformly in the `Option` table. Currently common items include:
|
||||
OpenResty performance and caching parameters are managed in the `options` table, including:
|
||||
|
||||
* `OpenRestyWorkerProcesses`
|
||||
* `OpenRestyWorkerConnections`
|
||||
@@ -107,96 +123,159 @@ OpenResty performance and caching parameters continue to be stored uniformly in
|
||||
* `OpenRestyCachePath`
|
||||
* `OpenRestyCacheMaxSize`
|
||||
|
||||
These parameters must be validated, saved, and participate in version rendering in a structured way.
|
||||
These parameters must be validated, saved, and rendered structurally.
|
||||
|
||||
Constraints:
|
||||
|
||||
* The management console no longer exposes `resolver` configuration.
|
||||
* Upstreams are uniformly rendered as named `upstream` blocks with keepalives enabled.
|
||||
* A single upstream carrying a base path or query will append the original URI in `proxy_pass`.
|
||||
* Multiple upstreams still require each upstream to be pure `scheme://host[:port]`, and the protocol must be consistent within the same rule.
|
||||
* `OpenRestyCacheEnabled` is used to enable the caching infrastructure and global default parameters; the actual caching enablement and hit policies (based on URL, suffix, or path) are decided separately by each individual `proxy_routes`.
|
||||
* The console no longer exposes `resolver` settings.
|
||||
* Upstreams are rendered uniformly as named `upstream` blocks with keepalive enabled.
|
||||
* Single upstreams carrying a base path or query have their URI correctly appended in `proxy_pass`.
|
||||
* Multi-upstreams must be pure `scheme://host[:port]` using the same protocol within a single rule.
|
||||
* `OpenRestyCacheEnabled` enables cache infrastructure and global defaults; the actual caching matching policies (by URL, suffix, or path) are configured per `proxy_routes`.
|
||||
* The default cache key is `$scheme$host$request_uri`.
|
||||
* The default `keepalive_timeout` is `20` seconds, and the default `proxy_connect_timeout` is `3` seconds.
|
||||
* The default event model is `epoll`, and `multi_accept` is enabled by default.
|
||||
* HTTPS listeners use the independent `http2 on;` directive by default to avoid deprecation warnings for `listen ... http2` in newer Nginx/OpenResty versions.
|
||||
* Default `keepalive_timeout` is `20` seconds; default `proxy_connect_timeout` is `3` seconds.
|
||||
* The default event model is `epoll` with `multi_accept` enabled.
|
||||
* HTTPS listeners use the independent `http2 on;` directive to avoid deprecation warnings for `listen ... http2` in newer Nginx/OpenResty versions.
|
||||
|
||||
## Frontend Build Variables
|
||||
## Frontend Build Environment Variables
|
||||
|
||||
| Variable | Purpose | Default |
|
||||
| Environment Variable | Description | Default Value |
|
||||
| --- | --- | --- |
|
||||
| `NEXT_PUBLIC_API_BASE_URL` | Frontend API request base path | `/api` |
|
||||
| `NEXT_PUBLIC_APP_VERSION` | Frontend displayed version number | `dev` |
|
||||
| `NEXT_DEV_BACKEND_URL` | Dev backend proxy target | `http://127.0.0.1:3000` |
|
||||
| `NEXT_PUBLIC_API_BASE_URL` | Base path for frontend API calls | `/api` |
|
||||
| `NEXT_PUBLIC_APP_VERSION` | Application version shown in the UI | `dev` |
|
||||
| `NEXT_DEV_BACKEND_URL` | Target backend proxied by the local dev server | `http://127.0.0.1:3000` |
|
||||
|
||||
## Agent Environment Variables
|
||||
|
||||
| Variable | Purpose | Default |
|
||||
| Environment Variable | Description | Default Value |
|
||||
| --- | --- | --- |
|
||||
| `LOG_LEVEL` | Agent log level | `info` |
|
||||
| `OPENFLARE_SERVER_URL` | Control plane URL, can override `agent.json` | empty |
|
||||
| `OPENFLARE_AGENT_TOKEN` | Node-exclusive auth token, can override `agent.json` | empty |
|
||||
| `OPENFLARE_DISCOVERY_TOKEN` | Global token for first registration, can override `agent.json` | empty |
|
||||
| `OPENFLARE_NODE_NAME` | Node name, can override `agent.json` | empty |
|
||||
| `OPENFLARE_NODE_IP` | Node IP, can override `agent.json` | empty |
|
||||
| `OPENFLARE_DATA_DIR` | Agent data directory, can override `agent.json` | empty |
|
||||
| `OPENFLARE_OPENRESTY_PATH` | OpenResty binary path, can override `agent.json` | empty |
|
||||
| `OPENFLARE_HEARTBEAT_INTERVAL` | Heartbeat interval, can override `agent.json` | empty |
|
||||
| `OPENFLARE_REQUEST_TIMEOUT` | Request timeout, can override `agent.json` | empty |
|
||||
| `OPENFLARE_OPENRESTY_OBSERVABILITY_PORT` | Local observability port, can override `agent.json` | empty |
|
||||
| `OPENFLARE_MMDB_PATH` | WAF GeoIP mmdb path, can override `agent.json` | empty |
|
||||
| `OPENFLARE_MMDB_UPDATE_INTERVAL` | WAF GeoIP mmdb update interval, can override `agent.json` | empty |
|
||||
| `OPENFLARE_MMDB_DOWNLOAD_URL` | WAF GeoIP mmdb download URL, can override `agent.json` | empty |
|
||||
| `LOG_LEVEL` | Logging level for the Agent | `info` |
|
||||
| `OPENFLARE_SERVER_URL` | Server URL; overrides `agent.json` | Empty |
|
||||
| `OPENFLARE_AGENT_TOKEN` | Node-specific Token; overrides `agent.json` | Empty |
|
||||
| `OPENFLARE_DISCOVERY_TOKEN` | Auto-registration Token; overrides `agent.json` | Empty |
|
||||
| `OPENFLARE_NODE_NAME` | Node name; overrides `agent.json` | Empty |
|
||||
| `OPENFLARE_NODE_IP` | Node IP; overrides `agent.json` | Empty |
|
||||
| `OPENFLARE_DATA_DIR` | Agent data directory; overrides `agent.json` | Empty |
|
||||
| `OPENFLARE_OPENRESTY_PATH` | Path to OpenResty binary; overrides `agent.json` | Empty |
|
||||
| `OPENFLARE_HEARTBEAT_INTERVAL` | Heartbeat interval; overrides `agent.json` | Empty |
|
||||
| `OPENFLARE_REQUEST_TIMEOUT` | Request timeout; overrides `agent.json` | Empty |
|
||||
| `OPENFLARE_OPENRESTY_OBSERVABILITY_PORT` | Local observability port; overrides `agent.json` | Empty |
|
||||
| `OPENFLARE_MMDB_PATH` | WAF GeoIP mmdb path; overrides `agent.json` | Empty |
|
||||
| `OPENFLARE_MMDB_UPDATE_INTERVAL` | GeoIP mmdb update interval; overrides `agent.json` | Empty |
|
||||
| `OPENFLARE_MMDB_DOWNLOAD_URL` | GeoIP mmdb download link; overrides `agent.json` | Empty |
|
||||
|
||||
## Agent CLI Flags
|
||||
## Agent CLI Arguments
|
||||
|
||||
| Flag | Purpose | Default |
|
||||
| Argument | Description | Default Value |
|
||||
| --- | --- | --- |
|
||||
| `-config` | Specify the path to the Agent configuration file | `./agent.json` |
|
||||
| `-config` | Path to the Agent configuration file | `./agent.json` |
|
||||
|
||||
## Agent Configuration Fields
|
||||
## Agent Configurations Fields
|
||||
|
||||
| Field | Purpose | Required | Default / Behavior |
|
||||
| Field | Description | Required | Default Value / Behavior |
|
||||
| --- | --- | --- | --- |
|
||||
| `server_url` | Control plane URL | yes | none |
|
||||
| `agent_token` | Node-exclusive auth token | one of `agent_token`/`discovery_token` | empty |
|
||||
| `discovery_token` | Global token for first registration | one of `agent_token`/`discovery_token` | empty |
|
||||
| `node_name` | Node name | no | automatically uses host name |
|
||||
| `node_ip` | Node IP | no | auto-detected; prioritizes obtaining the real public egress IP via third-party APIs, falling back to local interfaces on failure |
|
||||
| `openresty_path` | OpenResty binary path | no | `openresty` |
|
||||
| `openresty_observability_port` | Local observability and OpenResty health-check port | no | `18081` |
|
||||
| `data_dir` | Agent data directory | no | `data` under the config file directory |
|
||||
| `main_config_path` | OpenResty main config write path | no | `data_dir/etc/nginx/nginx.conf` |
|
||||
| `route_config_path` | Route config write path | no | `data_dir/etc/nginx/conf.d/openflare_routes.conf` |
|
||||
| `access_log_path` | OpenResty access log path | no | `data_dir/var/log/openflare/access.log` |
|
||||
| `cert_dir` | Certificate write directory | no | `data_dir/etc/nginx/certs` |
|
||||
| `openresty_cert_dir` | Certificate read directory in OpenResty config | no | same as `cert_dir` |
|
||||
| `lua_dir` | Lua scripts and static resources write directory | no | `data_dir/etc/nginx/lua` |
|
||||
| `openresty_lua_dir` | Lua read directory in OpenResty config | no | same as `lua_dir` |
|
||||
| `runtime_config_dir` | Agent runtime config write directory, e.g., `pow_config.json` | no | `data_dir/etc/openflare` |
|
||||
| `mmdb_path` | WAF GeoIP mmdb file path | no | `data_dir/etc/openflare/GeoLite2-Country.mmdb` |
|
||||
| `mmdb_update_interval` | WAF GeoIP mmdb update interval | no | `86400000` milliseconds |
|
||||
| `mmdb_download_url` | WAF GeoIP mmdb download URL | no | built-in GeoLite2 Country download URL |
|
||||
| `observability_buffer_path` | Observability buffering file path | no | `data_dir/var/lib/openflare/observability-buffer.json` |
|
||||
| `observability_replay_minutes` | Minutes to automatically replay recent observability data | no | `15` |
|
||||
| `state_path` | Agent local state file path | no | `data_dir/var/lib/openflare/agent-state.json` |
|
||||
| `heartbeat_interval` | Heartbeat interval | no | `10000` milliseconds |
|
||||
| `request_timeout` | HTTP request timeout | no | `10000` milliseconds |
|
||||
| `server_url` | Control plane URL | Yes | None |
|
||||
| `agent_token` | Node-specific access Token | Mutually exclusive with discovery_token | Empty |
|
||||
| `discovery_token` | Global auto-registration Token | Mutually exclusive with agent_token | Empty |
|
||||
| `node_name` | Node name | No | Hostname |
|
||||
| `node_ip` | Node IP | No | Auto-detect, resolves outbound public IP via realip.cc first, falls back to local adapters |
|
||||
| `openresty_path` | Path to the OpenResty binary | No | `"openresty"` |
|
||||
| `openresty_observability_port` | Observability port for health checks | No | `18081` |
|
||||
| `data_dir` | Agent data directory | No | `data` in the config folder |
|
||||
| `main_config_path` | Write path for Nginx main configuration | No | `data_dir/etc/nginx/nginx.conf` |
|
||||
| `route_config_path` | Write path for route configurations | No | `data_dir/etc/nginx/conf.d/openflare_routes.conf` |
|
||||
| `access_log_path` | Write path for OpenResty access logs | No | `data_dir/var/log/openflare/access.log` |
|
||||
| `cert_dir` | Write directory for SSL certificates | No | `data_dir/etc/nginx/certs` |
|
||||
| `openresty_cert_dir` | Read directory for certificates in Nginx | No | Same as `cert_dir` |
|
||||
| `lua_dir` | Write directory for Lua scripts and assets | No | `data_dir/etc/nginx/lua` |
|
||||
| `openresty_lua_dir` | Read directory for Lua scripts in Nginx | No | Same as `lua_dir` |
|
||||
| `runtime_config_dir` | Write directory for Agent runtime configs | No | `data_dir/etc/openflare` |
|
||||
| `mmdb_path` | WAF GeoIP database file path | No | `data_dir/etc/openflare/GeoLite2-Country.mmdb` |
|
||||
| `mmdb_update_interval` | WAF GeoIP database check interval | No | `86400000` milliseconds |
|
||||
| `mmdb_download_url` | WAF GeoIP database download URL | No | Built-in GeoLite2 Country URL |
|
||||
| `observability_buffer_path` | Buffer path for retry metrics logs | No | `data_dir/var/lib/openflare/observability-buffer.json` |
|
||||
| `observability_replay_minutes` | Lookback window for metric retries | No | `15` |
|
||||
| `state_path` | Path to store local state JSON file | No | `data_dir/var/lib/openflare/agent-state.json` |
|
||||
| `heartbeat_interval` | Heartbeat polling interval | No | `10000` milliseconds |
|
||||
| `request_timeout` | HTTP request timeout duration | No | `10000` milliseconds |
|
||||
|
||||
Description:
|
||||
Notes:
|
||||
|
||||
* `agent_token` and `discovery_token` cannot both be empty.
|
||||
* `heartbeat_interval` and `request_timeout` support integer milliseconds or Go duration strings.
|
||||
* When the Server runtime option `AgentWebsocketUpgradeEnabled` is enabled, the Agent will attempt to upgrade to a WebSocket after a successful HTTP heartbeat; it automatically falls back to HTTP heartbeats when connection fails or is disconnected.
|
||||
* When `openresty_path` is not configured, `openresty` is called by default.
|
||||
* The Agent's periodic health checks request `http://127.0.0.1:<openresty_observability_port>/openflare/stub_status`, no longer judging runtime health via high-frequency `openresty -t`; validation before configuration application, startup recovery, and reloads will still execute `openresty -t -c <main_config_path>`.
|
||||
* The Agent initializes and periodically updates `mmdb_path` for OpenResty WAF Lua to execute country-level geographical rules; update failures only record warnings, and do not block sync or reloads.
|
||||
* If `agent.json` does not exist but environment variables such as `OPENFLARE_SERVER_URL` and tokens are sufficient, the Agent can start directly; environment variables take precedence when both exist.
|
||||
* When the Agent is not configured with `node_ip`, it first queries `https://realip.cc` for the real public egress IP, adapting to Docker/NAT scenarios; it falls back to local interface detection on failure, preferring a public IPv4 address.
|
||||
* When the Agent automatically detects a private `node_ip`, the Server prioritizes retaining the public address of the Agent's direct connection during registration/heartbeat phases, avoiding misregistering internal interface addresses in NAT or multi-interface scenarios.
|
||||
* When "Lock node IP" is enabled in the admin UI, the Server keeps the manually configured node IP and Agent registration, HTTP heartbeat, or WebSocket status reports will not overwrite that field; after unlocking, the next report can fill it again.
|
||||
* If `AgentWebsocketUpgradeEnabled` is enabled on the Server, the Agent upgrades the HTTP heartbeat to WebSocket; it automatically falls back to HTTP heartbeats if it fails or disconnects.
|
||||
* If `openresty_path` is left blank, the Agent calls `openresty` on the host.
|
||||
* Periodic health checks query `http://127.0.0.1:<openresty_observability_port>/openflare/stub_status` instead of executing `openresty -t`; validation prior to reloads, starts, or rollbacks still runs `openresty -t -c <main_config_path>`.
|
||||
* The Agent initializes and periodically updates `mmdb_path` to support GeoIP region checks; failures to update write warnings and do not disrupt configuration synchronizations.
|
||||
* The Agent boots normally if `agent.json` is missing but environment variables (`OPENFLARE_SERVER_URL` and a Token) are available; environment variables override JSON settings.
|
||||
* If `node_ip` is left blank, the Agent resolves its outbound IP via `https://realip.cc` first, which is suitable for Docker/NAT networks.
|
||||
* If the Agent registers a private `node_ip`, the Server prioritizes saving the public TCP connection IP, preventing NAT adapters from registering internal IPs.
|
||||
* Enabling "Lock Node IP" in the console retains the manual IP; subsequent Agent registration or heartbeats do not overwrite it.
|
||||
|
||||
## Common Configuration Combinations
|
||||
## Relay Environment Variables
|
||||
|
||||
| Environment Variable | Description | Default Value |
|
||||
| --- | --- | --- |
|
||||
| `LOG_LEVEL` | Logging level for the Relay | `info` |
|
||||
| `OPENFLARE_SERVER_URL` | Server URL; overrides `relay.json` | Empty |
|
||||
| `OPENFLARE_AGENT_TOKEN` | Node-specific Token; overrides `relay.json` | Empty |
|
||||
| `OPENFLARE_DISCOVERY_TOKEN` | Auto-registration Token; overrides `relay.json` | Empty |
|
||||
| `OPENFLARE_NODE_NAME` | Node name; overrides `relay.json` | Empty |
|
||||
| `OPENFLARE_NODE_IP` | Node IP; overrides `relay.json` | Empty |
|
||||
| `OPENFLARE_DATA_DIR` | Relay data directory; overrides `relay.json` | Empty |
|
||||
| `OPENFLARE_FRPS_PATH` | frps binary path; overrides `relay.json` | Empty |
|
||||
|
||||
## Relay CLI Arguments
|
||||
|
||||
| Argument | Description | Default Value |
|
||||
| --- | --- | --- |
|
||||
| `-config` | Path to the Relay configuration file | `./relay.json` |
|
||||
|
||||
## Relay Configuration Fields
|
||||
|
||||
| Field | Description | Required | Default Value / Behavior |
|
||||
| --- | --- | --- | --- |
|
||||
| `server_url` | Control plane URL | Yes | None |
|
||||
| `agent_token` | Node-specific access Token | Mutually exclusive with discovery_token | Empty |
|
||||
| `discovery_token` | Global auto-registration Token | Mutually exclusive with agent_token | Empty |
|
||||
| `node_name` | Node name | No | Hostname |
|
||||
| `node_ip` | Relay listening IP for tunnel traffic | No | Auto-detect, prioritizes outbound public IP |
|
||||
| `frps_path` | Path to the `frps` binary | No | `frps` (system PATH) |
|
||||
| `data_dir` | Relay runtime data directory | No | `data` in the config folder |
|
||||
| `state_path` | Path to store local state JSON file | No | `data_dir/relay-state.json` |
|
||||
| `heartbeat_interval` | Heartbeat polling interval | No | `10000` milliseconds, supports Go duration strings |
|
||||
| `request_timeout` | HTTP request timeout duration | No | `10000` milliseconds, supports Go duration strings |
|
||||
|
||||
## OpenFlared (Client) Environment Variables
|
||||
|
||||
| Environment Variable | Description | Default Value |
|
||||
| --- | --- | --- |
|
||||
| `LOG_LEVEL` | Logging level for the client | `info` |
|
||||
| `OPENFLARE_SERVER_URL` | Server URL; overrides `flared.json` | Empty |
|
||||
| `OPENFLARE_TUNNEL_TOKEN` | Tunnel access Token; overrides `flared.json` | Empty |
|
||||
| `OPENFLARE_DATA_DIR` | Client data directory; overrides `flared.json` | Empty |
|
||||
| `OPENFLARE_FRPC_PATH` | frpc binary path; overrides `flared.json` | Empty |
|
||||
|
||||
## OpenFlared (Client) CLI Arguments
|
||||
|
||||
| Argument | Description | Default Value |
|
||||
| --- | --- | --- |
|
||||
| `-config` | Path to the client configuration file | `./flared.json` |
|
||||
|
||||
## OpenFlared (Client) Configuration Fields
|
||||
|
||||
| Field | Description | Required | Default Value / Behavior |
|
||||
| --- | --- | --- | --- |
|
||||
| `server_url` | Control plane URL | Yes | None |
|
||||
| `tunnel_token` | Tunnel dedicated access Token | Yes | None |
|
||||
| `frpc_path` | Path to the `frpc` binary | No | `frpc` (system PATH) |
|
||||
| `data_dir` | Client runtime data directory | No | `data` in the config folder |
|
||||
| `state_path` | Path to store local state JSON file | No | `data_dir/flared-state.json` |
|
||||
| `heartbeat_interval` | Heartbeat polling interval | No | `10000` milliseconds, supports Go duration strings |
|
||||
| `sync_interval` | Configuration sync interval | No | `30000` milliseconds, supports Go duration strings |
|
||||
| `request_timeout` | HTTP request timeout duration | No | `10000` milliseconds, supports Go duration strings |
|
||||
|
||||
## Common Configuration Combos
|
||||
|
||||
### Production Server + PostgreSQL
|
||||
|
||||
@@ -229,7 +308,7 @@ go run .
|
||||
}
|
||||
```
|
||||
|
||||
### Agent + Customized OpenResty Path
|
||||
### Agent + Customized OpenResty Paths
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -248,12 +327,44 @@ go run .
|
||||
}
|
||||
```
|
||||
|
||||
## Maintenance Requirements
|
||||
### Relay (Server-side) Default Configuration
|
||||
|
||||
When the following contents change, this document must be updated in sync:
|
||||
`relay.json`:
|
||||
|
||||
* Server command-line parameters.
|
||||
```json
|
||||
{
|
||||
"server_url": "http://your-server:3000",
|
||||
"agent_token": "replace-with-relay-auth-token",
|
||||
"frps_path": "frps",
|
||||
"data_dir": "/opt/openflare-relay/data",
|
||||
"heartbeat_interval": 10000,
|
||||
"request_timeout": 10000
|
||||
}
|
||||
```
|
||||
|
||||
### OpenFlared (Client-side) Default Configuration
|
||||
|
||||
`flared.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"server_url": "http://your-server:3000",
|
||||
"tunnel_token": "replace-with-tunnel-token",
|
||||
"frpc_path": "frpc",
|
||||
"data_dir": "/opt/openflared/data",
|
||||
"heartbeat_interval": 10000,
|
||||
"sync_interval": 30000,
|
||||
"request_timeout": 10000
|
||||
}
|
||||
```
|
||||
|
||||
## Maintenance Rules
|
||||
|
||||
This document must be updated in sync when any of the following change:
|
||||
|
||||
* Server CLI arguments.
|
||||
* Server environment variables.
|
||||
* Agent command-line parameters.
|
||||
* Agent configuration fields.
|
||||
* Default values, purposes, or examples of any configuration items.
|
||||
* Agent CLI arguments and configuration parameters.
|
||||
* Relay CLI arguments and configuration parameters.
|
||||
* Client CLI arguments and configuration parameters.
|
||||
* Default values, scopes, or examples of any configuration items.
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
# Reference
|
||||
# Reference Manuals
|
||||
|
||||
You will learn: What information belongs to stable reference materials, and where to look up configurations, commands, APIs, and the repository structure.
|
||||
You will learn: Which information belongs to stable reference manuals, and where to look up configurations, commands, APIs, and repository structures.
|
||||
|
||||
This section collects stable information at the runtime, interface, and repository levels, suitable for quick lookups during deployment, joint debugging, and troubleshooting.
|
||||
This section collects stable information at the runtime, API, and repository layers, suitable for rapid lookup during deployment, integration, and troubleshooting.
|
||||
|
||||
| Page | Content |
|
||||
| --- | --- |
|
||||
| [Configuration Items](./configuration.md) | Server environment variables, command line parameters, runtime Options, and Agent configuration fields |
|
||||
| [Commands and Scripts](./cli.md) | Common startup, build, test, install, and uninstall commands |
|
||||
| [API Conventions](./api.md) | Response structure, authentication, and path conventions of management and Agent APIs |
|
||||
| [Repository Layout](./repository.md) | Responsibilities of `openflare_server`, `openflare_agent`, `openflare_server/web`, and `docs` |
|
||||
| [Configuration Options](./configuration.md) | Server environment variables, CLI arguments, runtime Options, and Agent configuration parameters |
|
||||
| [CLI Commands](./cli.md) | Common CLI commands for starting, building, testing, installing, and uninstalling |
|
||||
| [API Conventions](./api.md) | Response structures, authentication, and routing paths for Admin and Agent APIs |
|
||||
| [Repository Structure](../design/repository.md) | Scope of responsibilities and folder layering of the Server, Agent, Relay, and Client |
|
||||
| [Deployment & Upgrade](../deployment/) | Server and Agent deployment, configuration, and upgrade guides (dedicated section) |
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
# Repository Layout
|
||||
|
||||
You will learn: What the Server, Agent, frontend, scripts, and documentation directories in the OpenFlare repository are responsible for, and which layer to place your logic when contributing code.
|
||||
|
||||
| Path | Responsibility |
|
||||
| --- | --- |
|
||||
| `openflare_server` | Gin + GORM + SQLite/PostgreSQL monolithic control plane |
|
||||
| `openflare_server/web` | Next.js 15 App Router management console frontend, statically exported and hosted by the Go Server |
|
||||
| `openflare_agent` | Go monolithic Agent, running on the node side |
|
||||
| `scripts` | Helper scripts such as Agent installation, uninstallation, etc. |
|
||||
| `docs` | VitePress documentation site, design baseline, development constraints, deployment, and configuration documents |
|
||||
|
||||
## Server Layering
|
||||
|
||||
| Directory | Responsibility |
|
||||
| --- | --- |
|
||||
| `controller/` | Parameter parsing, calling services, returning responses |
|
||||
| `service/` | Business logic, verification, transaction orchestration, configuration rendering |
|
||||
| `model/` | Model definition, database version, and migration |
|
||||
| `router/` | Route registration |
|
||||
| `middleware/` | Auth, authorization, rate limiting, and other cross-cutting logic |
|
||||
| `common/` | Configuration, global state, and initialization entry points |
|
||||
| `utils/` | Pure utility functions and general helpers |
|
||||
|
||||
## Agent Modules
|
||||
|
||||
| Module | Responsibility |
|
||||
| --- | --- |
|
||||
| `config` | Configuration reading and default values |
|
||||
| `heartbeat` | Heartbeat and version summary judgment |
|
||||
| `sync` | Configuration pulling and application orchestration |
|
||||
| `nginx` / `openresty` | OpenResty file writing, verification, reload, startup, and rollback |
|
||||
| `state` | Local state and observability supplementary reporting buffer |
|
||||
| `httpclient` | Server communication |
|
||||
| `protocol` | Agent API protocol types |
|
||||
| `internal/updater` | Agent self-updating |
|
||||
|
||||
## Frontend Layering
|
||||
|
||||
| Directory | Responsibility |
|
||||
| --- | --- |
|
||||
| `app/` | Routes, layouts, page assembly |
|
||||
| `features/` | Organize modules by business domains |
|
||||
| `components/` | Reuse components across features |
|
||||
| `lib/` | Request client, environment variables, utility functions, constants |
|
||||
| `store/` | A small amount of cross-page UI state |
|
||||
| `types/` | Shared type definitions |
|
||||
Reference in New Issue
Block a user