From 1bd5a88c894e6066ee9f11e389a996062a8da048 Mon Sep 17 00:00:00 2001 From: zhangyu <1990336738@qq.com> Date: Sun, 12 Oct 2025 19:27:11 +0800 Subject: [PATCH] v1.0 --- src/.coveragerc | 10 - src/.dockerignore | 3 +- src/.github/workflows/codeql-analysis.yml | 10 +- src/.github/workflows/deploy-master.yml | 176 +++++ src/.github/workflows/django.yml | 393 +++++++++--- src/.github/workflows/docker.yml | 10 +- src/.gitignore | 80 --- src/.vscode/launch.json | 22 - src/Dockerfile | 6 +- src/LICENSE | 2 +- src/README.md | 201 +++--- .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 155 bytes .../__pycache__/admin.cpython-311.pyc | Bin 0 -> 3741 bytes src/accounts/__pycache__/apps.cpython-311.pyc | Bin 0 -> 469 bytes .../__pycache__/forms.cpython-311.pyc | Bin 0 -> 6700 bytes .../__pycache__/models.cpython-311.pyc | Bin 0 -> 2516 bytes src/accounts/__pycache__/urls.cpython-311.pyc | Bin 0 -> 1438 bytes .../user_login_backend.cpython-311.pyc | Bin 0 -> 1691 bytes .../__pycache__/utils.cpython-311.pyc | Bin 0 -> 2422 bytes .../__pycache__/views.cpython-311.pyc | Bin 0 -> 12317 bytes src/accounts/admin.py | 1 + .../__pycache__/0001_initial.cpython-311.pyc | Bin 0 -> 4123 bytes ...user_created_time_and_more.cpython-311.pyc | Bin 0 -> 1915 bytes .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 166 bytes .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 168 bytes src/accounts/tests.py | 14 +- src/bin/docker_start.sh | 31 - src/bin/nginx.conf | 50 -- src/blog/__pycache__/__init__.cpython-311.pyc | Bin 0 -> 151 bytes src/blog/__pycache__/admin.cpython-311.pyc | Bin 0 -> 6349 bytes src/blog/__pycache__/apps.cpython-311.pyc | Bin 0 -> 457 bytes .../context_processors.cpython-311.pyc | Bin 0 -> 2511 bytes .../__pycache__/documents.cpython-311.pyc | Bin 0 -> 12113 bytes .../__pycache__/middleware.cpython-311.pyc | Bin 0 -> 2749 bytes src/blog/__pycache__/models.cpython-311.pyc | Bin 0 -> 22621 bytes src/blog/__pycache__/urls.cpython-311.pyc | Bin 0 -> 2538 bytes src/blog/__pycache__/views.cpython-311.pyc | Bin 0 -> 21360 bytes src/blog/admin.py | 25 +- .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 162 bytes .../__pycache__/0001_initial.cpython-311.pyc | Bin 0 -> 8699 bytes ...ngs_global_footer_and_more.cpython-311.pyc | Bin 0 -> 1031 bytes ...ttings_comment_need_review.cpython-311.pyc | Bin 0 -> 891 bytes ...gs_analytics_code_and_more.cpython-311.pyc | Bin 0 -> 969 bytes ..._category_options_and_more.cpython-311.pyc | Bin 0 -> 10660 bytes ...alter_blogsettings_options.cpython-311.pyc | Bin 0 -> 806 bytes .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 162 bytes src/blog/models.py | 11 + src/blog/static/blog/css/style.css | 440 ++++++++++++- src/blog/static/blog/fonts/fonts.css | 378 ----------- .../mem5YaGs126MiZpBA-UN_r8OUehpOqc.woff2 | Bin 14088 -> 0 bytes .../fonts/mem5YaGs126MiZpBA-UN_r8OUuhp.woff2 | Bin 27131 -> 0 bytes .../mem5YaGs126MiZpBA-UN_r8OVuhpOqc.woff2 | Bin 17304 -> 0 bytes .../mem5YaGs126MiZpBA-UN_r8OX-hpOqc.woff2 | Bin 31604 -> 0 bytes .../mem5YaGs126MiZpBA-UN_r8OXOhpOqc.woff2 | Bin 20844 -> 0 bytes .../mem5YaGs126MiZpBA-UN_r8OXehpOqc.woff2 | Bin 10957 -> 0 bytes .../mem5YaGs126MiZpBA-UN_r8OXuhpOqc.woff2 | Bin 4250 -> 0 bytes .../mem5YaGs126MiZpBA-UNirkOUehpOqc.woff2 | Bin 14269 -> 0 bytes .../fonts/mem5YaGs126MiZpBA-UNirkOUuhp.woff2 | Bin 27159 -> 0 bytes .../mem5YaGs126MiZpBA-UNirkOVuhpOqc.woff2 | Bin 17452 -> 0 bytes .../mem5YaGs126MiZpBA-UNirkOX-hpOqc.woff2 | Bin 31596 -> 0 bytes .../mem5YaGs126MiZpBA-UNirkOXOhpOqc.woff2 | Bin 21360 -> 0 bytes .../mem5YaGs126MiZpBA-UNirkOXehpOqc.woff2 | Bin 11354 -> 0 bytes .../mem5YaGs126MiZpBA-UNirkOXuhpOqc.woff2 | Bin 4181 -> 0 bytes .../mem6YaGs126MiZpBA-UFUK0Udc1UAw.woff2 | Bin 29070 -> 0 bytes .../mem6YaGs126MiZpBA-UFUK0Vdc1UAw.woff2 | Bin 4095 -> 0 bytes .../mem6YaGs126MiZpBA-UFUK0Wdc1UAw.woff2 | Bin 10790 -> 0 bytes .../mem6YaGs126MiZpBA-UFUK0Xdc1UAw.woff2 | Bin 19681 -> 0 bytes .../fonts/mem6YaGs126MiZpBA-UFUK0Zdc0.woff2 | Bin 25060 -> 0 bytes .../mem6YaGs126MiZpBA-UFUK0adc1UAw.woff2 | Bin 13133 -> 0 bytes .../mem6YaGs126MiZpBA-UFUK0ddc1UAw.woff2 | Bin 16985 -> 0 bytes .../fonts/mem8YaGs126MiZpBA-UFUZ0bbck.woff2 | Bin 16994 -> 0 bytes .../blog/fonts/mem8YaGs126MiZpBA-UFVZ0b.woff2 | Bin 26166 -> 0 bytes .../fonts/mem8YaGs126MiZpBA-UFVp0bbck.woff2 | Bin 13949 -> 0 bytes .../fonts/mem8YaGs126MiZpBA-UFW50bbck.woff2 | Bin 20653 -> 0 bytes .../fonts/mem8YaGs126MiZpBA-UFWJ0bbck.woff2 | Bin 30070 -> 0 bytes .../fonts/mem8YaGs126MiZpBA-UFWZ0bbck.woff2 | Bin 4171 -> 0 bytes .../fonts/mem8YaGs126MiZpBA-UFWp0bbck.woff2 | Bin 11091 -> 0 bytes .../memnYaGs126MiZpBA-UFUKWyV9hkIqOjjg.woff2 | Bin 10841 -> 0 bytes .../memnYaGs126MiZpBA-UFUKWyV9hlIqOjjg.woff2 | Bin 19853 -> 0 bytes .../memnYaGs126MiZpBA-UFUKWyV9hmIqOjjg.woff2 | Bin 28801 -> 0 bytes .../memnYaGs126MiZpBA-UFUKWyV9hnIqOjjg.woff2 | Bin 4247 -> 0 bytes .../memnYaGs126MiZpBA-UFUKWyV9hoIqOjjg.woff2 | Bin 13474 -> 0 bytes .../memnYaGs126MiZpBA-UFUKWyV9hrIqM.woff2 | Bin 25055 -> 0 bytes .../memnYaGs126MiZpBA-UFUKWyV9hvIqOjjg.woff2 | Bin 17036 -> 0 bytes .../memnYaGs126MiZpBA-UFUKXGUdhkIqOjjg.woff2 | Bin 11233 -> 0 bytes .../memnYaGs126MiZpBA-UFUKXGUdhlIqOjjg.woff2 | Bin 20235 -> 0 bytes .../memnYaGs126MiZpBA-UFUKXGUdhmIqOjjg.woff2 | Bin 29777 -> 0 bytes .../memnYaGs126MiZpBA-UFUKXGUdhnIqOjjg.woff2 | Bin 4096 -> 0 bytes .../memnYaGs126MiZpBA-UFUKXGUdhoIqOjjg.woff2 | Bin 13423 -> 0 bytes .../memnYaGs126MiZpBA-UFUKXGUdhrIqM.woff2 | Bin 25389 -> 0 bytes .../memnYaGs126MiZpBA-UFUKXGUdhvIqOjjg.woff2 | Bin 17652 -> 0 bytes ...MiZpBA-UFUIcVXSCEkx2cmqvXlWqW106F15M.woff2 | Bin 0 -> 26368 bytes ...MiZpBA-UFUIcVXSCEkx2cmqvXlWqWt06F15M.woff2 | Bin 0 -> 37752 bytes ...MiZpBA-UFUIcVXSCEkx2cmqvXlWqWtE6F15M.woff2 | Bin 0 -> 54944 bytes ...MiZpBA-UFUIcVXSCEkx2cmqvXlWqWtU6F15M.woff2 | Bin 0 -> 4844 bytes ...MiZpBA-UFUIcVXSCEkx2cmqvXlWqWtk6F15M.woff2 | Bin 0 -> 17212 bytes ...MiZpBA-UFUIcVXSCEkx2cmqvXlWqWu06F15M.woff2 | Bin 0 -> 17880 bytes ...126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWuU6F.woff2 | Bin 0 -> 50216 bytes ...MiZpBA-UFUIcVXSCEkx2cmqvXlWqWuk6F15M.woff2 | Bin 0 -> 22920 bytes ...MiZpBA-UFUIcVXSCEkx2cmqvXlWqWvU6F15M.woff2 | Bin 0 -> 32016 bytes ...MiZpBA-UFUIcVXSCEkx2cmqvXlWqWxU6F15M.woff2 | Bin 0 -> 56528 bytes ...Gs126MiZpBA-UvWbX2vVnXBbObj2OVTS-muw.woff2 | Bin 0 -> 48320 bytes ...126MiZpBA-UvWbX2vVnXBbObj2OVTS2mu1aB.woff2 | Bin 0 -> 16920 bytes ...126MiZpBA-UvWbX2vVnXBbObj2OVTSCmu1aB.woff2 | Bin 0 -> 16496 bytes ...126MiZpBA-UvWbX2vVnXBbObj2OVTSGmu1aB.woff2 | Bin 0 -> 35156 bytes ...126MiZpBA-UvWbX2vVnXBbObj2OVTSKmu1aB.woff2 | Bin 0 -> 49268 bytes ...126MiZpBA-UvWbX2vVnXBbObj2OVTSOmu1aB.woff2 | Bin 0 -> 4504 bytes ...126MiZpBA-UvWbX2vVnXBbObj2OVTSumu1aB.woff2 | Bin 0 -> 26588 bytes ...126MiZpBA-UvWbX2vVnXBbObj2OVTSymu1aB.woff2 | Bin 0 -> 21332 bytes ...126MiZpBA-UvWbX2vVnXBbObj2OVTUGmu1aB.woff2 | Bin 0 -> 26596 bytes ...126MiZpBA-UvWbX2vVnXBbObj2OVTVOmu1aB.woff2 | Bin 0 -> 51912 bytes src/blog/static/blog/fonts/open-sans.css | 600 ++++++++++++++++++ src/blog/static/blog/js/mathjax-loader.js | 142 +++++ src/blog/static/mathjax/js/mathjax-config.js | 21 - .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 164 bytes .../__pycache__/blog_tags.cpython-311.pyc | Bin 0 -> 25427 bytes src/blog/templatetags/blog_tags.py | 260 +++++++- src/blog/tests.py | 4 +- src/blog/views.py | 19 +- src/codecov.yml | 87 +++ .../compressed/css/output.f76962182efc.css | 1 + .../compressed/css/output.fbe498417efd.css | 1 + .../compressed/js/output.c56f0a57c4ca.js | 42 ++ .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 155 bytes .../__pycache__/admin.cpython-311.pyc | Bin 0 -> 2871 bytes src/comments/__pycache__/apps.cpython-311.pyc | Bin 0 -> 469 bytes .../__pycache__/forms.cpython-311.pyc | Bin 0 -> 968 bytes .../__pycache__/models.cpython-311.pyc | Bin 0 -> 2285 bytes src/comments/__pycache__/urls.cpython-311.pyc | Bin 0 -> 508 bytes .../__pycache__/utils.cpython-311.pyc | Bin 0 -> 2520 bytes .../__pycache__/views.cpython-311.pyc | Bin 0 -> 3895 bytes src/comments/admin.py | 6 +- .../__pycache__/0001_initial.cpython-311.pyc | Bin 0 -> 2422 bytes ...02_alter_comment_is_enable.cpython-311.pyc | Bin 0 -> 838 bytes ...ment_created_time_and_more.cpython-311.pyc | Bin 0 -> 2772 bytes .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 166 bytes .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 168 bytes .../__pycache__/comments_tags.cpython-311.pyc | Bin 0 -> 1507 bytes src/deploy/k8s/deployment.yaml | 4 +- src/djangoblog/__init__.py | 1 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 217 bytes .../__pycache__/admin_site.cpython-311.pyc | Bin 0 -> 3224 bytes .../__pycache__/apps.cpython-311.pyc | Bin 0 -> 952 bytes .../__pycache__/blog_signals.cpython-311.pyc | Bin 0 -> 6330 bytes .../elasticsearch_backend.cpython-311.pyc | Bin 0 -> 10699 bytes .../__pycache__/feeds.cpython-311.pyc | Bin 0 -> 3026 bytes .../__pycache__/logentryadmin.cpython-311.pyc | Bin 0 -> 4632 bytes .../__pycache__/settings.cpython-311.pyc | Bin 0 -> 11159 bytes .../__pycache__/sitemap.cpython-311.pyc | Bin 0 -> 3697 bytes .../__pycache__/spider_notify.cpython-311.pyc | Bin 0 -> 1528 bytes .../__pycache__/urls.cpython-311.pyc | Bin 0 -> 4202 bytes .../__pycache__/utils.cpython-311.pyc | Bin 0 -> 13709 bytes .../whoosh_cn_backend.cpython-311.pyc | Bin 0 -> 38132 bytes .../__pycache__/wsgi.cpython-311.pyc | Bin 0 -> 686 bytes .../__pycache__/base_plugin.cpython-311.pyc | Bin 0 -> 8162 bytes .../hook_constants.cpython-311.pyc | Bin 0 -> 790 bytes .../__pycache__/hooks.cpython-311.pyc | Bin 0 -> 2639 bytes .../__pycache__/loader.cpython-311.pyc | Bin 0 -> 4163 bytes src/djangoblog/plugin_manage/base_plugin.py | 155 ++++- .../plugin_manage/hook_constants.py | 15 + src/djangoblog/plugin_manage/loader.py | 51 +- src/djangoblog/settings.py | 110 +++- src/djangoblog/spider_notify.py | 10 - src/djangoblog/urls.py | 14 + src/djangoblog/utils.py | 48 +- src/djangoblog/whoosh_cn_backend.py | 2 +- src/docker-compose.es.yml | 48 -- src/docker-compose.yml | 59 -- src/docs/README-en.md | 202 +++--- src/docs/docker.md | 157 +++-- src/logs/djangoblog.log | 119 ++++ .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 152 bytes src/oauth/__pycache__/admin.cpython-311.pyc | Bin 0 -> 3292 bytes src/oauth/__pycache__/apps.cpython-311.pyc | Bin 0 -> 460 bytes src/oauth/__pycache__/forms.cpython-311.pyc | Bin 0 -> 1344 bytes src/oauth/__pycache__/models.cpython-311.pyc | Bin 0 -> 4628 bytes .../__pycache__/oauthmanager.cpython-311.pyc | Bin 0 -> 24786 bytes src/oauth/__pycache__/urls.cpython-311.pyc | Bin 0 -> 962 bytes src/oauth/__pycache__/views.cpython-311.pyc | Bin 0 -> 14267 bytes .../__pycache__/0001_initial.cpython-311.pyc | Bin 0 -> 3500 bytes ...oauthuser_options_and_more.cpython-311.pyc | Bin 0 -> 3439 bytes ...3_alter_oauthuser_nickname.cpython-311.pyc | Bin 0 -> 884 bytes .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 163 bytes .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 165 bytes .../__pycache__/oauth_tags.cpython-311.pyc | Bin 0 -> 1422 bytes .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 156 bytes .../__pycache__/admin.cpython-311.pyc | Bin 0 -> 474 bytes .../__pycache__/apps.cpython-311.pyc | Bin 0 -> 472 bytes .../__pycache__/models.cpython-311.pyc | Bin 0 -> 1417 bytes .../__pycache__/urls.cpython-311.pyc | Bin 0 -> 734 bytes .../__pycache__/views.cpython-311.pyc | Bin 0 -> 7760 bytes .../__pycache__/0001_initial.cpython-311.pyc | Bin 0 -> 1484 bytes ...ntracklog_options_and_more.cpython-311.pyc | Bin 0 -> 962 bytes .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 167 bytes src/owntracks/views.py | 10 +- .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 154 bytes .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 172 bytes .../__pycache__/plugin.cpython-311.pyc | Bin 0 -> 1939 bytes src/plugins/article_copyright/plugin.py | 5 + .../article_recommendation/__init__.py | 1 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 177 bytes .../__pycache__/plugin.cpython-311.pyc | Bin 0 -> 9906 bytes src/plugins/article_recommendation/plugin.py | 205 ++++++ .../css/recommendation.css | 166 +++++ .../js/recommendation.js | 93 +++ .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 169 bytes .../__pycache__/plugin.cpython-311.pyc | Bin 0 -> 2754 bytes src/plugins/image_lazy_loading/__init__.py | 1 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 173 bytes .../__pycache__/plugin.cpython-311.pyc | Bin 0 -> 7280 bytes src/plugins/image_lazy_loading/plugin.py | 182 ++++++ .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 167 bytes .../__pycache__/plugin.cpython-311.pyc | Bin 0 -> 2283 bytes src/plugins/reading_time/plugin.py | 8 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 168 bytes .../__pycache__/plugin.cpython-311.pyc | Bin 0 -> 7982 bytes src/plugins/seo_optimizer/plugin.py | 7 +- .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 165 bytes .../__pycache__/plugin.cpython-311.pyc | Bin 0 -> 1287 bytes src/requirements.txt | Bin 474 -> 1654 bytes .../MemcacheStorage.cpython-311.pyc | Bin 0 -> 2451 bytes .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 160 bytes .../__pycache__/admin.cpython-311.pyc | Bin 0 -> 1047 bytes .../__pycache__/apps.cpython-311.pyc | Bin 0 -> 484 bytes .../__pycache__/models.cpython-311.pyc | Bin 0 -> 2518 bytes .../__pycache__/robot.cpython-311.pyc | Bin 0 -> 10681 bytes .../__pycache__/urls.cpython-311.pyc | Bin 0 -> 457 bytes .../api/__pycache__/__init__.cpython-311.pyc | Bin 0 -> 164 bytes .../api/__pycache__/blogapi.cpython-311.pyc | Bin 0 -> 2131 bytes .../api/__pycache__/commonapi.cpython-311.pyc | Bin 0 -> 3908 bytes .../__pycache__/0001_initial.cpython-311.pyc | Bin 0 -> 2162 bytes ...ilsendlog_options_and_more.cpython-311.pyc | Bin 0 -> 1158 bytes .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 171 bytes src/templates/blog/article_detail.html | 24 - src/templates/blog/tags/article_info.html | 11 +- .../blog/tags/article_meta_info.html | 18 +- src/templates/blog/tags/sidebar.html | 2 +- src/templates/comments/tags/comment_item.html | 7 +- .../comments/tags/comment_item_tree.html | 7 +- .../article_recommendation/__init__.py | 1 + .../article_recommendation/bottom_widget.html | 23 + .../sidebar_widget.html | 17 + src/templates/plugins/css_includes.html | 4 + src/templates/plugins/js_includes.html | 4 + src/templates/share_layout/base.html | 88 +-- 245 files changed, 3690 insertions(+), 1235 deletions(-) delete mode 100644 src/.coveragerc create mode 100644 src/.github/workflows/deploy-master.yml delete mode 100644 src/.gitignore delete mode 100644 src/.vscode/launch.json create mode 100644 src/accounts/__pycache__/__init__.cpython-311.pyc create mode 100644 src/accounts/__pycache__/admin.cpython-311.pyc create mode 100644 src/accounts/__pycache__/apps.cpython-311.pyc create mode 100644 src/accounts/__pycache__/forms.cpython-311.pyc create mode 100644 src/accounts/__pycache__/models.cpython-311.pyc create mode 100644 src/accounts/__pycache__/urls.cpython-311.pyc create mode 100644 src/accounts/__pycache__/user_login_backend.cpython-311.pyc create mode 100644 src/accounts/__pycache__/utils.cpython-311.pyc create mode 100644 src/accounts/__pycache__/views.cpython-311.pyc create mode 100644 src/accounts/migrations/__pycache__/0001_initial.cpython-311.pyc create mode 100644 src/accounts/migrations/__pycache__/0002_alter_bloguser_options_remove_bloguser_created_time_and_more.cpython-311.pyc create mode 100644 src/accounts/migrations/__pycache__/__init__.cpython-311.pyc create mode 100644 src/accounts/templatetags/__pycache__/__init__.cpython-311.pyc delete mode 100644 src/bin/docker_start.sh delete mode 100644 src/bin/nginx.conf create mode 100644 src/blog/__pycache__/__init__.cpython-311.pyc create mode 100644 src/blog/__pycache__/admin.cpython-311.pyc create mode 100644 src/blog/__pycache__/apps.cpython-311.pyc create mode 100644 src/blog/__pycache__/context_processors.cpython-311.pyc create mode 100644 src/blog/__pycache__/documents.cpython-311.pyc create mode 100644 src/blog/__pycache__/middleware.cpython-311.pyc create mode 100644 src/blog/__pycache__/models.cpython-311.pyc create mode 100644 src/blog/__pycache__/urls.cpython-311.pyc create mode 100644 src/blog/__pycache__/views.cpython-311.pyc create mode 100644 src/blog/management/__pycache__/__init__.cpython-311.pyc create mode 100644 src/blog/migrations/__pycache__/0001_initial.cpython-311.pyc create mode 100644 src/blog/migrations/__pycache__/0002_blogsettings_global_footer_and_more.cpython-311.pyc create mode 100644 src/blog/migrations/__pycache__/0003_blogsettings_comment_need_review.cpython-311.pyc create mode 100644 src/blog/migrations/__pycache__/0004_rename_analyticscode_blogsettings_analytics_code_and_more.cpython-311.pyc create mode 100644 src/blog/migrations/__pycache__/0005_alter_article_options_alter_category_options_and_more.cpython-311.pyc create mode 100644 src/blog/migrations/__pycache__/0006_alter_blogsettings_options.cpython-311.pyc create mode 100644 src/blog/migrations/__pycache__/__init__.cpython-311.pyc delete mode 100644 src/blog/static/blog/fonts/fonts.css delete mode 100644 src/blog/static/blog/fonts/mem5YaGs126MiZpBA-UN_r8OUehpOqc.woff2 delete mode 100644 src/blog/static/blog/fonts/mem5YaGs126MiZpBA-UN_r8OUuhp.woff2 delete mode 100644 src/blog/static/blog/fonts/mem5YaGs126MiZpBA-UN_r8OVuhpOqc.woff2 delete mode 100644 src/blog/static/blog/fonts/mem5YaGs126MiZpBA-UN_r8OX-hpOqc.woff2 delete mode 100644 src/blog/static/blog/fonts/mem5YaGs126MiZpBA-UN_r8OXOhpOqc.woff2 delete mode 100644 src/blog/static/blog/fonts/mem5YaGs126MiZpBA-UN_r8OXehpOqc.woff2 delete mode 100644 src/blog/static/blog/fonts/mem5YaGs126MiZpBA-UN_r8OXuhpOqc.woff2 delete mode 100644 src/blog/static/blog/fonts/mem5YaGs126MiZpBA-UNirkOUehpOqc.woff2 delete mode 100644 src/blog/static/blog/fonts/mem5YaGs126MiZpBA-UNirkOUuhp.woff2 delete mode 100644 src/blog/static/blog/fonts/mem5YaGs126MiZpBA-UNirkOVuhpOqc.woff2 delete mode 100644 src/blog/static/blog/fonts/mem5YaGs126MiZpBA-UNirkOX-hpOqc.woff2 delete mode 100644 src/blog/static/blog/fonts/mem5YaGs126MiZpBA-UNirkOXOhpOqc.woff2 delete mode 100644 src/blog/static/blog/fonts/mem5YaGs126MiZpBA-UNirkOXehpOqc.woff2 delete mode 100644 src/blog/static/blog/fonts/mem5YaGs126MiZpBA-UNirkOXuhpOqc.woff2 delete mode 100644 src/blog/static/blog/fonts/mem6YaGs126MiZpBA-UFUK0Udc1UAw.woff2 delete mode 100644 src/blog/static/blog/fonts/mem6YaGs126MiZpBA-UFUK0Vdc1UAw.woff2 delete mode 100644 src/blog/static/blog/fonts/mem6YaGs126MiZpBA-UFUK0Wdc1UAw.woff2 delete mode 100644 src/blog/static/blog/fonts/mem6YaGs126MiZpBA-UFUK0Xdc1UAw.woff2 delete mode 100644 src/blog/static/blog/fonts/mem6YaGs126MiZpBA-UFUK0Zdc0.woff2 delete mode 100644 src/blog/static/blog/fonts/mem6YaGs126MiZpBA-UFUK0adc1UAw.woff2 delete mode 100644 src/blog/static/blog/fonts/mem6YaGs126MiZpBA-UFUK0ddc1UAw.woff2 delete mode 100644 src/blog/static/blog/fonts/mem8YaGs126MiZpBA-UFUZ0bbck.woff2 delete mode 100644 src/blog/static/blog/fonts/mem8YaGs126MiZpBA-UFVZ0b.woff2 delete mode 100644 src/blog/static/blog/fonts/mem8YaGs126MiZpBA-UFVp0bbck.woff2 delete mode 100644 src/blog/static/blog/fonts/mem8YaGs126MiZpBA-UFW50bbck.woff2 delete mode 100644 src/blog/static/blog/fonts/mem8YaGs126MiZpBA-UFWJ0bbck.woff2 delete mode 100644 src/blog/static/blog/fonts/mem8YaGs126MiZpBA-UFWZ0bbck.woff2 delete mode 100644 src/blog/static/blog/fonts/mem8YaGs126MiZpBA-UFWp0bbck.woff2 delete mode 100644 src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKWyV9hkIqOjjg.woff2 delete mode 100644 src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKWyV9hlIqOjjg.woff2 delete mode 100644 src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKWyV9hmIqOjjg.woff2 delete mode 100644 src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKWyV9hnIqOjjg.woff2 delete mode 100644 src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKWyV9hoIqOjjg.woff2 delete mode 100644 src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKWyV9hrIqM.woff2 delete mode 100644 src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKWyV9hvIqOjjg.woff2 delete mode 100644 src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKXGUdhkIqOjjg.woff2 delete mode 100644 src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKXGUdhlIqOjjg.woff2 delete mode 100644 src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKXGUdhmIqOjjg.woff2 delete mode 100644 src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKXGUdhnIqOjjg.woff2 delete mode 100644 src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKXGUdhoIqOjjg.woff2 delete mode 100644 src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKXGUdhrIqM.woff2 delete mode 100644 src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKXGUdhvIqOjjg.woff2 create mode 100644 src/blog/static/blog/fonts/memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqW106F15M.woff2 create mode 100644 src/blog/static/blog/fonts/memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWt06F15M.woff2 create mode 100644 src/blog/static/blog/fonts/memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWtE6F15M.woff2 create mode 100644 src/blog/static/blog/fonts/memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWtU6F15M.woff2 create mode 100644 src/blog/static/blog/fonts/memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWtk6F15M.woff2 create mode 100644 src/blog/static/blog/fonts/memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWu06F15M.woff2 create mode 100644 src/blog/static/blog/fonts/memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWuU6F.woff2 create mode 100644 src/blog/static/blog/fonts/memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWuk6F15M.woff2 create mode 100644 src/blog/static/blog/fonts/memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWvU6F15M.woff2 create mode 100644 src/blog/static/blog/fonts/memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWxU6F15M.woff2 create mode 100644 src/blog/static/blog/fonts/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTS-muw.woff2 create mode 100644 src/blog/static/blog/fonts/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTS2mu1aB.woff2 create mode 100644 src/blog/static/blog/fonts/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSCmu1aB.woff2 create mode 100644 src/blog/static/blog/fonts/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSGmu1aB.woff2 create mode 100644 src/blog/static/blog/fonts/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSKmu1aB.woff2 create mode 100644 src/blog/static/blog/fonts/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSOmu1aB.woff2 create mode 100644 src/blog/static/blog/fonts/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSumu1aB.woff2 create mode 100644 src/blog/static/blog/fonts/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSymu1aB.woff2 create mode 100644 src/blog/static/blog/fonts/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTUGmu1aB.woff2 create mode 100644 src/blog/static/blog/fonts/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTVOmu1aB.woff2 create mode 100644 src/blog/static/blog/fonts/open-sans.css create mode 100644 src/blog/static/blog/js/mathjax-loader.js delete mode 100644 src/blog/static/mathjax/js/mathjax-config.js create mode 100644 src/blog/templatetags/__pycache__/__init__.cpython-311.pyc create mode 100644 src/blog/templatetags/__pycache__/blog_tags.cpython-311.pyc create mode 100644 src/codecov.yml create mode 100644 src/collectedstatic/compressed/css/output.f76962182efc.css create mode 100644 src/collectedstatic/compressed/css/output.fbe498417efd.css create mode 100644 src/collectedstatic/compressed/js/output.c56f0a57c4ca.js create mode 100644 src/comments/__pycache__/__init__.cpython-311.pyc create mode 100644 src/comments/__pycache__/admin.cpython-311.pyc create mode 100644 src/comments/__pycache__/apps.cpython-311.pyc create mode 100644 src/comments/__pycache__/forms.cpython-311.pyc create mode 100644 src/comments/__pycache__/models.cpython-311.pyc create mode 100644 src/comments/__pycache__/urls.cpython-311.pyc create mode 100644 src/comments/__pycache__/utils.cpython-311.pyc create mode 100644 src/comments/__pycache__/views.cpython-311.pyc create mode 100644 src/comments/migrations/__pycache__/0001_initial.cpython-311.pyc create mode 100644 src/comments/migrations/__pycache__/0002_alter_comment_is_enable.cpython-311.pyc create mode 100644 src/comments/migrations/__pycache__/0003_alter_comment_options_remove_comment_created_time_and_more.cpython-311.pyc create mode 100644 src/comments/migrations/__pycache__/__init__.cpython-311.pyc create mode 100644 src/comments/templatetags/__pycache__/__init__.cpython-311.pyc create mode 100644 src/comments/templatetags/__pycache__/comments_tags.cpython-311.pyc create mode 100644 src/djangoblog/__pycache__/__init__.cpython-311.pyc create mode 100644 src/djangoblog/__pycache__/admin_site.cpython-311.pyc create mode 100644 src/djangoblog/__pycache__/apps.cpython-311.pyc create mode 100644 src/djangoblog/__pycache__/blog_signals.cpython-311.pyc create mode 100644 src/djangoblog/__pycache__/elasticsearch_backend.cpython-311.pyc create mode 100644 src/djangoblog/__pycache__/feeds.cpython-311.pyc create mode 100644 src/djangoblog/__pycache__/logentryadmin.cpython-311.pyc create mode 100644 src/djangoblog/__pycache__/settings.cpython-311.pyc create mode 100644 src/djangoblog/__pycache__/sitemap.cpython-311.pyc create mode 100644 src/djangoblog/__pycache__/spider_notify.cpython-311.pyc create mode 100644 src/djangoblog/__pycache__/urls.cpython-311.pyc create mode 100644 src/djangoblog/__pycache__/utils.cpython-311.pyc create mode 100644 src/djangoblog/__pycache__/whoosh_cn_backend.cpython-311.pyc create mode 100644 src/djangoblog/__pycache__/wsgi.cpython-311.pyc create mode 100644 src/djangoblog/plugin_manage/__pycache__/base_plugin.cpython-311.pyc create mode 100644 src/djangoblog/plugin_manage/__pycache__/hook_constants.cpython-311.pyc create mode 100644 src/djangoblog/plugin_manage/__pycache__/hooks.cpython-311.pyc create mode 100644 src/djangoblog/plugin_manage/__pycache__/loader.cpython-311.pyc delete mode 100644 src/docker-compose.es.yml delete mode 100644 src/docker-compose.yml create mode 100644 src/logs/djangoblog.log create mode 100644 src/oauth/__pycache__/__init__.cpython-311.pyc create mode 100644 src/oauth/__pycache__/admin.cpython-311.pyc create mode 100644 src/oauth/__pycache__/apps.cpython-311.pyc create mode 100644 src/oauth/__pycache__/forms.cpython-311.pyc create mode 100644 src/oauth/__pycache__/models.cpython-311.pyc create mode 100644 src/oauth/__pycache__/oauthmanager.cpython-311.pyc create mode 100644 src/oauth/__pycache__/urls.cpython-311.pyc create mode 100644 src/oauth/__pycache__/views.cpython-311.pyc create mode 100644 src/oauth/migrations/__pycache__/0001_initial.cpython-311.pyc create mode 100644 src/oauth/migrations/__pycache__/0002_alter_oauthconfig_options_alter_oauthuser_options_and_more.cpython-311.pyc create mode 100644 src/oauth/migrations/__pycache__/0003_alter_oauthuser_nickname.cpython-311.pyc create mode 100644 src/oauth/migrations/__pycache__/__init__.cpython-311.pyc create mode 100644 src/oauth/templatetags/__pycache__/__init__.cpython-311.pyc create mode 100644 src/oauth/templatetags/__pycache__/oauth_tags.cpython-311.pyc create mode 100644 src/owntracks/__pycache__/__init__.cpython-311.pyc create mode 100644 src/owntracks/__pycache__/admin.cpython-311.pyc create mode 100644 src/owntracks/__pycache__/apps.cpython-311.pyc create mode 100644 src/owntracks/__pycache__/models.cpython-311.pyc create mode 100644 src/owntracks/__pycache__/urls.cpython-311.pyc create mode 100644 src/owntracks/__pycache__/views.cpython-311.pyc create mode 100644 src/owntracks/migrations/__pycache__/0001_initial.cpython-311.pyc create mode 100644 src/owntracks/migrations/__pycache__/0002_alter_owntracklog_options_and_more.cpython-311.pyc create mode 100644 src/owntracks/migrations/__pycache__/__init__.cpython-311.pyc create mode 100644 src/plugins/__pycache__/__init__.cpython-311.pyc create mode 100644 src/plugins/article_copyright/__pycache__/__init__.cpython-311.pyc create mode 100644 src/plugins/article_copyright/__pycache__/plugin.cpython-311.pyc create mode 100644 src/plugins/article_recommendation/__init__.py create mode 100644 src/plugins/article_recommendation/__pycache__/__init__.cpython-311.pyc create mode 100644 src/plugins/article_recommendation/__pycache__/plugin.cpython-311.pyc create mode 100644 src/plugins/article_recommendation/plugin.py create mode 100644 src/plugins/article_recommendation/static/article_recommendation/css/recommendation.css create mode 100644 src/plugins/article_recommendation/static/article_recommendation/js/recommendation.js create mode 100644 src/plugins/external_links/__pycache__/__init__.cpython-311.pyc create mode 100644 src/plugins/external_links/__pycache__/plugin.cpython-311.pyc create mode 100644 src/plugins/image_lazy_loading/__init__.py create mode 100644 src/plugins/image_lazy_loading/__pycache__/__init__.cpython-311.pyc create mode 100644 src/plugins/image_lazy_loading/__pycache__/plugin.cpython-311.pyc create mode 100644 src/plugins/image_lazy_loading/plugin.py create mode 100644 src/plugins/reading_time/__pycache__/__init__.cpython-311.pyc create mode 100644 src/plugins/reading_time/__pycache__/plugin.cpython-311.pyc create mode 100644 src/plugins/seo_optimizer/__pycache__/__init__.cpython-311.pyc create mode 100644 src/plugins/seo_optimizer/__pycache__/plugin.cpython-311.pyc create mode 100644 src/plugins/view_count/__pycache__/__init__.cpython-311.pyc create mode 100644 src/plugins/view_count/__pycache__/plugin.cpython-311.pyc create mode 100644 src/servermanager/__pycache__/MemcacheStorage.cpython-311.pyc create mode 100644 src/servermanager/__pycache__/__init__.cpython-311.pyc create mode 100644 src/servermanager/__pycache__/admin.cpython-311.pyc create mode 100644 src/servermanager/__pycache__/apps.cpython-311.pyc create mode 100644 src/servermanager/__pycache__/models.cpython-311.pyc create mode 100644 src/servermanager/__pycache__/robot.cpython-311.pyc create mode 100644 src/servermanager/__pycache__/urls.cpython-311.pyc create mode 100644 src/servermanager/api/__pycache__/__init__.cpython-311.pyc create mode 100644 src/servermanager/api/__pycache__/blogapi.cpython-311.pyc create mode 100644 src/servermanager/api/__pycache__/commonapi.cpython-311.pyc create mode 100644 src/servermanager/migrations/__pycache__/0001_initial.cpython-311.pyc create mode 100644 src/servermanager/migrations/__pycache__/0002_alter_emailsendlog_options_and_more.cpython-311.pyc create mode 100644 src/servermanager/migrations/__pycache__/__init__.cpython-311.pyc create mode 100644 src/templates/plugins/article_recommendation/__init__.py create mode 100644 src/templates/plugins/article_recommendation/bottom_widget.html create mode 100644 src/templates/plugins/article_recommendation/sidebar_widget.html create mode 100644 src/templates/plugins/css_includes.html create mode 100644 src/templates/plugins/js_includes.html diff --git a/src/.coveragerc b/src/.coveragerc deleted file mode 100644 index 9757484..0000000 --- a/src/.coveragerc +++ /dev/null @@ -1,10 +0,0 @@ -[run] -source = . -include = *.py -omit = - *migrations* - *tests* - *.html - *whoosh_cn_backend* - *settings.py* - *venv* diff --git a/src/.dockerignore b/src/.dockerignore index 2818c38..bd68a58 100644 --- a/src/.dockerignore +++ b/src/.dockerignore @@ -8,4 +8,5 @@ settings_production.py *.md docs/ logs/ -static/ \ No newline at end of file +static/ +.github/ diff --git a/src/.github/workflows/codeql-analysis.yml b/src/.github/workflows/codeql-analysis.yml index 6b76522..52775e0 100644 --- a/src/.github/workflows/codeql-analysis.yml +++ b/src/.github/workflows/codeql-analysis.yml @@ -38,10 +38,12 @@ jobs: uses: actions/checkout@v3 - name: Initialize CodeQL - uses: github/codeql-action/init@v2 - + uses: github/codeql-action/init@v3 + with: + languages: python + - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 \ No newline at end of file + uses: github/codeql-action/analyze@v3 \ No newline at end of file diff --git a/src/.github/workflows/deploy-master.yml b/src/.github/workflows/deploy-master.yml new file mode 100644 index 0000000..c07a326 --- /dev/null +++ b/src/.github/workflows/deploy-master.yml @@ -0,0 +1,176 @@ +name: 自动部署到生产环境 + +on: + workflow_run: + workflows: ["Django CI"] + types: + - completed + branches: + - master + workflow_dispatch: + inputs: + environment: + description: '部署环境' + required: true + default: 'production' + type: choice + options: + - production + - staging + image_tag: + description: '镜像标签 (默认: latest)' + required: false + default: 'latest' + type: string + skip_tests: + description: '跳过测试直接部署' + required: false + default: false + type: boolean + +env: + REGISTRY: registry.cn-shenzhen.aliyuncs.com + IMAGE_NAME: liangliangyy/djangoblog + NAMESPACE: djangoblog + +jobs: + deploy: + name: 构建镜像并部署到生产环境 + runs-on: ubuntu-latest + if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'workflow_dispatch' }} + + steps: + - name: 检出代码 + uses: actions/checkout@v4 + + - name: 设置部署参数 + id: deploy-params + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + echo "trigger_type=手动触发" >> $GITHUB_OUTPUT + echo "environment=${{ github.event.inputs.environment }}" >> $GITHUB_OUTPUT + echo "image_tag=${{ github.event.inputs.image_tag }}" >> $GITHUB_OUTPUT + echo "skip_tests=${{ github.event.inputs.skip_tests }}" >> $GITHUB_OUTPUT + else + echo "trigger_type=CI自动触发" >> $GITHUB_OUTPUT + echo "environment=production" >> $GITHUB_OUTPUT + echo "image_tag=latest" >> $GITHUB_OUTPUT + echo "skip_tests=false" >> $GITHUB_OUTPUT + fi + + - name: 显示部署信息 + run: | + echo "🚀 部署信息:" + echo " 触发方式: ${{ steps.deploy-params.outputs.trigger_type }}" + echo " 部署环境: ${{ steps.deploy-params.outputs.environment }}" + echo " 镜像标签: ${{ steps.deploy-params.outputs.image_tag }}" + echo " 跳过测试: ${{ steps.deploy-params.outputs.skip_tests }}" + + - name: 设置Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: 登录私有镜像仓库 + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ secrets.REGISTRY_USERNAME }} + password: ${{ secrets.REGISTRY_PASSWORD }} + + - name: 提取镜像元数据 + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=ref,event=branch + type=sha,prefix={{branch}}- + type=raw,value=${{ steps.deploy-params.outputs.image_tag }} + + - name: 构建并推送Docker镜像 + uses: docker/build-push-action@v5 + with: + context: . + file: ./Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + platforms: linux/amd64 + + - name: 部署到生产服务器 + uses: appleboy/ssh-action@v1.0.3 + with: + host: ${{ secrets.PRODUCTION_HOST }} + username: ${{ secrets.PRODUCTION_USER }} + key: ${{ secrets.PRODUCTION_SSH_KEY }} + port: ${{ secrets.PRODUCTION_PORT || 22 }} + script: | + echo "🚀 开始部署 DjangoBlog..." + + # 检查kubectl是否可用 + if ! command -v kubectl &> /dev/null; then + echo "❌ 错误: kubectl 未安装或不在PATH中" + exit 1 + fi + + # 检查命名空间是否存在 + if ! kubectl get namespace ${{ env.NAMESPACE }} &> /dev/null; then + echo "❌ 错误: 命名空间 ${{ env.NAMESPACE }} 不存在" + exit 1 + fi + + # 更新deployment镜像 + echo "📦 更新deployment镜像为: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.deploy-params.outputs.image_tag }}" + kubectl set image deployment/djangoblog \ + djangoblog=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.deploy-params.outputs.image_tag }} \ + -n ${{ env.NAMESPACE }} + + # 重启deployment + echo "🔄 重启deployment..." + kubectl -n ${{ env.NAMESPACE }} rollout restart deployment djangoblog + + # 等待deployment完成 + echo "⏳ 等待deployment完成..." + kubectl rollout status deployment/djangoblog -n ${{ env.NAMESPACE }} --timeout=300s + + # 检查deployment状态 + echo "✅ 检查deployment状态..." + kubectl get deployment djangoblog -n ${{ env.NAMESPACE }} + kubectl get pods -l app=djangoblog -n ${{ env.NAMESPACE }} + + echo "🎉 部署完成!" + + - name: 发送部署通知 + if: always() + run: | + # 设置通知内容 + if [ "${{ job.status }}" = "success" ]; then + TITLE="✅ DjangoBlog部署成功" + STATUS="成功" + else + TITLE="❌ DjangoBlog部署失败" + STATUS="失败" + fi + + MESSAGE="部署状态: ${STATUS} + 触发方式: ${{ steps.deploy-params.outputs.trigger_type }} + 部署环境: ${{ steps.deploy-params.outputs.environment }} + 镜像标签: ${{ steps.deploy-params.outputs.image_tag }} + 提交者: ${{ github.actor }} + 时间: $(date '+%Y-%m-%d %H:%M:%S') + + 查看详情: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" + + # 发送Server酱通知 + if [ -n "${{ secrets.SERVERCHAN_KEY }}" ]; then + echo "{\"title\": \"${TITLE}\", \"desp\": \"${MESSAGE}\"}" > /tmp/serverchan.json + + curl --location "https://sctapi.ftqq.com/${{ secrets.SERVERCHAN_KEY }}.send" \ + --header "Content-Type: application/json" \ + --data @/tmp/serverchan.json \ + --silent > /dev/null + + rm -f /tmp/serverchan.json + echo "📱 部署通知已发送" + fi \ No newline at end of file diff --git a/src/.github/workflows/django.yml b/src/.github/workflows/django.yml index bf23242..ebe7953 100644 --- a/src/.github/workflows/django.yml +++ b/src/.github/workflows/django.yml @@ -9,7 +9,6 @@ on: - '**/*.md' - '**/*.css' - '**/*.js' - - '**/*.yml' pull_request: branches: - master @@ -18,58 +17,61 @@ on: - '**/*.md' - '**/*.css' - '**/*.js' - - '**/*.yml' jobs: - build-normal: + test: runs-on: ubuntu-latest strategy: - max-parallel: 4 + fail-fast: false matrix: - python-version: [ "3.8", "3.9","3.10","3.11" ] + include: + # 标准测试 - Python 3.10 + - python-version: "3.10" + test-type: "standard" + database: "mysql" + elasticsearch: false + coverage: false + + # 标准测试 - Python 3.11 + - python-version: "3.11" + test-type: "standard" + database: "mysql" + elasticsearch: false + coverage: false + + # 完整测试 - 包含ES和覆盖率 + - python-version: "3.11" + test-type: "full" + database: "mysql" + elasticsearch: true + coverage: true + + # Docker构建测试 + - python-version: "3.11" + test-type: "docker" + database: "none" + elasticsearch: false + coverage: false + name: Test (${{ matrix.test-type }}, Python ${{ matrix.python-version }}) + steps: - - name: Start MySQL - uses: samin/mysql-action@v1.3 - with: - host port: 3306 - container port: 3306 - character set server: utf8mb4 - collation server: utf8mb4_general_ci - mysql version: latest - mysql root password: root - mysql database: djangoblog - mysql user: root - mysql password: root - - - uses: actions/checkout@v3 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - cache: 'pip' - - name: Install Dependencies - run: | - python -m pip install --upgrade pip - pip install -r requirements.txt - - name: Run Tests - env: - DJANGO_MYSQL_PASSWORD: root - DJANGO_MYSQL_HOST: 127.0.0.1 + - name: Checkout代码 + uses: actions/checkout@v4 + + - name: 设置测试信息 + id: test-info run: | - python manage.py makemigrations - python manage.py migrate - python manage.py test - - build-with-es: - runs-on: ubuntu-latest - strategy: - max-parallel: 4 - matrix: - python-version: [ "3.8", "3.9","3.10","3.11" ] - - steps: - - name: Start MySQL + echo "test_name=${{ matrix.test-type }}-py${{ matrix.python-version }}" >> $GITHUB_OUTPUT + if [ "${{ matrix.test-type }}" = "docker" ]; then + echo "skip_python_setup=true" >> $GITHUB_OUTPUT + else + echo "skip_python_setup=false" >> $GITHUB_OUTPUT + fi + + # MySQL数据库设置 (只有需要数据库的测试才执行) + - name: 启动MySQL数据库 + if: matrix.database == 'mysql' uses: samin/mysql-action@v1.3 with: host port: 3306 @@ -81,56 +83,289 @@ jobs: mysql database: djangoblog mysql user: root mysql password: root - - - name: Configure sysctl limits + + # Elasticsearch设置 (只有完整测试才执行) + - name: 配置系统参数 (ES) + if: matrix.elasticsearch == true run: | sudo swapoff -a sudo sysctl -w vm.swappiness=1 sudo sysctl -w fs.file-max=262144 sudo sysctl -w vm.max_map_count=262144 - - - uses: miyataka/elasticsearch-github-actions@1 + + - name: 启动Elasticsearch + if: matrix.elasticsearch == true + uses: miyataka/elasticsearch-github-actions@1 with: stack-version: '7.12.1' - plugins: 'https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.12.1/elasticsearch-analysis-ik-7.12.1.zip' - - - uses: actions/checkout@v3 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + plugins: 'https://release.infinilabs.com/analysis-ik/stable/elasticsearch-analysis-ik-7.12.1.zip' + + # Python环境设置 (Docker测试跳过) + - name: 设置Python ${{ matrix.python-version }} + if: steps.test-info.outputs.skip_python_setup == 'false' + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} cache: 'pip' - - name: Install Dependencies + cache-dependency-path: 'requirements.txt' + + # 多层缓存策略优化 + - name: 缓存Python依赖 + if: steps.test-info.outputs.skip_python_setup == 'false' + uses: actions/cache@v4 + with: + path: | + ~/.cache/pip + .pytest_cache + key: ${{ runner.os }}-python-${{ matrix.python-version }}-${{ hashFiles('requirements.txt') }}-${{ hashFiles('**/pyproject.toml') }} + restore-keys: | + ${{ runner.os }}-python-${{ matrix.python-version }}-${{ hashFiles('requirements.txt') }}- + ${{ runner.os }}-python-${{ matrix.python-version }}- + ${{ runner.os }}-python- + + # Django缓存优化 (测试数据库等) + - name: 缓存Django资源 + if: matrix.test-type != 'docker' + uses: actions/cache@v4 + with: + path: | + .coverage* + htmlcov/ + .django_cache/ + key: ${{ runner.os }}-django-${{ matrix.test-type }}-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-django-${{ matrix.test-type }}- + ${{ runner.os }}-django- + + - name: 安装Python依赖 + if: steps.test-info.outputs.skip_python_setup == 'false' run: | - python -m pip install --upgrade pip + echo "📦 安装Python依赖 (Python ${{ matrix.python-version }})" + python -m pip install --upgrade pip setuptools wheel + + # 安装基础依赖 pip install -r requirements.txt - - name: Run Tests + + # 根据测试类型安装额外依赖 + if [ "${{ matrix.coverage }}" = "true" ]; then + echo "📊 安装覆盖率工具" + pip install coverage[toml] + fi + + # 验证关键依赖 + echo "🔍 验证关键依赖安装" + python -c "import django; print(f'Django version: {django.get_version()}')" + python -c "import MySQLdb; print('MySQL client: OK')" || python -c "import pymysql; print('PyMySQL client: OK')" + + if [ "${{ matrix.elasticsearch }}" = "true" ]; then + python -c "import elasticsearch; print('Elasticsearch client: OK')" + fi + + # Django环境准备 + - name: 准备Django环境 + if: matrix.test-type != 'docker' env: DJANGO_MYSQL_PASSWORD: root DJANGO_MYSQL_HOST: 127.0.0.1 - DJANGO_ELASTICSEARCH_HOST: 127.0.0.1:9200 + DJANGO_ELASTICSEARCH_HOST: ${{ matrix.elasticsearch && '127.0.0.1:9200' || '' }} run: | - python manage.py makemigrations - python manage.py migrate - coverage run manage.py test - coverage xml - - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v1 - - docker: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Set up QEMU - uses: docker/setup-qemu-action@v2 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - - name: Build and push - uses: docker/build-push-action@v3 + echo "🔧 准备Django测试环境" + + # 等待数据库就绪 + echo "⏳ 等待MySQL数据库启动..." + for i in {1..30}; do + if python -c "import MySQLdb; MySQLdb.connect(host='127.0.0.1', user='root', passwd='root', db='djangoblog')" 2>/dev/null; then + echo "✅ MySQL数据库连接成功" + break + fi + echo "🔄 等待数据库启动... ($i/30)" + sleep 2 + done + + # 等待Elasticsearch就绪 (如果启用) + if [ "${{ matrix.elasticsearch }}" = "true" ]; then + echo "⏳ 等待Elasticsearch启动..." + for i in {1..30}; do + if curl -s http://127.0.0.1:9200/_cluster/health | grep -q '"status":"green"\|"status":"yellow"'; then + echo "✅ Elasticsearch连接成功" + break + fi + echo "🔄 等待Elasticsearch启动... ($i/30)" + sleep 2 + done + fi + + # Django测试执行 + - name: 执行数据库迁移 + if: matrix.test-type != 'docker' + env: + DJANGO_MYSQL_PASSWORD: root + DJANGO_MYSQL_HOST: 127.0.0.1 + DJANGO_ELASTICSEARCH_HOST: ${{ matrix.elasticsearch && '127.0.0.1:9200' || '' }} + run: | + echo "🗄️ 执行数据库迁移" + + # 检查迁移文件 + echo "📋 检查待应用的迁移..." + python manage.py showmigrations + + # 检查是否有未创建的迁移 + python manage.py makemigrations --check --verbosity 2 + + # 执行迁移 + python manage.py migrate --verbosity 2 + + echo "✅ 数据库迁移完成" + + - name: 运行Django测试 + if: matrix.test-type != 'docker' + env: + DJANGO_MYSQL_PASSWORD: root + DJANGO_MYSQL_HOST: 127.0.0.1 + DJANGO_ELASTICSEARCH_HOST: ${{ matrix.elasticsearch && '127.0.0.1:9200' || '' }} + run: | + echo "🧪 开始执行 ${{ matrix.test-type }} 测试 (Python ${{ matrix.python-version }})" + + # 显示Django配置信息 + python manage.py diffsettings | head -20 + + # 运行测试 + if [ "${{ matrix.coverage }}" = "true" ]; then + echo "📊 运行测试并生成覆盖率报告" + coverage run --source='.' --omit='*/venv/*,*/migrations/*,*/tests/*,manage.py' manage.py test --verbosity=2 + + echo "📈 生成覆盖率报告" + coverage xml + coverage report --show-missing + coverage html + + echo "📋 覆盖率统计:" + coverage report | tail -1 + else + echo "🧪 运行标准测试" + python manage.py test --verbosity=2 --failfast + fi + + echo "✅ 测试执行完成" + + # 覆盖率报告上传 (只有完整测试才执行) + - name: 上传覆盖率到Codecov + if: matrix.coverage == true && success() + uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} + file: ./coverage.xml + flags: unittests + name: codecov-${{ steps.test-info.outputs.test_name }} + fail_ci_if_error: false + verbose: true + + - name: 上传覆盖率到Codecov (备用) + if: matrix.coverage == true && failure() + uses: codecov/codecov-action@v4 + with: + file: ./coverage.xml + flags: unittests + name: codecov-${{ steps.test-info.outputs.test_name }}-fallback + fail_ci_if_error: false + verbose: true + + # Docker构建测试 + - name: 设置QEMU + if: matrix.test-type == 'docker' + uses: docker/setup-qemu-action@v3 + + - name: 设置Docker Buildx + if: matrix.test-type == 'docker' + uses: docker/setup-buildx-action@v3 + + - name: Docker构建测试 + if: matrix.test-type == 'docker' + uses: docker/build-push-action@v5 with: context: . push: false - tags: djangoblog/djangoblog:dev + tags: djangoblog/djangoblog:test-${{ github.sha }} + cache-from: type=gha + cache-to: type=gha,mode=max + + # 收集测试工件 (失败时收集调试信息) + - name: 收集测试工件 + if: failure() && matrix.test-type != 'docker' + run: | + echo "🔍 收集测试失败的调试信息" + + # 收集Django日志 + if [ -d "logs" ]; then + echo "📄 Django日志文件:" + ls -la logs/ + if [ -f "logs/djangoblog.log" ]; then + echo "🔍 最新日志内容:" + tail -100 logs/djangoblog.log + fi + fi + + # 显示数据库状态 + echo "🗄️ 数据库连接状态:" + python -c " + try: + from django.db import connection + cursor = connection.cursor() + cursor.execute('SELECT VERSION()') + print(f'MySQL版本: {cursor.fetchone()[0]}') + cursor.execute('SHOW TABLES') + tables = cursor.fetchall() + print(f'数据库表数量: {len(tables)}') + except Exception as e: + print(f'数据库连接错误: {e}') + " || true + + # Elasticsearch状态 (如果启用) + if [ "${{ matrix.elasticsearch }}" = "true" ]; then + echo "🔍 Elasticsearch状态:" + curl -s http://127.0.0.1:9200/_cluster/health?pretty || true + fi + + # 上传测试工件 + - name: 上传覆盖率HTML报告 + if: matrix.coverage == true && always() + uses: actions/upload-artifact@v4 + with: + name: coverage-report-${{ steps.test-info.outputs.test_name }} + path: htmlcov/ + retention-days: 30 + + # 性能统计 + - name: 测试性能统计 + if: always() && matrix.test-type != 'docker' + run: | + echo "⚡ 测试性能统计:" + echo " 开始时间: $(date -d '@${{ job.started_at }}' '+%Y-%m-%d %H:%M:%S' 2>/dev/null || echo '未知')" + echo " 当前时间: $(date '+%Y-%m-%d %H:%M:%S')" + + # 系统资源使用情况 + echo "💻 系统资源:" + echo " CPU使用: $(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1)%" + echo " 内存使用: $(free -h | awk '/^Mem:/ {printf "%.1f%%", $3/$2 * 100}')" + echo " 磁盘使用: $(df -h / | awk 'NR==2{printf "%s", $5}')" + + # 测试结果汇总 + - name: 测试完成总结 + if: always() + run: | + echo "📋 ============ 测试执行总结 ============" + echo " 🏷️ 测试类型: ${{ matrix.test-type }}" + echo " 🐍 Python版本: ${{ matrix.python-version }}" + echo " 🗄️ 数据库: ${{ matrix.database }}" + echo " 🔍 Elasticsearch: ${{ matrix.elasticsearch }}" + echo " 📊 覆盖率: ${{ matrix.coverage }}" + echo " ⚡ 状态: ${{ job.status }}" + echo " 📅 完成时间: $(date '+%Y-%m-%d %H:%M:%S')" + echo "============================================" + + # 根据测试结果显示不同消息 + if [ "${{ job.status }}" = "success" ]; then + echo "🎉 测试执行成功!" + else + echo "❌ 测试执行失败,请检查上面的日志" + fi diff --git a/src/.github/workflows/docker.yml b/src/.github/workflows/docker.yml index a312e2f..904fef5 100644 --- a/src/.github/workflows/docker.yml +++ b/src/.github/workflows/docker.yml @@ -22,19 +22,19 @@ jobs: run: | echo "DOCKER_TAG=latest" >> $GITHUB_ENV - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up QEMU - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - name: Login to DockerHub - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and push - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v5 with: context: . push: true diff --git a/src/.gitignore b/src/.gitignore deleted file mode 100644 index 1c1fcbf..0000000 --- a/src/.gitignore +++ /dev/null @@ -1,80 +0,0 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -env/ -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -*.egg-info/ -.installed.cfg -*.egg - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*,cover - -# Translations -*.pot - -# Django stuff: -*.log -logs/ - -# Sphinx documentation -docs/_build/ - -# PyBuilder -target/ - - -# PyCharm -# http://www.jetbrains.com/pycharm/webhelp/project.html -.idea -.iml -#static/ -# virtualenv -venv/ - -collectedstatic/ -djangoblog/whoosh_index/ -google93fd32dbd906620a.html -baidu_verify_FlHL7cUyC9.html -BingSiteAuth.xml -cb9339dbe2ff86a5aa169d28dba5f615.txt -werobot_session.* -django.jpg -uploads/ -settings_production.py -werobot_session.db -bin/datas/ diff --git a/src/.vscode/launch.json b/src/.vscode/launch.json deleted file mode 100644 index f5f50ec..0000000 --- a/src/.vscode/launch.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - // 使用 IntelliSense 了解相关属性。 - // 悬停以查看现有属性的描述。 - // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 - - "version": "0.2.0", - "configurations": [ - { - "name": "Django: Run Server (Debug)", - "type": "debugpy", - "request": "launch", - "program": "${workspaceFolder}/manage.py", - "args": [ - "runserver", - "--noreload", // 禁用自动重载以确保断点命中 - "127.0.0.1:8000" // 指定主机和端口 - ], - "django": true, // 启用Django特定支持 - "console": "integratedTerminal" // 在集成终端中输出 - } - ] -} diff --git a/src/Dockerfile b/src/Dockerfile index 9b14ebe..80b46ac 100644 --- a/src/Dockerfile +++ b/src/Dockerfile @@ -6,10 +6,10 @@ RUN apt-get update && \ rm -rf /var/lib/apt/lists/* ADD requirements.txt requirements.txt RUN pip install --upgrade pip && \ - pip install --no-cache-dir -r requirements.txt && \ + pip install --no-cache-dir -r requirements.txt && \ pip install --no-cache-dir gunicorn[gevent] && \ pip cache purge ADD . . -RUN chmod +x /code/djangoblog/bin/docker_start.sh -ENTRYPOINT ["/code/djangoblog/bin/docker_start.sh"] +RUN chmod +x /code/djangoblog/deploy/entrypoint.sh +ENTRYPOINT ["/code/djangoblog/deploy/entrypoint.sh"] diff --git a/src/LICENSE b/src/LICENSE index 1e22954..3b08474 100644 --- a/src/LICENSE +++ b/src/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2016 车亮亮 +Copyright (c) 2025 车亮亮 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/src/README.md b/src/README.md index 54a27f2..56aa4cc 100644 --- a/src/README.md +++ b/src/README.md @@ -1,137 +1,158 @@ # DjangoBlog -🌍 -*[English](/docs/README-en.md) ∙ [简体中文](README.md)* +
+ +
+ 一款功能强大、设计优雅的现代化博客系统
+
+ English • 简体中文
+
-
-
+
+
+
+ (左) 支付宝 / (右) 微信 +
---- +## 🙏 鸣谢 + +特别感谢 **JetBrains** 为本项目提供的免费开源许可证。 -感谢jetbrains - +
+
+
+
+