FR-133 · B0 實跑結果 · 2026-10-09
十項全過,兩項走設計已備的退路:內網 Host 用 ALLOWED_HOSTS 覆寫、tag 篩選改客端過濾
D1、D2 都成立,後續卡照原設計走。 十項全數通過,其中兩項走了設計已備好的退路:
| # | 項目 | 結果 | 後續卡照做 |
|---|---|---|---|
| ① | 接 Postgres 起得來 | ✅ 通過 | cap_drop: [ALL]、no-new-privileges、mem_limit: 1g 照原設計不必放寬 |
| ② | 管理員 | ✅ 通過 | CREATE_SUPERUSER 只在 auth_user 零人時生效 |
| ③ | ORM 建 team/project | ✅ 通過(D1 成立) | 片段見 b0-bootstrap-snippet.sh |
| ④ | 讀 DSN | ✅ 通過 | 表 projects_project,欄 id、sentry_key |
| ⑤ | 建 API token | ✅ 通過 | 表 bsmain_authtoken,三個布林欄 |
| ⑥ | 443 子路徑 | ✅ 通過(D2 成立,不必退 8443) | 照 design §5.3.2 的 location |
| ⑦ | sentry-sdk 直送 | ✅ 通過 | — |
| ⑧ | 內網形狀 DSN | ⚠️ 原樣 400,退路一後通過 | compose 加 ALLOWED_HOSTS |
| ⑨ | tunnel 轉送 | ✅ 通過 | — |
| ⑩ | 讀取 API 與主機名 | ⚠️ 列得出;tag 不能篩 | T-D 改客端過濾;改網址要重寫 SENTRY_PUBLIC_DSN |
guidant-bugsink 加一行 ALLOWED_HOSTS: <站台主機名>,guidant-bugsink,localhost,127.0.0.1。不必開 guidant-fe 的 8081 內部 server。後端 SENTRY_DSN 維持 http://<key>@guidant-bugsink:8000/<id>。/api/canonical/0/issues/ 只吃 project、sort、order,其他參數一律忽略、回全部。request_id 在 event 詳情的 data.tags.request_id,診斷包要逐筆拉 event 詳情自己比對。ALLOWED_HOSTS 跟著新主機名換,舊主機名的請求回 400。內網 DSN 不受影響。SENTRY_PUBLIC_DSN 的主機部分必須跟著站台網址重寫,不能用「缺了才補」。bugsink-set-password 走 ORM 不走 changepassword(T-B4):兩者都能從 stdin 讀密碼,但 changepassword 遇到不合格密碼會重問、stdin 讀完就丟 EOFError 堆疊。ORM 寫法可以先驗證再印乾淨訊息,寫法見附錄。全部在 DEV(本機),沒碰 STG/POC。
| 項目 | 值 |
|---|---|
| Postgres | 本機 localhost:5432,PostgreSQL 16.10(Homebrew) |
| 測試庫與帳號 | 庫 bugsink、bugsink_b0_fresh,角色 bugsink(NOSUPERUSER NOCREATEDB NOCREATEROLE NOBYPASSRLS,兩庫的 owner) |
| docker network | b0-net |
| 站台容器 | guidant-bugsink(image bugsink/bugsink:2.6.1,digest sha256:c5e3657451f4…),volume b0-bugsink-data |
| 空白重跑容器 | b0-bugsink-fresh(接 bugsink_b0_fresh) |
| 前門 | b0-nginx(nginx:1.27-alpine,自簽憑證,-p 443:443) |
| 後端替身 | b0-backend(python:3.11-slim+sentry-sdk 2.71.0,同網段) |
| 轉送端點 | b0-tunnel(python:3.11-slim+Flask 3.1,127.0.0.1:5099) |
| 站台網址 | BASE_URL=https://guidant-b0.test/bugsink(瀏覽器與 SDK 以解析規則把該名稱對到 127.0.0.1) |
憑證值(DB 密碼 24 字元、SECRET_KEY 64 字元、管理員密碼 20 字元)都放在本 session 的暫存目錄,未進任何檔案。下文所有 key、token 一律遮成長度。
docker run -d --name guidant-bugsink --network b0-net --env-file <暫存 env 檔> \
-v b0-bugsink-data:/data --cap-drop ALL --security-opt no-new-privileges:true --memory 1g \
bugsink/bugsink:2.6.1
# env 檔內容:SECRET_KEY=<64 字元>、BASE_URL=https://guidant-b0.test/bugsink、
# DATABASE_URL=postgresql://bugsink:<24 字元>@host.docker.internal:5432/bugsink、
# CREATE_SUPERUSER=admin@guidantai.com:<20 字元>、BEHIND_HTTPS_PROXY=True、
# USER_REGISTRATION=CB_NOBODY、SINGLE_USER=True、PHONEHOME=false、TIME_ZONE=Asia/Taipei$ docker inspect -f "{{.State.Health.Status}}" guidant-bugsink
healthy
$ docker exec guidant-bugsink python -c "...urlopen(http://localhost:8000/health/ready)..."
200 b'OK'
$ psql -d bugsink -tAc "select count(*) from pg_tables where schemaname='public'"
38
$ psql -d bugsink(主要表)
auth_user, bsmain_authtoken, django_migrations, events_event, issues_issue, projects_project, teams_team
$ psql -d bugsink(表 owner)
bugsink
啟動 log 摘錄:Apply all migrations: admin, alerts, auth, bsmain, …, teams, users → Superuser created: admin@guidantai.com → gunicorn Listening at: http://0.0.0.0:8000 → health 200。約 25 秒內 healthy。
image 自帶的 CMD 會跑 check --deploy --fail-level WARNING,SECRET_KEY 太短或 BASE_URL 不合法會直接起不來,等同免費的設定檢查。
通過。 cap_drop: [ALL]、no-new-privileges、--memory 1g 下照常起來,§5.3.1 不必放寬。
$ psql -d bugsink -tAc "select id, username, is_superuser, is_active from auth_user"
1|admin@guidantai.com|t|t
瀏覽器(headless Chromium,經 ⑥ 的 nginx)以 admin@guidantai.com 登入成功,落到 /bugsink/projects/other/(見 ⑥ 的輸出)。
CREATE_SUPERUSER 的實際行為(bsmain/management/commands/prestart.py):
auth_user 有任何一人就跳過,印 Superuser not created: _any_ user(s) already exist(s)。email:password,以第一個冒號切,密碼可含冒號。create_superuser)。CREATE_SUPERUSER)不會報錯、不改密碼。通過。 T-B3 判斷首次建站用 select count(*) from auth_user。
model 路徑與欄位(從 image 內 /usr/local/lib/python3.12/site-packages/ 查):
| model | import | 表 | 唯一鍵 |
|---|---|---|---|
| Team | from teams.models import Team, TeamVisibility |
teams_team |
name(全站唯一),PK 是 UUID |
| Project | from projects.models import Project, ProjectVisibility |
projects_project |
slug 唯一;(team, name) 唯一約束 unique_project_name_per_team;PK 是整數 |
片段(b0-bootstrap-snippet.sh)以 Team.objects.get_or_create(name="Guidant AI")、Project.objects.get_or_create(team=team, name="guidant-ai", defaults={"slug": "guidant-ai"}) 建,整段包在 transaction.atomic()。
$ bash b0-bootstrap-snippet.sh guidant-bugsink # 第 1 次
exit=0
BUGSINK_PROJECT_ID=1
BUGSINK_SENTRY_KEY=<32 字元>
BUGSINK_API_TOKEN=<40 字元>
$ bash b0-bootstrap-snippet.sh guidant-bugsink # 第 2 次
exit=0
BUGSINK_PROJECT_ID=1
BUGSINK_SENTRY_KEY=<32 字元>
BUGSINK_API_TOKEN=<40 字元>
兩次輸出完全相同
$ psql -d bugsink(筆數)
teams|1
projects|1
tokens|1
$ psql -d bugsink(team/project)
Guidant AI|99|1|guidant-ai|guidant-ai|99|32
畫面確認(⑥ 的瀏覽器測試):teams 頁含「Guidant AI」: true、projects 頁含「guidant-ai」: true。
visibility 取 HIDDEN(99)/TEAM_MEMBERS(99):單一管理員是 superuser,看得到全部;不讓日後若開了其他帳號時自動看到。
通過,D1 成立。
DSN 組法(image 內 compat/dsn.py 的 build_dsn):<BASE_URL 的 scheme>://<sentry_key 去掉連字號>@<主機>[:埠]<BASE_URL 的 path>/<project id>。
psql -d bugsink -tAc "select 'https://'||replace(sentry_key::text,'-','')||'@guidant-b0.test/bugsink/'||id from projects_project where name='guidant-ai'"
docker exec -i guidant-bugsink bugsink-manage shell --no-imports -c "from projects.models import Project;print(Project.objects.get(name='guidant-ai').dsn)"SQL 組出 DSN == ORM project.dsn
https://<32 字元 key>@guidant-b0.test/bugsink/1
畫面上 DSN 顯示在 /bugsink/projects/1/sdk-setup/;瀏覽器測試 畫面上出現的 DSN == SQL 組出的 DSN: true。
projects_project;欄位 id(integer)、sentry_key(uuid,DB 存帶連字號,DSN 用 .hex 去連字號共 32 字元)。sentry_key.hex,install.sh 不必自己做連字號轉換。通過。
model:from bsmain.models import AuthToken,表 bsmain_authtoken。token 是 40 字元 hex,由 model 預設值產生。
權限是一組布林欄位,名稱=權限字串把 :、- 換成 _(bugsink/api_capabilities.py 的 CAPABILITY_FIELD_NAMES):
| 權限 | 欄位 | 本卡設定 |
|---|---|---|
issues:read |
issues_read |
True |
events:read |
events_read |
True |
projects:read |
projects_read |
True |
其餘九個(issues:triage、projects:manage、teams:read…) |
同規則 | False(預設) |
另有 is_user_bound、is_project_bound、expires_at。內建的 bugsink-manage create_auth_token 會發全權限token,所以不用它,改在片段裡以 ORM 只開三個讀權限。
$ psql -d bugsink -tAc "select description, is_user_bound, is_project_bound, issues_read, events_read, projects_read, issues_triage, projects_manage, teams_read, length(token) from bsmain_authtoken"
guidant-ai installer (read-only)|f|f|t|t|t|f|f|f|40
API 實測(⑩):/projects/ 200、/teams/(無 teams:read)403、無 token 401、錯 token 401。
冪等:以 description="guidant-ai installer (read-only)" 且未撤銷(expires_at is null)找回既有那支;重跑不多發。
通過。
nginx(b0-nginx)照 design §5.3.2 的 location 一字不改,外加 resolver 127.0.0.11(與 FE nginx.onprem.conf 同),listen 443 ssl。
$ curl -sk https://guidant-b0.test/bugsink/ (--resolve 到 127.0.0.1)
302 https://guidant-b0.test/bugsink/accounts/login/?next=/bugsink/
headless Chromium(playwright 1.58.2,compliance-manager-test 的 node_modules)整段操作:
未登入首頁轉到: https://guidant-b0.test/bugsink/accounts/login/?next=/bugsink/
登入後網址: https://guidant-b0.test/bugsink/projects/other/
stylesheet 連結: https://guidant-b0.test/bugsink/static/css/dist/styles.css
靜態檔 /bugsink/static/css/dist/styles.css: 200
teams 頁含「Guidant AI」: true
projects 頁含「guidant-ai」: true
projects 頁站內連結數: 18
不帶 /bugsink 前綴的連結: 無
DSN 頁網址: https://guidant-b0.test/bugsink/projects/1/sdk-setup/
畫面上出現的 DSN == SQL 組出的 DSN: true
projects 頁站內連結逐一 GET: 13 條已試
登出表單 action: /bugsink/accounts/logout/
登出後網址: https://guidant-b0.test/bugsink/accounts/logout/
登出後再進 projects 轉到: https://guidant-b0.test/bugsink/accounts/login/?next=/bugsink/projects/
過程中 4xx/5xx 回應: 無
envelope 經子路徑(⑦ 的送件),nginx access log:
"POST /bugsink/api/1/envelope/ HTTP/1.1" 200 42 "-" "sentry.python/2.71.0"
子路徑能用的原因:BASE_URL 帶 path 時,Bugsink 自動設 FORCE_SCRIPT_NAME=/bugsink、STATIC_URL=/bugsink/static/(/app/bugsink_conf.py 的 deduce_script_name),連結、靜態檔、登入登出轉址都帶前綴;nginx 只要負責把 /bugsink 剝掉再送上游。
通過,D2 成立,不必退 8443。
Python 3.11.9+sentry-sdk 2.71.0(主專案 poetry 環境),用 ④ 的對外 DSN、帶自簽憑證的 CA,送一個 RuntimeError 並設 tag request_id。探針腳本攔 transport 回應碼印出。
$ PUBLIC_DSN=https://<32 字元>@guidant-b0.test/bugsink/1 poetry run python send_sdk.py PUBLIC_DSN b0-07-direct <ca>
sentry_sdk=2.71.0 python=3.11.9 marker=b0-07-direct http_status=200
$ psql -d bugsink -tAc "select id, calculated_type, calculated_value, digested_event_count from issues_issue"
128ec7ff-…|RuntimeError|B0 probe b0-07-direct|1
通過。
b0-backend 與 guidant-bugsink 同在 b0-net,DSN 改 http://<32 字元>@guidant-bugsink:8000/1。
8a:原樣(不設 ALLOWED_HOSTS)——不通,而且 SDK 不報錯:
sentry_sdk=2.71.0 python=3.11.17 marker=b0-08a-inner-noallowed http_status=400
health 經容器名: HTTP Error 400: Bad Request
# guidant-bugsink log:
"POST /api/1/envelope/ HTTP/1.1" 400 1945 "-" "sentry.python/2.71.0"
django.core.exceptions.DisallowedHost: 'Host: guidant-bugsink' as sent by browser/proxy not in ALLOWED_HOSTS=['guidant-b0.test', 'localhost', '127.0.0.1']. …
$ psql(b0-08a 的 issue)
(0 筆)
設計預告的陷阱屬實:ALLOWED_HOSTS 由 BASE_URL 推成「主機名+localhost+127.0.0.1」,容器名被拒。capture_exception 不丟例外、程式照跑,事件靜默消失。
8b:退路一,ALLOWED_HOSTS 環境變數覆寫——通過:
image 支援此變數(/app/bugsink_conf.py:有 ALLOWED_HOSTS 就以逗號切開取代推導值)。重建容器加 ALLOWED_HOSTS=guidant-b0.test,guidant-bugsink,localhost,127.0.0.1(同 volume、同庫):
sentry_sdk=2.71.0 python=3.11.17 marker=b0-08b-inner-allowed http_status=200
"POST /api/1/envelope/ HTTP/1.1" 200 42 "-" "sentry.python/2.71.0"
$ psql(b0-08b 的 issue)
B0 probe b0-08b-inner-allowed|1
--- 偽造 Host(evil.example)仍被拒
偽造 Host: HTTP Error 400: Bad Request
--- 子路徑不受影響
302 https://guidant-b0.test/bugsink/accounts/login/?next=/bugsink/
另一個預告的陷阱不成立:BEHIND_HTTPS_PROXY=True 下,內網 http 請求沒帶 X-Forwarded-Proto 並不會被轉址。Bugsink 沒開 SECURE_SSL_REDIRECT(bugsink/settings/default.py 把 security.W008 列入靜默),這個旗標只影響 cookie 的 secure 屬性與「請求是否算 https」的判斷。
選定退路一。 T-B1 的 compose 加:
ALLOWED_HOSTS: ${GUIDANT_BUGSINK_ALLOWED_HOSTS:?必填——由 installer 依站台主機名寫入}值由 install.sh 從 GUIDANT_BUGSINK_BASE_URL 取出主機名,組成 <主機名>,guidant-bugsink,localhost,127.0.0.1。主機名若是 IP 也照放。變數名已 grep 確認全 codebase 無同義名。
轉送端點 b0-tunnel:Flask,22 行(含頁面與 bundle 兩條供測試用的 GET),核心 POST /api/1.0/error-tracking/tunnel 做三件事:讀 envelope 第一行 JSON 的 dsn,比對 key 與 project id 是否為本站那一個(不符回 400),原樣 POST 到 http://guidant-bugsink:8000/api/<id>/envelope/。
前端:@sentry/vue 11.6.0+vue 3.5.43,esbuild 打包,Sentry.init({ dsn: <對外 DSN>, tunnel: '/api/1.0/error-tracking/tunnel' })。headless Chromium 開頁、captureException 一個 Error。
POST 回應 200 /api/1.0/error-tracking/tunnel
Sentry.flush 結果 true
瀏覽器發出的 POST 目標 127.0.0.1:5099/api/1.0/error-tracking/tunnel
# b0-tunnel log:
"POST /api/1.0/error-tracking/tunnel HTTP/1.1" 200 -
$ psql(b0-09 的 issue 與 event)
Error|B0 browser probe b0-09-tunnel|1
javascript|sentry.javascript.vue|11.6.0
瀏覽器只對轉送端點發 POST,沒有直接打站台。轉送端點收到的 envelope 第一行帶的是對外 DSN(https://…@<主機>/bugsink/<id>),所以 T-C 比對時只比 key 與 project id,不比主機。
通過。
token 打 API(由 b0-backend 經內網):
GET /issues/?project=1 -> 200 筆數 3 鍵 ['next', 'previous', 'results']
issue 欄位 ['calculated_type', 'calculated_value', 'digest_order', 'digested_event_count', 'first_seen', 'friendly_id', 'id', 'is_muted', 'is_resolved', 'is_resolved_by_next_release', 'is_resolved_unconditionally', 'last_seen', 'project', 'stored_event_count', 'transaction']
GET /events/?issue=<id> -> 200 筆數 1 event 欄位 ['digest_order', 'digested_at', 'event_id', 'grouping', 'id', 'ingested_at', 'issue', 'project', 'timestamp']
GET /events/<id>/ -> 200 含 tags: True 含 request_id: True
tag 篩選嘗試 '&query=request_id:b0-07-direct' -> 200 筆數 3
tag 篩選嘗試 '&q=request_id:b0-07-direct' -> 200 筆數 3
tag 篩選嘗試 '&tag=request_id:b0-07-direct' -> 200 筆數 3
tag 篩選嘗試 '&request_id=b0-07-direct' -> 200 筆數 3
無 token -> 401
錯 token -> 401
GET /projects/ (projects:read) -> 200 ['guidant-ai']
GET /teams/ (無 teams:read,預期 403) -> 403
event 詳情頂層鍵 ['data', 'digest_order', 'digested_at', 'event_id', 'grouping', 'id', 'ingested_at', 'issue', 'project', 'stacktrace_md', 'timestamp']
request_id 出現在 .data.tags.request_id = b0-07-direct
project(必填)、sort(digest_order/last_seen/digested_event_count)、order(asc/desc)。event 列表只認 issue(必填)、order。其他參數不報錯、直接忽略。分頁是 cursor 式(next/previous)。request_id:xxx 語法)只存在網頁介面(tags/search.py),沒開到 API。tag 不能篩 → T-D 改客端過濾:取 issue 列表(sort=last_seen&order=desc)→ 逐 issue 取 event 列表 → 逐筆取 event 詳情比對 data.tags.request_id。請求數會隨 issue 數成長,T-D 要設上限(例如只看最近 N 個 issue)。
換主機名:BASE_URL 改 https://guidant-b0-renamed.test/bugsink、ALLOWED_HOSTS 跟著換,重建容器(同庫同 volume):
--- 換主機名後:project.dsn 變成
https://<32字元key>@guidant-b0-renamed.test/bugsink/1
--- 舊內網 DSN(key/id 不變)
sentry_sdk=2.71.0 python=3.11.17 marker=b0-10-rename-inner http_status=200
--- 舊對外 DSN(主機 guidant-b0.test,已不在 ALLOWED_HOSTS)
sentry_sdk=2.71.0 python=3.11.9 marker=b0-10-rename-oldpublic http_status=400
$ psql(b0-10 的 issue)
B0 probe b0-10-rename-inner
BASE_URL 變,內網 DSN 改網址後照常送得進去。ALLOWED_HOSTS,回 400、SDK 不報錯。前端走轉送端點時不受影響(轉送端點只比 key 與 id、往內網送),但 SENTRY_PUBLIC_DSN 回給前端的字串若沒更新,事件仍會到,只是 envelope 標頭裡的網址是舊的。T-B2 建議:SENTRY_PUBLIC_DSN 每次 setup_bugsink 都依當下 GUIDANT_BUGSINK_BASE_URL 重寫,不走「缺了才補」;ALLOWED_HOSTS 同理。測完已換回原 BASE_URL。
通過(列表可用;tag 篩選不可行,走退路)。
vacuum討論稿寫的名稱正確。bugsink-manage help 列在 [bsmain] 下。
$ bugsink-manage vacuum (不帶參數=預設集合)
Vacuuming files...
Vacuuming old events...
Skipping old events; MAX_EVENT_AGE_DAYS is not configured.
Vacuuming deleted issues...
Vacuuming eventless issuetags...
Vacuuming tags...
Checking for stale temporary ingest files...
Temporary ingest directory does not exist: /tmp/bugsink/ingestion
Vacuum complete.
--orphaned-issues 不在預設內。MAX_EVENT_AGE_DAYS 未設時跳過,也可用 --max-event-age-days N 臨時指定。每個 project 另有 retention_max_event_count(預設 10,000 筆),超過由 Bugsink 自己在收件時淘汰,與 vacuum 無關。T-B4 的 bugsink-vacuum 若要真的砍空間,需決定是否帶 --max-event-age-days。select count(*) from events_event 印前後對照。changepassword 可從 stdin 讀密碼,但不建議用changepassword 用 getpass;docker exec -i(不配 TTY)時 getpass 退回讀 stdin:
$ { echo <16 字元>; echo <16 字元>; } | docker exec -i guidant-bugsink bugsink-manage changepassword admin@guidantai.com
Password:
Warning: Password input may be echoed.
Password (again):
Password changed successfully for user 'admin@guidantai.com'
密碼雜湊已變更;authenticate(新密碼) -> True
但密碼不合格時會重問,stdin 讀完就崩:
$ printf 'abc\nabc\n' | docker exec -i guidant-bugsink bugsink-manage changepassword admin@guidantai.com
File "/usr/local/lib/python3.12/getpass.py", line 148, in _raw_input
raise EOFError
EOFError
驗證器四支:UserAttributeSimilarityValidator、MinimumLengthValidator(Django 預設 8)、CommonPasswordValidator、NumericPasswordValidator。
T-B4 改用 ORM(附錄 B),密碼只經 stdin、先跑驗證器,不合格印清楚訊息、exit 1。已實測 set_password 後 authenticate 為 True,並已把管理員密碼還原。
另:出貨烙印密碼走 CREATE_SUPERUSER,不經驗證器。gen_bugsink_admin_bake.sh 已要求 ≥12 字元,但仍可能產出過於常見或與帳號相似的值而在日後 set-password 時被拒,建議烙印腳本產隨機值而非手設。
auth_userusers.models.User(AUTH_USER_MODEL = "users.User"),Meta.db_table = 'auth_user'。T-B3 用 select count(*) from auth_user。
另開空庫 bugsink_b0_fresh、容器 b0-bugsink-fresh(不帶 CREATE_SUPERUSER、不帶 ALLOWED_HOSTS),等 healthy 後跑片段兩次:
auth_user 人數(未帶 CREATE_SUPERUSER): 0
$ b0-bootstrap-snippet.sh b0-bugsink-fresh # 第 1 次
exit=0
BUGSINK_PROJECT_ID=1
BUGSINK_SENTRY_KEY=<32 字元>
BUGSINK_API_TOKEN=<40 字元>
$ b0-bootstrap-snippet.sh b0-bugsink-fresh # 第 2 次
exit=0
BUGSINK_PROJECT_ID=1
BUGSINK_SENTRY_KEY=<32 字元>
BUGSINK_API_TOKEN=<40 字元>
兩次輸出完全相同
與主站台的 key/token 不同(各站台各自產生)
teams=1 projects=1 tokens=1
$ 容器不存在時
exit=1
SystemExit、ZeroDivisionError)時 bugsink-manage shell 回 exit 1;片段有 set -o pipefail,install.sh 會拿到非零。檔案 b0-bootstrap-snippet.sh(同目錄),可整段搬進 setup_bugsink():
guidant-bugsink)。DB 連線由容器自己的 DATABASE_URL 帶,片段不經手任何密碼,所以卡片寫的「DB 連線參數」輸入不需要。BUGSINK_PROJECT_ID=、BUGSINK_SENTRY_KEY=、BUGSINK_API_TOKEN=。(team, name)、token 以 description+未撤銷 找回既有那筆。install.sh 讀法示意:
local kv; kv="$(bash b0-bootstrap-snippet.sh guidant-bugsink)" || fail "錯誤追蹤站初始化失敗,請看 docker logs guidant-bugsink"
local bs_id bs_key bs_token
bs_id="$(sed -n 's/^BUGSINK_PROJECT_ID=//p' <<<"$kv")"
bs_key="$(sed -n 's/^BUGSINK_SENTRY_KEY=//p' <<<"$kv")"
bs_token="$(sed -n 's/^BUGSINK_API_TOKEN=//p' <<<"$kv")"
[ -n "$bs_id" ] && [ -n "$bs_key" ] && [ -n "$bs_token" ] || fail "錯誤追蹤站初始化沒拿到完整結果"密碼只走 stdin,不進命令列參數:
printf '%s\n' "$new_pw" | docker exec -i guidant-bugsink bugsink-manage shell --no-imports -c '
import sys
from django.contrib.auth import get_user_model
from django.contrib.auth.password_validation import validate_password
from django.core.exceptions import ValidationError
pw = sys.stdin.readline().rstrip("\n")
user = get_user_model().objects.get(username="admin@guidantai.com")
try:
validate_password(pw, user)
except ValidationError as e:
print("密碼不合格:" + ";".join(e.messages)); raise SystemExit(1)
user.set_password(pw); user.save(update_fields=["password"]); print("OK")
'留著供 T-B1/T-D 用:
| 資源 | 名稱 |
|---|---|
| 容器 | guidant-bugsink(目前 BASE_URL=https://guidant-b0.test/bugsink、有 ALLOWED_HOSTS)、b0-bugsink-fresh、b0-nginx、b0-backend、b0-tunnel |
| network | b0-net |
| volume | b0-bugsink-data |
| DEV Postgres | 庫 bugsink、bugsink_b0_fresh,角色 bugsink |
全部清掉:
docker rm -f guidant-bugsink b0-bugsink-fresh b0-nginx b0-backend b0-tunnel
docker network rm b0-net
docker volume rm b0-bugsink-data
psql -h localhost -d postgres -c "DROP DATABASE IF EXISTS bugsink" -c "DROP DATABASE IF EXISTS bugsink_b0_fresh" -c "DROP ROLE IF EXISTS bugsink"b0-nginx 佔著本機 443;之後要在本機起別的 443 服務前先停它。