Skip to content

Commit 4896670

Browse files
committed
replace ark webapp --daemon with systemd user service (ark webapp install/uninstall)
1 parent dc1f8b2 commit 4896670

File tree

4 files changed

+110
-1
lines changed

4 files changed

+110
-1
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,10 @@ ARK parses the PDF with PyMuPDF + Claude Haiku, pre-fills the wizard, and can ki
161161
| `ark delete <name>` | Remove project entirely |
162162
| `ark setup-bot` | Configure Telegram bot (one-time) |
163163
| `ark list` | List all projects with status |
164+
| `ark webapp install` | Install web portal as systemd user service |
165+
| `ark webapp uninstall` | Stop and remove the web portal service |
166+
167+
> **Note:** `ark webapp --daemon` is deprecated and will be removed in a future release. Use `ark webapp install` instead.
164168
165169
<details>
166170
<summary><strong>Direct orchestrator invocation</strong></summary>

README_ar.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,10 @@ ark new mma --from-pdf proposal.pdf
161161
| `ark delete <name>` | حذف المشروع بالكامل |
162162
| `ark setup-bot` | إعداد بوت تيليغرام (مرة واحدة) |
163163
| `ark list` | سرد جميع المشاريع وحالتها |
164+
| `ark webapp install` | تثبيت بوابة الويب كخدمة systemd للمستخدم |
165+
| `ark webapp uninstall` | إيقاف وإزالة خدمة بوابة الويب |
166+
167+
> **ملاحظة:** `ark webapp --daemon` مهمل وسيتم إزالته في إصدار مستقبلي. استخدم `ark webapp install` بدلاً منه.
164168
165169
<details>
166170
<summary><strong>استدعاء المُنسق مباشرة</strong></summary>

README_zh.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,10 @@ ARK 使用 PyMuPDF + Claude Haiku 解析 PDF,预填向导内容,可从提取
161161
| `ark delete <name>` | 完全删除项目 |
162162
| `ark setup-bot` | 配置 Telegram 机器人(一次性) |
163163
| `ark list` | 列出所有项目及状态 |
164+
| `ark webapp install` | 将 Web 门户安装为 systemd 用户服务 |
165+
| `ark webapp uninstall` | 停止并移除 Web 门户服务 |
166+
167+
> **注意:** `ark webapp --daemon` 已废弃,将在未来版本中移除。请使用 `ark webapp install` 代替。
164168
165169
<details>
166170
<summary><strong>直接调用 orchestrator</strong></summary>

ark/cli.py

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2886,6 +2886,92 @@ class RunArgs:
28862886
# ark webapp
28872887
# ============================================================
28882888

