Makelore 2.0 initial clean snapshot
This commit is contained in:
146
src/lib/image-workspace.ts
Normal file
146
src/lib/image-workspace.ts
Normal file
@@ -0,0 +1,146 @@
|
||||
import { AppError } from '@/lib/error-model';
|
||||
import { hostApiFetch } from '@/lib/host-api';
|
||||
import {
|
||||
IMAGE_WORKSPACE_UNAVAILABLE_CODE,
|
||||
IMAGE_WORKSPACE_UNAVAILABLE_MESSAGE,
|
||||
type ImageWorkspaceImage,
|
||||
type ImageWorkspaceReferenceUploadInput,
|
||||
type ImageWorkspaceReferenceUploadResult,
|
||||
type ImageWorkspaceSendMessageInput,
|
||||
type ImageWorkspaceSnapshot,
|
||||
} from '../../shared/image-workspace';
|
||||
|
||||
const IMAGE_WORKSPACE_API_PATH = '/api/works/image-workspace';
|
||||
|
||||
type ImageWorkspaceEnvelope = {
|
||||
success?: boolean;
|
||||
status?: number;
|
||||
code?: string;
|
||||
error?: string;
|
||||
workspace?: ImageWorkspaceSnapshot;
|
||||
reference?: ImageWorkspaceImage;
|
||||
};
|
||||
|
||||
export const IMAGE_WORKSPACE_CREATE_PROJECT_EVENT = 'niancode:image-workspace:create-project';
|
||||
|
||||
export class ImageWorkspaceApiError extends Error {
|
||||
readonly status: number;
|
||||
readonly code: string;
|
||||
|
||||
constructor(status: number, code: string, message: string) {
|
||||
super(message);
|
||||
this.name = 'ImageWorkspaceApiError';
|
||||
this.status = status;
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
|
||||
function getErrorStatus(error: unknown): number {
|
||||
if (error instanceof AppError && typeof error.details?.status === 'number') {
|
||||
return error.details.status;
|
||||
}
|
||||
return 502;
|
||||
}
|
||||
|
||||
async function requestImageWorkspace(
|
||||
accessToken: string | null | undefined,
|
||||
path: string,
|
||||
init: RequestInit = {},
|
||||
): Promise<ImageWorkspaceSnapshot> {
|
||||
const headers = accessToken?.trim()
|
||||
? { 'X-NianCode-Access-Token': accessToken.trim(), ...init.headers }
|
||||
: init.headers;
|
||||
let response: ImageWorkspaceEnvelope;
|
||||
try {
|
||||
response = await hostApiFetch<ImageWorkspaceEnvelope>(path, {
|
||||
...init,
|
||||
...(headers ? { headers } : {}),
|
||||
});
|
||||
} catch (error) {
|
||||
const status = getErrorStatus(error);
|
||||
throw new ImageWorkspaceApiError(
|
||||
status,
|
||||
status === 501 ? IMAGE_WORKSPACE_UNAVAILABLE_CODE : 'IMAGE_WORKSPACE_REQUEST_FAILED',
|
||||
error instanceof Error ? error.message : IMAGE_WORKSPACE_UNAVAILABLE_MESSAGE,
|
||||
);
|
||||
}
|
||||
|
||||
if (!response.success || !response.workspace) {
|
||||
throw new ImageWorkspaceApiError(
|
||||
response.status ?? 502,
|
||||
response.code ?? 'IMAGE_WORKSPACE_REQUEST_FAILED',
|
||||
response.error ?? '创作空间请求失败',
|
||||
);
|
||||
}
|
||||
return response.workspace;
|
||||
}
|
||||
|
||||
export function fetchImageWorkspace(accessToken?: string | null): Promise<ImageWorkspaceSnapshot> {
|
||||
return requestImageWorkspace(accessToken, IMAGE_WORKSPACE_API_PATH);
|
||||
}
|
||||
|
||||
export function createImageWorkspaceProject(
|
||||
accessToken: string | null | undefined,
|
||||
name: string,
|
||||
): Promise<ImageWorkspaceSnapshot> {
|
||||
return requestImageWorkspace(accessToken, `${IMAGE_WORKSPACE_API_PATH}/projects`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ name: name.trim() }),
|
||||
});
|
||||
}
|
||||
|
||||
export function addImageWorkspaceAgent(
|
||||
accessToken: string | null | undefined,
|
||||
projectId: string,
|
||||
): Promise<ImageWorkspaceSnapshot> {
|
||||
return requestImageWorkspace(
|
||||
accessToken,
|
||||
`${IMAGE_WORKSPACE_API_PATH}/projects/${encodeURIComponent(projectId)}/agents`,
|
||||
{ method: 'POST', body: JSON.stringify({}) },
|
||||
);
|
||||
}
|
||||
|
||||
export function sendImageWorkspaceMessage(
|
||||
accessToken: string | null | undefined,
|
||||
input: ImageWorkspaceSendMessageInput,
|
||||
): Promise<ImageWorkspaceSnapshot> {
|
||||
return requestImageWorkspace(
|
||||
accessToken,
|
||||
`${IMAGE_WORKSPACE_API_PATH}/projects/${encodeURIComponent(input.projectId)}/messages`,
|
||||
{ method: 'POST', body: JSON.stringify(input) },
|
||||
);
|
||||
}
|
||||
|
||||
export function uploadImageWorkspaceReference(
|
||||
accessToken: string | null | undefined,
|
||||
input: ImageWorkspaceReferenceUploadInput,
|
||||
): Promise<ImageWorkspaceReferenceUploadResult> {
|
||||
const headers = accessToken?.trim()
|
||||
? { 'X-NianCode-Access-Token': accessToken.trim() }
|
||||
: undefined;
|
||||
return hostApiFetch<ImageWorkspaceEnvelope>(
|
||||
`${IMAGE_WORKSPACE_API_PATH}/projects/${encodeURIComponent(input.projectId)}/references`,
|
||||
{
|
||||
method: 'POST',
|
||||
...(headers ? { headers } : {}),
|
||||
body: JSON.stringify(input),
|
||||
},
|
||||
).then((response) => {
|
||||
if (!response.success || !response.workspace || !response.reference) {
|
||||
throw new ImageWorkspaceApiError(
|
||||
response.status ?? 502,
|
||||
response.code ?? 'IMAGE_WORKSPACE_REQUEST_FAILED',
|
||||
response.error ?? '参考图上传失败',
|
||||
);
|
||||
}
|
||||
return { workspace: response.workspace, reference: response.reference };
|
||||
}).catch((error: unknown) => {
|
||||
if (error instanceof ImageWorkspaceApiError) throw error;
|
||||
const status = getErrorStatus(error);
|
||||
throw new ImageWorkspaceApiError(
|
||||
status,
|
||||
status === 501 ? IMAGE_WORKSPACE_UNAVAILABLE_CODE : 'IMAGE_WORKSPACE_REQUEST_FAILED',
|
||||
error instanceof Error ? error.message : IMAGE_WORKSPACE_UNAVAILABLE_MESSAGE,
|
||||
);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user