funcron 0.5.10__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.
Files changed (178) hide show
  1. funcron-0.5.10/.env.example +29 -0
  2. funcron-0.5.10/.gitignore +43 -0
  3. funcron-0.5.10/CHANGELOG.md +240 -0
  4. funcron-0.5.10/LICENSE +21 -0
  5. funcron-0.5.10/PKG-INFO +192 -0
  6. funcron-0.5.10/README.md +158 -0
  7. funcron-0.5.10/pyproject.toml +97 -0
  8. funcron-0.5.10/scripts/airflow-init.sh +39 -0
  9. funcron-0.5.10/scripts/lib/funcron-airflow-env.sh +107 -0
  10. funcron-0.5.10/scripts/lib/funcron-common.sh +256 -0
  11. funcron-0.5.10/scripts/services/airflow.sh +136 -0
  12. funcron-0.5.10/scripts/services/coin.sh +103 -0
  13. funcron-0.5.10/scripts/services/server.sh +113 -0
  14. funcron-0.5.10/scripts/setup.sh +97 -0
  15. funcron-0.5.10/src/funcron/__init__.py +0 -0
  16. funcron-0.5.10/src/funcron/airflow/.gitignore +2 -0
  17. funcron-0.5.10/src/funcron/airflow/dags/notecoin/hello.py +18 -0
  18. funcron-0.5.10/src/funcron/airflow/dags/notecoin/notecoin_binance_load_daily.py +52 -0
  19. funcron-0.5.10/src/funcron/airflow/dags/notecoin/notecoin_binance_stragegy.py +62 -0
  20. funcron-0.5.10/src/funcron/center/__init__.py +0 -0
  21. funcron-0.5.10/src/funcron/center/app.py +46 -0
  22. funcron-0.5.10/src/funcron/center/common/__init__.py +0 -0
  23. funcron-0.5.10/src/funcron/center/common/config/__init__.py +3 -0
  24. funcron-0.5.10/src/funcron/center/common/config/config.py +177 -0
  25. funcron-0.5.10/src/funcron/center/common/database.py +9 -0
  26. funcron-0.5.10/src/funcron/center/common/functions.py +122 -0
  27. funcron-0.5.10/src/funcron/center/common/scheduler/__init__.py +6 -0
  28. funcron-0.5.10/src/funcron/center/common/scheduler/cu_background_scheduler.py +161 -0
  29. funcron-0.5.10/src/funcron/center/common/scheduler/cu_gevent_scheduler.py +161 -0
  30. funcron-0.5.10/src/funcron/center/models.py +108 -0
  31. funcron-0.5.10/src/funcron/center/pages/__init__.py +65 -0
  32. funcron-0.5.10/src/funcron/center/pages/api/__init__.py +6 -0
  33. funcron-0.5.10/src/funcron/center/pages/api/core.py +5 -0
  34. funcron-0.5.10/src/funcron/center/pages/api/views.py +256 -0
  35. funcron-0.5.10/src/funcron/center/pages/crons/__init__.py +5 -0
  36. funcron-0.5.10/src/funcron/center/pages/crons/core.py +192 -0
  37. funcron-0.5.10/src/funcron/center/pages/decorated.py +88 -0
  38. funcron-0.5.10/src/funcron/center/pages/main/__init__.py +7 -0
  39. funcron-0.5.10/src/funcron/center/pages/main/core.py +5 -0
  40. funcron-0.5.10/src/funcron/center/pages/main/errors.py +18 -0
  41. funcron-0.5.10/src/funcron/center/pages/main/views.py +464 -0
  42. funcron-0.5.10/src/funcron/center/static/__init__.py +0 -0
  43. funcron-0.5.10/src/funcron/center/static/images/__init__.py +0 -0
  44. funcron-0.5.10/src/funcron/center/static/images/mini_code.png +0 -0
  45. funcron-0.5.10/src/funcron/center/static/jquery.js +2 -0
  46. funcron-0.5.10/src/funcron/center/static/js/__init__.py +0 -0
  47. funcron-0.5.10/src/funcron/center/static/js/ajaxForm.js +1078 -0
  48. funcron-0.5.10/src/funcron/center/static/js/artDialog/__init__.py +0 -0
  49. funcron-0.5.10/src/funcron/center/static/js/artDialog/artDialog.extend.js +77 -0
  50. funcron-0.5.10/src/funcron/center/static/js/artDialog/artDialog.js +9 -0
  51. funcron-0.5.10/src/funcron/center/static/js/artDialog/iframeTools.js +10 -0
  52. funcron-0.5.10/src/funcron/center/static/js/artDialog/skins/__init__.py +0 -0
  53. funcron-0.5.10/src/funcron/center/static/js/artDialog/skins/blue/__init__.py +0 -0
  54. funcron-0.5.10/src/funcron/center/static/js/artDialog/skins/blue/bg.png +0 -0
  55. funcron-0.5.10/src/funcron/center/static/js/artDialog/skins/blue/bg2.png +0 -0
  56. funcron-0.5.10/src/funcron/center/static/js/artDialog/skins/blue/bg_css3.png +0 -0
  57. funcron-0.5.10/src/funcron/center/static/js/artDialog/skins/blue/bg_css3_2.png +0 -0
  58. funcron-0.5.10/src/funcron/center/static/js/artDialog/skins/blue/ie6/__init__.py +0 -0
  59. funcron-0.5.10/src/funcron/center/static/js/artDialog/skins/blue/ie6/close.hover.png +0 -0
  60. funcron-0.5.10/src/funcron/center/static/js/artDialog/skins/blue/ie6/close.png +0 -0
  61. funcron-0.5.10/src/funcron/center/static/js/artDialog/skins/blue/ie6/e.png +0 -0
  62. funcron-0.5.10/src/funcron/center/static/js/artDialog/skins/blue/ie6/n.png +0 -0
  63. funcron-0.5.10/src/funcron/center/static/js/artDialog/skins/blue/ie6/ne.png +0 -0
  64. funcron-0.5.10/src/funcron/center/static/js/artDialog/skins/blue/ie6/nw.png +0 -0
  65. funcron-0.5.10/src/funcron/center/static/js/artDialog/skins/blue/ie6/s.png +0 -0
  66. funcron-0.5.10/src/funcron/center/static/js/artDialog/skins/blue/ie6/se.png +0 -0
  67. funcron-0.5.10/src/funcron/center/static/js/artDialog/skins/blue/ie6/sw.png +0 -0
  68. funcron-0.5.10/src/funcron/center/static/js/artDialog/skins/blue/ie6/w.png +0 -0
  69. funcron-0.5.10/src/funcron/center/static/js/artDialog/skins/blue.css +79 -0
  70. funcron-0.5.10/src/funcron/center/static/js/artDialog/skins/default.css +67 -0
  71. funcron-0.5.10/src/funcron/center/static/js/artDialog/skins/icons/error.png +0 -0
  72. funcron-0.5.10/src/funcron/center/static/js/artDialog/skins/icons/face-sad.png +0 -0
  73. funcron-0.5.10/src/funcron/center/static/js/artDialog/skins/icons/face-smile.png +0 -0
  74. funcron-0.5.10/src/funcron/center/static/js/artDialog/skins/icons/loading.gif +0 -0
  75. funcron-0.5.10/src/funcron/center/static/js/artDialog/skins/icons/question.png +0 -0
  76. funcron-0.5.10/src/funcron/center/static/js/artDialog/skins/icons/succeed.png +0 -0
  77. funcron-0.5.10/src/funcron/center/static/js/artDialog/skins/icons/warning.png +0 -0
  78. funcron-0.5.10/src/funcron/center/static/js/common.js +765 -0
  79. funcron-0.5.10/src/funcron/center/static/js/datePicker/__init__.py +0 -0
  80. funcron-0.5.10/src/funcron/center/static/js/datePicker/bg.png +0 -0
  81. funcron-0.5.10/src/funcron/center/static/js/datePicker/datePicker.js +839 -0
  82. funcron-0.5.10/src/funcron/center/static/js/datePicker/style.css +182 -0
  83. funcron-0.5.10/src/funcron/center/static/js/jquery.js +2 -0
  84. funcron-0.5.10/src/funcron/center/static/js/jquery.validate/__init__.py +0 -0
  85. funcron-0.5.10/src/funcron/center/static/js/jquery.validate/additional-methods.js +1056 -0
  86. funcron-0.5.10/src/funcron/center/static/js/jquery.validate/jquery.validate.js +1533 -0
  87. funcron-0.5.10/src/funcron/center/static/js/md5.js +1 -0
  88. funcron-0.5.10/src/funcron/center/static/js/noty/__init__.py +0 -0
  89. funcron-0.5.10/src/funcron/center/static/js/noty/noty.js +37 -0
  90. funcron-0.5.10/src/funcron/center/static/js/qrcode.min.js +1 -0
  91. funcron-0.5.10/src/funcron/center/static/js/requests.js +53 -0
  92. funcron-0.5.10/src/funcron/center/static/js/signs.js +1 -0
  93. funcron-0.5.10/src/funcron/center/static/js/simpleboot/__init__.py +0 -0
  94. funcron-0.5.10/src/funcron/center/static/js/simpleboot/bootstrap/__init__.py +0 -0
  95. funcron-0.5.10/src/funcron/center/static/js/simpleboot/bootstrap/css/bootstrap-responsive.min.css +9 -0
  96. funcron-0.5.10/src/funcron/center/static/js/simpleboot/bootstrap/js/__init__.py +0 -0
  97. funcron-0.5.10/src/funcron/center/static/js/simpleboot/bootstrap/js/bootstrap.min.js +6 -0
  98. funcron-0.5.10/src/funcron/center/static/js/simpleboot/css/__init__.py +0 -0
  99. funcron-0.5.10/src/funcron/center/static/js/simpleboot/css/simplebootadmin.css +7 -0
  100. funcron-0.5.10/src/funcron/center/static/js/simpleboot/css/simplebootadminindex-ie.css +8 -0
  101. funcron-0.5.10/src/funcron/center/static/js/simpleboot/font-awesome/4.2.0/__init__.py +0 -0
  102. funcron-0.5.10/src/funcron/center/static/js/simpleboot/font-awesome/4.2.0/css/font-awesome-ie7.min.css +14 -0
  103. funcron-0.5.10/src/funcron/center/static/js/simpleboot/font-awesome/4.2.0/css/font-awesome.min.css +4 -0
  104. funcron-0.5.10/src/funcron/center/static/js/simpleboot/font-awesome/4.2.0/fonts/FontAwesome.otf +0 -0
  105. funcron-0.5.10/src/funcron/center/static/js/simpleboot/font-awesome/4.2.0/fonts/__init__.py +0 -0
  106. funcron-0.5.10/src/funcron/center/static/js/simpleboot/font-awesome/4.2.0/fonts/fontawesome-webfont.eot +0 -0
  107. funcron-0.5.10/src/funcron/center/static/js/simpleboot/font-awesome/4.2.0/fonts/fontawesome-webfont.svg +520 -0
  108. funcron-0.5.10/src/funcron/center/static/js/simpleboot/font-awesome/4.2.0/fonts/fontawesome-webfont.ttf +0 -0
  109. funcron-0.5.10/src/funcron/center/static/js/simpleboot/font-awesome/4.2.0/fonts/fontawesome-webfont.woff +0 -0
  110. funcron-0.5.10/src/funcron/center/static/js/simpleboot/font-awesome/4.4.0/__init__.py +0 -0
  111. funcron-0.5.10/src/funcron/center/static/js/simpleboot/font-awesome/4.4.0/css/font-awesome-ie7.min.css +13 -0
  112. funcron-0.5.10/src/funcron/center/static/js/simpleboot/font-awesome/4.4.0/css/font-awesome.min.css +4 -0
  113. funcron-0.5.10/src/funcron/center/static/js/simpleboot/font-awesome/4.4.0/fonts/FontAwesome.otf +0 -0
  114. funcron-0.5.10/src/funcron/center/static/js/simpleboot/font-awesome/4.4.0/fonts/__init__.py +0 -0
  115. funcron-0.5.10/src/funcron/center/static/js/simpleboot/font-awesome/4.4.0/fonts/fontawesome-webfont.eot +0 -0
  116. funcron-0.5.10/src/funcron/center/static/js/simpleboot/font-awesome/4.4.0/fonts/fontawesome-webfont.svg +640 -0
  117. funcron-0.5.10/src/funcron/center/static/js/simpleboot/font-awesome/4.4.0/fonts/fontawesome-webfont.ttf +0 -0
  118. funcron-0.5.10/src/funcron/center/static/js/simpleboot/font-awesome/4.4.0/fonts/fontawesome-webfont.woff +0 -0
  119. funcron-0.5.10/src/funcron/center/static/js/simpleboot/font-awesome/4.4.0/fonts/fontawesome-webfont.woff2 +0 -0
  120. funcron-0.5.10/src/funcron/center/static/js/simpleboot/font-awesome/__init__.py +0 -0
  121. funcron-0.5.10/src/funcron/center/static/js/simpleboot/themes/__init__.py +0 -0
  122. funcron-0.5.10/src/funcron/center/static/js/simpleboot/themes/bluesky/__init__.py +0 -0
  123. funcron-0.5.10/src/funcron/center/static/js/simpleboot/themes/bluesky/img/glyphicons-halflings-white.png +0 -0
  124. funcron-0.5.10/src/funcron/center/static/js/simpleboot/themes/bluesky/img/glyphicons-halflings.png +0 -0
  125. funcron-0.5.10/src/funcron/center/static/js/simpleboot/themes/bluesky/loading.gif +0 -0
  126. funcron-0.5.10/src/funcron/center/static/js/simpleboot/themes/bluesky/loadingbg.png +0 -0
  127. funcron-0.5.10/src/funcron/center/static/js/simpleboot/themes/bluesky/simplebootadminindex.min.css +1 -0
  128. funcron-0.5.10/src/funcron/center/static/js/simpleboot/themes/bluesky/theme.min.css +5857 -0
  129. funcron-0.5.10/src/funcron/center/static/js/simpleboot/themes/flat/__init__.py +0 -0
  130. funcron-0.5.10/src/funcron/center/static/js/simpleboot/themes/flat/img/glyphicons-halflings-white.png +0 -0
  131. funcron-0.5.10/src/funcron/center/static/js/simpleboot/themes/flat/img/glyphicons-halflings.png +0 -0
  132. funcron-0.5.10/src/funcron/center/static/js/simpleboot/themes/flat/loading.gif +0 -0
  133. funcron-0.5.10/src/funcron/center/static/js/simpleboot/themes/flat/loadingbg.png +0 -0
  134. funcron-0.5.10/src/funcron/center/static/js/simpleboot/themes/flat/simplebootadminindex.min.css +1 -0
  135. funcron-0.5.10/src/funcron/center/static/js/simpleboot/themes/flat/theme.min.css +9 -0
  136. funcron-0.5.10/src/funcron/center/static/js/wind.js +836 -0
  137. funcron-0.5.10/src/funcron/center/static/vue.js +10710 -0
  138. funcron-0.5.10/src/funcron/center/templates/__init__.py +0 -0
  139. funcron-0.5.10/src/funcron/center/templates/admin_base.html +82 -0
  140. funcron-0.5.10/src/funcron/center/templates/admin_page.html +26 -0
  141. funcron-0.5.10/src/funcron/center/templates/api_doc.html +198 -0
  142. funcron-0.5.10/src/funcron/center/templates/check_pass.html +72 -0
  143. funcron-0.5.10/src/funcron/center/templates/cron_add.html +108 -0
  144. funcron-0.5.10/src/funcron/center/templates/cron_edit.html +119 -0
  145. funcron-0.5.10/src/funcron/center/templates/cron_list.html +134 -0
  146. funcron-0.5.10/src/funcron/center/templates/index.html +98 -0
  147. funcron-0.5.10/src/funcron/center/templates/job_log_all_list.html +124 -0
  148. funcron-0.5.10/src/funcron/center/templates/job_log_item_list.html +26 -0
  149. funcron-0.5.10/src/funcron/center/templates/job_log_list.html +38 -0
  150. funcron-0.5.10/src/funcron/center/utils/__init__.py +0 -0
  151. funcron-0.5.10/src/funcron/center/utils/json.py +63 -0
  152. funcron-0.5.10/src/funcron/center/utils/redis_cache.py +139 -0
  153. funcron-0.5.10/src/funcron/center/utils/times.py +46 -0
  154. funcron-0.5.10/src/funcron/py.typed +0 -0
  155. funcron-0.5.10/src/funcron/server/__init__.py +0 -0
  156. funcron-0.5.10/src/funcron/server/config.py +7 -0
  157. funcron-0.5.10/src/funcron/server/funcron_server.py +25 -0
  158. funcron-0.5.10/src/funcron/server/port_manage.py +113 -0
  159. funcron-0.5.10/src/funcron/server/script.py +81 -0
  160. funcron-0.5.10/src/funcron/tasks/__init__.py +5 -0
  161. funcron-0.5.10/src/funcron/tasks/core.py +68 -0
  162. funcron-0.5.10/src/funcron/tool/__init__.py +0 -0
  163. funcron-0.5.10/src/funcron/tool/mail.py +58 -0
  164. funcron-0.5.10/tests/conftest.py +24 -0
  165. funcron-0.5.10/tests/test_airflow_env.py +229 -0
  166. funcron-0.5.10/tests/test_config_defaults.py +85 -0
  167. funcron-0.5.10/tests/test_decorated.py +91 -0
  168. funcron-0.5.10/tests/test_error_handlers.py +44 -0
  169. funcron-0.5.10/tests/test_functions.py +46 -0
  170. funcron-0.5.10/tests/test_mail.py +86 -0
  171. funcron-0.5.10/tests/test_models.py +46 -0
  172. funcron-0.5.10/tests/test_port_manage.py +23 -0
  173. funcron-0.5.10/tests/test_redis_cache.py +111 -0
  174. funcron-0.5.10/tests/test_script_cli.py +54 -0
  175. funcron-0.5.10/tests/test_setup_scripts.py +322 -0
  176. funcron-0.5.10/tests/test_smoke.py +2 -0
  177. funcron-0.5.10/tests/test_times.py +39 -0
  178. funcron-0.5.10/tests/test_version_consistency.py +18 -0
