实现 Makelore 一键提交审核
需求:让非专业用户在项目操作区一次提交,运营审核通过后直接发布。 实现:由 Electron Main 完成安全打包、自动版本、幂等重试和状态脱敏;补齐友好失败反馈、唯一提交入口及隔离 Electron E2E fixture。
This commit is contained in:
@@ -82,6 +82,8 @@ export type WorksProjectMetadataInput = {
|
||||
export type WorksProjectVersionUpload = {
|
||||
version_id: string;
|
||||
review_status: string;
|
||||
build_job_id?: string | null;
|
||||
build_status?: string | null;
|
||||
};
|
||||
|
||||
export type WorksProjectVersion = {
|
||||
@@ -90,6 +92,10 @@ export type WorksProjectVersion = {
|
||||
review_status: string;
|
||||
change_log: string;
|
||||
build_job_id: string | null;
|
||||
build_status: string | null;
|
||||
build_error_code: string | null;
|
||||
release_id: string | null;
|
||||
rejection_reason?: string | null;
|
||||
created_at: string;
|
||||
};
|
||||
|
||||
@@ -112,6 +118,36 @@ export type WorksProjectUploadInput = {
|
||||
zipFilePath: string;
|
||||
};
|
||||
|
||||
export type WorksStaticPackageSummary = {
|
||||
archiveName: string;
|
||||
sha256: string;
|
||||
fileCount: number;
|
||||
sourceBytes: number;
|
||||
archiveBytes: number;
|
||||
excludedCount: number;
|
||||
excludedPaths: string[];
|
||||
manifest: {
|
||||
schema_version: 1;
|
||||
kind: 'web';
|
||||
runtime: 'static';
|
||||
build: {
|
||||
preset: 'vite';
|
||||
package_manager: 'npm';
|
||||
entry: 'index.html';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
export type WorksProjectSourcePublishInput = {
|
||||
projectId: string;
|
||||
project: WorksProjectMetadataInput;
|
||||
};
|
||||
|
||||
export type WorksProjectSourcePublishResult = {
|
||||
package: WorksStaticPackageSummary;
|
||||
upload: WorksProjectVersionUpload;
|
||||
};
|
||||
|
||||
export type WorksSpeechTranscription = {
|
||||
text: string;
|
||||
model: string;
|
||||
@@ -152,6 +188,7 @@ export type PlazaCard = {
|
||||
type WorksActionResponse<TField extends string, TValue> = {
|
||||
success: boolean;
|
||||
status?: number;
|
||||
code?: string;
|
||||
error?: string;
|
||||
} & {
|
||||
[key in TField]?: TValue;
|
||||
@@ -177,11 +214,13 @@ type FetchMyWorksProjectsInput = {
|
||||
|
||||
export class WorksSquareApiError extends Error {
|
||||
readonly statusCode?: number;
|
||||
readonly code?: string;
|
||||
|
||||
constructor(message: string, statusCode?: number) {
|
||||
constructor(message: string, statusCode?: number, code?: string) {
|
||||
super(message);
|
||||
this.name = 'WorksSquareApiError';
|
||||
this.statusCode = statusCode;
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,7 +230,7 @@ function assertSuccess<TField extends string, TValue>(
|
||||
fallback: string,
|
||||
): TValue {
|
||||
if (!response.success || response[field] === undefined) {
|
||||
throw new WorksSquareApiError(response.error || fallback, response.status);
|
||||
throw new WorksSquareApiError(response.error || fallback, response.status, response.code);
|
||||
}
|
||||
return response[field] as TValue;
|
||||
}
|
||||
@@ -366,6 +405,15 @@ export async function fetchMyWorksProjectStatus(
|
||||
return assertSuccess(response, 'status', 'Failed to load Works Square project status');
|
||||
}
|
||||
|
||||
export async function fetchCurrentWorksProjectStatus(
|
||||
appId: string,
|
||||
): Promise<WorksProjectStatusDetail> {
|
||||
const response = await hostApiFetch<WorksActionResponse<'status', WorksProjectStatusDetail>>(
|
||||
`/api/works/projects/mine/${encodeURIComponent(appId)}/status`,
|
||||
);
|
||||
return assertSuccess(response, 'status', 'Failed to load Works Square project status');
|
||||
}
|
||||
|
||||
export async function uploadWorksProjectZip(
|
||||
input: WorksProjectUploadInput,
|
||||
): Promise<WorksProjectVersionUpload> {
|
||||
@@ -385,6 +433,33 @@ export async function uploadWorksProjectZip(
|
||||
return assertSuccess(response, 'upload', 'Failed to upload Works Square project zip');
|
||||
}
|
||||
|
||||
export async function publishWorksProjectSource(
|
||||
input: WorksProjectSourcePublishInput,
|
||||
): Promise<WorksProjectSourcePublishResult> {
|
||||
const response = await hostApiFetch<{
|
||||
success: boolean;
|
||||
status?: number;
|
||||
code?: string;
|
||||
error?: string;
|
||||
package?: WorksStaticPackageSummary;
|
||||
upload?: WorksProjectVersionUpload;
|
||||
}>('/api/works/projects/publish-source', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
projectId: input.projectId,
|
||||
project: input.project,
|
||||
}),
|
||||
});
|
||||
if (!response.success || !response.package || !response.upload) {
|
||||
throw new WorksSquareApiError(
|
||||
response.error || 'Failed to package and upload Works Square project',
|
||||
response.status,
|
||||
response.code,
|
||||
);
|
||||
}
|
||||
return { package: response.package, upload: response.upload };
|
||||
}
|
||||
|
||||
export async function armWorksCloudDeployment(projectId: string): Promise<WorksCloudDeploymentRecord> {
|
||||
const response = await hostApiFetch<WorksActionResponse<'deployment', WorksCloudDeploymentRecord>>(
|
||||
`/api/opencode/projects/${encodeURIComponent(projectId)}/works-cloud-deploy`,
|
||||
|
||||
Reference in New Issue
Block a user