Commit Graph
58 Commits
Author SHA1 Message Date
Sakurasan 7c597a6aa0 fix: 补上漏掉的 schema 版本号,修好渠道添加模型失败
c39c3e7 给 Model 加了 cache_write_price/context_length/supports_* 等列
但没递增 schemaVersion,user_version>=2 的库直接跳过 AutoMigrate,
models 表缺新列导致 Create 失败、渠道管理添加模型报缺列错误。
bump 到 3 触发一次迁移补齐列。
2026-10-28 15:40:03 +08:00
Sakurasan c39c3e7da4 feat: 补齐上游细节、渠道观测与模型定价元数据
- 上游 429 的 Retry-After 转成渠道冷却时长(夹在 10 分钟内;比默认值短则不缩短,
  否则 1s 就把坏渠道放回轮转)。失败计数改由 proxyOne 就地报——只有它读得到响应头。
- 故障转移时间预算 OT_PROXY_FAILOVER_BUDGET(默认 30s):只约束第二个及以后的渠道,
  首个不受限(长回答本就要跑几分钟),但坏渠道不再把客户端拖到 N×timeout。
- 流式空闲心跳 OT_PROXY_STREAM_HEARTBEAT(默认 15s):补一行 SSE 注释 `: ping`,
  防止 nginx/Cloudflare 的空闲超时掐掉长思考。心跳与正文共用一把写的锁,
  gin 的 ResponseWriter 不是并发安全的;注释行不进转换也不计费。
- 渠道观测:GET /api/v1/admin/channels/stats?window=5m|1h|6h|24h|7d 聚合请求量、
  成功率、P50/P95/max 延迟、成本与 top_errors;分位只取成功样本,canceled(客户端
  主动断开)不计入渠道错误率。渠道页桌面表格加「近N小时」列、移动端加同内容条,
  顶部可切窗口。
- 修一个让统计瞎掉的归因问题:全部渠道失败时 recordError 传的是 nil,
  usage_logs 里 channel_id=0,这类失败在渠道维度完全看不见。改为归到最后一个
  真正试过的渠道。
- 模型定价与元数据:Model 新增 cache_write_price(0 则回退输入价×1.25,可显式配
  Anthropic 1h 的 ×2)、context_length 与 supports_tools/vision/reasoning;
  finishUsage 走 CacheWritePriceOr()。/v1/models 叠加用户与密钥白名单过滤
  (原来会把限制外的模型名直接泄漏在列表里)并返回元数据,价格不外泄。
- /v1/models 的 Anthropic 分支原是死代码:协议只按路径判,而两种客户端共用这个路径。
  改为按 x-api-key / anthropic-version 头判,OpenAI SDK 从不发这两个头,互不影响。
- NewGateway 的十个位置参数换成 Options 结构体(三个 Duration 两个 int,传错编译期发现不了)。

新增 6 个用例:Retry-After 两种写法与夹顶、重试预算截止、空闲心跳出帧、
/v1/models 过滤与双形状、缓存写价两条路径、失败归因。
2026-10-17 04:09:39 +08:00
Sakurasan ad10bdc400 security: 补上认证面与输入边界的四处防护
链路功能已经跑通,缺的是边界。这几处默认值都是仓库里公开已知的,忘配就等于没有认证。

- 认证端点限速:/auth/login、/auth/register 此前完全没接限速器(Limiter 只挂在代理
  Auth 上),爆破与批量建号零成本。新增 AllowWindow 滑动窗口,登录按「单 IP」和
  「单 IP×账号」两个桶(默认 30/min、10/min),注册按 IP(5/min)且在 argon2 之前拦,
  避免光靠建号请求打满 CPU;超限返回 429 + Retry-After。
- 生产启动断言:env=production 时拒绝默认 OT_JWT_SECRET、缺失或短于 16 字节的
  OT_MASTER_KEY(缺失时 NewEncryptor 会用 sha256("") 派生,渠道密文可被公开还原)、
  默认管理员密码;一次列全所有问题,不再改一个试一个。development 不拦。
- 请求体上限:parseBody 原来全量 io.ReadAll 且全项目没有一处 MaxBytesReader,
  合法密钥可把任意大 body 整份压进内存再转发。/v1 组挂 BodyLimit(默认 32MB,
  OT_PROXY_MAX_BODY_MB),超限按客户端协议返回 413 request_too_large 并点名上限。
