fix(storage): 修复腾讯云 COS 连接测试异常 (#362)
This commit is contained in:
1 parent
3d0adf1e9c
commit
875f45d70f
4 files changed
+68
-28
No files matched your search
+1
-1
@@ -7,7 +7,7 @@
|
|||||||
"build:bridge": "bun ../canvas-agent/scripts/build-comfy-bridge.ts",
|
"build:bridge": "bun ../canvas-agent/scripts/build-comfy-bridge.ts",
|
||||||
"dev": "vite --host 0.0.0.0 --port 3000",
|
"dev": "vite --host 0.0.0.0 --port 3000",
|
||||||
"build": "bun run build:bridge && tsc --noEmit && vite build",
|
"build": "bun run build:bridge && tsc --noEmit && vite build",
|
||||||
"pretest": "bun test test/admin-ui-regressions.test.ts test/password-reset.test.ts",
|
"pretest": "bun test test/admin-ui-regressions.test.ts test/password-reset.test.ts test/oss-settings.test.ts",
|
||||||
"typecheck": "tsc --noEmit",
|
"typecheck": "tsc --noEmit",
|
||||||
"test:canvas": "bun test test/canvas-media-performance.test.ts test/canvas-node-title-interaction.test.ts",
|
"test:canvas": "bun test test/canvas-media-performance.test.ts test/canvas-node-title-interaction.test.ts",
|
||||||
"test": "bun test test/api-request.test.ts test/video-api-contracts.test.ts test/image-api-contracts.test.ts test/channel-model-catalog.test.ts test/model-protocols.test.ts test/model-capabilities.test.ts test/agent-prompt-cache.test.ts test/canvas-agent-context.test.ts test/canvas-batch-connection.test.ts test/canvas-character-reference.test.ts test/canvas-document.test.ts test/canvas-drawing-engine.test.ts test/canvas-emotion-edit-plan.test.ts test/canvas-extension-nodes.test.ts test/canvas-folder.test.ts test/canvas-generation-layout.test.ts test/canvas-generation-submission.test.ts test/canvas-image-batch-retry.test.ts test/canvas-keyboard.test.ts test/canvas-node-generation-mentions.test.ts test/canvas-node-search.test.ts test/canvas-leafer-viewport.test.ts test/canvas-media-download.test.ts test/canvas-local-runtime.test.ts test/canvas-model-policy.test.ts test/canvas-resource-mention-editor.test.ts test/canvas-resource-references.test.ts test/canvas-storyboard-assets.test.ts test/canvas-storyboard-references.test.ts test/canvas-node-config-reset.test.ts test/canvas-node-registry.test.ts test/canvas-video-frame.test.ts test/canvas-video-regeneration.test.ts test/canvas-video-timeline-segments.test.ts test/create-canvas-handoff.test.ts test/create-library-button.test.ts test/creation-references.test.ts test/director-transform-semantics.test.ts test/director-bone-precedence.test.ts test/director-gesture-transaction.test.ts test/director-transaction-lifecycle.test.ts test/director-session-authority.test.ts test/director-placement.test.ts test/director-resources.test.ts test/director-recovery.test.ts test/director-node-preview.test.ts test/generation-asset-repository.node.test.ts test/generation-storage-consistency.test.ts test/generation-task-durability.node.test.ts test/generation-task-local.test.ts test/generation-task-materializer.test.ts test/task-media-fallback.test.ts test/user-scoped-generation-persistence.test.ts test/local-channel-permission.test.ts test/local-channel-runtime-projection.node.test.mjs test/local-channel-task-center.node.test.mjs test/local-channel-ui.node.test.mjs test/local-cli-settings.test.ts test/local-dreamina-cli.test.ts test/local-dreamina-generation.test.ts test/local-dreamina-model-catalog.test.ts test/local-runtime-bootstrap.test.ts test/local-runtime-connection.test.ts test/local-runtime.test.ts test/client-id.test.ts test/grok-image-prompt-limit.test.ts test/image-resolution-tiers.test.ts test/gemini-image.test.ts test/libtv-import.test.ts test/timeline-to-ffmpeg.test.ts test/canvas-video-segment-args.test.ts test/tapnow-import.test.ts test/prompt-optimizer-drawer.test.ts test/director-save-coordinator.test.ts test/director-save-wiring.test.ts test/director-diagnostics.test.ts test/director-repro-runtime.test.ts test/director-diagnostics-recorder.test.ts test/director-keyframe-editing.test.ts test/director-shortcuts.test.ts test/director-keyframe-delete-wiring.test.ts test/director-templates.test.ts test/director-modes.test.ts test/director-template-mode-wiring.test.ts test/director-view-modes.test.ts && bun test test/local-dreamina-cross-runtime.test.ts",
|
"test": "bun test test/api-request.test.ts test/video-api-contracts.test.ts test/image-api-contracts.test.ts test/channel-model-catalog.test.ts test/model-protocols.test.ts test/model-capabilities.test.ts test/agent-prompt-cache.test.ts test/canvas-agent-context.test.ts test/canvas-batch-connection.test.ts test/canvas-character-reference.test.ts test/canvas-document.test.ts test/canvas-drawing-engine.test.ts test/canvas-emotion-edit-plan.test.ts test/canvas-extension-nodes.test.ts test/canvas-folder.test.ts test/canvas-generation-layout.test.ts test/canvas-generation-submission.test.ts test/canvas-image-batch-retry.test.ts test/canvas-keyboard.test.ts test/canvas-node-generation-mentions.test.ts test/canvas-node-search.test.ts test/canvas-leafer-viewport.test.ts test/canvas-media-download.test.ts test/canvas-local-runtime.test.ts test/canvas-model-policy.test.ts test/canvas-resource-mention-editor.test.ts test/canvas-resource-references.test.ts test/canvas-storyboard-assets.test.ts test/canvas-storyboard-references.test.ts test/canvas-node-config-reset.test.ts test/canvas-node-registry.test.ts test/canvas-video-frame.test.ts test/canvas-video-regeneration.test.ts test/canvas-video-timeline-segments.test.ts test/create-canvas-handoff.test.ts test/create-library-button.test.ts test/creation-references.test.ts test/director-transform-semantics.test.ts test/director-bone-precedence.test.ts test/director-gesture-transaction.test.ts test/director-transaction-lifecycle.test.ts test/director-session-authority.test.ts test/director-placement.test.ts test/director-resources.test.ts test/director-recovery.test.ts test/director-node-preview.test.ts test/generation-asset-repository.node.test.ts test/generation-storage-consistency.test.ts test/generation-task-durability.node.test.ts test/generation-task-local.test.ts test/generation-task-materializer.test.ts test/task-media-fallback.test.ts test/user-scoped-generation-persistence.test.ts test/local-channel-permission.test.ts test/local-channel-runtime-projection.node.test.mjs test/local-channel-task-center.node.test.mjs test/local-channel-ui.node.test.mjs test/local-cli-settings.test.ts test/local-dreamina-cli.test.ts test/local-dreamina-generation.test.ts test/local-dreamina-model-catalog.test.ts test/local-runtime-bootstrap.test.ts test/local-runtime-connection.test.ts test/local-runtime.test.ts test/client-id.test.ts test/grok-image-prompt-limit.test.ts test/image-resolution-tiers.test.ts test/gemini-image.test.ts test/libtv-import.test.ts test/timeline-to-ffmpeg.test.ts test/canvas-video-segment-args.test.ts test/tapnow-import.test.ts test/prompt-optimizer-drawer.test.ts test/director-save-coordinator.test.ts test/director-save-wiring.test.ts test/director-diagnostics.test.ts test/director-repro-runtime.test.ts test/director-diagnostics-recorder.test.ts test/director-keyframe-editing.test.ts test/director-shortcuts.test.ts test/director-keyframe-delete-wiring.test.ts test/director-templates.test.ts test/director-modes.test.ts test/director-template-mode-wiring.test.ts test/director-view-modes.test.ts && bun test test/local-dreamina-cross-runtime.test.ts",
|
||||||
|
|||||||
+27
-13
@@ -18,6 +18,8 @@ export type OSSConnectionTestInput = {
|
|||||||
pathStyle?: boolean;
|
pathStyle?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type OSSConnectionTestDraft = Omit<Partial<OSSConnectionTestInput>, "provider"> & Pick<OSSConnectionTestInput, "provider">;
|
||||||
|
|
||||||
export type OSSConnectionTestResult = {
|
export type OSSConnectionTestResult = {
|
||||||
ok: boolean;
|
ok: boolean;
|
||||||
message?: string;
|
message?: string;
|
||||||
@@ -61,19 +63,7 @@ const S3_PRESET_HINTS: Record<S3Preset, { region: string; endpoint: string; help
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const CONNECTION_FIELDS = new Set([
|
const CONNECTION_FIELDS = new Set(["provider", "s3Preset", "region", "endpoint", "cdnBaseUrl", "bucket", "accessKeyId", "accessKeySecret", "sessionToken", "pathPrefix", "pathStyle"]);
|
||||||
"provider",
|
|
||||||
"s3Preset",
|
|
||||||
"region",
|
|
||||||
"endpoint",
|
|
||||||
"cdnBaseUrl",
|
|
||||||
"bucket",
|
|
||||||
"accessKeyId",
|
|
||||||
"accessKeySecret",
|
|
||||||
"sessionToken",
|
|
||||||
"pathPrefix",
|
|
||||||
"pathStyle",
|
|
||||||
]);
|
|
||||||
|
|
||||||
export function getS3PresetHints(preset?: S3Preset) {
|
export function getS3PresetHints(preset?: S3Preset) {
|
||||||
return S3_PRESET_HINTS[preset || "custom"];
|
return S3_PRESET_HINTS[preset || "custom"];
|
||||||
@@ -82,3 +72,27 @@ export function getS3PresetHints(preset?: S3Preset) {
|
|||||||
export function changesRequireOSSRetest(changedValues: Record<string, unknown>) {
|
export function changesRequireOSSRetest(changedValues: Record<string, unknown>) {
|
||||||
return Object.keys(changedValues).some((key) => CONNECTION_FIELDS.has(key));
|
return Object.keys(changedValues).some((key) => CONNECTION_FIELDS.has(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function normalizeOSSConnectionTestInput(input: OSSConnectionTestDraft): OSSConnectionTestInput {
|
||||||
|
return {
|
||||||
|
provider: input.provider,
|
||||||
|
s3Preset: input.s3Preset || "custom",
|
||||||
|
region: trimConnectionValue(input.region),
|
||||||
|
endpoint: trimConnectionURL(input.endpoint),
|
||||||
|
cdnBaseUrl: trimConnectionURL(input.cdnBaseUrl),
|
||||||
|
bucket: trimConnectionValue(input.bucket),
|
||||||
|
accessKeyId: trimConnectionValue(input.accessKeyId),
|
||||||
|
accessKeySecret: trimConnectionValue(input.accessKeySecret),
|
||||||
|
sessionToken: trimConnectionValue(input.sessionToken),
|
||||||
|
pathPrefix: (trimConnectionValue(input.pathPrefix) || DEFAULT_OSS_PATH_PREFIX).replace(/^\/+|\/+$/g, ""),
|
||||||
|
pathStyle: input.pathStyle === true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function trimConnectionValue(value?: string) {
|
||||||
|
return (value || "").trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
function trimConnectionURL(value?: string) {
|
||||||
|
return trimConnectionValue(value).replace(/\/+$/, "");
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@ import { AlertTriangle, BadgeCheck, Check, Cloud, Database, Globe2, HardDrive, K
|
|||||||
import { useCallback, useEffect, useRef, useState, type ReactNode } from "react";
|
import { useCallback, useEffect, useRef, useState, type ReactNode } from "react";
|
||||||
import { useBlocker } from "react-router";
|
import { useBlocker } from "react-router";
|
||||||
|
|
||||||
import { changesRequireOSSRetest, DEFAULT_OSS_PATH_PREFIX, getS3PresetHints, S3_PRESET_OPTIONS, type OSSConnectionTestResult, type S3Preset } from "@/lib/oss-settings";
|
import { changesRequireOSSRetest, DEFAULT_OSS_PATH_PREFIX, getS3PresetHints, normalizeOSSConnectionTestInput, S3_PRESET_OPTIONS, type OSSConnectionTestResult, type S3Preset } from "@/lib/oss-settings";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { getAdminOSSSetting, testAdminOSSConnection, updateAdminOSSSetting, type AdminOSSSetting } from "@/services/api/auth";
|
import { getAdminOSSSetting, testAdminOSSConnection, updateAdminOSSSetting, type AdminOSSSetting } from "@/services/api/auth";
|
||||||
import { AdminPageFrame } from "../components/admin-shell";
|
import { AdminPageFrame } from "../components/admin-shell";
|
||||||
@@ -770,20 +770,20 @@ function trimTrailingSlash(value: string) {
|
|||||||
return value.trim().replace(/\/+$/, "");
|
return value.trim().replace(/\/+$/, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
function connectionInput(values: OSSFormValues) {
|
function connectionInput(values: Partial<OSSFormValues>) {
|
||||||
return {
|
return normalizeOSSConnectionTestInput({
|
||||||
provider: values.mode === "local" ? ("aliyun" as const) : values.mode,
|
provider: !values.mode || values.mode === "local" ? ("aliyun" as const) : values.mode,
|
||||||
s3Preset: values.s3Preset,
|
s3Preset: values.s3Preset,
|
||||||
region: values.region.trim(),
|
region: values.region,
|
||||||
endpoint: trimTrailingSlash(values.endpoint),
|
endpoint: values.endpoint,
|
||||||
cdnBaseUrl: trimTrailingSlash(values.cdnBaseUrl),
|
cdnBaseUrl: values.cdnBaseUrl,
|
||||||
bucket: values.bucket.trim(),
|
bucket: values.bucket,
|
||||||
accessKeyId: values.accessKeyId.trim(),
|
accessKeyId: values.accessKeyId,
|
||||||
accessKeySecret: values.accessKeySecret.trim(),
|
accessKeySecret: values.accessKeySecret,
|
||||||
sessionToken: values.sessionToken.trim(),
|
sessionToken: values.sessionToken,
|
||||||
pathPrefix: (values.pathPrefix.trim() || DEFAULT_OSS_PATH_PREFIX).replace(/^\/+|\/+$/g, ""),
|
pathPrefix: values.pathPrefix,
|
||||||
pathStyle: values.pathStyle === true,
|
pathStyle: values.pathStyle === true,
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function ConnectionTestStatus({ result, stale }: { result: OSSConnectionTestResult | null; stale: boolean }) {
|
function ConnectionTestStatus({ result, stale }: { result: OSSConnectionTestResult | null; stale: boolean }) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { describe, expect, test } from "bun:test";
|
import { describe, expect, test } from "bun:test";
|
||||||
|
|
||||||
import { changesRequireOSSRetest, DEFAULT_OSS_PATH_PREFIX, getS3PresetHints } from "../src/lib/oss-settings";
|
import { changesRequireOSSRetest, DEFAULT_OSS_PATH_PREFIX, getS3PresetHints, normalizeOSSConnectionTestInput } from "../src/lib/oss-settings";
|
||||||
|
|
||||||
describe("OSS settings helpers", () => {
|
describe("OSS settings helpers", () => {
|
||||||
test("provides editable S3 endpoint hints for known presets", () => {
|
test("provides editable S3 endpoint hints for known presets", () => {
|
||||||
@@ -17,4 +17,30 @@ describe("OSS settings helpers", () => {
|
|||||||
test("uses the product path prefix by default", () => {
|
test("uses the product path prefix by default", () => {
|
||||||
expect(DEFAULT_OSS_PATH_PREFIX).toBe("open-ai-canvas");
|
expect(DEFAULT_OSS_PATH_PREFIX).toBe("open-ai-canvas");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("normalizes a Tencent COS test draft when S3-only fields are not mounted", () => {
|
||||||
|
const input = normalizeOSSConnectionTestInput({
|
||||||
|
provider: "tencent",
|
||||||
|
region: " ap-guangzhou ",
|
||||||
|
endpoint: " https://cos.ap-guangzhou.myqcloud.com/ ",
|
||||||
|
bucket: " example-1250000000 ",
|
||||||
|
accessKeyId: " secret-id ",
|
||||||
|
accessKeySecret: " secret-key ",
|
||||||
|
pathPrefix: " /canvas/ ",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(input).toMatchObject({
|
||||||
|
provider: "tencent",
|
||||||
|
region: "ap-guangzhou",
|
||||||
|
endpoint: "https://cos.ap-guangzhou.myqcloud.com",
|
||||||
|
cdnBaseUrl: "",
|
||||||
|
bucket: "example-1250000000",
|
||||||
|
accessKeyId: "secret-id",
|
||||||
|
accessKeySecret: "secret-key",
|
||||||
|
sessionToken: "",
|
||||||
|
pathPrefix: "canvas",
|
||||||
|
s3Preset: "custom",
|
||||||
|
pathStyle: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
Reference in new issue
Block a user