Commit Graph
2478 Commits
Author SHA1 Message Date
CaIon 385d2dfd10 feat(auth): add safe multi-RP ID passkey support
Preserve legacy credentials across primary RP ID changes and bind successful
assertions to their verified RP ID. Share domain selection across sign-in,
login verification and sensitive operations without replaying ceremonies.

Save domain settings atomically, preview known and unknown credential impact,
and require fresh confirmation before removing domains still in use. Recheck
trusted RP IDs when persisting credentials and record safe operation audits.

Use read-only configuration snapshots, accept trusted HTTPS intranet hosts,
check registration origins early, and preserve historical capitalization.
Update settings guidance and all supported translations.

Validation: SQLite 3.50.4, MySQL 8.0.46 and PostgreSQL 16.15, including fresh
and released-schema upgrade checks; Go regression and race checks; 79 frontend
tests; TypeScript, lint, frontend build and root Go build.

Security references: OWASP ASVS 5.0.0 V6.3.4 and V6.3.8, Authentication and
Session Management Cheat Sheets, and the WebAuthn RP ID/origin rules.
2026-09-11 23:15:29 +08:00
CaIon 74629e29f8 feat(plugins): enhance task streaming and model pricing 2026-09-11 21:44:56 +08:00
CaIon 251b76d863 feat(web): add administrator update reminders 2026-09-11 21:44:56 +08:00
CaIon ab489ab884 feat(channels): add plugin extensions to provider and model selection
Expose supported channel types in plugin options and let channel editors select extension models using the existing channel connection settings.

Add a gateway category, refine provider ordering and presentation, and prefer available plugins for legacy task providers during channel creation. Include the related translations and regression tests.

Validation in an isolated snapshot: 123 channel frontend tests, the plugin-options controller regression, TypeScript checks, affected-file lint, protected-header formatting, and the production frontend build passed.
2026-09-11 21:44:55 +08:00
CaIon d4c26bfb8d fix(channels): allow editing multi-key selection strategy
Preserve existing keys when updating random or polling selection, and omit the strategy for single-key channels. Validated with 46 configuration tests, type checks, lint, and formatting checks.
2026-09-11 21:44:55 +08:00
CaIon 505805a4c3 feat(web): unify channel setup and refine model and settings editors
- Share provider selection and tabbed configuration across channel creation and editing.
- Add provider descriptions, custom categories, inline model discovery, and model configuration tools.
- Improve batch model input, model mapping labels, parameter overrides, and required field indicators.
- Preserve existing Passkey domains and confirm domain changes.
- Include translations and regression coverage for the updated workflows.

Validation: 113 frontend tests, TypeScript checks, affected-file lint, production build, and protected-header formatting checks passed.
2026-09-11 21:44:55 +08:00
CaIon b3e279464c feat(web): review legacy pricing conversion before applying drafts
Add reviewed legacy-to-expression draft conversion with effective prices and provider billing adjustments. Standardize preview response handling and preserve explicit zero values, currency conversion, and save conflict detection. Reuse ConfirmDialog, CopyButton, and existing pricing preview components.
2026-09-11 21:44:54 +08:00
CaIon 39294418af feat(web): extend expression pricing editors and usage details
Support image cache and image count pricing across parsing, simulation, editing, summaries, and usage logs. Preserve task rule precedence, require confirmation before replacing expressions, and present ordered visual pricing rules. Include translations shared with the conversion flow.

