Skip to content

修复众多功能#7

Merged
zhaiiker merged 11 commits into
zhaiiker:mainfrom
zhaiker01:main
May 19, 2026
Merged

修复众多功能#7
zhaiiker merged 11 commits into
zhaiiker:mainfrom
zhaiker01:main

Conversation

@zhaiker01
Copy link
Copy Markdown

修复众多功能

kiro-agent and others added 11 commits May 19, 2026 05:17
This option was removed in TypeScript 5.5+. Use noImplicitAny: false
(already set) to suppress implicit any errors instead. Fixes:

  error TS5102: Option 'suppressImplicitAnyIndexErrors' has been removed.

Co-authored-by: zhaiker01 <285887163+zhaiker01@users.noreply.github.com>
- Fix import paths: '../../shared/types' -> '../shared/types' in oauth/types
  and providers/custom (file is at backend/shared/types, not /shared/types).
- shared/types: import LegacyToolPromptConfig and ToolCallingConfig as types
  before re-exporting so they're available in local scope (fixes TS2304).
- shared/types: add credentialFields to Provider interface so providers/
  custom.ts can construct Provider with credentialFields.
- store/types: add credentialFields to Provider so accountUtils can read it.
- oauth/types: add 'token' to TokenType union (used by deepseek/minimax
  ManualTokenConfig entries).
- oauth/types: relax MANUAL_TOKEN_CONFIGS to Partial<Record<...>> so it
  doesn't need entries for every ProviderType (mimo, zai are not configured).
- proxy/adapters/index: PerplexityStreamHandler lives in perplexity-stream,
  not perplexity.
- proxy/adapters/prompt: convert getPromptVariant() PromptVariant|null
  results to PromptVariant|undefined where needed (TransformResult.variant
  and toolsToPrompt() expect undefined, not null).
