auto-playwright-web 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- auto_playwright_web-0.1.0/PKG-INFO +68 -0
- auto_playwright_web-0.1.0/README.md +50 -0
- auto_playwright_web-0.1.0/app/__init__.py +1 -0
- auto_playwright_web-0.1.0/app/cli.py +161 -0
- auto_playwright_web-0.1.0/app/configs/__init__.py +1 -0
- auto_playwright_web-0.1.0/app/configs/base.py +19 -0
- auto_playwright_web-0.1.0/app/configs/config.py +3 -0
- auto_playwright_web-0.1.0/app/dependencies.py +123 -0
- auto_playwright_web-0.1.0/app/domain.py +65 -0
- auto_playwright_web-0.1.0/app/main.py +110 -0
- auto_playwright_web-0.1.0/app/repositories/__init__.py +1 -0
- auto_playwright_web-0.1.0/app/repositories/base.py +38 -0
- auto_playwright_web-0.1.0/app/repositories/memory.py +78 -0
- auto_playwright_web-0.1.0/app/repositories/sqlite.py +321 -0
- auto_playwright_web-0.1.0/app/routers/__init__.py +1 -0
- auto_playwright_web-0.1.0/app/routers/auth.py +45 -0
- auto_playwright_web-0.1.0/app/routers/common_library.py +37 -0
- auto_playwright_web-0.1.0/app/routers/executions.py +64 -0
- auto_playwright_web-0.1.0/app/routers/open_api.py +84 -0
- auto_playwright_web-0.1.0/app/routers/schedules.py +171 -0
- auto_playwright_web-0.1.0/app/routers/scripts.py +76 -0
- auto_playwright_web-0.1.0/app/routers/user_env_vars.py +39 -0
- auto_playwright_web-0.1.0/app/routers/webhooks.py +22 -0
- auto_playwright_web-0.1.0/app/scheduler.py +43 -0
- auto_playwright_web-0.1.0/app/schemas/__init__.py +1 -0
- auto_playwright_web-0.1.0/app/schemas/auth.py +32 -0
- auto_playwright_web-0.1.0/app/schemas/common_library.py +24 -0
- auto_playwright_web-0.1.0/app/schemas/execution.py +33 -0
- auto_playwright_web-0.1.0/app/schemas/schedule.py +42 -0
- auto_playwright_web-0.1.0/app/schemas/script.py +111 -0
- auto_playwright_web-0.1.0/app/schemas/token.py +30 -0
- auto_playwright_web-0.1.0/app/schemas/user.py +10 -0
- auto_playwright_web-0.1.0/app/schemas/user_env_var.py +31 -0
- auto_playwright_web-0.1.0/app/schemas/webhook.py +22 -0
- auto_playwright_web-0.1.0/app/security.py +90 -0
- auto_playwright_web-0.1.0/app/service/__init__.py +1 -0
- auto_playwright_web-0.1.0/app/service/auth_service.py +69 -0
- auto_playwright_web-0.1.0/app/service/common_library_service.py +34 -0
- auto_playwright_web-0.1.0/app/service/execution_service.py +229 -0
- auto_playwright_web-0.1.0/app/service/notification_service.py +44 -0
- auto_playwright_web-0.1.0/app/service/runner.py +146 -0
- auto_playwright_web-0.1.0/app/service/schedule_service.py +104 -0
- auto_playwright_web-0.1.0/app/service/script_loader.py +131 -0
- auto_playwright_web-0.1.0/app/service/script_service.py +205 -0
- auto_playwright_web-0.1.0/app/service/token_service.py +75 -0
- auto_playwright_web-0.1.0/app/service/user_env_var_service.py +97 -0
- auto_playwright_web-0.1.0/app/service/webhook_service.py +41 -0
- auto_playwright_web-0.1.0/app/static/assets/EnvVarsPage-BQXP7S1T.css +1 -0
- auto_playwright_web-0.1.0/app/static/assets/EnvVarsPage-C2RsrYZw.js +4 -0
- auto_playwright_web-0.1.0/app/static/assets/ExecutionsPage-2b2aSqsU.js +3 -0
- auto_playwright_web-0.1.0/app/static/assets/ExecutionsPage-DpccwQ-n.css +1 -0
- auto_playwright_web-0.1.0/app/static/assets/SchedulesPage-CJt0O7JF.css +1 -0
- auto_playwright_web-0.1.0/app/static/assets/SchedulesPage-CzIaVV1C.js +1 -0
- auto_playwright_web-0.1.0/app/static/assets/ScriptsPage-3yx9JHib.js +9 -0
- auto_playwright_web-0.1.0/app/static/assets/ScriptsPage-w9DcygkJ.css +1 -0
- auto_playwright_web-0.1.0/app/static/assets/abap-BrgZPUOV.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/apex-DyP6w7ZV.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/api-DM5brzz4.js +9 -0
- auto_playwright_web-0.1.0/app/static/assets/azcli-BaLxmfj-.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/bat-CFOPXBzS.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/bicep-BfEKNvv3.js +7 -0
- auto_playwright_web-0.1.0/app/static/assets/cameligo-BFG1Mk7z.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/clojure-DTECt2xU.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/codicon-DCmgc-ay.ttf +0 -0
- auto_playwright_web-0.1.0/app/static/assets/coffee-CDGzqUPQ.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/cpp-CLLBncYj.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/csharp-dUCx_-0o.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/csp-5Rap-vPy.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/css-D3h14YRZ.js +8 -0
- auto_playwright_web-0.1.0/app/static/assets/cssMode-Dd-OIW4z.js +9 -0
- auto_playwright_web-0.1.0/app/static/assets/cypher-DrQuvNYM.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/dart-CFKIUWau.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/dockerfile-Zznr-cwX.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/ecl-Ce3n6wWz.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/editor.worker-DX6ApQqM.js +12 -0
- auto_playwright_web-0.1.0/app/static/assets/elixir-deUWdS0T.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/flow9-i9-g7ZhI.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/freemarker2-DorxjobG.js +8 -0
- auto_playwright_web-0.1.0/app/static/assets/fsharp-CzKuDChf.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/go-Cphgjts3.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/graphql-Cg7bfA9N.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/handlebars-CZINcTMr.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/hcl-0cvrggvQ.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/html-Cwf08_sN.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/htmlMode-CsM53ifH.js +9 -0
- auto_playwright_web-0.1.0/app/static/assets/index-BivV9deF.css +1 -0
- auto_playwright_web-0.1.0/app/static/assets/index-w3kBh3h9.js +847 -0
- auto_playwright_web-0.1.0/app/static/assets/ini-Drc7WvVn.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/java-B_fMsGYe.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/javascript-CC0vVKQH.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/json.worker-CIZWV9vN.js +49 -0
- auto_playwright_web-0.1.0/app/static/assets/jsonMode-mEWbQgVL.js +15 -0
- auto_playwright_web-0.1.0/app/static/assets/julia-Bqgm2twL.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/kotlin-BSkB5QuD.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/less-BsTHnhdd.js +7 -0
- auto_playwright_web-0.1.0/app/static/assets/lexon-YWi4-JPR.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/liquid-b8gV0sP_.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/lua-nf6ki56Z.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/m3-Cpb6xl2v.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/markdown-DSZPf7rp.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/mdx-Bhwfss4Q.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/mips-B_c3zf-v.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/msdax-rUNN04Wq.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/mysql-DDwshQtU.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/objective-c-B5zXfXm9.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/pascal-CXOwvkN_.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/pascaligo-Bc-ZgV77.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/perl-CwNk8-XU.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/pgsql-tGk8EFnU.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/php-CpIb_Oan.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/pla-B03wrqEc.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/postiats-BKlk5iyT.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/powerquery-Bhzvs7bI.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/powershell-Dd3NCNK9.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/protobuf-COyEY5Pt.js +7 -0
- auto_playwright_web-0.1.0/app/static/assets/pug-BaJupSGV.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/python-CYyzhGkK.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/qsharp-DXyYeYxl.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/r-CdQndTaG.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/razor-BrDHW0IA.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/redis-CVwtpugi.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/redshift-25W9uPmb.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/restructuredtext-DfzH4Xui.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/ruby-Cp1zYvxS.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/rust-D5C2fndG.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/sb-CDntyWJ8.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/scala-BoFRg7Ot.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/scheme-Bio4gycK.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/scss-4Ik7cdeQ.js +8 -0
- auto_playwright_web-0.1.0/app/static/assets/shell-CX-rkNHf.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/solidity-Tw7wswEv.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/sophia-C5WLch3f.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/sparql-DHaeiCBh.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/sql-CCSDG5nI.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/st-pnP8ivHi.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/swift-DwJ7jVG9.js +8 -0
- auto_playwright_web-0.1.0/app/static/assets/systemverilog-B9Xyijhd.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/tcl-DnHyzjbg.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/tsMode-CNwb3tei.js +16 -0
- auto_playwright_web-0.1.0/app/static/assets/twig-CPajHgWi.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/typescript-CE4jKLq-.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/typespec-D-MeaMDU.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/vb-DgyLZaXg.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/wgsl-BIv9DU6q.js +303 -0
- auto_playwright_web-0.1.0/app/static/assets/xml-BRP0X2sF.js +6 -0
- auto_playwright_web-0.1.0/app/static/assets/yaml-O7uTeIlb.js +6 -0
- auto_playwright_web-0.1.0/app/static/favicon.svg +5 -0
- auto_playwright_web-0.1.0/app/static/index.html +14 -0
- auto_playwright_web-0.1.0/auto_playwright_web.egg-info/PKG-INFO +68 -0
- auto_playwright_web-0.1.0/auto_playwright_web.egg-info/SOURCES.txt +161 -0
- auto_playwright_web-0.1.0/auto_playwright_web.egg-info/dependency_links.txt +1 -0
- auto_playwright_web-0.1.0/auto_playwright_web.egg-info/entry_points.txt +2 -0
- auto_playwright_web-0.1.0/auto_playwright_web.egg-info/requires.txt +11 -0
- auto_playwright_web-0.1.0/auto_playwright_web.egg-info/top_level.txt +1 -0
- auto_playwright_web-0.1.0/pyproject.toml +32 -0
- auto_playwright_web-0.1.0/setup.cfg +4 -0
- auto_playwright_web-0.1.0/tests/test_cli.py +24 -0
- auto_playwright_web-0.1.0/tests/test_e2e.py +75 -0
- auto_playwright_web-0.1.0/tests/test_execution_file_first.py +74 -0
- auto_playwright_web-0.1.0/tests/test_script_loader.py +73 -0
- auto_playwright_web-0.1.0/tests/test_sqlite_repository.py +93 -0
- auto_playwright_web-0.1.0/tests/test_static_spa.py +23 -0
- auto_playwright_web-0.1.0/tests/test_streamlined_api.py +80 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: auto-playwright-web
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Add your description here
|
|
5
|
+
Requires-Python: <3.13,>=3.12
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: aiosqlite>=0.20.0
|
|
8
|
+
Requires-Dist: apscheduler>=3.10.4
|
|
9
|
+
Requires-Dist: fastapi>=0.115.0
|
|
10
|
+
Requires-Dist: httpx>=0.27.0
|
|
11
|
+
Requires-Dist: loguru>=0.7.2
|
|
12
|
+
Requires-Dist: playwright>=1.45.0
|
|
13
|
+
Requires-Dist: pytest>=8.2.0
|
|
14
|
+
Requires-Dist: pytest-asyncio>=0.24.0
|
|
15
|
+
Requires-Dist: python-dotenv>=1.0.1
|
|
16
|
+
Requires-Dist: pyyaml>=6.0.1
|
|
17
|
+
Requires-Dist: uvicorn>=0.30.0
|
|
18
|
+
|
|
19
|
+
# Auto Playwright Web
|
|
20
|
+
|
|
21
|
+
多用户 Playwright 脚本管理平台,后端使用 FastAPI,前端使用 Vite + Vue 3 + Element Plus + Monaco Editor。
|
|
22
|
+
|
|
23
|
+
## 默认账号
|
|
24
|
+
|
|
25
|
+
- 管理员:`admin / admin123`
|
|
26
|
+
- 测试内存仓储还包含普通用户:`demo / demo123`
|
|
27
|
+
|
|
28
|
+
生产 MySQL 首次初始化请先创建业务库,再执行:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
mysql -uroot -p123456 auto_playwright_web < app/scripts/init_db.sql
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## 本地开发
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
uv sync
|
|
38
|
+
uv run uvicorn app.main:app --reload
|
|
39
|
+
cd frontend
|
|
40
|
+
npm install
|
|
41
|
+
npm run dev
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
默认 API 前缀为 `/api`,前端开发服务器会代理到 `http://127.0.0.1:8000`。
|
|
45
|
+
|
|
46
|
+
## AI 生成脚本
|
|
47
|
+
|
|
48
|
+
管理员可以在脚本编辑抽屉里点击“AI 生成”,根据需求自动生成完整脚本草稿,包括脚本编码、名称、参数定义、超时时间和 Python Playwright 源码。后端不使用 OpenAI SDK,而是通过 HTTP 调用 Chat Completions 接口,并以流式响应填充前端编辑器。启用前需要配置:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
OPENAI_API_KEY=你的 OpenAI API Key
|
|
52
|
+
OPENAI_MODEL=gpt-5.4-mini
|
|
53
|
+
OPENAI_BASE_URL=https://api.openai.com/v1
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
`OPENAI_MODEL` 和 `OPENAI_BASE_URL` 都可以按部署环境覆盖。
|
|
57
|
+
|
|
58
|
+
## 脚本约定
|
|
59
|
+
|
|
60
|
+
脚本由管理员维护,所有用户共享。脚本入口必须为:
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
async def main(params: dict) -> dict:
|
|
64
|
+
print("print 输出会进入 stdout_text")
|
|
65
|
+
return {"ok": True}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
建议脚本使用 `logging` 输出排查日志,平台同时兼容 `print`。`print` 会进入 stdout,`logging` 默认进入 stderr。
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Auto Playwright Web
|
|
2
|
+
|
|
3
|
+
多用户 Playwright 脚本管理平台,后端使用 FastAPI,前端使用 Vite + Vue 3 + Element Plus + Monaco Editor。
|
|
4
|
+
|
|
5
|
+
## 默认账号
|
|
6
|
+
|
|
7
|
+
- 管理员:`admin / admin123`
|
|
8
|
+
- 测试内存仓储还包含普通用户:`demo / demo123`
|
|
9
|
+
|
|
10
|
+
生产 MySQL 首次初始化请先创建业务库,再执行:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
mysql -uroot -p123456 auto_playwright_web < app/scripts/init_db.sql
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## 本地开发
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
uv sync
|
|
20
|
+
uv run uvicorn app.main:app --reload
|
|
21
|
+
cd frontend
|
|
22
|
+
npm install
|
|
23
|
+
npm run dev
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
默认 API 前缀为 `/api`,前端开发服务器会代理到 `http://127.0.0.1:8000`。
|
|
27
|
+
|
|
28
|
+
## AI 生成脚本
|
|
29
|
+
|
|
30
|
+
管理员可以在脚本编辑抽屉里点击“AI 生成”,根据需求自动生成完整脚本草稿,包括脚本编码、名称、参数定义、超时时间和 Python Playwright 源码。后端不使用 OpenAI SDK,而是通过 HTTP 调用 Chat Completions 接口,并以流式响应填充前端编辑器。启用前需要配置:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
OPENAI_API_KEY=你的 OpenAI API Key
|
|
34
|
+
OPENAI_MODEL=gpt-5.4-mini
|
|
35
|
+
OPENAI_BASE_URL=https://api.openai.com/v1
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`OPENAI_MODEL` 和 `OPENAI_BASE_URL` 都可以按部署环境覆盖。
|
|
39
|
+
|
|
40
|
+
## 脚本约定
|
|
41
|
+
|
|
42
|
+
脚本由管理员维护,所有用户共享。脚本入口必须为:
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
async def main(params: dict) -> dict:
|
|
46
|
+
print("print 输出会进入 stdout_text")
|
|
47
|
+
return {"ok": True}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
建议脚本使用 `logging` 输出排查日志,平台同时兼容 `print`。`print` 会进入 stdout,`logging` 默认进入 stderr。
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Playwright 脚本管理平台后端包。"""
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import asyncio
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
__version__ = "0.1.0"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def init_workspace(target_dir: Path | str = ".") -> None:
|
|
14
|
+
"""初始化标准工作区目录与示例脚本。"""
|
|
15
|
+
ws = Path(target_dir).resolve()
|
|
16
|
+
scripts_dir = ws / "scripts"
|
|
17
|
+
common_dir = ws / "common"
|
|
18
|
+
storage_dir = ws / ".storage"
|
|
19
|
+
|
|
20
|
+
scripts_dir.mkdir(parents=True, exist_ok=True)
|
|
21
|
+
common_dir.mkdir(parents=True, exist_ok=True)
|
|
22
|
+
storage_dir.mkdir(parents=True, exist_ok=True)
|
|
23
|
+
|
|
24
|
+
# 写入 common/__init__.py
|
|
25
|
+
init_py = common_dir / "__init__.py"
|
|
26
|
+
if not init_py.exists():
|
|
27
|
+
init_py.write_text("# 公共工具包\n", encoding="utf-8")
|
|
28
|
+
|
|
29
|
+
# 写入 .env.example
|
|
30
|
+
env_example = ws / ".env.example"
|
|
31
|
+
if not env_example.exists():
|
|
32
|
+
env_example.write_text(
|
|
33
|
+
"# 工作区环境变量模板\n# APW_API_KEY=your_secret_key\n# BILIBILI_USERNAME=demo\n# BILIBILI_PASSWORD=secret\n",
|
|
34
|
+
encoding="utf-8",
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
# 写入示例脚本 scripts/example.py
|
|
38
|
+
example_script = scripts_dir / "example.py"
|
|
39
|
+
if not example_script.exists():
|
|
40
|
+
example_script.write_text(
|
|
41
|
+
'''"""
|
|
42
|
+
---
|
|
43
|
+
name: 示例演示任务
|
|
44
|
+
timeout: 30
|
|
45
|
+
params:
|
|
46
|
+
keyword:
|
|
47
|
+
type: string
|
|
48
|
+
default: "Playwright"
|
|
49
|
+
description: 搜索关键字
|
|
50
|
+
---
|
|
51
|
+
"""
|
|
52
|
+
async def main(params: dict) -> dict:
|
|
53
|
+
keyword = params.get("keyword", "")
|
|
54
|
+
print(f"[example] 正在执行任务,关键字: {keyword}")
|
|
55
|
+
return {"ok": True, "keyword": keyword}
|
|
56
|
+
|
|
57
|
+
if __name__ == "__main__":
|
|
58
|
+
import asyncio
|
|
59
|
+
asyncio.run(main({"keyword": "LocalTest"}))
|
|
60
|
+
''',
|
|
61
|
+
encoding="utf-8",
|
|
62
|
+
)
|
|
63
|
+
print(f"✅ 工作区初始化完成: {ws}")
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def start_server(host: str, port: int, workdir: str) -> None:
|
|
67
|
+
"""启动 Web 界面与 API 守护进程。"""
|
|
68
|
+
ws = Path(workdir).resolve()
|
|
69
|
+
os.environ["SCRIPTS_DIR"] = str(ws / "scripts")
|
|
70
|
+
os.environ["DB_PATH"] = str(ws / "data.db")
|
|
71
|
+
os.environ["RUNNER_WORKSPACE"] = str(ws / ".runner")
|
|
72
|
+
|
|
73
|
+
import uvicorn
|
|
74
|
+
from app.main import create_app
|
|
75
|
+
|
|
76
|
+
app = create_app(
|
|
77
|
+
db_path=ws / "data.db",
|
|
78
|
+
scripts_dir=ws / "scripts",
|
|
79
|
+
runner_workspace=ws / ".runner",
|
|
80
|
+
)
|
|
81
|
+
print(f"🚀 auto-playwright-web 启动中: http://{host}:{port}")
|
|
82
|
+
uvicorn.run(app, host=host, port=port)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def run_script_cli(script_code: str, params_str: str, workdir: str) -> None:
|
|
86
|
+
"""在终端直接触发指定脚本执行。"""
|
|
87
|
+
ws = Path(workdir).resolve()
|
|
88
|
+
from app.repositories.sqlite import SQLiteRepository
|
|
89
|
+
from app.service.execution_service import ExecutionService
|
|
90
|
+
from app.service.script_loader import ScriptLoader
|
|
91
|
+
|
|
92
|
+
repo = SQLiteRepository(ws / "data.db")
|
|
93
|
+
loader = ScriptLoader(ws / "scripts")
|
|
94
|
+
service = ExecutionService(
|
|
95
|
+
repository=repo,
|
|
96
|
+
script_loader=loader,
|
|
97
|
+
runner_workspace=ws / ".runner",
|
|
98
|
+
workspace_dir=ws,
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
try:
|
|
102
|
+
params = json.loads(params_str) if params_str else {}
|
|
103
|
+
except Exception as e:
|
|
104
|
+
print(f"❌ 参数解析失败,请提供合法的 JSON 字符串: {e}", file=sys.stderr)
|
|
105
|
+
sys.exit(1)
|
|
106
|
+
|
|
107
|
+
async def _execute():
|
|
108
|
+
await repo.init_db()
|
|
109
|
+
execution = await service.execute_sync(script_code, params, source="cli")
|
|
110
|
+
print("---------- 执行结果 ----------")
|
|
111
|
+
print(f"状态: {execution.status}")
|
|
112
|
+
print(f"耗时: {execution.duration_ms}ms")
|
|
113
|
+
if execution.stdout_text:
|
|
114
|
+
print(f"输出:\n{execution.stdout_text}")
|
|
115
|
+
if execution.result_json:
|
|
116
|
+
print(f"返回值:\n{json.dumps(execution.result_json, ensure_ascii=False, indent=2)}")
|
|
117
|
+
if execution.error_stack:
|
|
118
|
+
print(f"异常堆栈:\n{execution.error_stack}", file=sys.stderr)
|
|
119
|
+
|
|
120
|
+
asyncio.run(_execute())
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def main() -> None:
|
|
124
|
+
parser = argparse.ArgumentParser(prog="apw", description="Auto-Playwright-Web 命令行工具")
|
|
125
|
+
subparsers = parser.add_subparsers(dest="subcommand", help="子命令")
|
|
126
|
+
|
|
127
|
+
# start
|
|
128
|
+
p_start = subparsers.add_parser("start", help="启动 Web 界面与 API 服务")
|
|
129
|
+
p_start.add_argument("--host", default="0.0.0.0", help="监听地址 (默认 0.0.0.0)")
|
|
130
|
+
p_start.add_argument("--port", type=int, default=8000, help="端口 (默认 8000)")
|
|
131
|
+
p_start.add_argument("--workdir", default=".", help="工作区目录 (默认当前目录)")
|
|
132
|
+
|
|
133
|
+
# init
|
|
134
|
+
p_init = subparsers.add_parser("init", help="初始化标准工作区")
|
|
135
|
+
p_init.add_argument("--workdir", default=".", help="工作区目录 (默认当前目录)")
|
|
136
|
+
|
|
137
|
+
# run
|
|
138
|
+
p_run = subparsers.add_parser("run", help="命令行直接执行脚本")
|
|
139
|
+
p_run.add_argument("script_code", help="脚本编码")
|
|
140
|
+
p_run.add_argument("--params", default="{}", help="入参 JSON 字符串")
|
|
141
|
+
p_run.add_argument("--workdir", default=".", help="工作区目录 (默认当前目录)")
|
|
142
|
+
|
|
143
|
+
# version
|
|
144
|
+
subparsers.add_parser("version", help="查看当前版本")
|
|
145
|
+
|
|
146
|
+
args = parser.parse_args()
|
|
147
|
+
|
|
148
|
+
if args.subcommand == "start":
|
|
149
|
+
start_server(args.host, args.port, args.workdir)
|
|
150
|
+
elif args.subcommand == "init":
|
|
151
|
+
init_workspace(args.workdir)
|
|
152
|
+
elif args.subcommand == "run":
|
|
153
|
+
run_script_cli(args.script_code, args.params, args.workdir)
|
|
154
|
+
elif args.subcommand == "version":
|
|
155
|
+
print(f"auto-playwright-web v{__version__}")
|
|
156
|
+
else:
|
|
157
|
+
parser.print_help()
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
if __name__ == "__main__":
|
|
161
|
+
main()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""配置模块:按运行环境组织应用配置。"""
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@dataclass(slots=True)
|
|
6
|
+
class Settings:
|
|
7
|
+
"""应用配置。"""
|
|
8
|
+
|
|
9
|
+
app_name: str = os.getenv("APP_NAME", "auto-playwright-web")
|
|
10
|
+
app_env: str = os.getenv("APP_ENV", "prod")
|
|
11
|
+
enable_docs: bool = os.getenv("APP_ENABLE_DOCS", "true").lower() == "true"
|
|
12
|
+
api_key: str = os.getenv("APW_API_KEY", "")
|
|
13
|
+
runner_workspace: str = os.getenv("RUNNER_WORKSPACE", ".runner")
|
|
14
|
+
scripts_dir: str = os.getenv("SCRIPTS_DIR", "scripts")
|
|
15
|
+
db_path: str = os.getenv("DB_PATH", "data.db")
|
|
16
|
+
max_concurrent_executions: int = int(os.getenv("MAX_CONCURRENT_EXECUTIONS", "3"))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
settings = Settings()
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from fastapi import Depends, Header, HTTPException, Request, status
|
|
4
|
+
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
|
5
|
+
|
|
6
|
+
from app.domain import User
|
|
7
|
+
from app.repositories.base import Repository
|
|
8
|
+
from app.security import decode_access_token
|
|
9
|
+
from app.service.auth_service import AuthService
|
|
10
|
+
from app.service.ai_service import AIScriptService
|
|
11
|
+
from app.service.common_library_service import CommonLibraryService
|
|
12
|
+
from app.service.execution_service import ExecutionService
|
|
13
|
+
from app.service.schedule_service import ScheduleService
|
|
14
|
+
from app.service.script_service import ScriptService
|
|
15
|
+
from app.service.token_service import TokenService
|
|
16
|
+
from app.service.user_env_var_service import UserEnvVarService
|
|
17
|
+
from app.service.webhook_service import WebhookService
|
|
18
|
+
|
|
19
|
+
bearer_scheme = HTTPBearer(auto_error=False)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def get_repository(request: Request) -> Repository:
|
|
23
|
+
"""从应用状态获取仓储。"""
|
|
24
|
+
|
|
25
|
+
return request.app.state.repository
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def get_auth_service(request: Request) -> AuthService:
|
|
29
|
+
"""从应用状态获取认证服务。"""
|
|
30
|
+
|
|
31
|
+
return request.app.state.auth_service
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def get_ai_script_service(request: Request) -> AIScriptService:
|
|
35
|
+
"""从应用状态获取 AI 脚本生成服务。"""
|
|
36
|
+
|
|
37
|
+
return request.app.state.ai_script_service
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def get_script_service(request: Request) -> ScriptService:
|
|
41
|
+
"""从应用状态获取脚本服务。"""
|
|
42
|
+
|
|
43
|
+
return request.app.state.script_service
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def get_common_library_service(request: Request) -> CommonLibraryService:
|
|
47
|
+
"""从应用状态获取公共函数库服务。"""
|
|
48
|
+
|
|
49
|
+
return request.app.state.common_library_service
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def get_execution_service(request: Request) -> ExecutionService:
|
|
53
|
+
"""从应用状态获取执行服务。"""
|
|
54
|
+
|
|
55
|
+
return request.app.state.execution_service
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def get_token_service(request: Request) -> TokenService:
|
|
59
|
+
"""从应用状态获取 Token 服务。"""
|
|
60
|
+
|
|
61
|
+
return request.app.state.token_service
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def get_schedule_service(request: Request) -> ScheduleService:
|
|
65
|
+
"""从应用状态获取定时任务服务。"""
|
|
66
|
+
|
|
67
|
+
return request.app.state.schedule_service
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def get_webhook_service(request: Request) -> WebhookService:
|
|
71
|
+
"""从应用状态获取 Webhook 服务。"""
|
|
72
|
+
|
|
73
|
+
return request.app.state.webhook_service
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def get_user_env_var_service(request: Request) -> UserEnvVarService:
|
|
77
|
+
"""从应用状态获取用户环境变量服务。"""
|
|
78
|
+
|
|
79
|
+
return request.app.state.user_env_var_service
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
async def get_current_user(
|
|
83
|
+
credentials: HTTPAuthorizationCredentials | None = Depends(bearer_scheme),
|
|
84
|
+
auth_service: AuthService = Depends(get_auth_service),
|
|
85
|
+
) -> User:
|
|
86
|
+
"""解析 Bearer JWT 并返回当前用户。"""
|
|
87
|
+
|
|
88
|
+
if not credentials:
|
|
89
|
+
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="请先登录")
|
|
90
|
+
payload = decode_access_token(credentials.credentials)
|
|
91
|
+
return await auth_service.get_user(int(payload["sub"]))
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
async def get_open_api_token(x_api_token: str | None = Header(default=None)) -> str:
|
|
95
|
+
"""读取开放 API Token 请求头。"""
|
|
96
|
+
|
|
97
|
+
if not x_api_token:
|
|
98
|
+
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="缺少 X-API-Token")
|
|
99
|
+
return x_api_token
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
async def get_current_user_or_token(
|
|
103
|
+
request: Request,
|
|
104
|
+
credentials: HTTPAuthorizationCredentials | None = Depends(bearer_scheme),
|
|
105
|
+
x_api_token: str | None = Header(default=None),
|
|
106
|
+
auth_service: AuthService = Depends(get_auth_service),
|
|
107
|
+
token_service: TokenService = Depends(get_token_service),
|
|
108
|
+
) -> User:
|
|
109
|
+
"""统一认证:优先 Bearer JWT,其次 X-API-Token,并校验接口授权范围。"""
|
|
110
|
+
|
|
111
|
+
if credentials:
|
|
112
|
+
payload = decode_access_token(credentials.credentials)
|
|
113
|
+
return await auth_service.get_user(int(payload["sub"]))
|
|
114
|
+
|
|
115
|
+
if x_api_token:
|
|
116
|
+
user, scopes = await token_service.authenticate_token_scopes(x_api_token)
|
|
117
|
+
if scopes:
|
|
118
|
+
route_key = f"{request.method}:{request.scope['route'].path}"
|
|
119
|
+
if route_key not in scopes:
|
|
120
|
+
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="API Token 无权访问此接口")
|
|
121
|
+
return user
|
|
122
|
+
|
|
123
|
+
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="请先登录或提供 API Token")
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def now() -> datetime:
|
|
9
|
+
"""返回当前时间。"""
|
|
10
|
+
return datetime.now()
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass(slots=True)
|
|
14
|
+
class Execution:
|
|
15
|
+
"""脚本执行记录。"""
|
|
16
|
+
|
|
17
|
+
id: int
|
|
18
|
+
execution_id: str
|
|
19
|
+
script_id: int
|
|
20
|
+
script_code: str
|
|
21
|
+
user_id: int = 1
|
|
22
|
+
source: str = "manual"
|
|
23
|
+
params_json: dict[str, Any] = field(default_factory=dict)
|
|
24
|
+
status: str = "pending"
|
|
25
|
+
result_json: dict[str, Any] | None = None
|
|
26
|
+
stdout_text: str = ""
|
|
27
|
+
stderr_text: str = ""
|
|
28
|
+
error_stack: str = ""
|
|
29
|
+
duration_ms: int = 0
|
|
30
|
+
started_time: datetime | None = None
|
|
31
|
+
finished_time: datetime | None = None
|
|
32
|
+
create_time: datetime = field(default_factory=now)
|
|
33
|
+
update_time: datetime = field(default_factory=now)
|
|
34
|
+
create_by: str = ""
|
|
35
|
+
update_by: str = ""
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass(slots=True)
|
|
39
|
+
class Schedule:
|
|
40
|
+
"""定时任务。"""
|
|
41
|
+
|
|
42
|
+
id: int
|
|
43
|
+
user_id: int
|
|
44
|
+
script_id: int
|
|
45
|
+
script_code: str
|
|
46
|
+
name: str
|
|
47
|
+
cron_expr: str
|
|
48
|
+
params_json: dict[str, Any]
|
|
49
|
+
enabled: bool = True
|
|
50
|
+
last_run_time: datetime | None = None
|
|
51
|
+
next_run_time: datetime | None = None
|
|
52
|
+
create_time: datetime = field(default_factory=now)
|
|
53
|
+
update_time: datetime = field(default_factory=now)
|
|
54
|
+
create_by: str = ""
|
|
55
|
+
update_by: str = ""
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@dataclass(slots=True)
|
|
59
|
+
class User:
|
|
60
|
+
"""单用户身份兼容。"""
|
|
61
|
+
|
|
62
|
+
id: int = 1
|
|
63
|
+
username: str = "admin"
|
|
64
|
+
role: str = "admin"
|
|
65
|
+
enabled: bool = True
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from contextlib import asynccontextmanager
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from dotenv import load_dotenv
|
|
8
|
+
from fastapi import FastAPI, HTTPException, Request, status
|
|
9
|
+
from fastapi.middleware.cors import CORSMiddleware
|
|
10
|
+
from fastapi.responses import FileResponse, JSONResponse
|
|
11
|
+
from fastapi.staticfiles import StaticFiles
|
|
12
|
+
|
|
13
|
+
from app.configs.config import settings
|
|
14
|
+
from app.repositories.sqlite import SQLiteRepository
|
|
15
|
+
from app.routers import executions, open_api, schedules, scripts, user_env_vars
|
|
16
|
+
from app.scheduler import PlatformScheduler
|
|
17
|
+
from app.service.execution_service import ExecutionService
|
|
18
|
+
from app.service.script_loader import ScriptLoader
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def create_app(
|
|
22
|
+
db_path: Path | str | None = None,
|
|
23
|
+
scripts_dir: Path | str | None = None,
|
|
24
|
+
runner_workspace: Path | str | None = None,
|
|
25
|
+
start_scheduler: bool = True,
|
|
26
|
+
) -> FastAPI:
|
|
27
|
+
"""创建并装配轻量级 FastAPI 应用。"""
|
|
28
|
+
|
|
29
|
+
# 加载本地 .env
|
|
30
|
+
load_dotenv()
|
|
31
|
+
|
|
32
|
+
resolved_db_path = Path(db_path or settings.db_path)
|
|
33
|
+
resolved_scripts_dir = Path(scripts_dir or settings.scripts_dir)
|
|
34
|
+
resolved_workspace = Path(runner_workspace or settings.runner_workspace)
|
|
35
|
+
|
|
36
|
+
repository = SQLiteRepository(resolved_db_path)
|
|
37
|
+
script_loader = ScriptLoader(resolved_scripts_dir)
|
|
38
|
+
execution_service = ExecutionService(
|
|
39
|
+
repository=repository,
|
|
40
|
+
script_loader=script_loader,
|
|
41
|
+
runner_workspace=resolved_workspace,
|
|
42
|
+
max_concurrent_executions=settings.max_concurrent_executions,
|
|
43
|
+
)
|
|
44
|
+
platform_scheduler = PlatformScheduler(repository, execution_service)
|
|
45
|
+
|
|
46
|
+
@asynccontextmanager
|
|
47
|
+
async def lifespan(app: FastAPI):
|
|
48
|
+
await repository.init_db()
|
|
49
|
+
if start_scheduler:
|
|
50
|
+
await platform_scheduler.start()
|
|
51
|
+
try:
|
|
52
|
+
yield
|
|
53
|
+
finally:
|
|
54
|
+
await platform_scheduler.stop()
|
|
55
|
+
|
|
56
|
+
app = FastAPI(
|
|
57
|
+
title=settings.app_name,
|
|
58
|
+
docs_url="/docs" if settings.enable_docs else None,
|
|
59
|
+
redoc_url="/redoc" if settings.enable_docs else None,
|
|
60
|
+
openapi_url="/openapi.json" if settings.enable_docs else None,
|
|
61
|
+
lifespan=lifespan,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
# 全局 API Key 拦截(如果配置了 APW_API_KEY)
|
|
65
|
+
@app.middleware("http")
|
|
66
|
+
async def api_key_auth_middleware(request: Request, call_next):
|
|
67
|
+
api_key = settings.api_key or os.getenv("APW_API_KEY", "")
|
|
68
|
+
if api_key and request.url.path.startswith("/api/"):
|
|
69
|
+
client_key = request.headers.get("X-API-Key") or request.query_params.get("api_key")
|
|
70
|
+
if client_key != api_key:
|
|
71
|
+
return JSONResponse(status_code=401, content={"detail": "无效的 API Key"})
|
|
72
|
+
return await call_next(request)
|
|
73
|
+
|
|
74
|
+
app.add_middleware(
|
|
75
|
+
CORSMiddleware,
|
|
76
|
+
allow_origins=["*"],
|
|
77
|
+
allow_credentials=True,
|
|
78
|
+
allow_methods=["*"],
|
|
79
|
+
allow_headers=["*"],
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
app.state.repository = repository
|
|
83
|
+
app.state.script_loader = script_loader
|
|
84
|
+
app.state.execution_service = execution_service
|
|
85
|
+
app.state.platform_scheduler = platform_scheduler
|
|
86
|
+
|
|
87
|
+
app.include_router(scripts.router)
|
|
88
|
+
app.include_router(open_api.router)
|
|
89
|
+
app.include_router(executions.router)
|
|
90
|
+
app.include_router(schedules.router)
|
|
91
|
+
app.include_router(user_env_vars.router)
|
|
92
|
+
|
|
93
|
+
# 挂载内置静态前端(若存在)
|
|
94
|
+
static_dir = Path(__file__).parent / "static"
|
|
95
|
+
if static_dir.exists():
|
|
96
|
+
app.mount("/assets", StaticFiles(directory=static_dir / "assets"), name="assets")
|
|
97
|
+
|
|
98
|
+
@app.get("/{full_path:path}", include_in_schema=False)
|
|
99
|
+
async def serve_spa(full_path: str):
|
|
100
|
+
if full_path.startswith("api/") or full_path.startswith("docs") or full_path.startswith("openapi"):
|
|
101
|
+
raise HTTPException(status_code=404, detail="Not Found")
|
|
102
|
+
target = static_dir / full_path
|
|
103
|
+
if target.is_file():
|
|
104
|
+
return FileResponse(target)
|
|
105
|
+
return FileResponse(static_dir / "index.html")
|
|
106
|
+
|
|
107
|
+
return app
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
app = create_app()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""仓储模块:隔离业务逻辑与持久化实现。"""
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from typing import Protocol
|
|
5
|
+
|
|
6
|
+
from app.domain import Execution, Schedule
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Repository(Protocol):
|
|
10
|
+
"""仓储接口协议:SQLiteRepository 实现此协议。"""
|
|
11
|
+
|
|
12
|
+
async def init_db(self) -> None: ...
|
|
13
|
+
|
|
14
|
+
async def create_execution(self, execution: Execution) -> Execution: ...
|
|
15
|
+
|
|
16
|
+
async def update_execution(self, execution: Execution) -> Execution: ...
|
|
17
|
+
|
|
18
|
+
async def get_execution(self, execution_id: str | int) -> Execution | None: ...
|
|
19
|
+
|
|
20
|
+
async def list_executions(
|
|
21
|
+
self,
|
|
22
|
+
script_code: str | None = None,
|
|
23
|
+
start_time: datetime | None = None,
|
|
24
|
+
end_time: datetime | None = None,
|
|
25
|
+
limit: int = 100,
|
|
26
|
+
) -> list[Execution]: ...
|
|
27
|
+
|
|
28
|
+
async def create_schedule(self, schedule: Schedule) -> Schedule: ...
|
|
29
|
+
|
|
30
|
+
async def update_schedule(self, schedule_id: int, schedule: Schedule) -> Schedule: ...
|
|
31
|
+
|
|
32
|
+
async def delete_schedule(self, schedule_id: int) -> None: ...
|
|
33
|
+
|
|
34
|
+
async def get_schedule(self, schedule_id: int) -> Schedule | None: ...
|
|
35
|
+
|
|
36
|
+
async def list_schedules(self) -> list[Schedule]: ...
|
|
37
|
+
|
|
38
|
+
async def list_enabled_schedules(self) -> list[Schedule]: ...
|