Skip to content

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):

FieldTypeNotes
user_idintOwner
namevarchar(50)Display name
keyvarchar UNIQUESecret (clients prepend sk- when calling)
statusintTokenStatusEnabled=1 / Disabled=2 / Expired=3 / Exhausted=4
expired_timeint64unix seconds; -1 = never expires
remain_quotaint64Remaining quota
unlimited_quotaboolBypass quota checks
modelstext (comma separated)Allowed models whitelist; empty = all
subnettextClient subnet whitelist (e.g. 10.0.0.0/8,192.168.0.0/16)
created_time / accessed_timeint64unix seconds

Endpoints ​

EndpointMethodAuthDescription
/api/token/GETUserCurrent user's tokens (?p=, ?order=)
/api/token/search?keyword=GETUserFuzzy match on name
/api/token/:idGETUserSingle detail (must belong to caller)
/api/token/POSTUserCreate (key auto-generated by random.GenerateKey)
/api/token/PUTUserEdit (?status_only=true changes only status)
/api/token/:idDELETEUserDelete
/api/token/statusGETrelay authOpenAI-compatible credit_summary
/api/user/self/tokenGETUserGenerate / 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 with network.IsValidSubnets (CIDR list).
  • key is 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 <= 0 and !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 /v1 with a copy button and a usage-guide modal.
  • Add Token modal fields: name, expired_time (unix seconds or -1), remain_quota, unlimited_quota switch, 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 models opens a modal listing the full whitelist.

Implementation Pointers ​

ConcernLocation
CRUD handlercontroller/token.go
AccessToken (user-level)controller/user.go::GenerateAccessToken
OpenAI credit_summarycontroller/token.go::GetTokenStatus
Middleware (auth / quota)middleware/auth.go, relay/
Routesrouter/api.go