- CORS:不再无条件反射 Origin + Allow-Credentials: true——那唯一挡住跨站凭据读取的
  是 refresh cookie 的 SameSite=Lax,改成 None 就立刻变成窃取面。改为显式白名单,
  空白名单完全不发跨域头(同源部署与 vite proxy 本来就不需要),通配 * 不配凭据;
  允许头补上 X-Api-Key / Anthropic-Version / OpenAI-*,否则浏览器里跑官方 SDK 预检会挂。
- 配套 OT_SERVER_TRUSTED_PROXIES:不配置就不信 X-Forwarded-For(防伪造头绕过 IP 限速)。

顺手删掉 .env.example 末尾重复的 OT_PROXY_LOG_RAW 段。
2026-10-17 03:25:30 +08:00
Sakurasan d4c5263f1e feat: 运行期渠道冷却可见并可手动恢复
运行期冷却只存在网关进程内存里,DB 的 health_status 看不见它:渠道被踢到
30s 冷却后,管理后台仍显示 healthy,模型也照常出现在 /v1/models,但请求
返回 503 no_channel,管理员只能去翻日志。

- channel.Service 增 RuntimeState(连续失败次数 + 冷却剩余)与 ClearRuntime;
  Gateway 包装出 ChannelRuntime / RecoverChannel 供管理端调用。
- GET /api/v1/admin/channels 每项返回 runtime_fails 与 cooldown_seconds。
- 新增 POST /api/v1/admin/channels/:id/recover:清运行期冷却与失败计数,并把
  探测写的 health_status 置回 healthy;不改 enabled,上游若真挂着,下一次探测
  或再连续 3 次请求失败会重新把它踢掉。
- 渠道页健康列下方显示「冷却中 Ns」/「连续失败 n/3」,仅在冷却或探测不健康时
  出现「立即恢复」按钮(卡片与桌面表格两套布局都覆盖)。

仍未处理:冷却到期是裸恢复、没有 half-open 试探;计数是进程内的,多实例各自
为战,要共享得等 Redis 那一步。
2026-10-17 01:19:03 +08:00
Sakurasan e865f69a41 fix: 修掉模型调用链路上的四处失效逻辑
- 超时:NewGateway 里 gw_timeout*time.Second 把已是 Duration 的值二次换算,
  int64 溢出后无论 OT_PROXY_TIMEOUT 填多少都等于没有超时,渠道 timeout_ms
  也从没被读过。现在非流式用 context deadline 约束整段时长,流式只约束首包
  (Transport.ResponseHeaderTimeout),正文不限时长,长回答不会被砍。
- 断连:上游请求改用 NewRequestWithContext 绑定客户端 context,客户端断开即
  取消上游并记 canceled,不再空转生成继续付费;PATCH 已写 200 头后不再往
  响应里追加错误体。
- 白名单:APIKey.AllowedModels 以前只存不用,现在 Auth 注入 context 并参与
  判定(用户黑名单 > 密钥白名单 > 用户白名单 > 全局)。
- 选路:模型已有渠道绑定时只用绑定渠道,都不可用直接 503 no_channel,不再
  把请求发给不认识它的渠道;新增运行期健康(连续 3 次网络错误/超时/429/5xx
  进 30s 冷却,成功清零),与 60s 一次的 /models 探测互补。
- 密钥 API:PATCH 回显更新后的完整对象;限额填 0 统一视为不限制(原来
  token 配额存 0 会让密钥第一次请求就被 429 打死,而请求配额存 0 又等于
  不限制,两个字段语义相反);前端编辑框总是提交白名单,清空能真的取消。