@@ -0,0 +1,29 @@
1
+ # funcron 运行时环境变量模板。
2
+ #
3
+ # 用法:复制为仓库根目录的 .env(该文件已在 .gitignore 里,不会进版本库),
4
+ # 按需填写后由 scripts/setup.sh 启动的服务自动加载。
5
+ # 也可以用 FUNCRON_ENV_FILE 指向别处的文件。
6
+ #
7
+ # 注意:这里只放 Airflow 的配置。funcron 自身的数据库密码、Web 登录口令、Redis 密码、
8
+ # 通知 API key、API access token、Flask secret key 走 funsecret 下发,不在本文件里,
9
+ # 具体密钥路径见 README「凭据配置」一节。
10
+
11
+ # ---- Airflow 元数据库(prod 必填,dev 留空则回落到 AIRFLOW_HOME 下的 SQLite)----
12
+ # AIRFLOW__DATABASE__SQL_ALCHEMY_CONN=mysql+pymysql://<user>:<password>@<host>:3306/<db>?charset=utf8
13
+
14
+ # ---- Airflow 凭据(没有默认值,留空即不启用)----
15
+ # 用 `python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"` 生成
16
+ # AIRFLOW__CORE__FERNET_KEY=
17
+ # 用 `python -c "import secrets; print(secrets.token_urlsafe(32))"` 生成
18
+ # AIRFLOW__API__SECRET_KEY=
19
+
20
+ # ---- Celery(默认连本机 Redis,无口令)----
21
+ # AIRFLOW__CELERY__BROKER_URL=redis://:<password>@127.0.0.1:6379/0
22
+ # AIRFLOW__CELERY__RESULT_BACKEND=redis://:<password>@127.0.0.1:6379/0
23
+
24
+ # ---- 其余可选覆盖项(不配就用 scripts/lib/funcron-airflow-env.sh 里的默认值)----
25
+ # AIRFLOW_HOME_PROD=$HOME/airflow
26
+ # AIRFLOW_HOME_DEV=<仓库目录>/.run/airflow-dev-home
27
+ # AIRFLOW__CORE__EXECUTOR=CeleryExecutor
28
+ # AIRFLOW__CORE__DEFAULT_TIMEZONE=Asia/Shanghai
29
+ # AIRFLOW__CORE__DAGS_FOLDER=<绝对路径,留空则按运行环境自动解析>
@@ -0,0 +1,43 @@
1
+ __pycache__/
2
+ *.pyc
3
+ *.db
4
+ *.rar
5
+ logs/
6
+ temp/
7
+ build/
8
+ dist/
9
+ .venv/
10
+ .run/
11
+ # 运行时凭据(Airflow 元数据库连接串、fernet key 等),模板见 .env.example
12
+ .env
13
+ .env.local
14
+ .idea/
15
+ .vscode/
16
+ node_modules/
17
+
18
+ # ---- AI 编码助手 ----
19
+ # 只忽略本地状态;团队有意共享的 agent / 命令 / 规则定义仍然版本化。
20
+ # 必须写 `dir/*` 而不是 `dir/`:父目录被整体排除后 git 不会下探,
21
+ # 下面的 `!` 取消规则就永远不会生效。
22
+ .claude/*
23
+ !.claude/agents/
24
+ !.claude/commands/
25
+ !.claude/skills/
26
+ !.claude/settings.json
27
+ .codex/*
28
+ !.codex/prompts/
29
+ .cursor/*
30
+ !.cursor/agents/
31
+ !.cursor/rules/
32
+
33
+ # 其余助手:目前没有共享内容,整体忽略
34
+ .aider*
35
+ .gemini/
36
+ .continue/
37
+ .windsurf/
38
+ .roo/
39
+ .cline/
40
+ .specstory/
41
+ CLAUDE.local.md
42
+ AGENTS.local.md
43
+ uv.lock
@@ -0,0 +1,240 @@
1
+ # CHANGELOG
2
+
3
+ 本文件记录 funcron 的版本变更,按版本倒序排列。
4
+
5
+ ## [0.5.10] - 2026-11-12
6
+
7
+ ### 修复
8
+
9
+ - 构建时排除本机生成的 `src/funcron/airflow/airflow.cfg`,避免配置中的密钥再次进入 wheel 或 sdist;0.5.9 已发布产物仍需单独处置。
10
+ - `server/port_manage.py`、`tasks/core.py`、`tool/mail.py` 的日志调用误用 stdlib
11
+ logging 的 `%s` 占位符,farlog(loguru)不支持该语法,参数被静默丢弃;统一改为
12
+ loguru 的 `{}` 占位符。`center/common/scheduler/cu_gevent_scheduler.py`、
13
+ `cu_background_scheduler.py`(源自 apscheduler,用 stdlib logging)与
14
+ `center/pages/crons/core.py` 的 `current_app.logger`(Flask 的 stdlib logger)
15
+ `%s` 用法本身正确,未改动。
16
+ - **`JobLog.to_json()` 调用必抛 `AttributeError`**:它返回 `job_id` / `remark` /
17
+ `traces` / `status` 四个字段,而 `job_log` 表早已不存在这些列。现按实际列
18
+ (`id`/`log_id`/`cron_info_id`/`content`/`create_time`/`take_time`)重写,
19
+ 并加测试断言返回键集合与表列定义一致,拦住后续字段漂移。
20
+ - **`RedisCache.delete()` 永远删不掉键**:`set()`/`get()` 都按 `f"{prefix}{key}"`
21
+ 读写带前缀的键,只有 `delete()` 直接 `delete(key)`,前缀被漏掉。现统一走
22
+ `full_key()` 拼接;`clear()` 也改为一次 `delete(*keys)` 并返回实际删除数量。
23
+ - **`RedisCache` 的连接池在不同目标库之间串用**:`hasattr(RedisCache, "pool")`
24
+ 是全类单例,第二次用不同 host/port/db 构造出的实例会静默连到第一次那个库。
25
+ 现按 `(host, port, db)` 分别缓存连接池。
26
+ - `JobLog.job_log_list()` 的参数 `id` 遮蔽内置函数,改名为 `cron_info_id`,
27
+ 并与 `cron_list()` 一致支持 `page_size` 与空页码回落;调用方同步更新。
28
+ - **`src/funcron/airflow/airflow.cfg` 随包发布了明文凭据与开发机绝对路径**:
29
+ `sql_alchemy_conn` 带明文 `funcron:funcron` 账号密码,`[webserver] secret_key`
30
+ 是一个真实值,`dags_folder` / `plugins_folder` / `base_log_folder` 分别指向三台
31
+ 不同机器上的固定路径(其中 `plugins_folder` 指的目录在本仓库根本不存在),而
32
+ `scripts/services/airflow.sh` 从来没有引用过这个文件,等于只泄露、不生效。
33
+ 现已整文件删除,改由 `scripts/lib/funcron-airflow-env.sh` 按
34
+ `AIRFLOW__<SECTION>__<KEY>` 环境变量注入配置:目录类配置按脚本自身位置 / 已安装包
35
+ 位置解析,凭据从不进版本库的 `.env` 读取(模板 `.env.example`),prod 缺少
36
+ `AIRFLOW__DATABASE__SQL_ALCHEMY_CONN` 时直接拒绝启动而不是回落到 SQLite。
37
+ - **`airflow-webserver` 服务在当前依赖下必然起不来**:脚本执行 `airflow webserver`,
38
+ 而 Airflow 3 已移除该子命令(本仓库声明 `apache-airflow>=3.3.1`),运行会直接报
39
+ `Command 'airflow webserver' has been removed`。改为 `airflow api-server`,
40
+ 对外的服务名保持 `airflow-webserver` 不变。
41
+ - **`src/funcron/airflow/init.sh` 整个跑不通**:`cp airflow.cfg ~/airflow/airflow.cfg`
42
+ 用相对路径(只有恰好 cd 到该目录才成立),`airflow db init` 在 Airflow 3 已移除,
43
+ 后面还串着 `airflow webserver`、`airflow worker` 等早已改名的命令。已重写并移到 `scripts/airflow-init.sh`
44
+ (放在包里的话,装到 site-packages 后相对路径就不成立了),用法 `scripts/airflow-init.sh <dev|prod>`,
45
+ 复用统一的配置推导并执行 `airflow db migrate`。
46
+ - **默认时区 `GMT+8` 是错的**:那不是 IANA 时区名,按 POSIX TZ 语义解释反而是 UTC-8。
47
+ 新默认值为 `Asia/Shanghai`。
48
+
49
+ ### 移除
50
+
51
+ - `funcron.airflow.create_account`:`from airflow.contrib.auth.backends.password_auth
52
+ import PasswordUser` 自 Airflow 2.0 起即不存在(`airflow.contrib` 已整包移除),
53
+ `airflow.models.User` 同样已删,该模块 import 即 `ModuleNotFoundError`,从未能运行。
54
+ 替代方式:Airflow 3 默认的 SimpleAuthManager 用
55
+ `AIRFLOW__CORE__SIMPLE_AUTH_MANAGER_USERS` 配置管理员;装了
56
+ `apache-airflow-providers-fab` 则用 `airflow fab-manager create-user`。
57
+ - `funcron.airflow.webserver_config`:未作任何 funcron 定制的 Airflow 2 模板文件,
58
+ 顶部 `from airflow.www.fab_security.manager import AUTH_DB` 在 Airflow 3 下
59
+ `ModuleNotFoundError`(`airflow.www` 已移入 FAB provider),且 Airflow 只从
60
+ `$AIRFLOW_HOME/webserver_config.py` 加载,包里这份从来没被读过。
61
+ 需要定制鉴权时请在 `AIRFLOW_HOME` 下自行放置该文件。
62
+
63
+ ### 新增
64
+
65
+ - `.env.example`:Airflow 元数据库连接串、fernet key、Celery broker 等运行时凭据的
66
+ 配置模板;实际的 `.env` 已加入 `.gitignore`。
67
+ - `scripts/lib/funcron-airflow-env.sh`:Airflow 配置推导的唯一入口,被
68
+ `scripts/services/airflow.sh` 与 `scripts/airflow-init.sh` 共享,
69
+ 保证「初始化」与「运行」用的是同一套配置。
70
+ - `scripts/lib/funcron-common.sh` 新增 `funcron_installed_package_dir`,
71
+ 以与 `funcron_installed_package_file` 相同的隔离方式解析已安装包内的目录。
72
+ - 新增 `tests/test_airflow_env.py`,覆盖「仓库内不得再出现明文连接串与开发机绝对路径」、
73
+ 各 `AIRFLOW__*` 默认值推导、`.env` 与显式环境变量的优先级、prod 缺连接串时拒绝启动,
74
+ 并实跑 `airflow` CLI 确认 `api-server` 存在、`webserver` 确已移除。
75
+ - `center/models.py`、`center/utils/times.py`、`center/utils/redis_cache.py`
76
+ 的公开类、函数、方法补齐位于定义体首句的中文 docstring(原先只有游离在
77
+ 模块/方法之间的三引号文本,不构成 docstring)。
78
+ - 新增 `tests/test_models.py`、`tests/test_redis_cache.py`、`tests/test_times.py`,
79
+ 覆盖上述修复的正常路径、边界与失败路径(Redis 用内存假客户端,不连真实服务)。
80
+
81
+ ## [0.5.9] - 2026-10-02
82
+
83
+ ### 修复
84
+
85
+ - **`funcron` 命令行此前完全不可用**:`funcron/server/script.py` 顶部
86
+ `from funbuild.manage import BaseServer`,而 funbuild 已不再提供 `manage` 模块
87
+ (当前及 pyproject 声明下限 1.6.75 均无),控制台脚本一启动就
88
+ `ModuleNotFoundError: No module named 'funbuild.manage'`。已发布的 0.5.7 带着这个缺陷。
89
+ 现已重写 CLI,去掉对 funbuild/supervisor 的依赖。
90
+ - **README 示例与实现不一致**:旧 CLI 要求同时给 `cmd` 和 `service` 两个位置参数,
91
+ README 里的 `funcron server` / `funcron status` 实际都以用法错误退出;
92
+ `scripts/setup.sh status all` 也因为缺第三个参数退出码 1。现在 CLI 提供
93
+ `status` / `services` 两个子命令,`scripts/setup.sh status` 的环境参数可省略
94
+ (省略时报告 dev 与 prod 两个环境),README 示例已逐条实测可运行。
95
+ - **`db.session.execute()` 传裸字符串 SQL 在 SQLAlchemy 2.x 下直接报错**:
96
+ `funcron/center/pages/main/views.py` 的 `cron_del`、
97
+ `funcron/center/pages/crons/core.py` 的 `cron_check_db_sleep` / `cron_del_job_log`
98
+ 会抛 `ObjectNotExecutableError`(本仓库声明 flask-sqlalchemy>=3.1.1,即 SQLAlchemy 2.x)。
99
+ 现统一改为 `sqlalchemy.text()` + 参数绑定。
100
+ - **SQL 字符串插值改为参数绑定**:`cu_background_scheduler.py` / `cu_gevent_scheduler.py`
101
+ 的 `update_cron_info` 原先用 `"... where id='%s'" % cron_id` 拼 SQL,任务 ID 来自任务数据;
102
+ 现改为绑定参数,并在解析出的主键不是数字时拒绝执行并记日志。
103
+ `cron_del_job_log` 的 `delete ... limit` 同样改为绑定参数。
104
+ - **prod 启动的生产包校验此前被 editable 安装架空**:`scripts/services/server.sh` 用
105
+ `python3 -c "import funcron"` 判定「已安装」,但 `uv sync` 装的是 editable(`.pth` 指回源码),
106
+ 从仓库目录运行时裸 import 也会命中工作树,校验恒为真,`start prod` 实际在跑源码。
107
+ 现改为清空 `PYTHONPATH` + Python 隔离模式(`-I`)+ 切到 `.run/` + 断言模块文件落在
108
+ `site-packages`/`dist-packages`;校验下沉到各服务脚本的 `command_for`,使 `run prod`
109
+ 这条路径同样被覆盖;airflow 与 coin 服务也补上了同样的 prod 校验。
110
+ `prod` 下 gunicorn 的配置文件改为从已安装包内解析,不再引用仓库源码目录。
111
+ - **重复启动检查会把任意存活 PID 当成本服务**:`funcron_is_running` 原先只 `kill -0` PID 文件里的
112
+ 数字。现在 PID 文件旁多记一份 `.meta`(进程启动时刻 + 命令特征串),状态细分为
113
+ `missing`/`invalid`/`stale`/`mismatch`/`running`:陈旧文件清理后继续,PID 被其他进程复用时
114
+ 拒绝操作并提示人工确认(`stop` 也不会去 kill 未通过身份校验的进程)。
115
+ - **`default` / `testing` 配置默认开着 DEBUG**:`config_dict` 的 `default` 原先指向
116
+ `DevelopmentConfig`(`DEBUG = True`)。现在 `default` 指向 `ProductionConfig`,新增
117
+ `TestingConfig`(只开 `TESTING`,不开 `DEBUG`),`DEBUG` 只在显式选 `development` 时开启。
118
+ - **`create_app()` 无条件改写调用方传入的配置名**:函数体第一行 `config_name = "production"`
119
+ 让参数形同虚设。现在尊重入参,未知配置名直接抛 `KeyError` 而不是静默回落。
120
+ - **`api_deal_return` 对不支持的返回值隐式返回 None**:视图返回 `None` 或其他未覆盖类型时
121
+ 装饰器什么都不返回,Flask 随后报 "did not return a valid response",排查不到真正原因。
122
+ 现在返回带类型名的明确错误响应。顺带把 `type(x) == str` 这类判断改为 `isinstance`。
123
+ - **缺失的运行时依赖补齐声明**:`requests`、`six`、`sqlalchemy` 在代码里直接 import,
124
+ 此前完全没有声明,靠传递依赖"运气好"装上。`fundata` 下限提到 1.0.3(1.0.1/1.0.2 的
125
+ wheel 里带着失效的 notetool/notebuild import)。
126
+ - `funcron status` 的 IP 探测失败不再让命令整体崩溃,改为回落到 `127.0.0.1` / `unknown`
127
+ 并记录警告。端口清单里移除了已不再使用的 supervisor 条目,补上 funcron 管理后台自身端口。
128
+ - **非 root 环境 `import funcron.center.common.config` 直接 PermissionError**:配置模块在
129
+ import 期间就 `WorkApp("funcron").create()`,而 `fundata` 的默认工作目录写死在
130
+ `/opt/farfarfun/apps/funcron`,普通用户下 `os.makedirs` 抛 `PermissionError`,
131
+ 连测试收集都会整体失败。现在工作目录按
132
+ `FUNCRON_APP_DIR` → `FUNDATA_APP_DIR` → `/opt/farfarfun/apps/funcron`(可写才用)
133
+ → `~/.funcron` 的顺序解析,并有 4 个测试覆盖这条优先级链。
134
+ - **404 / 500 错误页以 HTTP 200 返回**:`funcron/center/pages/main/errors.py` 的
135
+ `app_errorhandler` 只返回一个字符串,Flask 不会自动套上异常的状态码,于是「找不到页面」
136
+ 和「服务端异常」在 HTTP 层都表现为成功,探活与调用方无法区分。现在显式返回
137
+ `(正文, 404)` / `(正文, 500)`,并有测试断言状态码。
138
+ - **`funcron.tool.mail` 里写死的真实邮箱地址与收件人**:发件账号 `15068733021@163.com`
139
+ 与默认收件人(两个个人 QQ 邮箱)直接写在源码里,随包发布到 PyPI。现在发件账号、
140
+ 口令、默认收件人全部经 `funsecret` 下发,缺配置时抛 `ValueError` 而不是静默发往写死的
141
+ 地址;顺带修掉「多个收件人时只有第一个出现在 `To` 头里」以及异常时 `smtp.close()`
142
+ 不会被执行(改用 `with`)两个问题。
143
+
144
+ ### 变更
145
+
146
+ - **源码改为 src 布局**:`funcron/` → `src/funcron/`(SPEC 4.1),同步调整 hatch 构建配置
147
+ 与服务脚本路径。包名、发布名、import 路径都不变(仍是 `funcron`),对使用方无影响;
148
+ 附带好处是在仓库根目录不会再误把工作树源码当成已安装包 import 到。
149
+ - **不再依赖 supervisor 做进程守护**:进程托管统一由 `scripts/setup.sh` + `.run/` 负责,
150
+ `supervisor` 与 `funbuild` 已从运行时依赖中移除(`funbuild` 本就只是构建/发布工具)。
151
+ - 新增 `[tool.ruff]` 配置(line-length 120、显式 select 规则集)与 `[tool.pytest.ini_options]`,
152
+ `ruff` 加入 dev 依赖组;补上 `[project.urls]`。`ruff check` 与 `ruff format --check`
153
+ 现已全仓库通过(存量问题逐条修掉或带理由标注豁免,不是靠放宽规则集蒙过去)。
154
+ - `uv.lock` 中的传递依赖升级,修掉 Dependabot 报出的安全告警(纯锁文件升级,本包代码未变,
155
+ 测试全绿):
156
+ - `urllib3` 2.7.0 → 2.8.0:2 HIGH + 1 MEDIUM(`HTTPResponse.stream()/read_chunked()`
157
+ 无界缓冲、HTTPS 代理的 TLS 配置可能被忽略、chunked deflate 响应可能无限循环)。
158
+ - `pyjwt` 2.13.0 → 2.15.1:1 CRITICAL + 5 HIGH + 多个 MEDIUM(非对称 PEM 检测绕过、
159
+ 公钥被当作 HMAC 密钥、BOM 绕过、空 HMAC 密钥、`PyJWKClient` 跟随重定向等)。
160
+ - `tornado` 6.5.8 → 6.5.10:2 HIGH(`CurlAsyncHTTPClient` 解压无响应大小上限、
161
+ `StaticFileHandler` 跟随软链接越出静态根目录)。
162
+ - 仍未修复的 `diskcache`(unsafe pickle 反序列化)上游无可用修复版本,funcron 只把它
163
+ 当本地缓存用、不反序列化外部输入,暂维持现状。
164
+ - 把 `script/__version__.md` 与 `pyproject.toml` 的 `[project].version` 对齐(此前漂成
165
+ 0.5.8 / 0.5.7:funbuild 的 `sync_all_manifest_versions()` 不同步 `script/__version__.md`)。
166
+ 新增 `tests/test_version_consistency.py` 断言两处一致,拦截后续漂移。
167
+
168
+ ### 新增
169
+
170
+ - `funcron` CLI 新增 `services` 子命令,列出 `scripts/setup.sh` 能托管的服务名;
171
+ 并有测试断言这份名单与 `scripts/setup.sh` 的 `ALL_SERVICES` 一致。
172
+ - 测试从 3 个文件 / 1 条断言扩充到 10 个文件 / 74 条,新增:CLI 子命令与参数解析、
173
+ `scripts/setup.sh` 的动作/服务/环境参数校验与 `status` 行为、重复启动检查五种状态的
174
+ 逐一验证、prod 生产包校验对源码树/editable 的拒绝(含「朴素 import 确实会通过」的
175
+ 前置断言,避免测试空跑)、`api_deal_return`/`login_required` 的正常与边界路径、
176
+ 配置默认值的安全性与工作目录解析优先级、邮件发送的缺配置/多收件人路径、
177
+ 404/500 错误页的状态码。原先两个用 `pytest.importorskip` 兜住 fundata 问题的测试
178
+ 已改为真实 import,环境退化时直接失败而不是静默跳过。
179
+ - Web/API 视图函数、蓝图模块与错误处理器补齐中文 docstring;`api/views.py` 里
180
+ `except Exception` 收窄为 `(TypeError, ValueError)`。
181
+
182
+ ### 废弃
183
+
184
+ - **移除 `funcron.core` 模块(`JobScheduler` 与 `funcron.core.funcron`)**。
185
+ 替代品:`scripts/setup.sh {start|stop|restart|status|run} <service> <dev|prod>`。
186
+ 移除原因:该模块通过 `subprocess(..., shell=True)` 执行 `nohup funcron _start ...`、
187
+ 并用 `ps x | grep funcron` 的文本输出拼 `kill -9 <pid>` 来停服务——既不安全(命令拼接、
188
+ 按进程名文本匹配可能误杀),也早已跑不通(它调用的 `funcron _start` 子命令在已注册的
189
+ 控制台脚本里并不存在)。迁移方法:
190
+ 原 `funcron.core.JobScheduler().start()/stop()/restart()`
191
+ → `scripts/setup.sh start|stop|restart <service> <dev|prod>`。
192
+ - **移除 `funcron.tasks.ba` 模块**。该模块是 2020 年给某德国电商站点写的个人抢购脚本:
193
+ funcron 内部没有任何调用点,与「定时任务调度中心」这个定位无关,且源码里带着一整份
194
+ 写死的第三方站点会话 Cookie(`frontend`、`bfd_sid`、`sensorsdata2015jssdkcross` 等)
195
+ 和两个个人 QQ 邮箱地址,随包发布到 PyPI。随之移除仅由它使用的 `demjson3` 运行时依赖。
196
+ 无迁移路径——它从未被 funcron 的任何入口暴露。
197
+
198
+ ## [0.5.8] - 未发布(未推送到 PyPI,内容已并入 0.5.9)
199
+
200
+ ### 新增
201
+
202
+ - 新增 `scripts/setup.sh` + `scripts/services/*.sh`,统一管理 server / airflow-webserver /
203
+ airflow-scheduler / airflow-worker / airflow-flower / coin 等长期运行服务,支持
204
+ `start`/`stop`/`restart`/`status`/`run`,区分 `dev`/`prod` 环境,运行时文件统一放在 `.run/`。
205
+ - README 补充安装命令、最小可运行示例、服务启动说明及组织介绍区块。
206
+
207
+ ### 修复
208
+
209
+ - 修复 `pyproject.toml` 中依赖 `redi`(无关的 REDCap 导入工具)误当作 `redis` 声明的问题,
210
+ 改为正确声明 `redis`;为全部运行时依赖补充经验证的最低版本号,并提交 `uv.lock`。
211
+ - 删除 `funcron/core/core.py` 里 `from funbuild.shell import run_shell, run_shell_list`
212
+ 这行死 import(funcron 从未调用过这两个函数,且 funbuild 已不再提供 `shell` 模块,
213
+ 对应能力在独立的 `funshell` 包里)。这是 funcron 内部的未使用 import,不是 funcron
214
+ 自己对外暴露过的入口,使用方无需做任何迁移。
215
+ - `funcron/center/common/config/config.py`、`funcron/tool/mail.py`、`funcron/airflow/create_account.py`
216
+ 中硬编码的登录口令、Redis 密码、通知 API key、API access token、Airflow 管理员账号密码
217
+ 改为通过 `funsecret`/环境变量下发,代码中不再出现明文真实凭据。
218
+ - `funcron/center/pages/main/views.py`、`funcron/center/common/scheduler/cu_background_scheduler.py`、
219
+ `cu_gevent_scheduler.py` 中吞掉异常的裸 `except`/`except Exception: pass` 改为记录带上下文的错误日志。
220
+ - `funcron/tasks/core.py`、`funcron/center/common/functions.py` 中用于诊断的 `print` 改为
221
+ `farlog` 日志输出。
222
+
223
+ ### 变更
224
+
225
+ - 统一日志入口为 `farlog.getLogger`,移除 `funcron/center/app.py` 中手写的 `logging`
226
+ handler 配置,以及 `funtool.tool.log` 的使用。
227
+ - 模块文件名改为 snake_case:`CuBackgroundScheduler.py` → `cu_background_scheduler.py`、
228
+ `CuGeventScheduler.py` → `cu_gevent_scheduler.py`、`RedisCache.py` → `redis_cache.py`
229
+ (类名保持不变,仅重命名文件并同步更新导入)。
230
+ - `.gitignore` 补充 `*.pyc`、`*.db`、`*.rar`、`.venv/`、`.run/`、`.idea/`、`.vscode/`、
231
+ `node_modules/`。
232
+
233
+ ### 废弃
234
+
235
+ - 无。
236
+
237
+ ## 历史版本
238
+
239
+ 0.5.8 之前的版本未维护本文件,历史变更请参考 Git 提交记录。
240
+ PyPI 上的最后一个已发布版本是 0.5.7;0.5.8 只在仓库里打了版本号、没有发布。
funcron-0.5.10/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 farfarfun
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,192 @@
1
+ Metadata-Version: 2.5
2
+ Name: funcron
3
+ Version: 0.5.10
4
+ Summary: 基于 Flask 与 Airflow 的定时任务调度管理中心,提供 Web 管理后台及 server/scheduler/worker/flower 等后台服务
5
+ Project-URL: Organization, https://github.com/farfarfun
6
+ Project-URL: Repository, https://github.com/farfarfun/funcron
7
+ Project-URL: Releases, https://github.com/farfarfun/funcron/releases
8
+ Author-email: 牛哥 <niuliangtao@qq.com>, farfarfun <farfarfun@qq.com>
9
+ Maintainer-email: 牛哥 <niuliangtao@qq.com>, farfarfun <farfarfun@qq.com>
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Requires-Python: >=3.10
13
+ Requires-Dist: apache-airflow-providers-celery>=3.23.1
14
+ Requires-Dist: apache-airflow>=3.3.1
15
+ Requires-Dist: apscheduler>=3.11.3
16
+ Requires-Dist: farlog>=1.1.8
17
+ Requires-Dist: flask-apscheduler>=1.13.1
18
+ Requires-Dist: flask-migrate>=4.1.0
19
+ Requires-Dist: flask-sqlalchemy>=3.1.1
20
+ Requires-Dist: flask>=3.1.3
21
+ Requires-Dist: funcoin>=1.0.51
22
+ Requires-Dist: fundata>=1.0.3
23
+ Requires-Dist: funsecret>=1.4.96
24
+ Requires-Dist: gevent>=26.8.0
25
+ Requires-Dist: gunicorn>=26.2.0
26
+ Requires-Dist: pymysql>=1.2.0
27
+ Requires-Dist: records>=0.6.0
28
+ Requires-Dist: redis>=6.4.0
29
+ Requires-Dist: requests>=2.32.0
30
+ Requires-Dist: six>=1.17.0
31
+ Requires-Dist: sqlalchemy>=2.0.0
32
+ Requires-Dist: typer>=0.12
33
+ Description-Content-Type: text/markdown
34
+
35
+ # funcron
36
+
37
+ 基于 Flask 与 Airflow 的定时任务调度管理中心,提供 Web 管理后台,以及 server(管理后台)/
38
+ scheduler/webserver/worker/flower(Airflow 各角色)/coin(行情下载)等长期运行服务。
39
+
40
+ ## 安装
41
+
42
+ ```bash
43
+ uv add funcron
44
+ # 或
45
+ pip install funcron
46
+ ```
47
+
48
+ ## 最小可运行示例
49
+
50
+ 安装完成后,基于 Typer 的 `funcron` 命令行提供两个查询类子命令:
51
+
52
+ ```bash
53
+ # 查看本机常用服务端口的可访问状态
54
+ funcron status
55
+
56
+ # 列出 scripts/setup.sh 支持托管的服务名
57
+ funcron services
58
+ ```
59
+
60
+ 长期运行服务的启停不走 CLI,统一由 `scripts/setup.sh` 管理,见下一节。
61
+
62
+ `scripts/` 是源码仓库和 source distribution 附带的运维脚本,不会安装到 PyPI
63
+ wheel 中。通过 `pip install funcron` 或 `uv add funcron` 安装后只能使用上述
64
+ `funcron` CLI;需要运行服务脚本时,请获取源码并在仓库根目录执行:
65
+
66
+ ```bash
67
+ git clone https://github.com/farfarfun/funcron.git
68
+ cd funcron
69
+ scripts/setup.sh install-dev
70
+ ```
71
+
72
+ ## 服务启动(scripts/setup.sh)
73
+
74
+ 生产环境或本地长期运行各服务,统一通过 `scripts/setup.sh` 管理。先安装所需版本,再按 `动作 → 服务` 解析运行参数:
75
+
76
+ ```bash
77
+ # 用法: scripts/setup.sh {start|stop|restart|run|status} <service>
78
+ # <service>: server | airflow-webserver | airflow-scheduler | airflow-worker
79
+ # | airflow-flower | coin | all
80
+
81
+ # 构建并安装当前源码,然后后台启动 Web 管理后台
82
+ scripts/setup.sh install-dev
83
+ scripts/setup.sh start server
84
+
85
+ # 生产机可安装指定正式版本
86
+ scripts/setup.sh install-prod 0.5.10
87
+
88
+ # 前台运行 Airflow scheduler,方便调试
89
+ scripts/setup.sh run airflow-scheduler
90
+
91
+ # 查看所有服务状态
92
+ scripts/setup.sh status all
93
+ ```
94
+
95
+ `start`/`stop`/`restart` 管理后台进程,`run` 是前台阻塞运行,方便调试单个服务,不支持 `all`。
96
+ 运行命令不接受 `dev`/`prod` 参数;当前安装的是本地构建还是正式版本,分别由
97
+ `install-dev` 和 `install-prod [version]` 决定。
98
+
99
+ PID、进程身份记录与日志统一放在仓库根目录的 `.run/` 下(按服务名区分)。重复启动检查会
100
+ 校验 PID 对应进程的启动时刻与命令特征:进程已退出的陈旧 PID 文件会被清理后继续启动;PID 已被
101
+ 其他进程复用时拒绝操作并提示人工确认,不会误杀无关进程。
102
+
103
+ 运行服务要求 funcron / airflow / funcoin 已安装为非 editable 包:校验会清空 `PYTHONPATH`、用
104
+ Python 隔离模式导入,并断言模块文件落在 `site-packages` 下,因此 editable 安装(`pip install -e`、
105
+ `uv sync`)或直接从源码工作树运行都会被拒绝;gunicorn 的配置文件也从已安装包内解析,
106
+ 不引用仓库源码。
107
+
108
+ ## Airflow
109
+
110
+ 仓库内**不保存 `airflow.cfg`**:配置文件一旦写上元数据库连接串,账号密码就会随版本库和
111
+ PyPI 包一起发出去;而 Airflow 的 `dags_folder` / `plugins_folder` / `base_log_folder`
112
+ 必须是绝对路径,写进包里就只在某一台机器上成立。
113
+
114
+ funcron 需要覆盖的配置项统一由 `scripts/lib/funcron-airflow-env.sh` 以
115
+ `AIRFLOW__<SECTION>__<KEY>` 环境变量注入,取值顺序为
116
+ 「调用方已导出的环境变量 → `.env` 文件 → 按运行环境推导的默认值」:
117
+
118
+ | 配置项 | dev 默认值 | prod 默认值 |
119
+ |---|---|---|
120
+ | `AIRFLOW_HOME` | `.run/airflow-dev-home` | `$AIRFLOW_HOME_PROD`(默认 `$HOME/airflow`) |
121
+ | `AIRFLOW__CORE__DAGS_FOLDER` | 源码树 `src/funcron/airflow/dags` | 已安装 funcron 包内的 `airflow/dags` |
122
+ | `AIRFLOW__CORE__PLUGINS_FOLDER` | `$AIRFLOW_HOME/plugins` | 同左 |
123
+ | `AIRFLOW__LOGGING__BASE_LOG_FOLDER` | `$AIRFLOW_HOME/logs` | 同左 |
124
+ | `AIRFLOW__DATABASE__SQL_ALCHEMY_CONN` | 不设置(回落到 `AIRFLOW_HOME` 下的 SQLite) | **必填**,缺失直接拒绝启动 |
125
+
126
+ 凭据写在仓库根目录的 `.env`(不进版本库),模板见 `.env.example`;也可用
127
+ `FUNCRON_ENV_FILE` 指向别处。
128
+
129
+ 首次部署先初始化元数据库,再按上一节启动各角色服务:
130
+
131
+ ```bash
132
+ scripts/airflow-init.sh prod
133
+ scripts/setup.sh start airflow-scheduler
134
+ ```
135
+
136
+ 管理员账号由所选 auth manager 负责创建(Airflow 3 默认的 SimpleAuthManager 用
137
+ `AIRFLOW__CORE__SIMPLE_AUTH_MANAGER_USERS` 配置;装了 `apache-airflow-providers-fab`
138
+ 时用 `airflow fab-manager create-user`),funcron 不再自带建账号脚本。
139
+
140
+ `airflow-webserver` 服务底层执行的是 `airflow api-server`——Airflow 3 已移除
141
+ `airflow webserver` 子命令,承载 Web UI 的组件改名为 api-server;对外的服务名保持不变。
142
+
143
+ ## 凭据配置
144
+
145
+ 数据库密码、Web 登录密码、Redis 密码、通知 API key、API access token、Flask session key
146
+ 均从 `funsecret` 读取,不提供默认凭据。对应路径位于 `funcron/database/mysql/password`、
147
+ `funcron/web/login/password`、`funcron/redis/password`、`funcron/notice/error_api_key`、
148
+ `funcron/api/access_token` 和 `funcron/web/flask/secret_key`。缺少登录密码时 Web 登录会拒绝访问;
149
+ 缺少 API access token 时 API 会返回配置错误;可选的 Redis 密码和通知 API key 为空时不启用相应认证或通知。
150
+
151
+ Flask 配置名可取 `development` / `testing` / `production` / `default`,由 `create_app(config_name)`
152
+ 显式选择。只有 `development` 会开启 `DEBUG`;`default` 指向生产配置,`testing` 只开 `TESTING`。
153
+
154
+ ## 工作目录
155
+
156
+ 数据库文件、日志与中间文件放在工作目录下,按以下顺序解析:`FUNCRON_APP_DIR` 环境变量 →
157
+ `FUNDATA_APP_DIR` 环境变量 → `/opt/farfarfun/apps/funcron`(当前用户可写时)→ `~/.funcron`。
158
+ 显式设置环境变量时按设置值使用,不再做可写性回落。
159
+
160
+ ## 开发
161
+
162
+ ```bash
163
+ uv sync
164
+ uv run pytest
165
+ uv run ruff check
166
+ uv run ruff format --check
167
+ ```
168
+
169
+ 源码采用 src 布局(`src/funcron/`),因此在仓库根目录直接 `import funcron` 不会命中工作树,
170
+ 需要先 `uv sync` 安装。
171
+
172
+ ---
173
+
174
+ ## 关于 farfarfun
175
+
176
+ [farfarfun](https://github.com/farfarfun) 是一个专注于实用工具库的开源组织,
177
+ 涵盖云存储、数据处理、AI、多媒体与开发工具链等方向。
178
+
179
+ - 🏠 组织主页:<https://github.com/farfarfun>
180
+ - 📦 PyPI:<https://pypi.org/user/niuliangtao/>
181
+ - 📧 联系:farfarfun@qq.com
182
+
183
+ 本项目基于 [MIT](LICENSE) 协议开源。
184
+
185
+ ## 第三方组件
186
+
187
+ | 组件 | 版本 | 来源 | 原始许可证 |
188
+ | --- | --- | --- | --- |
189
+ | artDialog | 4.1.6 | [artDialog 项目](http://code.google.com/p/artdialog/)(TangBin,2009-2012) | [GNU LGPL-2.1-or-later](http://creativecommons.org/licenses/LGPL/2.1/) |
190
+
191
+ artDialog 的分发文件位于 `src/funcron/center/static/js/artDialog/`;其中保留了原始
192
+ 版权与许可证声明。