- proxy/adapters/prompt: guard part.text type narrowing before push.
- proxy/adapters/prompt/DefaultPromptAdapter: import TOOL_PROMPT_SIGNATURES
  from utils/tools (where it's defined) instead of constants/signatures.
- proxy/index: routes/index.ts only has a default export; use
  'export { default as routes }'.
- proxy/utils/accountUtils: default account name to '' when accountInfo is
  undefined (Account.name is required).
- proxy/utils/clientDetector: look up promptSectionMarkers via
  CLIENT_SIGNATURES[clientType] instead of from DetectionResult (which
  doesn't carry that field).
- proxy/utils/index: avoid duplicate re-exports between toolParser/index
  and the deprecated streamToolHandler (createBaseChunk, flushToolCallBuffer,
  shouldBlockOutput).
- store/store: export StoreManager class.
- store/index: import storeManager so initializeStore() can call it.
The createAccount helper was returning userId, usageCount, and metadata,
none of which exist on the Account interface (which uses requestCount,
not usageCount, and does not track userId or metadata). Drop them to
match the declared return type Omit<Account, 'id' | 'createdAt' | 'updatedAt'>.

Fixes:
  error TS2353: Object literal may only specify known properties, and
  'userId' does not exist in type 'Omit<Account, ...>'.
The Logs page imports { RequestLogList } from '@/components/logs', but the
component never existed in this repo, causing vite build to fail with:

  [vite:load-fallback] Could not load .../frontend/src/components/logs

Add a real implementation that:
- Lists request logs in a table (time, status, model, provider, account, latency)
- Supports search and status filtering (all/success/error)
- Auto-refreshes every 5 seconds and on demand
- Lets the user clear all logs (with confirm dialog)
- Opens a detail dialog with tabs (Info, User Input, Request, Response, Error)

Also:
- Export the RequestLogEntry type from @shared/types so api.ts and the new
  component can import it from a single location.
- Update .gitignore: the existing 'logs/' rule was unintentionally hiding
  the new components/logs source folder. The un-ignore line still pointed
  at the old Electron path src/renderer/src/components/logs/, so add
  !frontend/src/components/logs/ for the post-migration layout.
The handler for GET / was setting ctx.status = 404 and returning, with a
comment saying 'let the static fallback render index.html'. But koa-router
will not invoke later middleware if the handler returns without calling
next(), so the static asset fallback (which knows how to serve index.html)
never ran. Browsers hitting http://host:8080/ saw a bare 404.

Accept next as a parameter and await it instead of returning, so the
fallback in the trailing app.use() runs and serves index.html. Sub-routes
already worked because they never matched this handler.
The API key middleware allow-listed only '/', '/health', '/stats' and
'/v0/management/*'. Everything else (including the SPA's own
/assets/*.js, /assets/*.css, *.png, and any client-side route like
/dashboard) was forced through API key validation. As soon as an
operator turned on 'Enable API Key' from the management UI, the next
page load fetched /assets/index-XXXX.js, hit the gate without an
Authorization header, and got a 401. The browser then rendered a blank
page because the SPA bundle never executed.

Restrict the gate to the OpenAI-compatible proxy endpoints (/v1/*),
which is the only surface the key was ever meant to protect. The
management API still has its own auth; the SPA bundle is now served
without authentication, the same way every other SPA-host setup does.
The /v0/management/oauth/bookmarklet/ingest endpoint is the only
management route that legitimately accepts cross-origin requests —
operators run the bookmarklet from the provider's own site
(chatglm.cn, chat.deepseek.com, ...) and POST the captured token
back here. The endpoint authenticates with a one-shot ticket baked
into the bookmarklet, not with cookies, so wildcard CORS is safe.

The route handler in routes/management/oauth/bookmarklet.ts already
sets Access-Control-Allow-Origin: * for itself, but the global CORS
middleware in setupMiddleware() runs first and:

  1. Refuses to emit CORS headers for /v0/management/* unless the
     Origin is in CHAT2API_CORS_ORIGINS (which obviously won't list
     every AI provider's domain).
  2. Short-circuits OPTIONS with status 204 BEFORE the request
     reaches the router, so the route-level CORS setup never runs
     for preflight.

Net result: GLM bookmarklet (and every other provider's) sees the
preflight come back without Access-Control-Allow-Origin and aborts
with 'Failed to fetch'.

Special-case the ingest path in the global middleware so it gets
Allow-Origin:* on both the preflight and the POST. Other management
routes still go through the strict whitelist.
After the bookmarklet flow finishes, the backend GLM adapter returns
credentials as { refreshToken, accessToken } (camelCase), but the form
field is named 'refresh_token' (snake_case) and the old mapper was
looking for 'chatglm_refresh_token' (the raw cookie name) - so the
mapping silently failed and the UI complained 'fill required field:
Refresh Token' even though we already had the token in hand.

Replace the per-provider single-key map with a list of candidate keys.
For each provider we now try every name the OAuth path may emit (raw
cookie names, the bookmarklet's relabelled 'token' field, snake_case,
and the camelCase keys the adapters actually return) and pick the
first non-empty value. The candidate gets written into the form field
the credentialFields config expects.
The OAuth success handler was calling setCredentials() and
setActiveTab('manual') in the same synchronous batch. When
React renders the tab content for 'manual', the credential fields
component mounts fresh — but because the state update for credentials
may not have been committed yet when the TabsContent mounts, the
fields render as empty.

Fix: switch tab first (so the fields mount), then set credentials
in a setTimeout(0) microtask so React can flush the tab switch
before filling the inputs. Also add console.log so operators can
see exactly what the OAuth flow returned and what got mapped.
The old BookmarkletPanel required users to drag a link into their
bookmark bar, navigate to the provider page, and click the bookmark.
This was confusing and often failed due to CORS/mixed-content issues
when the backend runs on a different server.

Replace with ConsoleScriptPanel — a much simpler flow:
  1. Click 'Generate Script' (issues a ticket on the backend)
  2. Click 'Copy Script' (copies one-line JS to clipboard)
  3. Open provider page (e.g. chatglm.cn), F12 → Console → Paste → Enter
  4. Script reads token from cookie/localStorage and POSTs to backend
  5. Panel auto-polls and picks up the result

Changes:
- New: frontend/src/components/oauth/ConsoleScriptPanel.tsx
- Modified: TokenExtractionGuide now renders ConsoleScriptPanel instead
  of BookmarkletPanel as the primary OAuth capture method
- Added oauth.console i18n keys (bilingual zh-CN / en-US) with
  step-by-step instructions
- BookmarkletPanel.tsx is kept in the repo (backend ingest endpoint
  still works) but no longer referenced from the UI

The backend bookmarklet ingest endpoint is unchanged — the console
script uses the exact same ticket/ingest/poll API, just triggered from
a pasted script instead of a dragged bookmark.
Replace the ticket/fetch/polling ConsoleScriptPanel with a dead-simple
version that just shows a per-provider one-liner JS command:

  GLM:       document.cookie.match(/chatglm_refresh_token=([^;]+)/)?.[1]
  DeepSeek:  localStorage.getItem('userToken')
  Kimi:      document.cookie.match(/kimi-auth=([^;]+)/)?.[1]
  Qwen:      document.cookie.match(/tongyi_sso_ticket=([^;]+)/)?.[1]
  etc.

User flow is now:
  1. Click 'Copy Command' in Chat2API
  2. Go to provider page → F12 → Console → Paste → Enter
  3. Right-click the returned value → 'Copy string'
  4. Paste into the token field in Chat2API → Add Account

No bookmarklets, no tickets, no auto-POST, no CORS issues.
The old BookmarkletPanel and backend ingest endpoint remain in the
codebase (they still work) but are no longer referenced from the UI.
@zhaiiker zhaiiker merged commit 04c40b4 into zhaiiker:main May 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants