mirror of
https://github.com/Gloridust/WechatOnCloud.git
synced 2026-08-30 18:21:27 -07:00
- #74 安装失败「unable to find user abc: no matching entries in passwd」:容器 init 未完成时 abc 用户还没建好,docker 创建 exec 即报 400。加 execCreate 重试包装(对该错误重试 ~12s), 超时给清晰中文提示,而非透传难懂的 docker 400。 - #82 开音频后关窗再进必须重启实例:React 清理在直接关窗时不一定执行,残留 audio socket.io (开麦时还占着麦克风)与下次新连接并存把实例顶到"需重启"。加 pagehide(非 bfcache)即断开音频桥。 - #81 输入条回车只落字、还得点发送:发送后把键盘焦点交回 VM iframe,用户再按一次回车即在微信里发出。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -403,10 +403,28 @@ export async function instanceRuntime(inst: Instance): Promise<RuntimeState> {
|
||||
}
|
||||
}
|
||||
|
||||
// 创建 exec 实例。容器 init 未完成时,linuxserver 基镜像的 'abc' 用户可能还没建好,docker 会以
|
||||
// 400「unable to find user abc: no matching entries in passwd file」直接拒绝创建 exec(见 issue #74)。
|
||||
// 对这种"用户未就绪"错误短暂重试,给容器 init 一点时间;超时则抛清晰的中文错误,而非透传难懂的 docker 400。
|
||||
async function execCreate(c: any, opts: any): Promise<any> {
|
||||
let lastErr: any;
|
||||
for (let i = 0; i < 8; i++) {
|
||||
try {
|
||||
return await c.exec(opts);
|
||||
} catch (e: any) {
|
||||
const msg = String(e?.message || e);
|
||||
if (!/no matching entries in passwd|unable to find user/i.test(msg)) throw e;
|
||||
lastErr = e;
|
||||
await new Promise((r) => setTimeout(r, 1500));
|
||||
}
|
||||
}
|
||||
throw new Error(`容器仍在初始化(桌面用户未就绪),请等待约十几秒后重试(${lastErr?.message || lastErr})`);
|
||||
}
|
||||
|
||||
// 在实例容器内执行命令,返回 stdout;若命令失败,把 stderr 透出给调用方。
|
||||
async function execCapture(inst: Instance, cmd: string[]): Promise<string> {
|
||||
const c = docker.getContainer(inst.containerName);
|
||||
const exec = await c.exec({ Cmd: cmd, AttachStdout: true, AttachStderr: true, Tty: false, User: 'abc' });
|
||||
const exec = await execCreate(c, { Cmd: cmd, AttachStdout: true, AttachStderr: true, Tty: false, User: 'abc' });
|
||||
const stream = await exec.start({ hijack: true, stdin: false });
|
||||
return await new Promise<string>((resolve, reject) => {
|
||||
let out = '';
|
||||
@@ -437,7 +455,7 @@ export async function triggerWechat(inst: Instance, cmd: 'install' | 'update'):
|
||||
const c = docker.getContainer(inst.containerName);
|
||||
const at = instanceAppType(inst);
|
||||
const action = cmd === 'update' ? 'update' : 'install';
|
||||
const exec = await c.exec({
|
||||
const exec = await execCreate(c, {
|
||||
Cmd: ['bash', '-c', `if [ -x /woc/app-ctl.sh ]; then /woc/app-ctl.sh ${at} ${action}; else /woc/wechat-ctl.sh ${action}; fi`],
|
||||
AttachStdout: false,
|
||||
AttachStderr: false,
|
||||
|
||||
@@ -403,13 +403,21 @@ export default function InstanceView({ onOpenMenu }: { onOpenMenu: () => void })
|
||||
const isFocused = () => !document.hidden && document.hasFocus();
|
||||
const sync = () => audio.setActive(isFocused());
|
||||
sync(); // 初始:若当前已聚焦则立即开声
|
||||
// 关窗/关标签页时彻底断开音频桥(issue #82):React effect 的清理在直接关闭窗口时不一定执行,
|
||||
// 残留的 audio socket.io(开了麦克风时还占着 getUserMedia)会留在实例上,下次再进与新连接并存,
|
||||
// 把实例顶到"需重启"。pagehide 在页面真正被丢弃(非进 bfcache)时同步断开,避免该残留。
|
||||
const onPageHide = (e: PageTransitionEvent) => {
|
||||
if (!e.persisted) audio.destroy();
|
||||
};
|
||||
document.addEventListener('visibilitychange', sync);
|
||||
window.addEventListener('focus', sync);
|
||||
window.addEventListener('blur', sync);
|
||||
window.addEventListener('pagehide', onPageHide);
|
||||
return () => {
|
||||
document.removeEventListener('visibilitychange', sync);
|
||||
window.removeEventListener('focus', sync);
|
||||
window.removeEventListener('blur', sync);
|
||||
window.removeEventListener('pagehide', onPageHide);
|
||||
audio.destroy();
|
||||
audioRef.current = null;
|
||||
};
|
||||
@@ -623,6 +631,9 @@ export default function InstanceView({ onOpenMenu }: { onOpenMenu: () => void })
|
||||
try {
|
||||
await api.typeInInstance(id, t);
|
||||
setImeText('');
|
||||
// 发送后把键盘焦点交回虚拟机(issue #81):字已落进微信输入框,焦点回到桌面后用户再按一次回车
|
||||
// 即可在微信里发出去,省掉用鼠标点「发送」。延迟一拍,等 React 清空输入框、避免被本框抢回焦点。
|
||||
setTimeout(() => focusFrame(), 60);
|
||||
} catch (e: any) {
|
||||
toast(e?.message || '发送失败:请确认实例已「升级实例」(镜像含 xclip/xdotool)', 'error');
|
||||
} finally {
|
||||
@@ -989,7 +1000,7 @@ export default function InstanceView({ onOpenMenu }: { onOpenMenu: () => void })
|
||||
sendImeText();
|
||||
}
|
||||
}}
|
||||
placeholder="中文输入这里 → 回车送进应用(先点好应用的输入框)。Shift+回车换行。"
|
||||
placeholder="中文输入这里 → 回车送进应用(先点好应用的输入框),焦点会自动回到桌面,再按一次回车即发送。Shift+回车换行。"
|
||||
rows={1}
|
||||
/>
|
||||
<button
|
||||
|
||||
Reference in New Issue
Block a user