Fix robustness issues across backend and frontend
- Add shutil.which guard to _run() in settings, asi_bridge routers - Catch RuntimeError on WebSocket disconnect in services, asi_bridge - Make file listing resilient to individual entry errors - Fix keyboard double-fire on touch devices (touchstart + click) - Update install.sh with correct Gitea repo URL - Add six to requirements.txt (python-pam dependency) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
f9743bb29a
commit
342dc0f0cf
7 changed files with 21 additions and 5 deletions
|
|
@ -1,6 +1,7 @@
|
||||||
fastapi==0.115.6
|
fastapi==0.115.6
|
||||||
uvicorn[standard]==0.34.0
|
uvicorn[standard]==0.34.0
|
||||||
python-pam==2.0.2
|
python-pam==2.0.2
|
||||||
|
six==1.17.0
|
||||||
PyJWT==2.10.1
|
PyJWT==2.10.1
|
||||||
psutil==6.1.1
|
psutil==6.1.1
|
||||||
ptyprocess==0.7.0
|
ptyprocess==0.7.0
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,13 @@ async def list_dir(
|
||||||
entries = sorted(p.iterdir(), key=lambda x: (not x.is_dir(), x.name.lower()))
|
entries = sorted(p.iterdir(), key=lambda x: (not x.is_dir(), x.name.lower()))
|
||||||
except PermissionError:
|
except PermissionError:
|
||||||
raise HTTPException(403, f"Permission denied: {path}")
|
raise HTTPException(403, f"Permission denied: {path}")
|
||||||
return [_file_info(e) for e in entries]
|
results = []
|
||||||
|
for e in entries:
|
||||||
|
try:
|
||||||
|
results.append(_file_info(e))
|
||||||
|
except (OSError, PermissionError):
|
||||||
|
continue
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
@router.get("/info")
|
@router.get("/info")
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,9 @@ class MountConfig(BaseModel):
|
||||||
|
|
||||||
|
|
||||||
async def _run(cmd: list[str]) -> tuple[int, str, str]:
|
async def _run(cmd: list[str]) -> tuple[int, str, str]:
|
||||||
|
import shutil
|
||||||
|
if not shutil.which(cmd[0]):
|
||||||
|
return 1, "", f"{cmd[0]}: command not found"
|
||||||
proc = await asyncio.create_subprocess_exec(
|
proc = await asyncio.create_subprocess_exec(
|
||||||
*cmd,
|
*cmd,
|
||||||
stdout=asyncio.subprocess.PIPE,
|
stdout=asyncio.subprocess.PIPE,
|
||||||
|
|
@ -195,7 +198,7 @@ async def bridge_ws(websocket: WebSocket):
|
||||||
seen_files = current_paths
|
seen_files = current_paths
|
||||||
|
|
||||||
await asyncio.sleep(5)
|
await asyncio.sleep(5)
|
||||||
except WebSocketDisconnect:
|
except (WebSocketDisconnect, RuntimeError):
|
||||||
pass
|
pass
|
||||||
except Exception:
|
except Exception:
|
||||||
log.exception("ASI Bridge WS error")
|
log.exception("ASI Bridge WS error")
|
||||||
|
|
|
||||||
|
|
@ -122,7 +122,7 @@ async def service_logs(websocket: WebSocket, unit: str):
|
||||||
"unit": unit,
|
"unit": unit,
|
||||||
"line": line.decode(errors="replace").rstrip(),
|
"line": line.decode(errors="replace").rstrip(),
|
||||||
})
|
})
|
||||||
except WebSocketDisconnect:
|
except (WebSocketDisconnect, RuntimeError):
|
||||||
pass
|
pass
|
||||||
except Exception:
|
except Exception:
|
||||||
log.exception("log stream error for %s", unit)
|
log.exception("log stream error for %s", unit)
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,9 @@ class TimezoneRequest(BaseModel):
|
||||||
|
|
||||||
|
|
||||||
async def _run(cmd: list[str]) -> tuple[int, str, str]:
|
async def _run(cmd: list[str]) -> tuple[int, str, str]:
|
||||||
|
import shutil
|
||||||
|
if not shutil.which(cmd[0]):
|
||||||
|
return 1, "", f"{cmd[0]}: command not found"
|
||||||
proc = await asyncio.create_subprocess_exec(
|
proc = await asyncio.create_subprocess_exec(
|
||||||
*cmd,
|
*cmd,
|
||||||
stdout=asyncio.subprocess.PIPE,
|
stdout=asyncio.subprocess.PIPE,
|
||||||
|
|
|
||||||
|
|
@ -160,12 +160,15 @@
|
||||||
const btn = document.createElement('button');
|
const btn = document.createElement('button');
|
||||||
btn.className = 'osk-key ' + className;
|
btn.className = 'osk-key ' + className;
|
||||||
btn.textContent = label;
|
btn.textContent = label;
|
||||||
|
let touchFired = false;
|
||||||
btn.addEventListener('touchstart', (e) => {
|
btn.addEventListener('touchstart', (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
touchFired = true;
|
||||||
handler();
|
handler();
|
||||||
});
|
});
|
||||||
btn.addEventListener('click', (e) => {
|
btn.addEventListener('click', (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
if (touchFired) { touchFired = false; return; }
|
||||||
handler();
|
handler();
|
||||||
});
|
});
|
||||||
return btn;
|
return btn;
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Atlus installer — targets DietPi, Armbian, Debian, Ubuntu
|
# Atlus installer — targets DietPi, Armbian, Debian, Ubuntu
|
||||||
# Usage: curl -sSL https://raw.githubusercontent.com/YOUR_REPO/atlus/main/install.sh | bash
|
# Usage: curl -sSL https://git.bytestud.io/roberts/atlus/raw/branch/main/install.sh | sudo bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
INSTALL_DIR="/opt/atlus"
|
INSTALL_DIR="/opt/atlus"
|
||||||
CONFIG_DIR="/etc/atlus"
|
CONFIG_DIR="/etc/atlus"
|
||||||
DATA_DIR="/var/lib/atlus"
|
DATA_DIR="/var/lib/atlus"
|
||||||
SERVICE_FILE="/etc/systemd/system/atlus.service"
|
SERVICE_FILE="/etc/systemd/system/atlus.service"
|
||||||
REPO_URL="https://github.com/YOUR_REPO/atlus.git"
|
REPO_URL="https://git.bytestud.io/roberts/atlus.git"
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Helpers
|
# Helpers
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue