diff --git a/haixun-backend/.dockerignore b/haixun-backend/.dockerignore index f2cc82a..c0864c4 100644 --- a/haixun-backend/.dockerignore +++ b/haixun-backend/.dockerignore @@ -1,7 +1,7 @@ .run .git web/node_modules -web/dist +# web/dist 保留給 deploy/Dockerfile.web.static(本機 make web-build 後 COPY) worker/node_modules **/*_test.go **/.DS_Store diff --git a/haixun-backend/.run/logs/api.log b/haixun-backend/.run/logs/api.log index 606276c..d267559 100644 --- a/haixun-backend/.run/logs/api.log +++ b/haixun-backend/.run/logs/api.log @@ -1662,3 +1662,153 @@ Starting backend backend at 0.0.0.0:8890... {"@timestamp":"2026-06-25T17:49:05.203+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2069.1ms","level":"info","span":"0a4994cf697b29d0","trace":"0f685fd382d5680b7209970ee4808dc6"} {"@timestamp":"2026-06-25T17:49:06.224+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58275 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"8.2ms","level":"info","span":"8447e57ea152313d","trace":"3f8a9dcf4e4550c04faa79e9c620b2d2"} {"@timestamp":"2026-06-25T17:49:08.218+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58279 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"4.9ms","level":"info","span":"24c6ce2604e43c9b","trace":"1b8bd5b533a55488943f9e04478f9f4b"} +{"@timestamp":"2026-06-25T17:49:10.213+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2006.2ms)","duration":"2006.2ms","level":"slow","span":"0045ecc2abcb4687","trace":"4e079a0a132652cda9b6dfc4714bf8ff"} +{"@timestamp":"2026-06-25T17:49:10.213+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2006.2ms","level":"info","span":"0045ecc2abcb4687","trace":"4e079a0a132652cda9b6dfc4714bf8ff"} +{"@timestamp":"2026-06-25T17:49:10.215+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58283 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"3.1ms","level":"info","span":"61e6a20f0e72d0a1","trace":"653e05b0b32a7c53302a3c8f6bf3f7d5"} +{"@timestamp":"2026-06-25T17:49:12.217+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58290 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"4.1ms","level":"info","span":"856376c3d56dd339","trace":"6d06ff846806c3c1b325b387f5105b17"} +{"@timestamp":"2026-06-25T17:49:14.214+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58294 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"2.3ms","level":"info","span":"afefaa7f35fe4ae1","trace":"4c7dee2269d572b553dcf0813eb66ae4"} +{"@timestamp":"2026-06-25T17:49:15.260+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2044.9ms)","duration":"2044.9ms","level":"slow","span":"0cd9fcc1ba61775b","trace":"d367869a0d4e34078f356bc0919b4b85"} +{"@timestamp":"2026-06-25T17:49:15.261+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2044.9ms","level":"info","span":"0cd9fcc1ba61775b","trace":"d367869a0d4e34078f356bc0919b4b85"} +{"@timestamp":"2026-06-25T17:49:16.220+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58300 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"5.7ms","level":"info","span":"2f2ffddd4aef493b","trace":"08e55751814ae17098daafa6807d213a"} +{"@timestamp":"2026-06-25T17:49:18.220+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58306 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"5.9ms","level":"info","span":"241b0c35f7d72642","trace":"de395eb037a65622449c455f028881e3"} +{"@timestamp":"2026-06-25T17:49:20.220+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58311 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"5.1ms","level":"info","span":"ebb12748e813216b","trace":"a8b767ba9b34af74a649d931d69905d0"} +{"@timestamp":"2026-06-25T17:49:20.296+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2032.2ms)","duration":"2032.2ms","level":"slow","span":"39a3c2d3133995f3","trace":"1049728af2c76a9639d1424bd4a6d2be"} +{"@timestamp":"2026-06-25T17:49:20.296+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2032.2ms","level":"info","span":"39a3c2d3133995f3","trace":"1049728af2c76a9639d1424bd4a6d2be"} +{"@timestamp":"2026-06-25T17:49:22.219+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58315 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"5.1ms","level":"info","span":"fed5fc50b94a7bbb","trace":"ccadda96bdaca92b176b579c3547ed71"} +{"@timestamp":"2026-06-25T17:49:24.219+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58320 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"6.0ms","level":"info","span":"ff68e44165a71845","trace":"6e083c659a37ba59631b7c26610ae15a"} +{"@timestamp":"2026-06-25T17:49:25.311+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2014.0ms)","duration":"2014.0ms","level":"slow","span":"0052f4348fe91396","trace":"ebca1cb8758082a9b75e25ba23860e1b"} +{"@timestamp":"2026-06-25T17:49:25.312+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2014.0ms","level":"info","span":"0052f4348fe91396","trace":"ebca1cb8758082a9b75e25ba23860e1b"} +{"@timestamp":"2026-06-25T17:49:26.214+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58324 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"2.4ms","level":"info","span":"e5efbc87e657d0c1","trace":"26ae069f11f440369bfae45758c7156b"} +{"@timestamp":"2026-06-25T17:49:28.218+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58329 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"4.9ms","level":"info","span":"ca11a97305d9421c","trace":"a189bffaf538f2aa5bc4a7622e31624f"} +{"@timestamp":"2026-06-25T17:49:30.260+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58333 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"46.2ms","level":"info","span":"26c03d93588a5fc2","trace":"e81504443fdc0abdede4f7abc1126e97"} +{"@timestamp":"2026-06-25T17:49:30.327+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2013.0ms)","duration":"2013.0ms","level":"slow","span":"6407e1bae63cbee8","trace":"b0568fca29c0ecf72408c51fa611f261"} +{"@timestamp":"2026-06-25T17:49:30.327+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2013.0ms","level":"info","span":"6407e1bae63cbee8","trace":"b0568fca29c0ecf72408c51fa611f261"} +{"@timestamp":"2026-06-25T17:49:32.221+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58340 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"5.9ms","level":"info","span":"4160ad0c252c15cc","trace":"e2a51c91b1fb9cae462c63e07cd8c8df"} +{"@timestamp":"2026-06-25T17:49:34.221+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58344 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"4.8ms","level":"info","span":"e6c255aa2c58d067","trace":"847d9b6b85e5dfdb38dbe101f3044858"} +{"@timestamp":"2026-06-25T17:49:35.340+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2009.3ms)","duration":"2009.3ms","level":"slow","span":"14d2351d66936498","trace":"465672fb8ffaa6c11e1b22336df434ec"} +{"@timestamp":"2026-06-25T17:49:35.340+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2009.3ms","level":"info","span":"14d2351d66936498","trace":"465672fb8ffaa6c11e1b22336df434ec"} +{"@timestamp":"2026-06-25T17:49:36.215+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58349 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"3.4ms","level":"info","span":"14ea1676fe9c7e75","trace":"5e206497dfe0476c1fb31009bb491ccd"} +{"@timestamp":"2026-06-25T17:49:38.218+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58354 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"4.5ms","level":"info","span":"d77c5109885d4969","trace":"ccf04a26b821ce088995288234c9b959"} +{"@timestamp":"2026-06-25T17:49:40.217+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58358 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"2.1ms","level":"info","span":"b5264f792418d59d","trace":"9583e73d114e0d6d73b4caf28839cc07"} +{"@timestamp":"2026-06-25T17:49:40.387+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2044.5ms)","duration":"2044.5ms","level":"slow","span":"18ec04d4275ba4f0","trace":"afa6f275f2232eb7e1128d0fcf50a9fb"} +{"@timestamp":"2026-06-25T17:49:40.387+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2044.5ms","level":"info","span":"18ec04d4275ba4f0","trace":"afa6f275f2232eb7e1128d0fcf50a9fb"} +{"@timestamp":"2026-06-25T17:49:42.219+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58363 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"4.7ms","level":"info","span":"efbcb6fb22f8d67f","trace":"1883cfcd153290035b23eb1c78a8a358"} +{"@timestamp":"2026-06-25T17:49:42.566+08:00","caller":"stat/usage.go:82","content":"CPU: 0m, MEMORY: Alloc=4.5Mi, TotalAlloc=300.4Mi, Sys=27.3Mi, NumGC=117","level":"stat"} +{"@timestamp":"2026-06-25T17:49:42.598+08:00","caller":"load/sheddingstat.go:61","content":"(api) shedding_stat [1m], cpu: 0, total: 42, pass: 42, drop: 0","level":"stat"} +{"@timestamp":"2026-06-25T17:49:42.979+08:00","caller":"stat/metrics.go:210","content":"(haixun-backend) - qps: 0.7/s, drops: 0, avg time: 585.5ms, med: 5.3ms, 90th: 2044.4ms, 99th: 2069.1ms, 99.9th: 2069.1ms","level":"stat"} +{"@timestamp":"2026-06-25T17:49:44.218+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58367 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"4.9ms","level":"info","span":"f9243a2bfdf7d222","trace":"ca91e2df498357b759f89ee062687956"} +{"@timestamp":"2026-06-25T17:49:45.418+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2027.3ms)","duration":"2027.3ms","level":"slow","span":"fad6af145d59ae77","trace":"d47baec2e8a473f7836659b92e198d6a"} +{"@timestamp":"2026-06-25T17:49:45.418+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2027.3ms","level":"info","span":"fad6af145d59ae77","trace":"d47baec2e8a473f7836659b92e198d6a"} +{"@timestamp":"2026-06-25T17:49:46.218+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58371 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"1.9ms","level":"info","span":"ad2ec7244f658598","trace":"9bc5c42cb10c6b08926d0d77338192f9"} +{"@timestamp":"2026-06-25T17:49:50.451+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2031.9ms)","duration":"2031.9ms","level":"slow","span":"7fbd631630cd7543","trace":"e1fb9b12f329f8eebefc10a0105d154c"} +{"@timestamp":"2026-06-25T17:49:50.452+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2031.9ms","level":"info","span":"7fbd631630cd7543","trace":"e1fb9b12f329f8eebefc10a0105d154c"} +{"@timestamp":"2026-06-25T17:49:55.466+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2010.5ms)","duration":"2010.5ms","level":"slow","span":"96202158bcaa10ff","trace":"cf8227d43ba9bde0b181e9bff9b1ff9b"} +{"@timestamp":"2026-06-25T17:49:55.466+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2010.5ms","level":"info","span":"96202158bcaa10ff","trace":"cf8227d43ba9bde0b181e9bff9b1ff9b"} +{"@timestamp":"2026-06-25T17:50:00.490+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2020.7ms)","duration":"2020.7ms","level":"slow","span":"f2ea252d156aa0f3","trace":"543d7b96373b59d9400ef3cc0bd68cc9"} +{"@timestamp":"2026-06-25T17:50:00.491+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2020.7ms","level":"info","span":"f2ea252d156aa0f3","trace":"543d7b96373b59d9400ef3cc0bd68cc9"} +{"@timestamp":"2026-06-25T17:50:05.526+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2031.7ms)","duration":"2031.7ms","level":"slow","span":"27ba86e6454af95e","trace":"a889c2e713d333d87d7649223d85207e"} +{"@timestamp":"2026-06-25T17:50:05.526+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2031.7ms","level":"info","span":"27ba86e6454af95e","trace":"a889c2e713d333d87d7649223d85207e"} +{"@timestamp":"2026-06-25T17:50:06.225+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58406 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"7.0ms","level":"info","span":"d1d1a4a5ee2a9e13","trace":"b646c843fb2ff47322c9b57ac1b01b7f"} +{"@timestamp":"2026-06-25T17:50:10.572+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2042.2ms)","duration":"2042.2ms","level":"slow","span":"5906925838b1dbb4","trace":"b7182c489e58db90a25dfa2fe31f0a58"} +{"@timestamp":"2026-06-25T17:50:10.572+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2042.2ms","level":"info","span":"5906925838b1dbb4","trace":"b7182c489e58db90a25dfa2fe31f0a58"} +{"@timestamp":"2026-06-25T17:50:15.617+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2040.7ms)","duration":"2040.7ms","level":"slow","span":"9120342af7f9fc2c","trace":"6759f558e00a93625fd1afa2c2381863"} +{"@timestamp":"2026-06-25T17:50:15.617+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2040.7ms","level":"info","span":"9120342af7f9fc2c","trace":"6759f558e00a93625fd1afa2c2381863"} +{"@timestamp":"2026-06-25T17:50:20.651+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2030.5ms)","duration":"2030.5ms","level":"slow","span":"18079375ce1b1185","trace":"5624020102cb94d8281d013c54479320"} +{"@timestamp":"2026-06-25T17:50:20.652+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2030.5ms","level":"info","span":"18079375ce1b1185","trace":"5624020102cb94d8281d013c54479320"} +{"@timestamp":"2026-06-25T17:50:25.684+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2028.0ms)","duration":"2028.0ms","level":"slow","span":"df7a34bb8a85bd63","trace":"0390db05e76182bd28b0a5797531a1e7"} +{"@timestamp":"2026-06-25T17:50:25.684+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2028.0ms","level":"info","span":"df7a34bb8a85bd63","trace":"0390db05e76182bd28b0a5797531a1e7"} +{"@timestamp":"2026-06-25T17:50:30.727+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2038.3ms)","duration":"2038.3ms","level":"slow","span":"f36ec7af7253e31b","trace":"015ab49a486fad0f9127295be668fba8"} +{"@timestamp":"2026-06-25T17:50:30.727+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2038.3ms","level":"info","span":"f36ec7af7253e31b","trace":"015ab49a486fad0f9127295be668fba8"} +{"@timestamp":"2026-06-25T17:50:35.763+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2032.6ms)","duration":"2032.6ms","level":"slow","span":"2aa9dfdc5dc8a85e","trace":"b8de737d8ab04873a78f84a404437987"} +{"@timestamp":"2026-06-25T17:50:35.763+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2032.6ms","level":"info","span":"2aa9dfdc5dc8a85e","trace":"b8de737d8ab04873a78f84a404437987"} +{"@timestamp":"2026-06-25T17:50:39.226+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58419 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"3.4ms","level":"info","span":"f63faa48c6bb1197","trace":"5dde53bc0b074cd28ee44ea91f513f33"} +{"@timestamp":"2026-06-25T17:50:39.700+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58421 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"10.3ms","level":"info","span":"c3e561d21852b694","trace":"32eb6fcc5bb3e5aa87b5bbdc4b6b48f8"} +{"@timestamp":"2026-06-25T17:50:40.794+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2028.7ms)","duration":"2028.7ms","level":"slow","span":"2edd3c9c57d63b85","trace":"e5d6ae91768e94027179ac546ffe7bf3"} +{"@timestamp":"2026-06-25T17:50:40.794+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2028.7ms","level":"info","span":"2edd3c9c57d63b85","trace":"e5d6ae91768e94027179ac546ffe7bf3"} +{"@timestamp":"2026-06-25T17:50:41.693+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58424 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"5.0ms","level":"info","span":"5dbf83f4424ef014","trace":"8ea6587d087f51d7924e69207c4208d3"} +{"@timestamp":"2026-06-25T17:50:42.566+08:00","caller":"stat/usage.go:82","content":"CPU: 0m, MEMORY: Alloc=3.9Mi, TotalAlloc=302.4Mi, Sys=27.3Mi, NumGC=118","level":"stat"} +{"@timestamp":"2026-06-25T17:50:42.598+08:00","caller":"load/sheddingstat.go:61","content":"(api) shedding_stat [1m], cpu: 0, total: 18, pass: 18, drop: 0","level":"stat"} +{"@timestamp":"2026-06-25T17:50:42.979+08:00","caller":"stat/metrics.go:210","content":"(haixun-backend) - qps: 0.3/s, drops: 0, avg time: 1355.2ms, med: 2027.9ms, 90th: 2042.2ms, 99th: 2042.2ms, 99.9th: 2042.2ms","level":"stat"} +{"@timestamp":"2026-06-25T17:50:43.692+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58426 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"5.3ms","level":"info","span":"5295b3d06737e601","trace":"ce7e94c80bcbb0bf56a8562d79b6e29b"} +{"@timestamp":"2026-06-25T17:50:45.188+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas/51f4d954-b152-4127-bfb9-e47bde06d1f8 - 127.0.0.1:58432 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"5.9ms","level":"info","span":"d00916bfb349cc40","trace":"ac2541ca5ed3e9a20badcf6df59f2d88"} +{"@timestamp":"2026-06-25T17:50:45.188+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas - 127.0.0.1:58430 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"6.9ms","level":"info","span":"ef1f8cfe58e03264","trace":"9f582b67aff50e79ec32e0b36df21042"} +{"@timestamp":"2026-06-25T17:50:45.191+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas/51f4d954-b152-4127-bfb9-e47bde06d1f8/copy-missions - 127.0.0.1:58431 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"9.0ms","level":"info","span":"367437d8bc78db0d","trace":"6e5d56c577d43c0f206162b38099ad7d"} +{"@timestamp":"2026-06-25T17:50:45.194+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas/51f4d954-b152-4127-bfb9-e47bde06d1f8 - 127.0.0.1:58435 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"2.6ms","level":"info","span":"3e1dabad1de57351","trace":"61a5afea13f332a8f61dfa7630cb643a"} +{"@timestamp":"2026-06-25T17:50:45.195+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas - 127.0.0.1:58436 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"2.5ms","level":"info","span":"e551bcd1cf2f0b3a","trace":"29c57709c7a7ecd220f80e0efff76a80"} +{"@timestamp":"2026-06-25T17:50:45.200+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas - 127.0.0.1:58440 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"2.6ms","level":"info","span":"98cff0faaf497ecb","trace":"adaf628de399aeff7f168e440b470fc3"} +{"@timestamp":"2026-06-25T17:50:45.202+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas/51f4d954-b152-4127-bfb9-e47bde06d1f8/copy-missions - 127.0.0.1:58439 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"4.4ms","level":"info","span":"a152fe1fe7e27989","trace":"f792504bf7db0c16f60fed1351c4c20e"} +{"@timestamp":"2026-06-25T17:50:45.208+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/threads-accounts/bf9e27eb-d529-4ef5-be11-20870194a4b8/connection - 127.0.0.1:58442 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"4.5ms","level":"info","span":"ba8c14203dd96ab0","trace":"0c814962fdbf66c6f40276246deecb03"} +{"@timestamp":"2026-06-25T17:50:45.691+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58444 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"3.3ms","level":"info","span":"b0711dd4d554a1e8","trace":"7755c0a1f40b24efbca864661701cfae"} +{"@timestamp":"2026-06-25T17:50:45.824+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2025.2ms)","duration":"2025.2ms","level":"slow","span":"199c25f04bbefc86","trace":"ba3fbca99a84dfdc904c968b4d00e39a"} +{"@timestamp":"2026-06-25T17:50:45.824+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2025.2ms","level":"info","span":"199c25f04bbefc86","trace":"ba3fbca99a84dfdc904c968b4d00e39a"} +{"@timestamp":"2026-06-25T17:50:47.692+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58447 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"4.8ms","level":"info","span":"dd8cd5bc8c1247da","trace":"b1c3722f03618bfc96b3b71236b8d64e"} +{"@timestamp":"2026-06-25T17:50:49.693+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58450 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"5.1ms","level":"info","span":"41a8707a4910eb6f","trace":"27e8f55827230ca837821f0e8920adc6"} +{"@timestamp":"2026-06-25T17:50:50.857+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2029.9ms)","duration":"2029.9ms","level":"slow","span":"1fcf049d1b1bd648","trace":"4476dc380df5731883950db3a6ae79cc"} +{"@timestamp":"2026-06-25T17:50:50.857+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2029.9ms","level":"info","span":"1fcf049d1b1bd648","trace":"4476dc380df5731883950db3a6ae79cc"} +{"@timestamp":"2026-06-25T17:50:51.692+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58452 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"4.5ms","level":"info","span":"b8ecb07eaa5680a3","trace":"d52a86282e0bca9d2e0b30e11f28ce3d"} +{"@timestamp":"2026-06-25T17:50:54.225+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58454 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"10.7ms","level":"info","span":"0c3e262e5475ca06","trace":"9a8abe779a8654a48434b1370edd7a30"} +{"@timestamp":"2026-06-25T17:50:55.875+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2016.3ms)","duration":"2016.3ms","level":"slow","span":"f51bda9751613512","trace":"33b5a9419f30665fcd79eef1c31fa3d7"} +{"@timestamp":"2026-06-25T17:50:55.875+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2016.3ms","level":"info","span":"f51bda9751613512","trace":"33b5a9419f30665fcd79eef1c31fa3d7"} +{"@timestamp":"2026-06-25T17:50:56.220+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58457 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"5.2ms","level":"info","span":"c49c02a13e39069a","trace":"b465d0c3a72bcfb62030ca84623052d2"} +{"@timestamp":"2026-06-25T17:50:58.221+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58459 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"5.7ms","level":"info","span":"0e6441e87a99303c","trace":"5b398f0ca6e742eb914c135d22f8b244"} +{"@timestamp":"2026-06-25T17:51:00.235+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58461 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"19.0ms","level":"info","span":"f2487ec5ee595dd8","trace":"53c1c19f4db9f8109e9f4ab07878695d"} +{"@timestamp":"2026-06-25T17:51:00.927+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2049.2ms)","duration":"2049.2ms","level":"slow","span":"6eb401c7ab06d74a","trace":"64f7e4c2f1d77ca0bf2c659e0c8a7fde"} +{"@timestamp":"2026-06-25T17:51:00.927+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2049.2ms","level":"info","span":"6eb401c7ab06d74a","trace":"64f7e4c2f1d77ca0bf2c659e0c8a7fde"} +{"@timestamp":"2026-06-25T17:51:02.221+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58463 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"5.8ms","level":"info","span":"3f5caad1374ce1f3","trace":"5b06b7d99dcadf5602ec5d4c8d8150b9"} +{"@timestamp":"2026-06-25T17:51:04.215+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58465 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"1.6ms","level":"info","span":"dd0afe2b69cdb27a","trace":"798294bae1ca5c1c46fde67cf1e9a8b9"} +{"@timestamp":"2026-06-25T17:51:05.967+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2037.7ms)","duration":"2037.7ms","level":"slow","span":"7c2f4832ee5aabd1","trace":"48cce458c4a3c84f4f6a4102fa6c39bd"} +{"@timestamp":"2026-06-25T17:51:05.967+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2037.7ms","level":"info","span":"7c2f4832ee5aabd1","trace":"48cce458c4a3c84f4f6a4102fa6c39bd"} +{"@timestamp":"2026-06-25T17:51:06.224+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58467 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"6.6ms","level":"info","span":"edd18ada52671c29","trace":"3312c1a5f56301d016b0afa98c781931"} +{"@timestamp":"2026-06-25T17:51:08.220+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58469 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"5.2ms","level":"info","span":"34b02bb405bd1b90","trace":"c8ec011edca4590bfd58fe0e83ca1b5e"} +{"@timestamp":"2026-06-25T17:51:10.219+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58472 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"5.2ms","level":"info","span":"4474a888892f8ff6","trace":"23fe7854bc517996597e29643aa2987d"} +{"@timestamp":"2026-06-25T17:51:10.999+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2029.1ms)","duration":"2029.1ms","level":"slow","span":"996ced2da175458b","trace":"c4d00a9719a9143cbe3c4bfe8af5e991"} +{"@timestamp":"2026-06-25T17:51:10.999+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2029.1ms","level":"info","span":"996ced2da175458b","trace":"c4d00a9719a9143cbe3c4bfe8af5e991"} +{"@timestamp":"2026-06-25T17:51:12.220+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58474 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"5.6ms","level":"info","span":"8db9340523f3a251","trace":"095616c09e7f8629e3e326fc18f173b7"} +{"@timestamp":"2026-06-25T17:51:14.216+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58478 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"3.2ms","level":"info","span":"0e39fc9c845980dd","trace":"65286215c4f12101a42710bbca78597b"} +{"@timestamp":"2026-06-25T17:51:16.019+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2017.3ms)","duration":"2017.3ms","level":"slow","span":"d950a2dcc230c049","trace":"3871c0ea220c07da7fe07288e71a2594"} +{"@timestamp":"2026-06-25T17:51:16.019+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2017.3ms","level":"info","span":"d950a2dcc230c049","trace":"3871c0ea220c07da7fe07288e71a2594"} +{"@timestamp":"2026-06-25T17:51:16.215+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58485 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"2.4ms","level":"info","span":"d03742ac346b532f","trace":"b2555159fb9070dd85a1f032c88b352e"} +{"@timestamp":"2026-06-25T17:51:18.215+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58488 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"2.7ms","level":"info","span":"01f9298b948bf166","trace":"c1752a135acb880783ad5831c4efa5d9"} +{"@timestamp":"2026-06-25T17:51:20.223+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58494 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"7.0ms","level":"info","span":"49e4e8b4b8cec944","trace":"eea5fb85e331cd7d33193ed71fc48539"} +{"@timestamp":"2026-06-25T17:51:21.075+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2053.6ms)","duration":"2053.6ms","level":"slow","span":"3bc0d76f7d3db4ca","trace":"c5288ede8dfb7e778df0557038c7edef"} +{"@timestamp":"2026-06-25T17:51:21.076+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2053.6ms","level":"info","span":"3bc0d76f7d3db4ca","trace":"c5288ede8dfb7e778df0557038c7edef"} +{"@timestamp":"2026-06-25T17:51:22.222+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58496 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"6.1ms","level":"info","span":"1422529e6457549a","trace":"5797f1318d59cae164576333eb2a5516"} +{"@timestamp":"2026-06-25T17:51:24.213+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58498 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"1.4ms","level":"info","span":"16810873c3a150d7","trace":"284933b494e48ddbb84152c908e2b2ed"} +{"@timestamp":"2026-06-25T17:51:26.109+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2031.2ms)","duration":"2031.2ms","level":"slow","span":"6f3a53767a69f02e","trace":"d014df4a55b42d3527a1300b7430e5a2"} +{"@timestamp":"2026-06-25T17:51:26.109+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2031.2ms","level":"info","span":"6f3a53767a69f02e","trace":"d014df4a55b42d3527a1300b7430e5a2"} +{"@timestamp":"2026-06-25T17:51:26.218+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58502 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"4.2ms","level":"info","span":"454cb458c55f3450","trace":"2ef7f10af5595f3f206df38fe2e487ef"} +{"@timestamp":"2026-06-25T17:51:28.215+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58504 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"2.4ms","level":"info","span":"92ab12dde6eb8e25","trace":"13bdff2fef9d291414d75774d7d5850f"} +{"@timestamp":"2026-06-25T17:51:30.215+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58507 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"1.8ms","level":"info","span":"a2b66cf42b9d80d7","trace":"859c1c2f93df26557715339904f5e4a0"} +{"@timestamp":"2026-06-25T17:51:31.138+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2027.4ms)","duration":"2027.4ms","level":"slow","span":"222d672b8af171ef","trace":"7774b5a322a0f272f8e433e73efbadad"} +{"@timestamp":"2026-06-25T17:51:31.138+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2027.4ms","level":"info","span":"222d672b8af171ef","trace":"7774b5a322a0f272f8e433e73efbadad"} +{"@timestamp":"2026-06-25T17:51:32.219+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58509 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"4.9ms","level":"info","span":"4d453174257f763b","trace":"dac800d8040a7734a4c39f0759135d9d"} +{"@timestamp":"2026-06-25T17:51:34.219+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58512 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"3.1ms","level":"info","span":"9690bcff62330f3f","trace":"3c6ee4e927b8a3f3f9bbb2902695fc3a"} +{"@timestamp":"2026-06-25T17:51:36.152+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2012.1ms)","duration":"2012.1ms","level":"slow","span":"eb094bd8e3ff788c","trace":"e7bb841e096a837a00e2c83f626a4882"} +{"@timestamp":"2026-06-25T17:51:36.153+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2012.1ms","level":"info","span":"eb094bd8e3ff788c","trace":"e7bb841e096a837a00e2c83f626a4882"} +{"@timestamp":"2026-06-25T17:51:36.213+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58520 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"1.4ms","level":"info","span":"de12cdc824373804","trace":"02bd9ca2a817f644c8a70c7e59e75236"} +{"@timestamp":"2026-06-25T17:51:38.226+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58522 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"12.9ms","level":"info","span":"c7c9ba2bee527bf6","trace":"9d907e9a44f5c1286fb45d65c0195498"} +{"@timestamp":"2026-06-25T17:51:40.215+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58526 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"2.6ms","level":"info","span":"3410340cb0412ae1","trace":"fb9caf1acee3bb2248d75e2419cdd60f"} +{"@timestamp":"2026-06-25T17:51:41.179+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2023.9ms)","duration":"2023.9ms","level":"slow","span":"0a8b251823ce2178","trace":"8d1c6cf3173221aea935a06b0f7944f3"} +{"@timestamp":"2026-06-25T17:51:41.179+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2023.9ms","level":"info","span":"0a8b251823ce2178","trace":"8d1c6cf3173221aea935a06b0f7944f3"} +{"@timestamp":"2026-06-25T17:51:42.217+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58528 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"2.2ms","level":"info","span":"91508369a2aee1e5","trace":"4ab53fc6f3d90c86bdf2505acffbeb86"} +{"@timestamp":"2026-06-25T17:51:42.567+08:00","caller":"stat/usage.go:82","content":"CPU: 0m, MEMORY: Alloc=5.1Mi, TotalAlloc=308.5Mi, Sys=27.3Mi, NumGC=120","level":"stat"} +{"@timestamp":"2026-06-25T17:51:42.599+08:00","caller":"load/sheddingstat.go:61","content":"(api) shedding_stat [1m], cpu: 0, total: 50, pass: 50, drop: 0","level":"stat"} +{"@timestamp":"2026-06-25T17:51:42.979+08:00","caller":"stat/metrics.go:210","content":"(haixun-backend) - qps: 0.8/s, drops: 0, avg time: 490.7ms, med: 5.2ms, 90th: 2029.9ms, 99th: 2053.5ms, 99.9th: 2053.5ms","level":"stat"} +{"@timestamp":"2026-06-25T17:51:44.221+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58530 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"3.7ms","level":"info","span":"f4063584c58f806f","trace":"5c70fc3b9a5cc5df38e3f964087576fb"} +{"@timestamp":"2026-06-25T17:51:46.204+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2019.2ms)","duration":"2019.2ms","level":"slow","span":"fae02e25cad201f7","trace":"686a1ba85f584297003cb991620fe88e"} +{"@timestamp":"2026-06-25T17:51:46.206+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2019.2ms","level":"info","span":"fae02e25cad201f7","trace":"686a1ba85f584297003cb991620fe88e"} +{"@timestamp":"2026-06-25T17:51:46.222+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58532 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"3.3ms","level":"info","span":"738d6627b067d5f1","trace":"a0292fb431e29dcf8d25348a484d9b0a"} +{"@timestamp":"2026-06-25T17:51:48.225+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58537 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"7.1ms","level":"info","span":"018d4a86363d10cf","trace":"0877b0d0b36177a1d5da13707ce874a7"} +{"@timestamp":"2026-06-25T17:51:50.216+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58540 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"2.8ms","level":"info","span":"d89b8aa1faa1b169","trace":"f0adc9c21f26a4a2eafb2042cd8e0476"} +{"@timestamp":"2026-06-25T17:51:51.237+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2028.1ms)","duration":"2028.1ms","level":"slow","span":"75f14dfcfdfe228e","trace":"188cfd8052d7f630bbb85faf12503ed8"} +{"@timestamp":"2026-06-25T17:51:51.238+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2028.1ms","level":"info","span":"75f14dfcfdfe228e","trace":"188cfd8052d7f630bbb85faf12503ed8"} +{"@timestamp":"2026-06-25T17:51:52.216+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58543 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"3.3ms","level":"info","span":"3c2583d6dae63260","trace":"186b2e23203da867e75f657b15809084"} +{"@timestamp":"2026-06-25T17:51:56.266+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2025.4ms)","duration":"2025.4ms","level":"slow","span":"d897f9cf146ea347","trace":"b48506c748ff40276de0bb8be770f4fa"} +{"@timestamp":"2026-06-25T17:51:56.266+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2025.4ms","level":"info","span":"d897f9cf146ea347","trace":"b48506c748ff40276de0bb8be770f4fa"} +{"@timestamp":"2026-06-25T17:52:01.302+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2032.3ms)","duration":"2032.3ms","level":"slow","span":"528bb1ad8cd51bb4","trace":"9685192c77fbd9af1b8fcd355a646452"} +{"@timestamp":"2026-06-25T17:52:01.302+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2032.3ms","level":"info","span":"528bb1ad8cd51bb4","trace":"9685192c77fbd9af1b8fcd355a646452"} +{"@timestamp":"2026-06-25T17:52:06.238+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:58545 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36","duration":"5.5ms","level":"info","span":"151e42783870fe7d","trace":"b9c18c085426c4116b902001ff8ebaee"} +{"@timestamp":"2026-06-25T17:52:06.339+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2031.8ms)","duration":"2031.8ms","level":"slow","span":"f737d68a7addff8e","trace":"c61cdcaa62bfd5f61a29f5aa295b15e1"} +{"@timestamp":"2026-06-25T17:52:06.339+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2031.8ms","level":"info","span":"f737d68a7addff8e","trace":"c61cdcaa62bfd5f61a29f5aa295b15e1"} +{"@timestamp":"2026-06-25T17:52:11.374+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2029.7ms)","duration":"2029.7ms","level":"slow","span":"634961175c1cc5e5","trace":"91ec634ef6bf3d04b47381c204442170"} +{"@timestamp":"2026-06-25T17:52:11.374+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2029.7ms","level":"info","span":"634961175c1cc5e5","trace":"91ec634ef6bf3d04b47381c204442170"} diff --git a/haixun-backend/Makefile b/haixun-backend/Makefile index a9e7c9b..a980c1d 100644 --- a/haixun-backend/Makefile +++ b/haixun-backend/Makefile @@ -79,11 +79,20 @@ check: fmt test ## 格式化並測試 prod: ## 一鍵啟動 production Docker(API + Web + workers,分身數見 deploy/.env) bash scripts/prod-up.sh -prod-down: ## 停止 production Docker stack +prod-update: ## 只重建/重啟 API+Web+Workers;mongo/redis 不重啟,資料留在 volume + bash scripts/prod-update.sh + +prod-deps: ## 只啟動 mongo+redis(named volume 持久化) + bash scripts/prod-deps.sh + +prod-down: ## 停止 stack(不刪 volume;Mongo/Redis 資料保留) bash scripts/prod-down.sh +prod-wipe-data: ## 停止並刪除 mongo/redis volume(危險,需輸入 yes) + bash scripts/prod-wipe-data.sh + prod-logs: ## 追蹤 production logs(可傳 service 名,例:make prod-logs ARGS=api) bash scripts/prod-logs.sh $(ARGS) -prod-build: ## 只建置 production images(不啟動) +prod-build: web-build ## 建置靜態前端 + production images(不啟動) cd deploy && docker compose -f docker-compose.prod.yml build diff --git a/haixun-backend/deploy/.env.example b/haixun-backend/deploy/.env.example index 5be0037..88ff0f0 100644 --- a/haixun-backend/deploy/.env.example +++ b/haixun-backend/deploy/.env.example @@ -3,11 +3,19 @@ # ── 對外埠 ── HAIXUN_WEB_PORT=8080 +# ── 前端打包模式 ── +# static = 本機 make web-build 後 nginx 只 COPY dist(預設,最快) +# docker = 在 Docker 內跑 npm build(需改 compose 用 Dockerfile.web) +# HAIXUN_WEB_BUILD_MODE=static + # ── Worker 分身數(make prod 會帶入 docker compose --scale)── GO_WORKER_REPLICAS=1 NODE_STYLE8D_WORKER_REPLICAS=1 # ── Mongo / Redis(容器內預設,通常不用改)── +# 資料存在 Docker named volume:haixun-prod_mongo_data、haixun-prod_redis_data +# prod-down 不會刪 volume;重啟 container 資料仍在。 +# 只改版程式:make prod-update(不碰 mongo/redis) HAIXUN_MONGO_URI=mongodb://mongo:27017 HAIXUN_MONGO_DATABASE=haixun HAIXUN_REDIS_ADDR=redis:6379 @@ -26,5 +34,7 @@ INIT_ADMIN_PASSWORD=Admin-Pass-1! # HAIXUN_NODE_WORKER_ID=custom-node-worker-1 # HAIXUN_WORKER_POLL_MS=3000 -# ── 略過自動 init(已有資料庫時可設 1)── +# ── 略過自動 init ── +# 預設:若 Mongo 已有 members 會自動跳過 init。 +# 強制重跑 init:PROD_FORCE_INIT=1 make prod # HAIXUN_SKIP_INIT=1 \ No newline at end of file diff --git a/haixun-backend/deploy/Dockerfile.web b/haixun-backend/deploy/Dockerfile.web index 7bac6fc..0159b78 100644 --- a/haixun-backend/deploy/Dockerfile.web +++ b/haixun-backend/deploy/Dockerfile.web @@ -1,4 +1,5 @@ # syntax=docker/dockerfile:1 +# 備用:無本機 Node 時在 Docker 內編譯。預設請用 Dockerfile.web.static + make web-build。 FROM node:22-bookworm AS web-builder WORKDIR /src/web diff --git a/haixun-backend/deploy/Dockerfile.web.static b/haixun-backend/deploy/Dockerfile.web.static new file mode 100644 index 0000000..e6f5999 --- /dev/null +++ b/haixun-backend/deploy/Dockerfile.web.static @@ -0,0 +1,7 @@ +# syntax=docker/dockerfile:1 +# 本機先執行 make web-build,再打包純靜態檔 + nginx(無 Node 編譯,建置最快) + +FROM nginx:1.27-alpine +COPY deploy/nginx.conf /etc/nginx/conf.d/default.conf +COPY web/dist /usr/share/nginx/html +EXPOSE 80 \ No newline at end of file diff --git a/haixun-backend/deploy/docker-compose.prod.yml b/haixun-backend/deploy/docker-compose.prod.yml index 98fb503..6333d6d 100644 --- a/haixun-backend/deploy/docker-compose.prod.yml +++ b/haixun-backend/deploy/docker-compose.prod.yml @@ -6,6 +6,7 @@ services: restart: unless-stopped environment: MONGO_INITDB_DATABASE: haixun + # named volume:重啟/改版不會清資料(只有 prod-wipe-data 或 docker volume rm 才會) volumes: - mongo_data:/data/db healthcheck: @@ -19,6 +20,7 @@ services: image: redis:7-alpine restart: unless-stopped command: ["redis-server", "--appendonly", "yes"] + # AOF + named volume:重啟後 queue/lock 狀態可從磁碟恢復 volumes: - redis_data:/data healthcheck: @@ -81,7 +83,7 @@ services: web: build: context: .. - dockerfile: deploy/Dockerfile.web + dockerfile: deploy/Dockerfile.web.static restart: unless-stopped ports: - "${HAIXUN_WEB_PORT:-8080}:80" diff --git a/haixun-backend/deploy/nginx.conf b/haixun-backend/deploy/nginx.conf index 664ddfc..7ea24d1 100644 --- a/haixun-backend/deploy/nginx.conf +++ b/haixun-backend/deploy/nginx.conf @@ -5,7 +5,37 @@ server { index index.html; gzip on; - gzip_types text/css application/javascript application/json image/svg+xml; + gzip_comp_level 5; + gzip_min_length 256; + gzip_types + text/css + text/javascript + application/javascript + application/json + application/xml + image/svg+xml; + + # Vite 產物:檔名含 hash,可長期快取 + location /assets/ { + add_header Cache-Control "public, max-age=31536000, immutable"; + try_files $uri =404; + } + + location /downloads/ { + add_header Cache-Control "public, max-age=86400"; + try_files $uri =404; + } + + location /illustrations/ { + add_header Cache-Control "public, max-age=86400"; + try_files $uri =404; + } + + # SPA 入口與路由:不快取,避免部署後仍載入舊版 shell + location = /index.html { + add_header Cache-Control "no-cache"; + try_files $uri =404; + } location /api/ { proxy_pass http://api:8890; diff --git a/haixun-backend/scripts/prod-common.sh b/haixun-backend/scripts/prod-common.sh new file mode 100755 index 0000000..c1aa9ef --- /dev/null +++ b/haixun-backend/scripts/prod-common.sh @@ -0,0 +1,178 @@ +#!/usr/bin/env bash +# shellcheck disable=SC2034 +# Shared helpers for production Docker scripts. + +_PROD_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +BACKEND_DIR="$(cd "$_PROD_SCRIPT_DIR/.." && pwd)" +DEPLOY_DIR="$BACKEND_DIR/deploy" +COMPOSE_FILE="$DEPLOY_DIR/docker-compose.prod.yml" +ENV_FILE="$DEPLOY_DIR/.env" +ENV_EXAMPLE="$DEPLOY_DIR/.env.example" + +prod_common_init() { + : +} + +prod_load_env() { + prod_common_init + + if [[ ! -f "$ENV_FILE" ]]; then + if [[ -f "$ENV_EXAMPLE" ]]; then + cp "$ENV_EXAMPLE" "$ENV_FILE" + echo "[prod] created $ENV_FILE from .env.example — 請先修改密鑰與管理員密碼" + else + echo "[prod] missing $ENV_FILE" >&2 + exit 1 + fi + fi + + set -a + # shellcheck disable=SC1090 + source "$ENV_FILE" + set +a + + GO_REPLICAS="${GO_WORKER_REPLICAS:-1}" + NODE_REPLICAS="${NODE_STYLE8D_WORKER_REPLICAS:-1}" + WEB_PORT="${HAIXUN_WEB_PORT:-8080}" + MONGO_DB="${HAIXUN_MONGO_DATABASE:-haixun}" +} + +prod_require_docker() { + if ! command -v docker >/dev/null 2>&1; then + echo "[prod] docker is required" >&2 + exit 1 + fi +} + +prod_compose() { + prod_common_init + docker compose -f "$COMPOSE_FILE" "$@" +} + +prod_service_health() { + local service="$1" + prod_compose ps --format json "$service" 2>/dev/null \ + | grep -o '"Health":"[^"]*"' \ + | head -1 \ + | cut -d'"' -f4 \ + || true +} + +prod_deps_healthy() { + local mongo_ok redis_ok + mongo_ok="$(prod_service_health mongo)" + redis_ok="$(prod_service_health redis)" + [[ "$mongo_ok" == "healthy" && "$redis_ok" == "healthy" ]] +} + +prod_wait_deps_healthy() { + echo "[prod] waiting for mongo/redis..." + for _ in $(seq 1 90); do + if prod_deps_healthy; then + return 0 + fi + sleep 1 + done + echo "[prod] mongo/redis did not become healthy in time" >&2 + exit 1 +} + +prod_ensure_deps() { + if prod_deps_healthy; then + echo "[prod] mongo + redis already healthy — 略過重啟(資料在 named volume)" + return 0 + fi + + echo "[prod] starting mongo + redis..." + prod_compose up -d mongo redis + prod_wait_deps_healthy +} + +prod_mongo_has_members() { + prod_compose exec -T mongo mongosh --quiet "$MONGO_DB" --eval \ + 'db.members.countDocuments({})' 2>/dev/null \ + | tr -d '\r' \ + | grep -Eq '^[1-9][0-9]*$' +} + +prod_should_skip_init() { + if [[ "${HAIXUN_SKIP_INIT:-0}" == "1" ]]; then + return 0 + fi + if [[ "${PROD_FORCE_INIT:-0}" == "1" ]]; then + return 1 + fi + if prod_mongo_has_members; then + return 0 + fi + return 1 +} + +prod_run_init_if_needed() { + if prod_should_skip_init; then + if [[ "${HAIXUN_SKIP_INIT:-0}" == "1" ]]; then + echo "[prod] skip init (HAIXUN_SKIP_INIT=1)" + else + echo "[prod] skip init (Mongo 已有資料;若要強制重跑請設 PROD_FORCE_INIT=1)" + fi + return 0 + fi + + echo "[prod] running bootstrap init..." + prod_compose --profile init run --rm init +} + +prod_build_web_if_static() { + prod_common_init + if [[ "${HAIXUN_WEB_BUILD_MODE:-static}" == "static" ]]; then + echo "[prod] building frontend static files (vite → web/dist)..." + (cd "$BACKEND_DIR" && make web-build) + else + echo "[prod] HAIXUN_WEB_BUILD_MODE=docker — web image will compile inside Docker" + fi +} + +prod_start_app_services() { + local build_flag=() + if [[ "${PROD_SKIP_BUILD:-0}" != "1" ]]; then + build_flag=(--build) + fi + + echo "[prod] starting api, web, workers (go=${GO_REPLICAS}, node-style-8d=${NODE_REPLICAS})..." + prod_compose up -d "${build_flag[@]}" \ + --no-deps \ + --scale "go-worker=${GO_REPLICAS}" \ + --scale "node-worker-style-8d=${NODE_REPLICAS}" \ + api web go-worker node-worker-style-8d +} + +prod_wait_api_health() { + echo "[prod] waiting for API health..." + for _ in $(seq 1 60); do + if curl -fsS "http://127.0.0.1:${WEB_PORT}/api/v1/health" >/dev/null 2>&1; then + return 0 + fi + sleep 1 + done + echo "[prod] API health check timed out" >&2 + exit 1 +} + +prod_print_volume_hint() { + echo " Data: Mongo/Redis 使用 named volume(重啟 container 不會清資料)" + echo " Update app: make -C haixun-backend prod-update" + echo " Wipe data: make -C haixun-backend prod-wipe-data # 會刪除 volume" +} + +prod_print_stack_summary() { + echo "" + echo "[prod] stack is up" + echo " Web: http://127.0.0.1:${WEB_PORT}" + echo " API: http://127.0.0.1:${WEB_PORT}/api/v1/health (via nginx)" + echo " Go worker: ${GO_REPLICAS} replica(s)" + echo " Node 8D: ${NODE_REPLICAS} replica(s)" + echo " Env: ${ENV_FILE}" + echo " Stop: make -C haixun-backend prod-down" + echo " Logs: make -C haixun-backend prod-logs" + prod_print_volume_hint +} \ No newline at end of file diff --git a/haixun-backend/scripts/prod-deps.sh b/haixun-backend/scripts/prod-deps.sh new file mode 100755 index 0000000..3ac8b63 --- /dev/null +++ b/haixun-backend/scripts/prod-deps.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -euo pipefail + +# shellcheck source=scripts/prod-common.sh +source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/prod-common.sh" + +prod_load_env +prod_require_docker + +cd "$DEPLOY_DIR" + +prod_ensure_deps + +echo "" +echo "[prod] mongo + redis ready" +prod_print_volume_hint \ No newline at end of file diff --git a/haixun-backend/scripts/prod-down.sh b/haixun-backend/scripts/prod-down.sh index 256ecbe..94e9cf1 100755 --- a/haixun-backend/scripts/prod-down.sh +++ b/haixun-backend/scripts/prod-down.sh @@ -1,9 +1,11 @@ #!/usr/bin/env bash set -euo pipefail -BACKEND_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" -COMPOSE_FILE="$BACKEND_DIR/deploy/docker-compose.prod.yml" +# shellcheck source=scripts/prod-common.sh +source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/prod-common.sh" -cd "$BACKEND_DIR/deploy" -docker compose -f "$COMPOSE_FILE" down -echo "[prod] stopped" \ No newline at end of file +prod_common_init +cd "$DEPLOY_DIR" + +prod_compose down --remove-orphans +echo "[prod] stopped(Mongo/Redis 資料仍在 named volume,下次 prod / prod-deps 會沿用)" \ No newline at end of file diff --git a/haixun-backend/scripts/prod-up.sh b/haixun-backend/scripts/prod-up.sh index ed50fc1..e3dce92 100755 --- a/haixun-backend/scripts/prod-up.sh +++ b/haixun-backend/scripts/prod-up.sh @@ -1,81 +1,23 @@ #!/usr/bin/env bash set -euo pipefail -BACKEND_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" -DEPLOY_DIR="$BACKEND_DIR/deploy" -COMPOSE_FILE="$DEPLOY_DIR/docker-compose.prod.yml" -ENV_FILE="$DEPLOY_DIR/.env" -ENV_EXAMPLE="$DEPLOY_DIR/.env.example" +# shellcheck source=scripts/prod-common.sh +source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/prod-common.sh" -if [[ ! -f "$ENV_FILE" ]]; then - if [[ -f "$ENV_EXAMPLE" ]]; then - cp "$ENV_EXAMPLE" "$ENV_FILE" - echo "[prod] created $ENV_FILE from .env.example — 請先修改密鑰與管理員密碼" - else - echo "[prod] missing $ENV_FILE" >&2 - exit 1 - fi -fi +prod_load_env +prod_require_docker -set -a -# shellcheck disable=SC1090 -source "$ENV_FILE" -set +a - -GO_REPLICAS="${GO_WORKER_REPLICAS:-1}" -NODE_REPLICAS="${NODE_STYLE8D_WORKER_REPLICAS:-1}" -WEB_PORT="${HAIXUN_WEB_PORT:-8080}" - -if ! command -v docker >/dev/null 2>&1; then - echo "[prod] docker is required" >&2 - exit 1 -fi +cd "$BACKEND_DIR" +prod_build_web_if_static cd "$DEPLOY_DIR" echo "[prod] building images..." -docker compose -f "$COMPOSE_FILE" build +prod_compose build -echo "[prod] starting mongo + redis..." -docker compose -f "$COMPOSE_FILE" up -d mongo redis +prod_ensure_deps +prod_run_init_if_needed -echo "[prod] waiting for mongo/redis..." -for _ in $(seq 1 90); do - mongo_ok=$(docker compose -f "$COMPOSE_FILE" ps --format json mongo 2>/dev/null | grep -o '"Health":"[^"]*"' | head -1 | cut -d'"' -f4 || true) - redis_ok=$(docker compose -f "$COMPOSE_FILE" ps --format json redis 2>/dev/null | grep -o '"Health":"[^"]*"' | head -1 | cut -d'"' -f4 || true) - if [[ "$mongo_ok" == "healthy" && "$redis_ok" == "healthy" ]]; then - break - fi - sleep 1 -done - -if [[ "${HAIXUN_SKIP_INIT:-0}" != "1" ]]; then - echo "[prod] running bootstrap init..." - docker compose -f "$COMPOSE_FILE" --profile init run --rm init -else - echo "[prod] skip init (HAIXUN_SKIP_INIT=1)" -fi - -echo "[prod] starting api, web, workers (go=${GO_REPLICAS}, node-style-8d=${NODE_REPLICAS})..." -docker compose -f "$COMPOSE_FILE" up -d \ - --scale "go-worker=${GO_REPLICAS}" \ - --scale "node-worker-style-8d=${NODE_REPLICAS}" \ - api web go-worker node-worker-style-8d - -echo "[prod] waiting for API health..." -for _ in $(seq 1 60); do - if curl -fsS "http://127.0.0.1:${WEB_PORT}/api/v1/health" >/dev/null 2>&1; then - break - fi - sleep 1 -done - -echo "" -echo "[prod] stack is up" -echo " Web: http://127.0.0.1:${WEB_PORT}" -echo " API: http://127.0.0.1:${WEB_PORT}/api/v1/health (via nginx)" -echo " Go worker: ${GO_REPLICAS} replica(s)" -echo " Node 8D: ${NODE_REPLICAS} replica(s)" -echo " Env: ${ENV_FILE}" -echo " Stop: make -C haixun-backend prod-down" -echo " Logs: make -C haixun-backend prod-logs" \ No newline at end of file +prod_start_app_services +prod_wait_api_health +prod_print_stack_summary \ No newline at end of file diff --git a/haixun-backend/scripts/prod-update.sh b/haixun-backend/scripts/prod-update.sh new file mode 100755 index 0000000..5398784 --- /dev/null +++ b/haixun-backend/scripts/prod-update.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +set -euo pipefail + +# 只重建/重啟 API、Web、Workers;不碰 mongo/redis(資料留在 volume)。 + +# shellcheck source=scripts/prod-common.sh +source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/prod-common.sh" + +prod_load_env +prod_require_docker + +cd "$BACKEND_DIR" +prod_build_web_if_static + +cd "$DEPLOY_DIR" + +if ! prod_deps_healthy; then + echo "[prod] mongo/redis 未在運行,先啟動依賴(不會清 volume)..." + prod_ensure_deps +else + echo "[prod] mongo + redis 維持運行 — 只更新應用層" +fi + +echo "[prod] building app images (api, web, workers)..." +prod_compose build api web go-worker node-worker-style-8d + +prod_start_app_services +prod_wait_api_health +prod_print_stack_summary \ No newline at end of file diff --git a/haixun-backend/scripts/prod-wipe-data.sh b/haixun-backend/scripts/prod-wipe-data.sh new file mode 100755 index 0000000..1bbb805 --- /dev/null +++ b/haixun-backend/scripts/prod-wipe-data.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +set -euo pipefail + +# 危險:停止 stack 並刪除 Mongo/Redis named volume。 + +# shellcheck source=scripts/prod-common.sh +source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/prod-common.sh" + +prod_load_env +prod_require_docker + +cd "$DEPLOY_DIR" + +echo "[prod] 這會刪除 haixun-prod_mongo_data 與 haixun-prod_redis_data 內所有資料。" +read -r -p "輸入 yes 才會繼續: " confirm +if [[ "$confirm" != "yes" ]]; then + echo "[prod] cancelled" + exit 1 +fi + +prod_compose down -v --remove-orphans +echo "[prod] volumes removed — 下次 make prod 會是全新資料庫" \ No newline at end of file