Unverified 提交 213def4a authored 作者: Will Chen's avatar Will Chen 提交者: GitHub

Use user info proxy (#1963)

<!-- CURSOR_SUMMARY --> > [!NOTE] > Switches budget fetch to `https://api.dyad.sh/v1/user/info` and validates/consumes `usedCredits`, `totalCredits`, and `budgetResetDate` directly via Zod. > > - **IPC/Pro handlers (`src/ipc/handlers/pro_handlers.ts`)**: > - **Endpoint**: Update user info URL to `https://api.dyad.sh/v1/user/info`. > - **Validation**: Add `zod` schema `UserInfoResponseSchema` to validate API response. > - **Data mapping**: Use `usedCredits`, `totalCredits`, `budgetResetDate`, and `userId` from response directly; remove conversion logic and old nested `user_info` parsing. > - **Redaction**: Compute `redactedUserId` from `userId` and return parsed `UserBudgetInfo`. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit da1f192c2cabb2154bd10b69555c27d62fbb6368. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> <!-- This is an auto-generated description by cubic. --> ## Summary by cubic Switched user budget fetch to the new user info proxy and added schema validation. Uses API-provided credits directly and removes the old conversion logic. - **Refactors** - Use https://api.dyad.sh/v1/user/info instead of llm-gateway. - Validate response with a Zod schema (usedCredits, totalCredits, budgetResetDate, userId). - Map fields directly to UserBudgetInfo and remove CONVERSION_RATIO. - Keep redacted user ID format (****1234). - **Dependencies** - Removed unused html-dom-parser, html-react-parser, and react-property from the lockfile. <sup>Written for commit da1f192c2cabb2154bd10b69555c27d62fbb6368. Summary will update automatically on new commits.</sup> <!-- End of auto-generated description by cubic. -->
上级 9d33f375
...@@ -12898,37 +12898,6 @@ ...@@ -12898,37 +12898,6 @@
"dev": true, "dev": true,
"license": "ISC" "license": "ISC"
}, },
"node_modules/html-dom-parser": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/html-dom-parser/-/html-dom-parser-5.1.1.tgz",
"integrity": "sha512-+o4Y4Z0CLuyemeccvGN4bAO20aauB2N9tFEAep5x4OW34kV4PTarBHm6RL02afYt2BMKcr0D2Agep8S3nJPIBg==",
"license": "MIT",
"dependencies": {
"domhandler": "5.0.3",
"htmlparser2": "10.0.0"
}
},
"node_modules/html-react-parser": {
"version": "5.2.6",
"resolved": "https://registry.npmjs.org/html-react-parser/-/html-react-parser-5.2.6.tgz",
"integrity": "sha512-qcpPWLaSvqXi+TndiHbCa+z8qt0tVzjMwFGFBAa41ggC+ZA5BHaMIeMJla9g3VSp4SmiZb9qyQbmbpHYpIfPOg==",
"license": "MIT",
"dependencies": {
"domhandler": "5.0.3",
"html-dom-parser": "5.1.1",
"react-property": "2.0.2",
"style-to-js": "1.1.17"
},
"peerDependencies": {
"@types/react": "0.14 || 15 || 16 || 17 || 18 || 19",
"react": "0.14 || 15 || 16 || 17 || 18 || 19"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/html-to-image": { "node_modules/html-to-image": {
"version": "1.11.13", "version": "1.11.13",
"resolved": "https://registry.npmjs.org/html-to-image/-/html-to-image-1.11.13.tgz", "resolved": "https://registry.npmjs.org/html-to-image/-/html-to-image-1.11.13.tgz",
...@@ -17952,12 +17921,6 @@ ...@@ -17952,12 +17921,6 @@
"react": ">=18" "react": ">=18"
} }
}, },
"node_modules/react-property": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/react-property/-/react-property-2.0.2.tgz",
"integrity": "sha512-+PbtI3VuDV0l6CleQMsx2gtK0JZbZKbpdu5ynr+lbsuvtmgbNcS3VM0tuY2QjFNOcWxvXeHjDpy42RO+4U2rug==",
"license": "MIT"
},
"node_modules/react-reconciler": { "node_modules/react-reconciler": {
"version": "0.33.0", "version": "0.33.0",
"resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.33.0.tgz", "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.33.0.tgz",
......
...@@ -4,12 +4,19 @@ import { createLoggedHandler } from "./safe_handle"; ...@@ -4,12 +4,19 @@ import { createLoggedHandler } from "./safe_handle";
import { readSettings } from "../../main/settings"; // Assuming settings are read this way import { readSettings } from "../../main/settings"; // Assuming settings are read this way
import { UserBudgetInfo, UserBudgetInfoSchema } from "../ipc_types"; import { UserBudgetInfo, UserBudgetInfoSchema } from "../ipc_types";
import { IS_TEST_BUILD } from "../utils/test_utils"; import { IS_TEST_BUILD } from "../utils/test_utils";
import { z } from "zod";
export const UserInfoResponseSchema = z.object({
usedCredits: z.number(),
totalCredits: z.number(),
budgetResetDate: z.string(), // ISO date string from API
userId: z.string(),
});
export type UserInfoResponse = z.infer<typeof UserInfoResponseSchema>;
const logger = log.scope("pro_handlers"); const logger = log.scope("pro_handlers");
const handle = createLoggedHandler(logger); const handle = createLoggedHandler(logger);
const CONVERSION_RATIO = (10 * 3) / 2;
export function registerProHandlers() { export function registerProHandlers() {
// This method should try to avoid throwing errors because this is auxiliary // This method should try to avoid throwing errors because this is auxiliary
// information and isn't critical to using the app // information and isn't critical to using the app
...@@ -36,7 +43,7 @@ export function registerProHandlers() { ...@@ -36,7 +43,7 @@ export function registerProHandlers() {
return null; return null;
} }
const url = "https://llm-gateway.dyad.sh/user/info"; const url = "https://api.dyad.sh/v1/user/info";
const headers = { const headers = {
"Content-Type": "application/json", "Content-Type": "application/json",
Authorization: `Bearer ${apiKey}`, Authorization: `Bearer ${apiKey}`,
...@@ -57,21 +64,28 @@ export function registerProHandlers() { ...@@ -57,21 +64,28 @@ export function registerProHandlers() {
return null; return null;
} }
const data = await response.json(); const rawData = await response.json();
const userInfoData = data["user_info"];
const userId = userInfoData["user_id"]; // Validate the API response structure
const data = UserInfoResponseSchema.parse(rawData);
// Turn user_abc1234 => "****1234" // Turn user_abc1234 => "****1234"
// Preserve the last 4 characters so we can correlate bug reports // Preserve the last 4 characters so we can correlate bug reports
// with the user. // with the user.
const redactedUserId = const redactedUserId =
userId.length > 8 ? "****" + userId.slice(-4) : "<redacted>"; data.userId.length > 8 ? "****" + data.userId.slice(-4) : "<redacted>";
logger.info("Successfully fetched user budget information."); logger.info("Successfully fetched user budget information.");
return UserBudgetInfoSchema.parse({
usedCredits: userInfoData["spend"] * CONVERSION_RATIO, // Transform to UserBudgetInfo format
totalCredits: userInfoData["max_budget"] * CONVERSION_RATIO, const userBudgetInfo = UserBudgetInfoSchema.parse({
budgetResetDate: new Date(userInfoData["budget_reset_at"]), usedCredits: data.usedCredits,
totalCredits: data.totalCredits,
budgetResetDate: new Date(data.budgetResetDate),
redactedUserId: redactedUserId, redactedUserId: redactedUserId,
}); });
return userBudgetInfo;
} catch (error: any) { } catch (error: any) {
logger.error(`Error fetching user budget: ${error.message}`, error); logger.error(`Error fetching user budget: ${error.message}`, error);
return null; return null;
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论