测试:新增 proxy 链路集成测试(超时生效、流式不被截断、断连取消并记账、
密钥白名单 403、连续 429 后候选剔除)与 channel 用例(绑定模型不退全局、
冷却阈值与到期恢复、成功清零)。
2026-10-16 19:15:51 +08:00
Sakurasan d911881499 feat: add reset-password CLI command for any user 2026-09-07 02:33:11 +08:00
Sakurasan 2e3bb22f81 fix: use weighted random pick for load balancing instead of sequential iteration 2026-09-02 17:32:12 +08:00
Sakurasan 923ac79039 删除请求超时 2026-09-02 13:08:32 +08:00
Sakurasan 6d951a0d61 fix: sync schemaVersion to 2 matching production 2026-08-31 18:16:19 +08:00
Sakurasan 1f5f60e905 fix: move timeout migration outside schema version check 2026-08-31 18:01:33 +08:00
Sakurasan c6a4fcfbbc fix: migrate existing channels timeout_ms to 300000 2026-08-31 17:47:52 +08:00
Sakurasan 5cb428bba9 feat: increase default timeout to 5 minutes 2026-08-31 16:56:18 +08:00
Sakurasan 658a9cb580 fix: SQLite 迁移加版本锁,消除每次启动全量重建表
GORM sqlite AutoMigrate 对已有表判定不收敛(实测已迁移库再跑
仍触发 12 次表重建),SQLite 无在线 ALTER,每次重建走
CREATE __temp + INSERT SELECT + DROP 全表拷贝;usage_logs 含
raw_request/raw_response 大文本,一轮 40~80s,生产每次重启
重演导致启动数分钟。

- 新增 schemaVersion 常量,PRAGMA user_version 记录已迁移版本
- 版本达标跳过 AutoMigrate 秒开;旧库首次升级迁移一次写版本
- postgres 不受影响(支持 ALTER,保持原行为)
- 维护约定:改表结构须递增 schemaVersion
2026-08-29 12:46:52 +08:00
Sakurasan d8257df100 fix: messages 流式缓存场景 token 记账错乱
qwen/dashscope 等上游 messages 流式的 usage 语义:
- message_start.usage.input_tokens = 总输入
- message_delta.usage.input_tokens = 非缓存输入(缓存部分单列
  cache_read/cache_creation 字段),是最终计费口径

原 usageSink 字段级合并中 delta 的 input 覆盖 start 的 input,
总输入丢失(31790 → 8);缓存写也未参与计费。

- push:带 cache_* 字段的 usage 视为最终口径,整体替换 sink
- finishUsage:缓存写按 1.25× 输入价计费(Anthropic 5m 口径);
  落库 input_tokens 存总量(含缓存读/写)便于对账
- 估算兜底条件排除已有缓存计数的请求
- 回归测试:缓存写/缓存命中/chat 末块合并不回归
2026-08-28 18:11:24 +08:00
Sakurasan cd015d370c fix: handle zero channel_id in admin usage logs 2026-08-28 16:55:08 +08:00
Sakurasan 939a98d389 feat: show channel name in admin usage logs 2026-08-28 16:40:23 +08:00
Sakurasan 0b31cc209e fix: chat→responses 转换 input 始终输出数组
单条消息时原逻辑把 input 塌缩成单个对象,火山方舟等上游只接受
[]*InputItem 数组,直接 400(Mismatch type)。chat/messages 经
vole 渠道转换因此全挂。改为恒为数组,三种协议全矩阵实测恢复。
2026-08-27 09:57:48 +08:00
Sakurasan 305b3ed731 refactor: 删除无用的模型展示名(display_name)字段
该字段仅存取、从不展示:管理列表只渲染 name,/v1/models 与用户侧
均不返回它。与渠道绑定 upstream_model 的名字映射职责重叠且悬空。
- store.Model 结构体、admin CRUD、seed 数据移除 display_name
- 前端编辑弹窗输入框、表单状态、types.ts 同步移除
- 本地库已 ALTER TABLE DROP COLUMN;生产残留列无害
2026-08-27 09:57:30 +08:00
Sakurasan 79545aa48e fix: 兼容上游 SSE data: 后无空格格式,流式记账不再 0 消耗
火山方舟等上游流式返回 data:{...}(data: 后无空格),原解析只认
'data: '(带空格),导致 content 文本与 usage 全部漏解析:
- sseContentText 抽成 sseDataPayload,统一处理 data: 前缀
  (支持带/不带空格 + event:+data: 多行块)
