Compare commits
18
Commits
1.2.1
...
27242aed5e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
27242aed5e | ||
|
|
e200d850f9 | ||
|
|
c90858f45c | ||
|
|
92c456c7d1 | ||
|
|
9a5ce6e4af | ||
|
|
8825f6c639 | ||
|
|
d87edf0530 | ||
|
|
c89b82c6f4 | ||
|
|
f7536d3a3f | ||
|
|
6d8ed567b0 | ||
|
|
136db1d47e | ||
|
|
c609cd0d02 | ||
|
|
848db95623 | ||
|
|
3d61deefd3 | ||
|
|
881cee958d | ||
|
|
719ce60b51 | ||
|
|
49fd8598e7 | ||
|
|
6588e128bd |
@@ -17,6 +17,8 @@ API_ANON_RATE=120/minute
|
|||||||
API_USER_RATE=600/minute
|
API_USER_RATE=600/minute
|
||||||
CALCULATOR_RATE=30/minute
|
CALCULATOR_RATE=30/minute
|
||||||
SECURE_HSTS_SECONDS=31536000
|
SECURE_HSTS_SECONDS=31536000
|
||||||
|
# Set true only when the HTTPS reverse proxy emits a non-zero HSTS max-age.
|
||||||
|
DJANGO_HSTS_AT_EDGE=false
|
||||||
|
|
||||||
# 仅供部署脚本直接访问 127.0.0.1:8000 时设置 Host 头。
|
# 仅供部署脚本直接访问 127.0.0.1:8000 时设置 Host 头。
|
||||||
DEPLOY_HEALTH_HOST=math.example.com
|
DEPLOY_HEALTH_HOST=math.example.com
|
||||||
|
|||||||
@@ -26,13 +26,19 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
env:
|
env:
|
||||||
REPOSITORY_URL: http://117.72.28.96:8765/Jacky/Hulumath-Web.git
|
CHECKOUT_TOKEN: ${{ github.token }}
|
||||||
REVISION: ${{ gitea.sha }}
|
REVISION: ${{ gitea.sha }}
|
||||||
run: |
|
run: |
|
||||||
git clone "$REPOSITORY_URL" .
|
set -euo pipefail
|
||||||
|
git init .
|
||||||
|
git remote add origin "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git"
|
||||||
|
git -c http.extraHeader="Authorization: token ${CHECKOUT_TOKEN}" \
|
||||||
|
fetch --depth=1 origin "$REVISION"
|
||||||
git checkout --detach "$REVISION"
|
git checkout --detach "$REVISION"
|
||||||
- name: Show Python version
|
- name: Show Python version
|
||||||
run: python3 --version
|
run: python3 --version
|
||||||
|
- name: Validate repository Capsule
|
||||||
|
run: python3 scripts/check_context.py
|
||||||
- name: Install MySQL build dependencies
|
- name: Install MySQL build dependencies
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
|
|||||||
+15
-10
@@ -19,8 +19,14 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: 检出 main
|
- name: 检出 main
|
||||||
env:
|
env:
|
||||||
REPOSITORY_URL: http://117.72.28.96:8765/Jacky/Hulumath-Web.git
|
CHECKOUT_TOKEN: ${{ github.token }}
|
||||||
run: git clone --branch main --single-branch "$REPOSITORY_URL" .
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
git init .
|
||||||
|
git remote add origin "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git"
|
||||||
|
git -c http.extraHeader="Authorization: token ${CHECKOUT_TOKEN}" \
|
||||||
|
fetch --depth=1 origin main
|
||||||
|
git checkout --detach FETCH_HEAD
|
||||||
|
|
||||||
- name: 检查发布文件和脚本语法
|
- name: 检查发布文件和脚本语法
|
||||||
run: |
|
run: |
|
||||||
@@ -63,7 +69,7 @@ jobs:
|
|||||||
set -Eeuo pipefail
|
set -Eeuo pipefail
|
||||||
|
|
||||||
PROJECT="/www/wwwroot/Hulumath-Web"
|
PROJECT="/www/wwwroot/Hulumath-Web"
|
||||||
REPOSITORY_URL="http://117.72.28.96:8765/Jacky/Hulumath-Web.git"
|
REPOSITORY_URL="https://git.jacky0987.xyz/Jacky/Hulumath-Web.git"
|
||||||
PYTHON_BIN="/www/server/pyporject_evn/versions/3.12.13/bin/python3"
|
PYTHON_BIN="/www/server/pyporject_evn/versions/3.12.13/bin/python3"
|
||||||
|
|
||||||
exec 9>/tmp/hulumath-production-deploy.lock
|
exec 9>/tmp/hulumath-production-deploy.lock
|
||||||
@@ -94,11 +100,14 @@ jobs:
|
|||||||
|
|
||||||
chmod +x scripts/deploy_production.sh
|
chmod +x scripts/deploy_production.sh
|
||||||
|
|
||||||
|
SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt \
|
||||||
|
DJANGO_HSTS_AT_EDGE=true \
|
||||||
PROJECT_DIR="$PROJECT" \
|
PROJECT_DIR="$PROJECT" \
|
||||||
PYTHON_BIN="$PYTHON_BIN" \
|
PYTHON_BIN="$PYTHON_BIN" \
|
||||||
PREVIOUS_REVISION="$PREVIOUS_REVISION" \
|
PREVIOUS_REVISION="$PREVIOUS_REVISION" \
|
||||||
DEPLOY_SMOKE_BASE_URL="http://127.0.0.1:4321" \
|
DEPLOY_HEALTH_HOST="math.jacky0987.xyz" \
|
||||||
DEPLOY_SMOKE_WS_URL="ws://127.0.0.1:4321/ws/health/" \
|
DEPLOY_SMOKE_BASE_URL="https://math.jacky0987.xyz" \
|
||||||
|
DEPLOY_SMOKE_WS_URL="wss://math.jacky0987.xyz/ws/health/" \
|
||||||
bash scripts/deploy_production.sh
|
bash scripts/deploy_production.sh
|
||||||
|
|
||||||
echo "========================================"
|
echo "========================================"
|
||||||
@@ -108,13 +117,9 @@ jobs:
|
|||||||
EOF
|
EOF
|
||||||
|
|
||||||
- name: 从 Runner 验证 Nginx 健康状态
|
- name: 从 Runner 验证 Nginx 健康状态
|
||||||
env:
|
|
||||||
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
|
|
||||||
DEPLOY_PUBLIC_HOST: 117.72.28.96
|
|
||||||
run: |
|
run: |
|
||||||
curl --fail --silent --show-error \
|
curl --fail --silent --show-error \
|
||||||
--retry 5 \
|
--retry 5 \
|
||||||
--retry-delay 2 \
|
--retry-delay 2 \
|
||||||
--header "Host: $DEPLOY_PUBLIC_HOST" \
|
"https://math.jacky0987.xyz/health/" \
|
||||||
"http://$DEPLOY_HOST:4321/health/" \
|
|
||||||
| grep -q '"status": "ok"'
|
| grep -q '"status": "ok"'
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# Hulumath AI Working Agreement
|
||||||
|
|
||||||
|
Read `context/boot.md` and `context/capsule.json` before changing the product.
|
||||||
|
Then select the smallest relevant product, architecture or deployment
|
||||||
|
document under `docs/`.
|
||||||
|
|
||||||
|
## Source Boundaries
|
||||||
|
|
||||||
|
- Hulumath owns its Django application, mathematical content, product rules
|
||||||
|
and release documentation.
|
||||||
|
- JLearn may link to Hulumath but does not own its users or runtime data.
|
||||||
|
- JOps owns shared edge, deployment observation and platform authentication
|
||||||
|
facts.
|
||||||
|
- Secrets, production databases, uploads and user data remain outside Git.
|
||||||
|
|
||||||
|
## Change Protocol
|
||||||
|
|
||||||
|
1. Preserve server-side authorization, scoring and anti-cheat checks.
|
||||||
|
2. Keep API and WebSocket routes backward compatible or version them.
|
||||||
|
3. Update product or deployment documentation with behavioral changes.
|
||||||
|
4. Run `make context-check`, `make check` and `make test` before committing.
|
||||||
|
5. Treat checked-in release notes as source history, not live runtime proof.
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
.PHONY: install migrate seed local-admin run run-asgi test check
|
.PHONY: install migrate seed local-admin run run-asgi test check context-check
|
||||||
|
|
||||||
install:
|
install:
|
||||||
python3 -m venv .venv
|
python3 -m venv .venv
|
||||||
@@ -23,7 +23,10 @@ run-asgi:
|
|||||||
test:
|
test:
|
||||||
.venv/bin/python -m pytest -q
|
.venv/bin/python -m pytest -q
|
||||||
|
|
||||||
check:
|
context-check:
|
||||||
|
python3 scripts/check_context.py
|
||||||
|
|
||||||
|
check: context-check
|
||||||
.venv/bin/ruff check backend scripts
|
.venv/bin/ruff check backend scripts
|
||||||
cd backend && ../.venv/bin/python manage.py check
|
cd backend && ../.venv/bin/python manage.py check
|
||||||
cd backend && ../.venv/bin/python manage.py makemigrations --check --dry-run
|
cd backend && ../.venv/bin/python manage.py makemigrations --check --dry-run
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[2]
|
||||||
|
|
||||||
|
|
||||||
|
def test_edge_hsts_ownership_is_explicit_and_verified():
|
||||||
|
settings = (ROOT / "backend/config/settings.py").read_text(encoding="utf-8")
|
||||||
|
deploy = (ROOT / "scripts/deploy_production.sh").read_text(encoding="utf-8")
|
||||||
|
workflow = (ROOT / ".gitea/workflows/deploy.yml").read_text(encoding="utf-8")
|
||||||
|
example = (ROOT / ".env.production.example").read_text(encoding="utf-8")
|
||||||
|
|
||||||
|
assert 'os.getenv("DJANGO_HSTS_AT_EDGE", "false")' in settings
|
||||||
|
assert '["security.W004"]' in settings
|
||||||
|
assert "DJANGO_HSTS_AT_EDGE=true" in workflow
|
||||||
|
assert "SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt" in workflow
|
||||||
|
assert 'DEPLOY_HEALTH_HOST="math.jacky0987.xyz"' in workflow
|
||||||
|
assert 'DEPLOY_SMOKE_BASE_URL="https://math.jacky0987.xyz"' in workflow
|
||||||
|
assert 'DEPLOY_SMOKE_WS_URL="wss://math.jacky0987.xyz/ws/health/"' in workflow
|
||||||
|
assert '"https://math.jacky0987.xyz/health/"' in workflow
|
||||||
|
assert "127.0.0.1:4321" not in workflow
|
||||||
|
assert "curl --silent --show-error --head" in deploy
|
||||||
|
assert "curl --fail --silent --show-error --head" not in deploy
|
||||||
|
assert "strict-transport-security:.*max-age=[1-9][0-9]*" in deploy
|
||||||
|
assert "unverified" not in deploy.lower()
|
||||||
|
assert "DJANGO_HSTS_AT_EDGE=false" in example
|
||||||
@@ -189,6 +189,12 @@ SECURE_HSTS_SECONDS = int(
|
|||||||
)
|
)
|
||||||
SECURE_HSTS_INCLUDE_SUBDOMAINS = USE_HTTPS
|
SECURE_HSTS_INCLUDE_SUBDOMAINS = USE_HTTPS
|
||||||
SECURE_HSTS_PRELOAD = USE_HTTPS
|
SECURE_HSTS_PRELOAD = USE_HTTPS
|
||||||
|
HSTS_AT_EDGE = os.getenv("DJANGO_HSTS_AT_EDGE", "false").lower() == "true"
|
||||||
|
SILENCED_SYSTEM_CHECKS = (
|
||||||
|
["security.W004"]
|
||||||
|
if USE_HTTPS and HSTS_AT_EDGE and SECURE_HSTS_SECONDS == 0
|
||||||
|
else []
|
||||||
|
)
|
||||||
|
|
||||||
LOGGING = {
|
LOGGING = {
|
||||||
"version": 1,
|
"version": 1,
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# Hulumath Context Boot
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Hulumath is a public mathematics product built as a Django modular monolith.
|
||||||
|
It combines mathematical identity, stories, competitions, courses,
|
||||||
|
progression and constrained mathematical tools.
|
||||||
|
|
||||||
|
## Repository Map
|
||||||
|
|
||||||
|
```text
|
||||||
|
backend/ Django applications, APIs, templates and static assets
|
||||||
|
docs/ product, release and deployment documentation
|
||||||
|
scripts/ deployment and maintenance helpers
|
||||||
|
deploy/ production configuration templates
|
||||||
|
context/ bounded repository identity for Universal Context
|
||||||
|
```
|
||||||
|
|
||||||
|
## Read Order
|
||||||
|
|
||||||
|
```text
|
||||||
|
AGENTS.md
|
||||||
|
-> context/boot.md
|
||||||
|
-> context/capsule.json
|
||||||
|
-> README.md
|
||||||
|
-> relevant document under docs/
|
||||||
|
-> relevant Django application under backend/
|
||||||
|
```
|
||||||
|
|
||||||
|
Runtime credentials, databases, uploads and account data are never context
|
||||||
|
inputs. Current production claims require a fresh JOps observation.
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
{
|
||||||
|
"schema": 1,
|
||||||
|
"kind": "jcm.repository-capsule",
|
||||||
|
"repository": {
|
||||||
|
"id": "hulumath",
|
||||||
|
"name": "Hulumath",
|
||||||
|
"remote": "Jacky/Hulumath-Web",
|
||||||
|
"role": "public-mathematics-product",
|
||||||
|
"summary": "Owns the public Hulumath Django application, mathematical content, competitions, learning tools and product release evidence."
|
||||||
|
},
|
||||||
|
"domains": [
|
||||||
|
"platform",
|
||||||
|
"learning"
|
||||||
|
],
|
||||||
|
"context": {
|
||||||
|
"entrypoint": "AGENTS.md",
|
||||||
|
"read_order": [
|
||||||
|
"AGENTS.md",
|
||||||
|
"context/boot.md",
|
||||||
|
"context/capsule.json",
|
||||||
|
"context/manifest.json",
|
||||||
|
"README.md",
|
||||||
|
"docs/RELEASE_NOTES_V1.2.1.md",
|
||||||
|
"docs/DEPLOYMENT.md"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ownership": {
|
||||||
|
"owns": [
|
||||||
|
"Hulumath product behavior and mathematical content",
|
||||||
|
"Django application, APIs and WebSocket contracts",
|
||||||
|
"Hulumath release and migration documentation"
|
||||||
|
],
|
||||||
|
"excludes": [
|
||||||
|
"JLearn accounts, sessions and learner data",
|
||||||
|
"Shared platform edge and runtime operations",
|
||||||
|
"Production secrets, databases and uploads"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"contracts": null,
|
||||||
|
"services": [
|
||||||
|
{
|
||||||
|
"id": "hulumath-web",
|
||||||
|
"name": "Hulumath",
|
||||||
|
"kind": "public-mathematics-application",
|
||||||
|
"summary": "Responsive mathematics product with identity, stories, contests, courses and tools.",
|
||||||
|
"public_urls": [
|
||||||
|
"https://math.jacky0987.xyz/"
|
||||||
|
],
|
||||||
|
"domains": [
|
||||||
|
"platform",
|
||||||
|
"learning"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"relationships": [
|
||||||
|
{
|
||||||
|
"target": "jlearn",
|
||||||
|
"type": "integrates",
|
||||||
|
"summary": "Receives a public link from JLearn but keeps independent accounts and data."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"target": "jops",
|
||||||
|
"type": "operates",
|
||||||
|
"summary": "Relies on JOps for shared edge and production runtime evidence."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"runtime": {
|
||||||
|
"authority": "none"
|
||||||
|
},
|
||||||
|
"current_state": {
|
||||||
|
"status": "released",
|
||||||
|
"summary": "The repository release baseline is v1.2.1; this source state does not claim current production health.",
|
||||||
|
"evidence": [
|
||||||
|
"README.md",
|
||||||
|
"docs/RELEASE_NOTES_V1.2.1.md",
|
||||||
|
"docs/DEPLOYMENT.md"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"facts": [
|
||||||
|
{
|
||||||
|
"id": "hulumath-platform-boundary",
|
||||||
|
"scope": "platform",
|
||||||
|
"summary": "Hulumath is an independently deployed product whose shared edge and runtime evidence belong to JOps.",
|
||||||
|
"authority": "canonical",
|
||||||
|
"evidence": "docs/DEPLOYMENT.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "hulumath-learning-scope",
|
||||||
|
"scope": "learning",
|
||||||
|
"summary": "Hulumath combines mathematical identity, stories, competitions, courses, progression and constrained tools.",
|
||||||
|
"authority": "canonical",
|
||||||
|
"evidence": "README.md"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "2.0.0",
|
||||||
|
"bundle_type": "repo-local-ai",
|
||||||
|
"repository": {
|
||||||
|
"owner": "Jacky",
|
||||||
|
"name": "Hulumath-Web",
|
||||||
|
"role": "public-mathematics-product"
|
||||||
|
},
|
||||||
|
"state": "RELEASED",
|
||||||
|
"runtime_state": "UNVERIFIED",
|
||||||
|
"entrypoint": "AGENTS.md",
|
||||||
|
"read_order": [
|
||||||
|
"AGENTS.md",
|
||||||
|
"context/boot.md",
|
||||||
|
"context/capsule.json",
|
||||||
|
"context/manifest.json",
|
||||||
|
"README.md",
|
||||||
|
"docs/RELEASE_NOTES_V1.2.1.md",
|
||||||
|
"docs/DEPLOYMENT.md"
|
||||||
|
],
|
||||||
|
"contracts": {},
|
||||||
|
"dependencies": {
|
||||||
|
"strategy": "none"
|
||||||
|
},
|
||||||
|
"runtime": {
|
||||||
|
"freshness": "requires_jops_observation"
|
||||||
|
},
|
||||||
|
"artifacts": [
|
||||||
|
"context/boot.md",
|
||||||
|
"context/capsule.json",
|
||||||
|
"README.md",
|
||||||
|
"docs/RELEASE_NOTES_V1.2.1.md",
|
||||||
|
"docs/DEPLOYMENT.md"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -24,7 +24,7 @@ systemd: Gunicorn + UvicornWorker
|
|||||||
约定:
|
约定:
|
||||||
|
|
||||||
- Ubuntu 22.04 或 24.04。
|
- Ubuntu 22.04 或 24.04。
|
||||||
- Gitea 仓库:`http://117.72.28.96:8765/Jacky/Hulumath-Web.git`。
|
- Gitea 仓库:`https://git.jacky0987.xyz/Jacky/Hulumath-Web.git`。
|
||||||
- 项目目录:`/www/wwwroot/Hulumath-Web`。
|
- 项目目录:`/www/wwwroot/Hulumath-Web`。
|
||||||
- 应用监听:`127.0.0.1:8000`。
|
- 应用监听:`127.0.0.1:8000`。
|
||||||
- MySQL 8 宿主机端口:`3307`。
|
- MySQL 8 宿主机端口:`3307`。
|
||||||
@@ -206,7 +206,7 @@ mysql -h 127.0.0.1 -P 3307 \
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
git ls-remote \
|
git ls-remote \
|
||||||
http://117.72.28.96:8765/Jacky/Hulumath-Web.git \
|
https://git.jacky0987.xyz/Jacky/Hulumath-Web.git \
|
||||||
refs/heads/main
|
refs/heads/main
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -223,7 +223,7 @@ if [ -d Hulumath-Web ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
git clone \
|
git clone \
|
||||||
http://117.72.28.96:8765/Jacky/Hulumath-Web.git \
|
https://git.jacky0987.xyz/Jacky/Hulumath-Web.git \
|
||||||
Hulumath-Web
|
Hulumath-Web
|
||||||
cd Hulumath-Web
|
cd Hulumath-Web
|
||||||
git checkout main
|
git checkout main
|
||||||
|
|||||||
+2
-2
@@ -51,7 +51,7 @@ PR 质量门禁由 `.gitea/workflows/ci.yml` 执行:
|
|||||||
|
|
||||||
自动部署前,服务器必须满足:
|
自动部署前,服务器必须满足:
|
||||||
|
|
||||||
- Gitea 仓库为 `http://117.72.28.96:8765/Jacky/Hulumath-Web.git`。
|
- Gitea 仓库为 `https://git.jacky0987.xyz/Jacky/Hulumath-Web.git`。
|
||||||
- 项目目录为 `/www/wwwroot/Hulumath-Web`。
|
- 项目目录为 `/www/wwwroot/Hulumath-Web`。
|
||||||
- Python 位于 `/www/server/pyporject_evn/versions/3.12.13/bin/python3` 或同目录 `python`。
|
- Python 位于 `/www/server/pyporject_evn/versions/3.12.13/bin/python3` 或同目录 `python`。
|
||||||
- 已安装 MySQL 8 客户端命令 `mysqldump`。
|
- 已安装 MySQL 8 客户端命令 `mysqldump`。
|
||||||
@@ -76,7 +76,7 @@ chmod 600 .env.production
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /www/wwwroot/Hulumath-Web
|
cd /www/wwwroot/Hulumath-Web
|
||||||
git remote set-url origin http://117.72.28.96:8765/Jacky/Hulumath-Web.git
|
git remote set-url origin https://git.jacky0987.xyz/Jacky/Hulumath-Web.git
|
||||||
git fetch origin main
|
git fetch origin main
|
||||||
PREVIOUS_REVISION="$(git rev-parse HEAD)"
|
PREVIOUS_REVISION="$(git rev-parse HEAD)"
|
||||||
git reset --hard origin/main
|
git reset --hard origin/main
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
CAPSULE_FILE = ROOT / "context" / "capsule.json"
|
||||||
|
MANIFEST_FILE = ROOT / "context" / "manifest.json"
|
||||||
|
|
||||||
|
|
||||||
|
def fail(message):
|
||||||
|
raise SystemExit(f"context_check_error={message}")
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
capsule = json.loads(CAPSULE_FILE.read_text(encoding="utf-8"))
|
||||||
|
manifest = json.loads(MANIFEST_FILE.read_text(encoding="utf-8"))
|
||||||
|
except (OSError, json.JSONDecodeError) as error:
|
||||||
|
fail(f"invalid_capsule:{error}")
|
||||||
|
|
||||||
|
if (
|
||||||
|
capsule.get("schema") != 1
|
||||||
|
or capsule.get("kind") != "jcm.repository-capsule"
|
||||||
|
or capsule.get("repository", {}).get("id") != "hulumath"
|
||||||
|
or capsule.get("repository", {}).get("remote") != "Jacky/Hulumath-Web"
|
||||||
|
):
|
||||||
|
fail("invalid_identity")
|
||||||
|
if (
|
||||||
|
manifest.get("schema_version") != "2.0.0"
|
||||||
|
or manifest.get("repository", {}).get("name") != "Hulumath-Web"
|
||||||
|
or "context/capsule.json" not in manifest.get("read_order", [])
|
||||||
|
):
|
||||||
|
fail("invalid_manifest")
|
||||||
|
|
||||||
|
paths = [
|
||||||
|
capsule.get("context", {}).get("entrypoint"),
|
||||||
|
*capsule.get("context", {}).get("read_order", []),
|
||||||
|
*capsule.get("current_state", {}).get("evidence", []),
|
||||||
|
*[fact.get("evidence") for fact in capsule.get("facts", [])],
|
||||||
|
]
|
||||||
|
for relative in paths:
|
||||||
|
if not relative or Path(relative).is_absolute() or ".." in Path(relative).parts:
|
||||||
|
fail(f"unsafe_path:{relative}")
|
||||||
|
if not (ROOT / relative).is_file():
|
||||||
|
fail(f"missing_path:{relative}")
|
||||||
|
|
||||||
|
print("context_check=ok")
|
||||||
@@ -234,6 +234,16 @@ if [ -d "$PROJECT_DIR/backend/staticfiles" ]; then
|
|||||||
fi
|
fi
|
||||||
cd "$PROJECT_DIR/backend"
|
cd "$PROJECT_DIR/backend"
|
||||||
if [ "${DJANGO_USE_HTTPS:-true}" = "true" ]; then
|
if [ "${DJANGO_USE_HTTPS:-true}" = "true" ]; then
|
||||||
|
if [ "${DJANGO_HSTS_AT_EDGE:-false}" = "true" ]; then
|
||||||
|
curl --silent --show-error --head \
|
||||||
|
--noproxy '*' \
|
||||||
|
--resolve "${HEALTH_HOST}:443:127.0.0.1" \
|
||||||
|
--max-time 10 \
|
||||||
|
"https://${HEALTH_HOST}/" |
|
||||||
|
tr -d '\r' |
|
||||||
|
grep -Eiq '^strict-transport-security:.*max-age=[1-9][0-9]*'
|
||||||
|
log "边缘代理 HSTS 已验证"
|
||||||
|
fi
|
||||||
"$VENV_DIR/bin/python" manage.py check --deploy --fail-level WARNING --database default
|
"$VENV_DIR/bin/python" manage.py check --deploy --fail-level WARNING --database default
|
||||||
else
|
else
|
||||||
log "临时 HTTP 模式:部署安全检查只阻断 ERROR;绑定域名后应启用 HTTPS"
|
log "临时 HTTP 模式:部署安全检查只阻断 ERROR;绑定域名后应启用 HTTPS"
|
||||||
|
|||||||
Reference in New Issue
Block a user