增加前端运行时后端地址配置
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script src="/model-library-config.js"></script>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
9
web/public/model-library-config.js
Normal file
9
web/public/model-library-config.js
Normal file
@@ -0,0 +1,9 @@
|
||||
window.__DMT_MODEL_LIBRARY_CONFIG__ = {
|
||||
// 后端服务根地址。为空时使用当前页面域名,例如 /api/models。
|
||||
// 示例:"https://model-api.example.com"
|
||||
API_BASE_URL: "",
|
||||
|
||||
// 模型文件和缩略图服务根地址。为空时优先复用 API_BASE_URL。
|
||||
// 示例:"https://model-api.example.com"
|
||||
STORAGE_BASE_URL: ""
|
||||
};
|
||||
52
web/src/config/runtime.ts
Normal file
52
web/src/config/runtime.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
type RuntimeConfig = {
|
||||
API_BASE_URL?: string;
|
||||
STORAGE_BASE_URL?: string;
|
||||
};
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
__DMT_MODEL_LIBRARY_CONFIG__?: RuntimeConfig;
|
||||
}
|
||||
}
|
||||
|
||||
function runtimeConfig() {
|
||||
return window.__DMT_MODEL_LIBRARY_CONFIG__ ?? {};
|
||||
}
|
||||
|
||||
function cleanBaseUrl(value?: string) {
|
||||
return value?.trim().replace(/\/+$/g, "") ?? "";
|
||||
}
|
||||
|
||||
function isAbsoluteUrl(url: string) {
|
||||
return /^https?:\/\//i.test(url);
|
||||
}
|
||||
|
||||
function joinUrl(baseUrl: string, path: string) {
|
||||
if (!baseUrl || isAbsoluteUrl(path)) return path;
|
||||
return `${baseUrl}${path.startsWith("/") ? "" : "/"}${path}`;
|
||||
}
|
||||
|
||||
function joinApiUrl(baseUrl: string, path: string) {
|
||||
if (!baseUrl || isAbsoluteUrl(path)) return path;
|
||||
|
||||
try {
|
||||
const base = new URL(baseUrl);
|
||||
if (base.pathname.replace(/\/+$/g, "") === "/api" && path.startsWith("/api/")) {
|
||||
return joinUrl(baseUrl, path.slice(4));
|
||||
}
|
||||
} catch {
|
||||
// 非完整 URL 时按普通路径拼接。
|
||||
}
|
||||
|
||||
return joinUrl(baseUrl, path);
|
||||
}
|
||||
|
||||
export function resolveApiUrl(path: string) {
|
||||
return joinApiUrl(cleanBaseUrl(runtimeConfig().API_BASE_URL), path);
|
||||
}
|
||||
|
||||
export function resolveStorageUrl(path: string) {
|
||||
const storageBaseUrl = cleanBaseUrl(runtimeConfig().STORAGE_BASE_URL);
|
||||
const apiBaseUrl = cleanBaseUrl(runtimeConfig().API_BASE_URL);
|
||||
return joinUrl(storageBaseUrl || apiBaseUrl, path);
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import type { DictionaryResponse, ModelItem, ModelListResponse } from "../../../
|
||||
import { confirmDialog, formDialog, notify, notifyError } from "../../../ui/dialogs";
|
||||
import { downloadFileFromUrl } from "../../../utils/fileDownload";
|
||||
import { escapeHtml, modelNameFromFile } from "../../../utils/format";
|
||||
import { resolveStorageUrl } from "../../../config/runtime";
|
||||
import { appState } from "../appState";
|
||||
|
||||
type UploadFormState = {
|
||||
@@ -287,7 +288,7 @@ function renderModelCard(item: ModelItem) {
|
||||
const canWrite = item.permissions.write;
|
||||
const prop = item.properties ?? {};
|
||||
const thumb = item.thumbnail_url
|
||||
? `<img src="${item.thumbnail_url}" alt="" />`
|
||||
? `<img src="${resolveStorageUrl(item.thumbnail_url)}" alt="" />`
|
||||
: `<div class="thumb-placeholder">暂无预览图</div>`;
|
||||
return `
|
||||
<article class="model-card" data-model-id="${item.id}">
|
||||
|
||||
@@ -9,6 +9,7 @@ import { downloadFileFromUrl } from "../../../utils/fileDownload";
|
||||
import { escapeHtml, formatBytes } from "../../../utils/format";
|
||||
import { notify, notifyError } from "../../../ui/dialogs";
|
||||
import { api } from "../../../services/api";
|
||||
import { resolveStorageUrl } from "../../../config/runtime";
|
||||
|
||||
type PreviewRuntime = {
|
||||
renderer: WebGLRenderer;
|
||||
@@ -146,7 +147,7 @@ export function openModelPreview(
|
||||
) {
|
||||
const context = beginPreviewContext();
|
||||
const canWrite = model.permissions.write;
|
||||
const url = model.file_url;
|
||||
const url = resolveStorageUrl(model.file_url);
|
||||
const operations = parseModelOperations(model.operation_tree);
|
||||
const popup = w2popup.open({
|
||||
title: `模型预览 - ${escapeHtml(model.name)}【${formatBytes(model.file_size)}】`,
|
||||
@@ -300,7 +301,7 @@ function buildParentImportScenePayload(payload: PreviewImportScenePayload): Pare
|
||||
id: payload.model.id,
|
||||
name: payload.model.name,
|
||||
original_filename: payload.model.original_filename,
|
||||
file_url: payload.model.file_url
|
||||
file_url: resolveStorageUrl(payload.model.file_url)
|
||||
},
|
||||
modelUrl: payload.modelUrl,
|
||||
basePointEnabled: payload.basePointEnabled,
|
||||
@@ -739,7 +740,7 @@ function buildImportScenePayload(options: {
|
||||
}): PreviewImportScenePayload {
|
||||
return {
|
||||
model: options.model,
|
||||
modelUrl: options.model.file_url,
|
||||
modelUrl: resolveStorageUrl(options.model.file_url),
|
||||
basePointEnabled: options.basePointEnabled,
|
||||
basePoint: options.basePoint,
|
||||
rawOperationTree: options.model.operation_tree,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { getToken } from "./authState";
|
||||
import { resolveApiUrl } from "../config/runtime";
|
||||
|
||||
function authHeaders(): Record<string, string> {
|
||||
const token = getToken();
|
||||
@@ -7,7 +8,7 @@ function authHeaders(): Record<string, string> {
|
||||
|
||||
export async function api<T>(url: string, options: RequestInit = {}): Promise<T> {
|
||||
const hasBody = options.body !== undefined && options.body !== null;
|
||||
const response = await fetch(url, {
|
||||
const response = await fetch(resolveApiUrl(url), {
|
||||
...options,
|
||||
headers: {
|
||||
...(hasBody && !(options.body instanceof FormData) ? { "Content-Type": "application/json" } : {}),
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { resolveStorageUrl } from "../config/runtime";
|
||||
|
||||
export async function downloadFileFromUrl(fileUrl: string, fileName: string) {
|
||||
const response = await fetch(fileUrl, {
|
||||
const response = await fetch(resolveStorageUrl(fileUrl), {
|
||||
credentials: "include"
|
||||
});
|
||||
if (!response.ok) {
|
||||
|
||||
Reference in New Issue
Block a user