Token Admin
Manage per-user API tokens (the
sk-…keys used by the OpenAI-compatible/v1/...endpoints). UI:web/default-pro/src/views/token/Token.vue.
The page is the same for regular users and admins — admins see their own tokens (the API does not expose a cross-user listing). For cross-user inspection use /api/user/self (admin) or the log-search endpoint.
Data Model
model.Token (table tokens, key fields):
| Field | Type | Notes |
|---|---|---|
user_id | int | Owner |
name | varchar(50) | Display name |
key | varchar UNIQUE | Secret (clients prepend sk- when calling) |
status | int | TokenStatusEnabled=1 / Disabled=2 / Expired=3 / Exhausted=4 |
expired_time | int64 | unix seconds; -1 = never expires |
remain_quota | int64 | Remaining quota |
unlimited_quota | bool | Bypass quota checks |
models | text (comma separated) | Allowed models whitelist; empty = all |
subnet | text | Client subnet whitelist (e.g. 10.0.0.0/8,192.168.0.0/16) |
created_time / accessed_time | int64 | unix seconds |
Endpoints
| Endpoint | Method | Auth | Description |
|---|---|---|---|
/api/token/ | GET | User | Current user's tokens (?p=, ?order=) |
/api/token/search?keyword= | GET | User | Fuzzy match on name |
/api/token/:id | GET | User | Single detail (must belong to caller) |
/api/token/ | POST | User | Create (key auto-generated by random.GenerateKey) |
/api/token/ | PUT | User | Edit (?status_only=true changes only status) |
/api/token/:id | DELETE | User | Delete |
/api/token/status | GET | relay auth | OpenAI-compatible credit_summary |
/api/user/self/token | GET | User | Generate / regenerate the user's access_token (dashboard session token — distinct from token.key) |
Implementation: controller/token.go.
Create Token — POST /api/token/
json
{
"name": "my-bot",
"expired_time": -1,
"remain_quota": 500000,
"unlimited_quota": false,
"models": "gpt-4o,gpt-4o-mini",
"subnet": "10.0.0.0/8"
}name≤ 30 chars.subnet(when non-empty) is validated withnetwork.IsValidSubnets(CIDR list).keyis generated server-side; the response contains the full Token so the UI can show the key once.- New tokens default to
TokenStatusEnabled=1.
Enabling Preconditions
UpdateToken re-validates when flipping status to Enabled:
- If the previous
expired_time <= now: returns "令牌已过期,无法启用,请先修改令牌过期时间,或者设置为永不过期". - If
remain_quota <= 0and!unlimited_quota: returns "令牌可用额度已用尽...".
OpenAI-Compatible /api/token/status
GetTokenStatus is called by the relay and returns:
json
{
"object": "credit_summary",
"total_granted": <remain_quota>,
"total_used": 0,
"total_available": <remain_quota>,
"expires_at": <expired_time * 1000>
}expires_at is 0 when expired_time == -1. total_used is currently always 0 because tokens do not maintain their own used_quota counter.
Quota Deduction Path
The relay middleware reads the token on each /v1/chat/completions call:
unlimited_quota=true→ skip deduction.- Non-empty
models→ only allow requests whose model is in the whitelist. - Non-empty
subnet→ check the client IP matches. expired_time > 0 && expired_time <= now→ treat as expired.remain_quota <= 0(and not unlimited) → treat as exhausted.
Frontend Guide
- Top Base URL card shows
/v1with a copy button and a usage-guide modal. - Add Token modal fields:
name,expired_time(unix seconds or-1),remain_quota,unlimited_quotaswitch,models(multi-select / comma-separated),subnet(CIDR). - Row actions:
- View full key: modal that shows the plaintext (only available right after creation).
- Edit: update all fields except
key. - Enable / Disable: popconfirm.
- Delete: confirmation.
- Clicking the eye icon next to
modelsopens a modal listing the full whitelist.
Implementation Pointers
| Concern | Location |
|---|---|
| CRUD handler | controller/token.go |
| AccessToken (user-level) | controller/user.go::GenerateAccessToken |
| OpenAI credit_summary | controller/token.go::GetTokenStatus |
| Middleware (auth / quota) | middleware/auth.go, relay/ |
| Routes | router/api.go |