2889+
_SYSTEMD_SERVICE_NAME = "ark-webapp"
2890+
2891+
2892+
def _service_file_path() -> Path:
2893+
return Path.home() / ".config" / "systemd" / "user" / f"{_SYSTEMD_SERVICE_NAME}.service"
2894+
2895+
2896+
def _generate_service_unit(host: str, port: int) -> str:
2897+
"""Generate a systemd user service unit file for the ARK webapp."""
2898+
python_bin = sys.executable
2899+
ark_root = get_ark_root()
2900+
return f"""\
2901+
[Unit]
2902+
Description=ARK Research Portal
2903+
After=network.target
2904+
2905+
[Service]
2906+
Type=simple
2907+
WorkingDirectory={ark_root}
2908+
ExecStart={python_bin} -m ark.cli webapp --host {host} --port {port}
2909+
Restart=on-failure
2910+
RestartSec=5
2911+
StandardOutput=journal
2912+
StandardError=journal
2913+
2914+
[Install]
2915+
WantedBy=default.target
2916+
"""
2917+
2918+
2919+
def _cmd_webapp_install(host: str, port: int):
2920+
"""Install and start ARK webapp as a systemd user service."""
2921+
import subprocess as _sp
2922+
2923+
svc_path = _service_file_path()
2924+
svc_path.parent.mkdir(parents=True, exist_ok=True)
2925+
2926+
unit = _generate_service_unit(host, port)
2927+
svc_path.write_text(unit)
2928+
print(f" Service file written to {_c(str(svc_path), Colors.CYAN)}")
2929+
2930+
# Reload, enable, and start
2931+
_sp.run(["systemctl", "--user", "daemon-reload"], check=True)
2932+
_sp.run(["systemctl", "--user", "enable", _SYSTEMD_SERVICE_NAME], check=True)
2933+
_sp.run(["systemctl", "--user", "start", _SYSTEMD_SERVICE_NAME], check=True)
2934+
2935+
print(f"\n {_c('ARK Webapp', Colors.BOLD)} installed and started as systemd user service.")
2936+
print(f" URL: {_c(f'http://{host}:{port}', Colors.CYAN)}")
2937+
print()
2938+
print(f" Manage with:")
2939+
print(f" {_c(f'systemctl --user status {_SYSTEMD_SERVICE_NAME}', Colors.DIM)}")
2940+
print(f" {_c(f'systemctl --user restart {_SYSTEMD_SERVICE_NAME}', Colors.DIM)}")
2941+
print(f" {_c(f'journalctl --user -u {_SYSTEMD_SERVICE_NAME} -f', Colors.DIM)}")
2942+
print()
2943+
2944+
# Check linger
2945+
try:
2946+
r = _sp.run(["loginctl", "show-user", os.environ.get("USER", "")],
2947+
capture_output=True, text=True)
2948+
if "Linger=no" in r.stdout:
2949+
print(f" {_c('Note:', Colors.YELLOW)} Linger is not enabled for your user.")
2950+
print(f" The service will stop when you log out. To keep it running:")
2951+
user = os.environ.get("USER", "")
2952+
print(f" {_c(f'sudo loginctl enable-linger {user}', Colors.BOLD)}")
2953+
print()
2954+
except FileNotFoundError:
2955+
pass
2956+
2957+
2958+
def _cmd_webapp_uninstall():
2959+
"""Stop and remove the ARK webapp systemd user service."""
2960+
import subprocess as _sp
2961+
2962+
svc_path = _service_file_path()
2963+
if not svc_path.exists():
2964+
print(f" {_c('Not installed:', Colors.YELLOW)} No systemd service found at {svc_path}")
2965+
return
2966+
2967+
_sp.run(["systemctl", "--user", "stop", _SYSTEMD_SERVICE_NAME], capture_output=True)
2968+
_sp.run(["systemctl", "--user", "disable", _SYSTEMD_SERVICE_NAME], capture_output=True)
2969+
svc_path.unlink(missing_ok=True)
2970+
_sp.run(["systemctl", "--user", "daemon-reload"], check=True)
2971+
2972+
print(f" {_c('ARK Webapp', Colors.BOLD)} service stopped and removed.")
2973+
2974+
28892975
def cmd_webapp(args):
28902976
"""Start the ARK web app (lab-facing project submission portal)."""
28912977
subcmd = getattr(args, 'webapp_cmd', None)
@@ -2896,6 +2982,13 @@ def cmd_webapp(args):
28962982
cmd_web(args)
28972983
return
28982984

2985+
if subcmd == 'install':
2986+
_cmd_webapp_install(args.host, args.port)
2987+
return
2988+
if subcmd == 'uninstall':
2989+
_cmd_webapp_uninstall()
2990+
return
2991+
28992992
try:
29002993
import uvicorn
29012994
from ark.webapp import create_app
@@ -2915,6 +3008,8 @@ def cmd_webapp(args):
29153008
print(f" Edit {_c(str(_env_file()), Colors.CYAN)} and set SMTP_USER / SMTP_PASSWORD.\n")
29163009

29173010
if args.daemon:
3011+
print(f"\n {_c('Deprecation:', Colors.YELLOW)} --daemon uses os.fork() and will be removed in a future release.")
3012+
print(f" Use {_c('ark webapp install', Colors.BOLD)} instead for a systemd user service.\n")
29183013
_root = get_ark_root()
29193014
_webapp_dir = _root / "ark_webapp"
29203015
_webapp_dir.mkdir(exist_ok=True)
@@ -3401,9 +3496,11 @@ def main():
34013496
webapp_sub = p_webapp.add_subparsers(dest="webapp_cmd")
34023497
webapp_sub.add_parser("disable", help="Block new project submissions")
34033498
webapp_sub.add_parser("enable", help="Allow new project submissions")
3499+
webapp_sub.add_parser("install", help="Install as systemd user service (auto-start on boot)")
3500+
webapp_sub.add_parser("uninstall", help="Stop and remove systemd user service")
34043501
p_webapp.add_argument("--port", type=int, default=8423, help="Port (default: 8423)")
34053502
p_webapp.add_argument("--host", default="0.0.0.0", help="Host (default: 0.0.0.0)")
3406-
p_webapp.add_argument("--daemon", action="store_true", help="Run in background")
3503+
p_webapp.add_argument("--daemon", action="store_true", help="Run in background (deprecated, use 'install')")
34073504
p_webapp.set_defaults(func=cmd_webapp)
34083505

34093506
# ark web disable/enable [project]

0 commit comments

Comments
 (0)