Self-host compose stack and production stability fixes
这个提交包含在:
@@ -1,6 +1,8 @@
|
||||
// Preconfigured storage helpers for Manus WebDev templates
|
||||
// Uses the Biz-provided storage proxy (Authorization: Bearer <token>)
|
||||
|
||||
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { ENV } from './_core/env';
|
||||
|
||||
type StorageConfig = { baseUrl: string; apiKey: string };
|
||||
@@ -18,6 +20,31 @@ function getStorageConfig(): StorageConfig {
|
||||
return { baseUrl: baseUrl.replace(/\/+$/, ""), apiKey };
|
||||
}
|
||||
|
||||
function canUseRemoteStorage(): boolean {
|
||||
return Boolean(ENV.forgeApiUrl && ENV.forgeApiKey);
|
||||
}
|
||||
|
||||
function getLocalStoragePath(relKey: string): string {
|
||||
return path.join(ENV.localStorageDir, normalizeKey(relKey));
|
||||
}
|
||||
|
||||
async function writeLocalFile(
|
||||
relKey: string,
|
||||
data: Buffer | Uint8Array | string
|
||||
): Promise<void> {
|
||||
const filePath = getLocalStoragePath(relKey);
|
||||
await mkdir(path.dirname(filePath), { recursive: true });
|
||||
const content =
|
||||
typeof data === "string" ? Buffer.from(data) : Buffer.from(data);
|
||||
await writeFile(filePath, content);
|
||||
}
|
||||
|
||||
async function readLocalFile(relKey: string): Promise<string> {
|
||||
const filePath = getLocalStoragePath(relKey);
|
||||
await readFile(filePath);
|
||||
return `/uploads/${normalizeKey(relKey)}`;
|
||||
}
|
||||
|
||||
function buildUploadUrl(baseUrl: string, relKey: string): URL {
|
||||
const url = new URL("v1/storage/upload", ensureTrailingSlash(baseUrl));
|
||||
url.searchParams.set("path", normalizeKey(relKey));
|
||||
@@ -72,6 +99,12 @@ export async function storagePut(
|
||||
data: Buffer | Uint8Array | string,
|
||||
contentType = "application/octet-stream"
|
||||
): Promise<{ key: string; url: string }> {
|
||||
if (!canUseRemoteStorage()) {
|
||||
const key = normalizeKey(relKey);
|
||||
await writeLocalFile(key, data);
|
||||
return { key, url: `/uploads/${key}` };
|
||||
}
|
||||
|
||||
const { baseUrl, apiKey } = getStorageConfig();
|
||||
const key = normalizeKey(relKey);
|
||||
const uploadUrl = buildUploadUrl(baseUrl, key);
|
||||
@@ -93,6 +126,14 @@ export async function storagePut(
|
||||
}
|
||||
|
||||
export async function storageGet(relKey: string): Promise<{ key: string; url: string; }> {
|
||||
if (!canUseRemoteStorage()) {
|
||||
const key = normalizeKey(relKey);
|
||||
return {
|
||||
key,
|
||||
url: await readLocalFile(key),
|
||||
};
|
||||
}
|
||||
|
||||
const { baseUrl, apiKey } = getStorageConfig();
|
||||
const key = normalizeKey(relKey);
|
||||
return {
|
||||
|
||||
在新工单中引用
屏蔽一个用户