Reuse ConfirmDialog and existing pricing controls. Expression, pricing, usage log, and editor regression suites pass; affected frontend files pass lint.
2026-09-11 21:44:54 +08:00
CaIon 3e1b8b1532 feat(plugins): display localized changelogs with English fallback 2026-09-11 21:44:53 +08:00
CaIon b6566f33d9 feat: clarify passkey website configuration 2026-09-11 21:44:52 +08:00
Seefs bdef117505 fix: restore add split button in advanced custom routes (#7289) 2026-09-09 19:20:56 +08:00
CaIon 064ed943e1 feat(billing): support fixed per-request expression pricing
Add tier(name, fixed(usd)) pricing leaves to conditional token expressions,
with static validation, token/request billing metadata, and safe settlement
when successful requests have missing or zero token usage. Preserve group
and request multipliers, separate tool charges, and explicit zero prices.
Reject fixed expressions on unsupported Realtime and task-usage entrances.

Expose per-token and per-call tier editing, preserve both drafts, and show
request units consistently in pricing summaries and consume logs. Retain
matched-branch metadata and update all seven frontend locales.

Validation passed:
- go test ./pkg/billingexpr ./relay/helper ./service ./setting/billing_setting ./relay -count=1
- Frontend pricing, model-pricing, editor and usage-log regression suites
- Typecheck, scoped lint, protected-header formatting, i18n validation and build
- go test ./service -run TestFixedPriceBillingDatabaseMatrix -count=1 -v
  with TEST_FIXED_MYSQL_DSN and TEST_FIXED_POSTGRES_DSN set to isolated local
  test databases: SQLite 3.50.4, MySQL 8.0.46, PostgreSQL 16.15.
  All 39 database cases passed, including reserve/settle/refund, zero price,
  missing usage, branch changes and insufficient-quota handling.

No production database schema or migration changes.
2026-09-09 15:05:37 +08:00
CaIon 7cf9b473f6 fix(web): keep model pricing content in one scroll area
Move the current pricing summary and save errors into the editor scroll
container through its optional scrollHeader slot. Keep the save controls
available without nesting a separate capped scroll area above the editor.

Validation: model-pricing and related frontend regression tests, typecheck,
scoped lint, and protected-header formatting passed.
2026-09-09 15:05:37 +08:00
CaIon 12be9975c0 fix(web): unify server error notifications
Preserve backend details and error causes, and report each failed operation
once across request, Query/Mutation, and page handling layers. Independent
operations with the same error message still receive separate notifications.

Migrate direct requests and query consumers, preserve inline and silent error
handling, and reject failed settings writes before success callbacks run.
Retain safe authentication messages, refresh behavior, and single-use proof
replay protection.

Validation: independently checked the staged snapshot with 117 frontend test
files (1105 tests), typecheck, scoped lint, and protected-header formatting.

Security references: OWASP ASVS 5.0.0, Authentication Cheat Sheet, and
Session Management Cheat Sheet:
https://owasp.org/www-project-application-security-verification-standard/
https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html
https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html
2026-09-09 15:05:36 +08:00
CaIon d52bdc0b40 feat(billing): add time-based pricing editor and expression previews 2026-09-09 15:04:15 +08:00
OrrinandCaIon c79b74b683 fix(frontend): deduplicate /api/status requests (#7189)
* fix(frontend): deduplicate /api/status requests

* refactor(frontend): 明确守卫状态读取语义并补充去重回归测试

将 getFreshModuleAccess 重命名为 getModuleAccessForGuard。原名容易被
理解为强制获取最新状态,而该函数实际读取共享 ['status']  缓存,命中
过期条目时会立即返回旧值并在后台刷新,不阻塞导航。

新增 web/src/lib/__tests__/status-query.test.tsx,覆盖共享查询的三项
契约:多个消费者并发读取只产生一次 /api/status 请求;缓存命中时不再
请求;过期条目立即返回旧值并在后台刷新。三项断言均已通过反向改动
验证可捕获回归。

同时为本次改动涉及的函数补充 docstring,并为测试 helper 显式声明返
回类型。

* fix(frontend): refresh status before guards and subscribe binding dialog

Fixed  #7157
---------

Co-authored-by: CaIon <[email protected]>
2026-09-09 13:58:46 +08:00
CaIon ea7cb0ba4e refactor(web): unify table cells and quota details 2026-09-08 20:56:03 +08:00
CaIon 524455fac3 feat(redemptions): add batch deletion and optional file exports
Add confirmed multi-select deletion through one batch API request and one
soft-delete statement. Record the affected count and requested IDs in a
separate batch audit event, and identify legacy events with missing counts.

After creation, offer an unchecked Save as a file option with TXT/Markdown
formats and optional name/quota fields. Keep Done as the default completion
action. Include translations for all seven frontend locales.

Validation:
- Redemption and audit frontend regression tests, typecheck, and scoped lint.
- go build ./...
- go test ./controller -run '^TestDeleteRedemptionBatch$' -count=1 -v
  with TEST_MYSQL_DSN, TEST_MYSQL_LOG_DSN, TEST_POSTGRES_DSN,
  and TEST_POSTGRES_LOG_DSN set to isolated primary and log databases.
- Real SQLite 3.50.4, MySQL 8.4.11, and PostgreSQL 16.15 passed, including
  deletion of 15 records, duplicate/missing IDs, zero-row retries, audit
  deduplication, invalid input, and exclusion of credentials from logs.
2026-09-08 20:47:01 +08:00
CaIon 950644c9d5 fix(keys): preserve spacing after desktop quota content
Bound desktop quota content to 180px so column width provides whitespace before the group column. Remove the quota-only padding override to use the same cell padding as other columns. Preserve the mobile quota layout.

Validation: layout regressions failed before the fix and all 26 API key listing tests passed afterward. TypeScript, scoped lint, formatting, production build, and git diff checks passed.
2026-09-08 19:59:27 +08:00
CaIon bd22e45a74 style(keys): widen the desktop quota column
Increase the quota column width and minimum width from 220px to 260px to give the side-by-side amounts more room.

Validation: all 26 API key listing tests, TypeScript, scoped lint, formatting, and git diff checks passed.
2026-09-08 19:56:41 +08:00
CaIon 551bb63edf fix(keys): show desktop quota amounts side by side
Place remaining quota on the left and used quota on the right above the progress bar, without visible labels in desktop rows. Preserve accessible descriptions and the existing mobile labels and stacked layout.

Validation: updated regressions failed before the change and all 26 API key listing tests passed afterward. TypeScript, scoped lint, formatting, production build, and git diff checks passed.
2026-09-08 19:55:28 +08:00
CaIon 8f72ecbbfd feat(usage-logs): add searchable group filter
Reuse the shared Combobox to suggest groups for the active log view while allowing historical group names to be entered manually. Exclude the automatic routing pseudo-group from suggestions and preserve masking, reset, URL navigation, and mobile drawer behavior.

Extend the existing Combobox with keyboard event forwarding and accessible labels, preserve the selected custom value on focus, and close suggestions on blur. Enter confirms a selection before submitting the filter.

Validation: 25 tests passed across the shared Combobox and log group, type, and mobile filter suites. TypeScript, scoped lint, formatting, and git diff checks passed.
2026-09-08 19:51:33 +08:00
CaIon 2bec370629 feat(web): refine API key and user quota displays
Show remaining and used API key quota with a progress bar, and use consistent mobile cards, group multiplier badges, and activity timestamps. Present available user balance with used quota underneath and translate the new labels.

Resolve full API keys only for explicit copy or chat actions. Reviewed OWASP Authentication and Session Management guidance and ASVS 5.0.0 V14.2.6 and V8.3.1; backend authorization is unchanged, and regression tests cover refused and denied key resolution. This frontend change does not assert application-wide ASVS compliance.

Validation: 55 related component tests passed; the latest mobile group and quota changes passed 35 focused tests. TypeScript, scoped lint, formatting, production build, and git diff checks passed. Responsive previews verified narrow screens and finite, unlimited, exhausted, and inactive quota states.
2026-09-08 19:49:55 +08:00
CaIon a5e41a8933 feat(usage-logs): refine mobile layout and keep quick actions visible 2026-09-08 17:31:19 +08:00
CaIon 71c1fd7caa feat(models): improve model listing, pricing and visibility filters
Include configured channel models without creating metadata, derive square
visibility from live routes and metadata policy, and filter before pagination.

Share pricing display with the model square, show expression tiers and task
unit prices, preserve zero rates, and expose full pricing and visibility
reasons from compact responsive rows. Complete all seven locale translations.

Validated frontend tests, typecheck, lint and production build, plus the
model database matrix on SQLite 3.50.4, MySQL 5.7.44 and PostgreSQL 9.6.24.
2026-09-08 17:03:10 +08:00
CaIon 0e0ba152bd feat(pricing): improve pricing editors and log display 2026-09-08 15:15:56 +08:00
CaIon eb76b136b8 feat(channels): improve plugin channel setup and icons 2026-09-08 15:15:55 +08:00
CaIon 9843309200 feat(web): improve plugin management and marketplace 2026-09-08 15:15:55 +08:00
CaIon 75e5332094 feat(pricing): support site currency in pricing editors 2026-09-08 12:12:46 +08:00
CaIon bee45b58a3 fix(web): switch the pricing card grid to three columns at xl
The model card grid and its loading skeleton only reached three columns at
2xl, leaving a two-column layout on common desktop widths. Use the xl
breakpoint for both and lock it with a test.
2026-09-07 22:40:11 +08:00
CaIon 5c7cca0155 fix(perf): return hourly success-rate series for model health bar
The perf summary API only returned the last three non-empty buckets as
bare success rates, so the 24-slot status strip on model cards never lit
more than three bars and could not show hours without traffic.

Replace recent_success_rates with recent_success_series: one timestamped
point per hour that had requests, aligned to hour start regardless of
the configured bucket size. The badge now anchors its 24 slots to the
client's current hour and places each point by timestamp, leaving hours
without data gray.
2026-09-07 16:24:31 +08:00
CaIon 387a409148 fix(web): keep drawer popups interactive and shim storage in tests
- Portal Combobox and Select popups into the vaul DrawerContent via a
  portal-container context so they stay inside the Radix modal layer
  instead of inheriting body pointer-events: none
- Provide an in-memory localStorage/sessionStorage in test-setup when the
  Node 25+ global accessor resolves to undefined and shadows jsdom
- Add regression tests for popups rendered inside and outside the drawer
2026-09-07 15:02:17 +08:00
CaIon 0c76e4dae7 feat(models): rework model/vendor management and pricing
Introduce a unified model management experience: catalog metadata
validation, vendor management, batch delete with channel/pricing
cleanup, model pricing snapshot editing with optimistic concurrency,
and an upstream ratio-sync flow with price cells. Move configuration
into dedicated pricing config/metadata-sync/vendor-management backend
services and add audit records for model/vendor/pricing mutations.

Rework the models page around vendors and model connections, add
model-pricing and vendor-management dialogs, and replace the shared
Select usages with the Combobox component across subscriptions,
plugins, OAuth presets, audit filters, and settings. Add the model
pricing panel and verify behavior with focused tests.
2026-09-06 22:49:02 +08:00
CaIon 6f23339906 feat(auth): unify login verification and secure account deletion
Treat TOTP and Passkey as alternative enrolled factors across login and
sensitive account operations. Gate every primary login transport before
issuing a session, require WebAuthn user verification, and consume login
challenges atomically with session creation.

Reuse the shared verification UI for login, 2FA management, and account
deletion. Require scoped, single-use deletion proof; recheck the session
inside the deletion transaction and revoke all sessions afterward.

Validation: controller/service/model/middleware tests; real SQLite 3.50.4,
MySQL 8.4.11, and PostgreSQL 16.15 security regressions; frontend tests,
TypeScript, targeted lint, formatting, and production build.

Deploy the frontend and all backend nodes together. No schema changes.
2026-09-06 22:49:02 +08:00
CaIon 521cebf585 fix(dashboard): simplify completed setup guide 2026-09-06 22:49:01 +08:00
CaIon 3f8a50cf88 feat(audit): complete token and quota operation records
Record successful and failed API token operations with safe target metadata. Capture quota adjustments in a transaction, synchronize committed cache differences, and correlate audit and top-up records.

Show operation targets, changes, quota balances, and failure details consistently across audit and usage logs, with translations for all seven locales.

Validated controller, middleware, and model tests; 78 frontend tests; typecheck and lint; real SQLite 3.50.4, MySQL 8.4.11, and PostgreSQL 16.15 with shared and separate log databases.
2026-09-06 22:49:01 +08:00
CaIon 0973dc2b8f feat(security): harden account binding and password changes
Require scoped, single-use verification for account bindings and password
operations. Bind OAuth authorization and email confirmations to the initiating
session; preserve the last usable login method and audit operation outcomes.

Apply Unicode-aware password length limits, Argon2id writes with bcrypt
compatibility, and long-password encryption.
Return has_password with the existing profile SELECT without extra queries.
Reuse the existing security dialogs and add all seven locale translations.

Validation:
- Go: go test ./common ./model ./service ./middleware ./controller ./router -count=1
- DB: SQLite 3.50.4, MySQL 8.4.11, PostgreSQL 16.15; separate main/log databases
- MySQL/PostgreSQL: TEST_SECURITY_DIALECT=<dialect> with TEST_<DIALECT>_DSN,
  go test ./controller -run '^(TestSecurityAccount|TestSecurityEnrollment|TestGenerateOAuthCode|TestOAuthBind|TestTelegramOAuth)' -count=1 -v
- Web: relevant Vitest suites, bun run typecheck, targeted oxlint/format,
  bun run i18n:sync, and bun run build

Roll out dual-format readers to every instance with
ACCOUNT_PASSWORD_HASH_ALGORITHM=bcrypt before enabling Argon2id writes
and the new UI. Rollbacks must retain Argon2id and v2 envelope readers.

Relevant controls: ASVS 5.0.0 6.2.1-6.2.3, 6.2.5-6.2.9, 6.3.7, 7.4.3, 7.5.1;
this change does not assert application-wide ASVS certification.
2026-09-06 22:49:00 +08:00
CaIon a8729b5c37 feat(security): require verification for access token management 2026-09-06 22:49:00 +08:00
CaIon 3e84ec0ab8 feat(auth): migrate Telegram to unified OAuth
Use authorization code flow with PKCE and verified ID tokens for Telegram login, binding, and security verification. Preserve existing bindings and require administrator OAuth configuration.

Keep the restricted WeChat first-enrollment session proof, fix missing-target authentication errors, and preserve callback requests after OAuth popups close.
2026-09-06 22:48:59 +08:00
CaIon 45c3fbe8ae fix(security): bind verification proofs to sessions and actions
Require single-use operation proofs for passkey enrollment, two-factor setup, and channel key access. Add password and OAuth verification flows, enforce session-bound enrollment, and redact OAuth callback secrets from logs.

Validation: affected Go packages pass; frontend typecheck, changed-file lint, and 111 tests pass. Security enrollment regressions pass on SQLite 3.50.4, MySQL 8.0.46, and PostgreSQL 16.15. Full frontend lint has pre-existing errors outside the changed files.
2026-09-06 22:48:59 +08:00
CaIon d8cb177440 feat(security): add access token management and audit logs
Move account security settings into a dedicated page and add token status, rotation, revocation, and access history.

Store audit events with role snapshots and JSON metadata, add audit.read authorization and an independent audit page, and upgrade the ClickHouse driver to v2.46.0.
2026-09-06 22:48:58 +08:00
CaIon 7c044d7c5c feat(relay): explicit @ model modifiers and canonical billing identity
Model-name post-processing is rebuilt around an explicit trailing
@key:value modifier syntax (thinking/effort/temperature/topp) that
overrides request fields, survives model mapping, and records
conversion diagnostics on the consume log.

- Legacy naked aliases (-thinking, -nothinking, -thinking-<budget>,
  effort tails) now parse only for positively matched families
  (gpt-*/o-series, claude-*, gemini-*, incl. vendor/ namespaces);
  names like qwen-max stay opaque. EffortTailModelIDs remains the
  escape hatch for real in-family IDs such as gpt-5.1-codex-max.
- Billing identity resolves once in ModelPriceHelper via a ladder:
  configured request name first (legacy wildcard entries intact), then
  canonical billing names rebuilt from parsed intent
  (base@effort:E@thinking:S, then base@thinking:S; order, duplicates,
  and budget values are irrelevant; temperature/topp never priced),
  then base. Routing and token limits fall back through
  RoutingMatchModelName; pricing lookups stay wildcard-only.
- Pass-through stays byte-identical: modifiers and aliases are neither
  parsed nor validated there and forward verbatim for the upstream
  (or a chained gateway) to interpret.
- Unknown modifier keys and invalid known-key values are rejected with
  400; models whose real names contain @tag:value are exempted via the
  thinking-suffix blacklist, which now supports re:-prefixed Go regex
  entries.
- Claude reasoning render coerces unsupported combinations (disable,
  adaptive, budgets) with warning diagnostics instead of erroring;
  native-protocol requests without host syntax pass through untouched.

BREAKING(openrouter): drop the host-invented "-thinking" model-name
alias (added in 4f6d16e36) that trimmed any *-thinking model on
OpenRouter channels and injected reasoning.enabled. It matched too
broadly and mangled real model IDs such as kimi-k2-thinking.
Migration: use some-model@thinking:on, or keep the old public name via
a channel model mapping {"some-model-thinking": "some-model@thinking:on"}.
Claude/Gemini family aliases (incl. anthropic/claude-*-thinking) keep
working via the family whitelist.
2026-09-04 20:45:52 +08:00
Orrinandcoderabbitai[bot] 219c9e0634 优化匿名冷启动与公开内容接口的重复回源请求 (#7166)
* fix: reduce public bootstrap requests and revalidate content

* fix(controller): use a weak ETag for revalidated public JSON

/api is gzip-compressed by middleware that runs after the handler returns,
and the validator is computed over the uncompressed body. The compressed and
identity forms of one payload therefore share a validator, which a strong ETag
must not do -- it asserts byte-for-byte equality across representations
(RFC 9110 8.8.1). Serve W/ instead.

Weak comparison ignores W/ on both operands, so etagMatches now strips it from
the served validator as well as from each candidate. Stripping only the
candidate would make a weak served validator match nothing and silently
disable every 304.

Vary: Accept-Encoding stays. Weakening the validator makes revalidation
correct, but it does not separate the two encodings in a shared cache.

* fix(test): align response cookie helper name

* fix(auth): revalidate stale route sessions

* Update web/src/features/about/api.ts

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* test: remove newly added PR tests

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-09-03 14:26:35 +08:00
CaIon 057f71c233 fix(logs): isolate privileged metadata 2026-09-03 11:28:57 +08:00
ruiyunzhaoandClaude b5b94bc685 fix(subscription): 无有效订阅时前端如实显示「仅用订阅」偏好 (#6222) (#7086)
Co-authored-by: Claude <[email protected]>
2026-08-30 20:45:24 +08:00
CaIon dc4732cfed feat(web): factory task plugins update only with the system
Marketplace install/upgrade on a factory-served plugin actually created
a permanent override shadowing every future built-in release. The card
now shows an informational "Updates with the system" badge instead of
the action, while keeping the built-in vs marketplace version line and
the upgradable state badge visible. Deliberate overrides are untouched:
upload and marketplace actions on overridden or third-party plugins
behave as before, and the plugins table now hints when an override
lags behind the shipped built-in version so operators know deleting it
restores the newer factory plugin.
2026-08-30 20:37:06 +08:00
CaIon 918427d8ab feat(auth): make password encryption opt-in #6743 2026-08-29 20:54:07 +08:00
憧憬Licoy 8454082f93 feat(chat): add AQBot preset (#7079) 2026-08-29 20:34:56 +08:00
CaIon b80d633cf5 feat(auth): encrypt password login transport
Closes #6743
2026-08-29 20:11:36 +08:00
Seefs 98d50d5383 fix(web): recheck setup status after page reload (#6968) 2026-08-29 19:24:14 +08:00