feat(desktop): drop parked review flow, add publish management

SaaS 侧人工审核后才创建 publish job,desktop 不再做二次审核:移除
manual/waiting_user/parked/from_parked 状态机与 LeaseFromParked 查询,
desktop client 只执行发布并新增"发布管理"页(待发布队列 / 历史 / 再次
发送)。同时抽离 @geo/publisher-platforms 共享适配器包、新增 Redis-based
desktop_presence 与 publish_record_support,刷新 admin-web 发布弹窗与
媒体库;plan A / spec 文档同步口径。
This commit is contained in:
2026-04-20 09:52:48 +08:00
parent b16e9f0bd1
commit a617d39a4a
93 changed files with 5519 additions and 3594 deletions
@@ -0,0 +1,74 @@
import type { WebContents } from "electron/main";
interface RendererProxyRequest {
url: string;
method: string;
headers?: Record<string, string>;
body?: string;
}
interface RendererProxyResponse {
ok: boolean;
status: number;
statusText: string;
headers: Record<string, string>;
bodyText: string;
error?: string;
}
let rendererWebContents: WebContents | null = null;
export function registerRendererDevtoolsProxyTarget(webContents: WebContents): void {
rendererWebContents = webContents;
webContents.once("destroyed", () => {
if (rendererWebContents === webContents) {
rendererWebContents = null;
}
});
}
export function canUseRendererDevtoolsProxy(): boolean {
return Boolean(process.env.ELECTRON_RENDERER_URL && rendererWebContents && !rendererWebContents.isDestroyed());
}
export async function rendererDevtoolsFetch(
request: RendererProxyRequest,
): Promise<RendererProxyResponse> {
if (!rendererWebContents || rendererWebContents.isDestroyed()) {
throw new Error("renderer_devtools_proxy_unavailable");
}
const script = `(async () => {
try {
const response = await fetch(${JSON.stringify(request.url)}, {
method: ${JSON.stringify(request.method)},
headers: ${JSON.stringify(request.headers ?? {})},
body: ${JSON.stringify(request.body ?? null)},
});
const bodyText = await response.text();
return JSON.stringify({
ok: response.ok,
status: response.status,
statusText: response.statusText,
headers: Object.fromEntries(response.headers.entries()),
bodyText,
});
} catch (error) {
return JSON.stringify({
ok: false,
status: 0,
statusText: "",
headers: {},
bodyText: "",
error: String(error && (error.message || error)),
});
}
})()`;
const serialized = await rendererWebContents.executeJavaScript(script, true);
if (typeof serialized !== "string" || !serialized) {
throw new Error("renderer_devtools_proxy_empty_response");
}
return JSON.parse(serialized) as RendererProxyResponse;
}