- scanUsage 复用 sseDataPayload
- convert/stream.go parseLine 也支持 data:{ 无空格
- 新增回归测试覆盖无空格/带空格/多行块三种形态
2026-08-27 01:24:30 +08:00
Sakurasan d818cf3b4c refactor: 命令树移入 internal/cli,去掉 DefaultRun 注入
- cmd/server/cmd → internal/cli,与 internal/api、internal/proxy 平级
- serve() 移入 cli 包作为默认子命令,main.go 退化为纯入口
- 消除 cmd/cmd 冗余层级和 main → cmd 的回调注入
2026-08-26 18:14:08 +08:00
Sakurasan 98f296101e refactor: 改用 Cobra 命令行框架,reset-admin-password 带交互提示
- root.go 作为 Cobra 入口,DefaultRun 由 main 注入 serve()
- reset_admin.go 改为 Cobra 子命令,支持 --user/--password 参数
- 交互式:多管理员时选择目标、密码支持手动输入或随机生成(term.ReadPassword 不回显)
- 帮助输出自动生成,无需手写 Usage
- 删除旧的 flag 实现
2026-08-26 18:03:03 +08:00
Sakurasan 2983c89b42 feat: 新增 reset-admin-password 命令行重置管理员密码
- cmd/server 子命令分发:reset-admin-password 不启动服务
- -password 留空随机生成 16 位(去除易混淆字符),打印一次
- -user 指定目标管理员,仅匹配 role=admin
- 密码长度校验 8-72 与 API 一致,argon2id 参数取自配置
- Makefile 增加 make reset-admin-password 入口
2026-08-26 15:57:04 +08:00
Sakurasan a45a6e16bd fix: API Key 前缀调整为 sk-ot- 2026-08-26 15:57:04 +08:00
Sakurasan c78a473e59 feat: API Key 前缀改为 sk-ot
- apikey 包 prefix 常量 sk- → sk-ot(生成/校验/展示前缀统一驱动)
- 鉴权错误提示同步更新为 sk-ot 格式
- 测试断言改用常量,避免硬编码长度
- 前端 curl 示例与 README/PLANNING 文档同步
2026-08-25 00:34:50 +08:00
Sakurasan f7a5741b33 fix: 模型列表仅显示已启用渠道中的模型 2026-08-22 14:19:45 +08:00
Sakurasan daf6b8c66b fix: 获取模型列表时过滤已禁用渠道的绑定 2026-08-22 13:26:17 +08:00
Sakurasan 10f51cbdae 请求明细支持查看原始请求/响应:OT_PROXY_LOG_RAW 开关控制,仅管理员记录与可见,流式全量捕获
- 配置: ProxyConfig.LogRaw (OT_PROXY_LOG_RAW, 默认 false)
- 存储: usage_logs 新增 raw_request/raw_response 文本列 (AutoMigrate)
- 网关: NewGateway 接收 logRaw 参数
- handlers: 三个协议入口按 开关+管理员 条件记录原始请求体
- passthrough: 非流式 copyAndCapture 捕获响应, 流式 streamCopy 累积全部原始 SSE 行, finishUsage 统一写入
- admin API: AdminUsage 返回 raw_request/raw_response (仅管理员)
- 前端: 用量页新增查看入口, 弹窗 tab 切换请求/响应
- gitignore: 修正 server/web/ 忽略规则(尾随空格导致未生效)
2026-08-20 00:37:48 +08:00
Sakurasan 0939f98fb5 修复流式请求记账为 0 消耗:支持 SSE 多行块 usage 提取 + 无上游 usage 时 tiktoken 估算
- sseContentText/scanUsage 兼容 event:+data: 多行块(转换器 eventLine 产出)
- usageFromMap 新增 delta.usage 支持(Anthropic message_delta 标准格式)
- finishUsage: 流式成功但上游不返回 usage(如火山方舟)时按内容 tiktoken 估算,in/out 独立估算
- .gitignore 忽略 server/web 本地静态托管软链
2026-08-19 20:59:04 +08:00
Sakurasan 30ab9e842c claude code tools error 2026-08-19 03:46:39 +08:00
Sakurasan 3e7efb3c88 fix passkey
server/internal/passkey/passkey.go
 的 takeSession,过期校验只在显式设置了 Expires 时才生效(与 go-webauthn 库内部 !IsZero() 的检查一致)
2026-08-19 00:00:51 +08:00
SakurasanandClaude 967c54ae32 代理: 鉴权支持 Anthropic x-api-key 头
Claude Code / Anthropic SDK 用 x-api-key 头而不是 Authorization:
Bearer,缺失 Authorization 时回退读取 x-api-key,错误文案同步提示两种方式

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-18 00:51:53 +08:00
SakurasanandClaude eac6938baa 服务: SPA 缓存策略修复重建后首页黑屏
- index.html 设 Cache-Control: no-cache,避免浏览器启发式缓存保留引用已删除 chunk 的旧 HTML
- /assets/*(文件名含内容 hash,不可变)设 immutable 长缓存,减少回源

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-17 20:30:17 +08:00
SakurasanandClaude c84fea9ef0 记账: Recorder Close 幂等防重复关闭
- main 的 defer Shutdown 与显式 Close 双调用导致停机 panic close of closed channel
- 用 sync.Once 保证只关闭一次

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-16 18:55:15 +08:00
SakurasanandClaude 4137b6fe20 渠道: 远程候选按渠道过滤 + key 掩码 + 移除批量导入端点
- 远程模型候选只排除本渠道已允许的模型,同名模型可被多个渠道各自允许
- 渠道 key 掩码统一 xxxxxxx******Mq4Y(保留前 7 位与后 4 位)
- 移除已弃用的批量导入端点 POST /channels/:id/models/import

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-16 18:55:05 +08:00
SakurasanandClaude d0c879a2ed 模型定价: 悬空模型标记 + 一键清除 + 允许渠道只读
- 无渠道绑定的模型以「悬空」琥珀色徽标 + 卡片着色标记
- 新增「清除悬空」一键删除未绑定渠道的模型(DELETE /models/unused)
- 移除页面内「绑定渠道」弹窗与 chips 的解除按钮,渠道支持列表只在渠道抽屉管理
- 缺失提示带上渠道真实模型名(upstream_model)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-16 18:54:59 +08:00
SakurasanandClaude fb7db00817 密钥: 删除改为硬删(立即失效不可恢复)
- DELETE /api/v1/keys/:id 由软吊销(置 revoked)改为物理删除

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-16 09:04:18 +08:00
SakurasanandClaude 0ee92d78e7 代理: 流式中断按已生成部分计费
- 新增 tokenizer 包(tiktoken-go)按模型估算 token, 未知模型回退 cl100k_base
- passthrough 提取请求输入文本 + SSE 已生成内容, 客户端断开时记 canceled
- usage 计费范围扩展: canceled(流式中断)按已生成部分收费

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-16 09:04:18 +08:00
SakurasanandClaude 4b507d8064 转换器: 图片内容块跨协议互转保留(base64/url)
- 支持 OpenAI image_url ↔ Anthropic image.source(base64/url) 双向转换
- messages→chat / responses→chat 不再丢弃图片块, 保留图文原始顺序
- chat→messages 的 data URL 拆成 base64 source(media_type+data)
- chat→responses 的 image_url 转 input_image
- responses↔messages 经 chat 中转, 六方向全覆盖
- 新增多图/顺序回归测试

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-16 09:04:09 +08:00
SakurasanandClaude e96f194a59 转换器: 修复 Responses 流式 response.completed 缺 usage
- 上游 chat 流 finish_reason 块先于 usage 块, 原实现在 finish 时即发
  response.completed 导致 usage 为空; 改为等 usage 块到达(或 [DONE] 兜底)再发,
  completed 携带 input/output tokens

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-16 05:05:46 +08:00
SakurasanandClaude db83972b6f 渠道: 分协议 Base URL + 模型抽屉/远程拉取/操作图标
- 渠道支持分协议 base_url(chat/responses/messages 各一), 网关按协议选 base 直通
  (如智谱三种格式不同 base, 一个渠道即可), UpstreamURL 按 proto 拼接
- 渠道模型改为下方抽屉: 当前绑定列表(内联改上游/解除)、从接口拉取(remote 预览+勾选添加)、手动添加
- 新增 /channels/:id/models/remote 预览接口; 操作按钮加 Phosphor 图标
- 修复 formats jsonb 更新未序列化问题; 手机端渠道卡片化

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-16 04:31:28 +08:00
SakurasanandClaude 9324a782d5 Passkey: 账户设置绑定 + 免密登录(WebAuthn)
- 引入 go-webauthn, Passkey 表存凭据, challenge 会话内存存储(带过期)
- API: /webauthn/register|login begin/complete, /webauthn/passkeys 列表/删除
- 配置 OT_WEBAUTHN_RP_ID/RP_ORIGIN/RP_NAME;登录成功发 JWT+refresh cookie
- 前端 lib/webauthn(编解码+凭据序列化+安全上下文检测), 账户设置绑定区, 登录页免密按钮
- 需 HTTPS 或 localhost(安全上下文)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-16 02:00:08 +08:00
SakurasanandClaude 40c1ae43e9 模型定价: 一致性检查与提示(渠道可用模型应全部纳入定价)
- /admin/models 返回每模型 used/needs_pricing/denied + summary(total/unpriced/missing)
- missing 检测孤儿绑定(渠道提供但目录缺失)
- 模型定价页: 缺失警告、在用未定价提示、已禁止/未定价徽章

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-16 01:34:22 +08:00
SakurasanandClaude b365779188 模型限制: 系统配置全局开放/禁止 + 用户级限制 + 网关拦截
- User 新增 allowed_models/denied_models(用户级模型限制)
- 系统配置 model_allowlist/model_denylist 全局策略, 保存即时失效网关缓存
- 网关 checkModelAllowed: 用户级 > 全局(禁止命中→403, 白名单非空→仅白名单)
- 三个协议处理器均校验, 错误按客户端协议格式返回
- 配置页"模型限制"卡片(全局允许/禁止多选); 用户编辑支持允许/禁止模型

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-16 01:34:12 +08:00
SakurasanandClaude 298bf89c90 用户管理: 编辑用户信息(用户名/邮箱/重置密码/角色/状态)
- AdminPatchUser 扩展 username/email/password 更新, 含唯一性与格式校验
- 编辑弹窗补充用户名/邮箱/重置密码字段(留空不改密码)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-16 00:57:15 +08:00
SakurasanandClaude 79f3f4395c 渠道: 支持手工录入模型(无 /models) + 模型映射自定义名称
- AdminChannelAddModel 改为 upstream_model + custom_name:
  无 /models 接口的渠道直接填上游模型名, 可选自定义名作为客户端调用名,
  全局模型不存在时自动创建
- "支持的模型"弹窗: 手工添加 + 建议下拉(自建,紧贴输入框)/水平排列/交换位置
- 网关仍按绑定映射改写请求 model 字段

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-16 00:57:06 +08:00
SakurasanandClaude 4d4d09ba58 渠道: 模型名称映射(网关生效 + 渠道侧管理)
- 候选渠道携带 upstream_model, prepareUpstream 改写请求体 model 字段为上游名
- 新增渠道视角绑定 CRUD: GET/POST/PATCH/DELETE /admin/channels/:id/models
- 前端渠道页新增"模型映射": 列出绑定、内联改上游名、解除、添加
- rewriteModel 三种协议通用(model 均在顶层)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-16 00:09:01 +08:00
SakurasanandClaude 8d9940ad5a 渠道: Base URL 智能识别前缀/完整端点 + API 格式垂直堆叠
- upstreamURL 按内容智能拼接: 完整端点直接用 / 含 /v1 只拼资源路径 / 纯域名拼全路径
- resolveBaseURL 不再去 /v1, 交网关识别
- 渠道表 API 格式改垂直堆叠, 显示短标识 chat/completions / responses / messages
- 前端 Base URL 提示支持前缀或完整端点

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-16 00:08:52 +08:00
SakurasanandClaude a8e2cd214c 账户: 修改密码接口 + 账户设置页
- 后端 POST /auth/password: 校验旧密码(argon2id)后重哈希更新
- 前端 /console/settings 账户设置页: 个人资料卡 + 修改密码表单

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-15 20:55:45 +08:00
SakurasanandClaude eb57a09c5d API 密钥: 支持编辑与高级选项, 修复 jsonb 白名单更新
- 前端密钥页新增"编辑": 改名/每日配额/模型白名单/状态切换
- 新建密钥展开"高级选项": 每日 Token/请求上限、模型白名单
- 修复 PATCH 更新 allowed_models(jsonb)不走序列化导致失败,
  改为 JSON 字符串写入, 跨 SQLite/Postgres 可靠

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-15 20:55:26 +08:00
SakurasanandClaude d0bc28a4fe 渠道: 移除主页/favicon 特性, Base URL 可选+完整地址
- 回退渠道 homepage/favicon 字段与代理接口(未采用)
- Base URL 改为可选: 留空按供应商默认(openai/anthropic),
  兼容兼容型渠道必须填; 填完整地址(含 /v1)时归一化去尾
- 网关 upstreamURL 兜底去 /v1, 避免路径重复

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-15 20:55:17 +08:00