diff --git a/haixun-backend/.run/api.pid b/haixun-backend/.run/api.pid index 63de6e4..c633f75 100644 --- a/haixun-backend/.run/api.pid +++ b/haixun-backend/.run/api.pid @@ -1 +1 @@ -26382 +51557 diff --git a/haixun-backend/.run/logs/api.log b/haixun-backend/.run/logs/api.log index 3ee7cff..707653d 100644 --- a/haixun-backend/.run/logs/api.log +++ b/haixun-backend/.run/logs/api.log @@ -1,43 +1,248 @@ -2026/06/25 16:19:34 job scheduler started: holder=wangxinghuadeMac-mini.local-scheduler interval=1m0s -2026/06/25 16:19:34 job reaper started: interval=30s -2026/06/25 16:19:34 job worker started: id=wangxinghuadeMac-mini.local-go-worker type=go +2026/06/25 17:30:42 job scheduler started: holder=wangxinghuadeMac-mini.local-scheduler interval=1m0s +2026/06/25 17:30:42 job worker started: id=wangxinghuadeMac-mini.local-go-worker type=go +2026/06/25 17:30:42 job reaper started: interval=30s Starting backend backend at 0.0.0.0:8890... -{"@timestamp":"2026-06-25T16:19:35.055+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/health - 127.0.0.1:60604 - curl/8.7.1","duration":"0.3ms","level":"info","span":"6415334576486240","trace":"950fc5b3ba616a4912d51241eb2b3a48"} -{"@timestamp":"2026-06-25T16:19:35.063+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/health - 127.0.0.1:60605 - curl/8.7.1","duration":"0.1ms","level":"info","span":"32b367b9757aa4ce","trace":"b8d6e2b9b9f6991f39ca52286514f8d0"} -{"@timestamp":"2026-06-25T16:19:37.414+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 401 - GET /api/v1/members/me - 127.0.0.1:60617 - 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":"0.3ms","level":"info","span":"0a0ab6f5c14d5d26","trace":"a092f127556e39b4d17762ca139e6068"} -{"@timestamp":"2026-06-25T16:19:37.416+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 401 - GET /api/v1/members/me - 127.0.0.1:60618 - 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":"0.1ms","level":"info","span":"79c81d67629142ba","trace":"d819499213c89d08de3b5b5d2f35b7e6"} -{"@timestamp":"2026-06-25T16:19:37.419+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/auth/refresh - 127.0.0.1:60619 - 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":"028c5080eabb0c20","trace":"91f42acca9e2bd90c32b9bd5425f5243"} -{"@timestamp":"2026-06-25T16:19:37.444+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/members/me - 127.0.0.1:60623 - 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":"11.7ms","level":"info","span":"e505d50d87cf5e3f","trace":"a1dc33e9536bba807f03d824b68d11ae"} -{"@timestamp":"2026-06-25T16:19:37.446+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/members/me - 127.0.0.1:60624 - 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":"0.8ms","level":"info","span":"dc5a2ac116d2793f","trace":"bf0c219c6522b3f0026595075c847f2a"} -{"@timestamp":"2026-06-25T16:19:37.472+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas/51f4d954-b152-4127-bfb9-e47bde06d1f8 - 127.0.0.1:60632 - 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":"13.7ms","level":"info","span":"3986579b07eb7cc6","trace":"62e9f8ae8907e878c693ed5cc909bacd"} -{"@timestamp":"2026-06-25T16:19:37.480+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:60633 - 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":"21.1ms","level":"info","span":"b106b93beea0b78b","trace":"26cc88f8ff2121b4df8f38b43d7304c0"} -{"@timestamp":"2026-06-25T16:19:37.481+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas/51f4d954-b152-4127-bfb9-e47bde06d1f8 - 127.0.0.1:60646 - 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":"0ac7545e219b276a","trace":"9801452fce8d5260fd59282be8d94535"} -{"@timestamp":"2026-06-25T16:19:37.483+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:60648 - 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.5ms","level":"info","span":"514548e176d35c49","trace":"3ea5c3db304edb00642910eb331cad11"} -{"@timestamp":"2026-06-25T16:19:37.485+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas - 127.0.0.1:60630 - 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":"27.3ms","level":"info","span":"846dd3bd919fabfc","trace":"bc5287a47b8d1e9aff648aa6f1801b30"} -{"@timestamp":"2026-06-25T16:19:37.487+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/threads-accounts - 127.0.0.1:60636 - 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":"27.5ms","level":"info","span":"4274ae675b087aa5","trace":"c4e5d877a268cd28ed5560216c49fbb3"} -{"@timestamp":"2026-06-25T16:19:37.487+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas - 127.0.0.1:60650 - 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.0ms","level":"info","span":"d95aa13b6583ddbd","trace":"1b963cd6f167ad24b1b5a7f425b1382e"} -{"@timestamp":"2026-06-25T16:19:37.489+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:60631 - 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":"31.0ms","level":"info","span":"74793b560680c5b3","trace":"759db809c9ccaaee2681541c1ac9e4c0"} -{"@timestamp":"2026-06-25T16:19:37.489+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas - 127.0.0.1:60654 - 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":"0.8ms","level":"info","span":"7ecde433bfd09aa3","trace":"34f4e4ff4c1efc22ad6c2b71434e5327"} -{"@timestamp":"2026-06-25T16:19:37.489+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/threads-accounts - 127.0.0.1:60652 - 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":"33138517c2669bfd","trace":"d7bb1ef130cd1fc4c0ea13d5857eb43a"} -{"@timestamp":"2026-06-25T16:19:37.490+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/threads-accounts/bf9e27eb-d529-4ef5-be11-20870194a4b8/ai-settings - 127.0.0.1:60635 - 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":"31.3ms","level":"info","span":"832aa7d686494971","trace":"3a88a027e63eccbd06bec130f801209a"} -{"@timestamp":"2026-06-25T16:19:37.492+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas - 127.0.0.1:60658 - 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":"0.6ms","level":"info","span":"c27fd79c0ce6aa3b","trace":"2102a91a76c1610f1622df77402f0993"} -{"@timestamp":"2026-06-25T16:19:37.492+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/threads-accounts/bf9e27eb-d529-4ef5-be11-20870194a4b8/ai-settings - 127.0.0.1:60660 - 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":"0.8ms","level":"info","span":"41eac6809cffa5fc","trace":"acd5544f67f670d2614da16744372bfc"} -{"@timestamp":"2026-06-25T16:19:37.492+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:60659 - 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":"0.9ms","level":"info","span":"edc1fd6192317f35","trace":"0389ec83da5ac17ae72d4fc745389dc3"} -{"@timestamp":"2026-06-25T16:19:37.493+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas - 127.0.0.1:60662 - 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":"0.4ms","level":"info","span":"7eb2e6972f3e44c9","trace":"5af3f0bad4af7c49455e5756f60cdb07"} -{"@timestamp":"2026-06-25T16:19:37.527+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:60664 - 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.5ms","level":"info","span":"ade7a29f73a7c288","trace":"5ab83b6cd21393e1de22b5f87cf3da1b"} -{"@timestamp":"2026-06-25T16:19:37.533+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas - 127.0.0.1:60666 - 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.8ms","level":"info","span":"2cbde8636870326f","trace":"dfeeff52daffd47979d1bf27132ff1fe"} -{"@timestamp":"2026-06-25T16:19:37.554+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:60668 - 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":"21.5ms","level":"info","span":"e99768c1e5a60f19","trace":"ed1142224257855380d4c7d82ef30d7d"} -{"@timestamp":"2026-06-25T16:19:37.562+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:60670 - 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":"682d76eaf77ba854","trace":"2debafb3df297667243d29080aaf46f3"} -{"@timestamp":"2026-06-25T16:19:37.567+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:60672 - 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":"ede6f51d147113cc","trace":"ed9e3a6e7d3dce0809583ad7069428a6"} -{"@timestamp":"2026-06-25T16:19:38.079+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:60606 - node - slowcall(2059.6ms)","duration":"2059.6ms","level":"slow","span":"154f34f3c1f12155","trace":"f8ed314cbff91e799bccd0404c1e8d10"} -{"@timestamp":"2026-06-25T16:19:38.079+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:60606 - node","duration":"2059.6ms","level":"info","span":"154f34f3c1f12155","trace":"f8ed314cbff91e799bccd0404c1e8d10"} -{"@timestamp":"2026-06-25T16:19:39.468+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:60674 - 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":"3aa1e079de1afc33","trace":"c83e8c4576b691e3c046fcf69afab8ba"} -{"@timestamp":"2026-06-25T16:19:42.157+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:60676 - 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.8ms","level":"info","span":"49f9c0136a1bb10e","trace":"1af1b6eda97a33a254a83ef3528a232f"} -{"@timestamp":"2026-06-25T16:19:43.159+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:60606 - node - slowcall(2065.1ms)","duration":"2065.1ms","level":"slow","span":"b0466c9c7c11a34b","trace":"6c3157a0083e8935e5d727cefa561016"} -{"@timestamp":"2026-06-25T16:19:43.159+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:60606 - node","duration":"2065.1ms","level":"info","span":"b0466c9c7c11a34b","trace":"6c3157a0083e8935e5d727cefa561016"} -{"@timestamp":"2026-06-25T16:19:44.154+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:60678 - 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":"0b8ef70e28a0b93c","trace":"fa02be6dc695e628d454c08e32b04602"} -{"@timestamp":"2026-06-25T16:19:46.152+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:60682 - 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":"1de3a57a2b185f05","trace":"113b14351258aa182b42f764d9fc3d78"} -{"@timestamp":"2026-06-25T16:19:48.151+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:60684 - 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.6ms","level":"info","span":"6bfcce92975c0546","trace":"154518eadb36033595c2b0608bbff0d1"} -{"@timestamp":"2026-06-25T16:19:48.248+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:60606 - node - slowcall(2084.2ms)","duration":"2084.2ms","level":"slow","span":"efaa36b9549d8d3d","trace":"fed1ba361d31f1dca61f215e56dcb2ca"} -{"@timestamp":"2026-06-25T16:19:48.248+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:60606 - node","duration":"2084.2ms","level":"info","span":"efaa36b9549d8d3d","trace":"fed1ba361d31f1dca61f215e56dcb2ca"} -{"@timestamp":"2026-06-25T16:19:50.152+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:60689 - 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.6ms","level":"info","span":"d34bb8e081297bc5","trace":"36e0c61a91be1e5075ac51dbe563ff7a"} +{"@timestamp":"2026-06-25T17:30:42.961+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/health - 127.0.0.1:55160 - curl/8.7.1","duration":"0.5ms","level":"info","span":"045ba34c000a4d69","trace":"162a6986337c530f2a88d15d9486f01b"} +{"@timestamp":"2026-06-25T17:30:42.975+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/health - 127.0.0.1:55161 - curl/8.7.1","duration":"0.1ms","level":"info","span":"53345b0a3c07f4f5","trace":"e3dfecd1de18e183f93bc45d19581b53"} +{"@timestamp":"2026-06-25T17:30:45.843+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.6ms)","duration":"2044.6ms","level":"slow","span":"9aca8d10547dac0f","trace":"b886a38ebf0791d14e081ecb6d7ed106"} +{"@timestamp":"2026-06-25T17:30:45.843+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.6ms","level":"info","span":"9aca8d10547dac0f","trace":"b886a38ebf0791d14e081ecb6d7ed106"} +{"@timestamp":"2026-06-25T17:30:50.602+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/auth/login - 127.0.0.1:55190 - 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":"103.3ms","level":"info","span":"c9d9bb386da27ea9","trace":"836f3f3c7831c91ec30eb7b3fc8ca0c4"} +{"@timestamp":"2026-06-25T17:30:50.615+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/members/me - 127.0.0.1:55192 - 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":"70e96ee134e675e3","trace":"9dc9ea0636288a4f404fd0e5811988d0"} +{"@timestamp":"2026-06-25T17:30:50.633+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/job/schedules?page=1&pageSize=100 - 127.0.0.1:55195 - 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.7ms","level":"info","span":"efb917d662253464","trace":"08f52016878848d8d8ca6f442d0ba7f3"} +{"@timestamp":"2026-06-25T17:30:50.636+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/job/schedules?page=1&pageSize=100 - 127.0.0.1:55208 - 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.5ms","level":"info","span":"0818d78ae2e89a7f","trace":"4dffd229432d026d33abea4323b5198c"} +{"@timestamp":"2026-06-25T17:30:50.636+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=50 - 127.0.0.1:55193 - 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.6ms","level":"info","span":"1046ad31eb987738","trace":"24c8e91f7fbe024108c2a3f9db90dde8"} +{"@timestamp":"2026-06-25T17:30:50.638+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:55196 - 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.3ms","level":"info","span":"3aa47488bcdd1604","trace":"884c0060f20b5e484741198dc4ca3152"} +{"@timestamp":"2026-06-25T17:30:50.639+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=50 - 127.0.0.1:55210 - 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.5ms","level":"info","span":"7609b8075ef2eee7","trace":"18a169caf39dcc4345c12a3cdbaa2d9b"} +{"@timestamp":"2026-06-25T17:30:50.641+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas - 127.0.0.1:55197 - 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":"15.0ms","level":"info","span":"cfbcfeb0821e857b","trace":"0a479a0de3a42fe1ed30a5eacbe01ff3"} +{"@timestamp":"2026-06-25T17:30:50.641+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:55212 - 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":"c60cfa9cdee18f32","trace":"d4663d1888e3e42bc566969a35bc5184"} +{"@timestamp":"2026-06-25T17:30:50.643+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/threads-accounts - 127.0.0.1:55201 - 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":"16.8ms","level":"info","span":"87a95c55afefe871","trace":"d5c9aab073e7c0b07713a310013f8ac6"} +{"@timestamp":"2026-06-25T17:30:50.645+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas - 127.0.0.1:55214 - 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":"9b34d1828c68edff","trace":"fc03532d8b9e854a866115f0daf1bde0"} +{"@timestamp":"2026-06-25T17:30:50.648+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/threads-accounts - 127.0.0.1:55216 - 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":"85ec75935d353dd6","trace":"3e50d1982ee1a69fa8aea7329449225c"} +{"@timestamp":"2026-06-25T17:30:50.648+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas - 127.0.0.1:55218 - 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":"0.8ms","level":"info","span":"b215edceb1f23e97","trace":"a6cf1b0662541ef1a31543861d85cfd6"} +{"@timestamp":"2026-06-25T17:30:50.657+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas - 127.0.0.1:55220 - 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":"0.9ms","level":"info","span":"45df893595b47cc0","trace":"3951796ab3abbe515d7f773bddac0ba2"} +{"@timestamp":"2026-06-25T17:30:50.673+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas - 127.0.0.1:55223 - 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.5ms","level":"info","span":"ee3bfef0ea16861c","trace":"5d69ba637d00496e147e94f4ee06fa25"} +{"@timestamp":"2026-06-25T17:30:50.675+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/threads-accounts/a76918ed-69a7-4f45-8ea2-c984d8a85409/connection - 127.0.0.1:55224 - 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":"0b01b7ac3436d854","trace":"16a644b25a2d990c0e17080b00c975ef"} +{"@timestamp":"2026-06-25T17:30:50.678+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/threads-accounts/a76918ed-69a7-4f45-8ea2-c984d8a85409/connection - 127.0.0.1:55226 - 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.3ms","level":"info","span":"c2e07676a4032951","trace":"59b892589ae8dd18cf94b9d6d8fa4ad4"} +{"@timestamp":"2026-06-25T17:30:50.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(2071.2ms)","duration":"2071.2ms","level":"slow","span":"bc8917b8a7e369d1","trace":"9ee3d32d8264e74da8337a6c7d73d37a"} +{"@timestamp":"2026-06-25T17:30:50.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":"2071.2ms","level":"info","span":"bc8917b8a7e369d1","trace":"9ee3d32d8264e74da8337a6c7d73d37a"} +{"@timestamp":"2026-06-25T17:30:51.901+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas - 127.0.0.1:55230 - 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.9ms","level":"info","span":"7ad2d1a8325fa704","trace":"3abd6632e7c80697176e734cf753a5af"} +{"@timestamp":"2026-06-25T17:30:51.902+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 404 - GET /api/v1/personas/51f4d954-b152-4127-bfb9-e47bde06d1f8/copy-missions - 127.0.0.1:55231 - 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.4ms","level":"info","span":"d058165b1246ef75","trace":"485546586d2d29478fbaf67de46cf79d"} +{"@timestamp":"2026-06-25T17:30:51.902+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 404 - GET /api/v1/personas/51f4d954-b152-4127-bfb9-e47bde06d1f8 - 127.0.0.1:55232 - 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.4ms","level":"info","span":"caab402214329961","trace":"8ce648cae59691a7cc0343b5a5b88458"} +{"@timestamp":"2026-06-25T17:30:51.903+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas - 127.0.0.1:55234 - 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.0ms","level":"info","span":"af6416f906ac1a54","trace":"470ebfa460168763428cbe5e0efecefd"} +{"@timestamp":"2026-06-25T17:30:51.905+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 404 - GET /api/v1/personas/51f4d954-b152-4127-bfb9-e47bde06d1f8/copy-missions - 127.0.0.1:55237 - 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":"0.9ms","level":"info","span":"102df3903a0141a5","trace":"f00e87c07bbf62c3ead92f4e8c252f8b"} +{"@timestamp":"2026-06-25T17:30:51.905+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 404 - GET /api/v1/personas/51f4d954-b152-4127-bfb9-e47bde06d1f8 - 127.0.0.1:55239 - 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":"0.7ms","level":"info","span":"0092616cd44b40d3","trace":"790f9d14191133a97cc9e247b6814345"} +{"@timestamp":"2026-06-25T17:30:51.905+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas - 127.0.0.1:55240 - 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.0ms","level":"info","span":"a309118e34ed5a0f","trace":"ea7eb9e26f977c5db6a4319983a2eca0"} +{"@timestamp":"2026-06-25T17:30:51.909+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas/d539b02a-ea8e-4d6e-8ebb-3d94a1f31cb8 - 127.0.0.1:55244 - 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.2ms","level":"info","span":"ebac4f23af901a17","trace":"dfae73fa78d7ed5d0dc789bf4607df80"} +{"@timestamp":"2026-06-25T17:30:51.911+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas - 127.0.0.1:55247 - 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.1ms","level":"info","span":"e870871cf77b2547","trace":"f76091fcd5bfd6b5531737793226c789"} +{"@timestamp":"2026-06-25T17:30:51.913+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas - 127.0.0.1:55250 - 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.0ms","level":"info","span":"af6699a87fe4ac3c","trace":"9563aacda92c6243ab744e13458879b8"} +{"@timestamp":"2026-06-25T17:30:51.914+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/threads-accounts/a76918ed-69a7-4f45-8ea2-c984d8a85409/connection - 127.0.0.1:55248 - 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.3ms","level":"info","span":"12de4c48e88ed5b6","trace":"bf1fae11ed90857242f6c622f30f2ba8"} +{"@timestamp":"2026-06-25T17:30:51.914+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas/d539b02a-ea8e-4d6e-8ebb-3d94a1f31cb8/copy-missions - 127.0.0.1:55243 - 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":"0aacc80c57455da4","trace":"33d7d6b3b0001d61905ca344584ba5da"} +{"@timestamp":"2026-06-25T17:30:51.917+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas/d539b02a-ea8e-4d6e-8ebb-3d94a1f31cb8/copy-missions - 127.0.0.1:55252 - 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":"1b3b6aca6dc53382","trace":"ed1bf91d8e0328c40dc9f7d5b4f2192a"} +{"@timestamp":"2026-06-25T17:30:51.920+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas/d539b02a-ea8e-4d6e-8ebb-3d94a1f31cb8/copy-missions - 127.0.0.1:55254 - 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":"88ee7b8163e1e59e","trace":"72706beab614f76decaad9832a35cacd"} +{"@timestamp":"2026-06-25T17:30:52.631+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:55257 - 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.5ms","level":"info","span":"f8f9c1b34a503b8c","trace":"d99aa9424ef6164a3245e984b25dcf13"} +{"@timestamp":"2026-06-25T17:30:54.633+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:55259 - 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":"0ea6ec0bbb6a4bc6","trace":"57e282aa4117d5ac1606b786c7b138bc"} +{"@timestamp":"2026-06-25T17:30:55.970+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.7ms)","duration":"2038.7ms","level":"slow","span":"08539fb389748e1a","trace":"c9fb51ce83c0be9e6489261fc5a428ed"} +{"@timestamp":"2026-06-25T17:30:55.970+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.7ms","level":"info","span":"08539fb389748e1a","trace":"c9fb51ce83c0be9e6489261fc5a428ed"} +{"@timestamp":"2026-06-25T17:30:56.630+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:55262 - 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":"8083aeacc63cca50","trace":"d6eade15023e75f4b280079447013bd7"} +{"@timestamp":"2026-06-25T17:30:57.926+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/members/me - 127.0.0.1:55272 - 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":"aa89e894abd169f5","trace":"2d64a34f896f4e5151f42251dc070e42"} +{"@timestamp":"2026-06-25T17:30:57.929+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/members/me - 127.0.0.1:55274 - 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.5ms","level":"info","span":"1254fab35f182638","trace":"aebdfaaf3752c01fdc1976c3de194882"} +{"@timestamp":"2026-06-25T17:30:57.935+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas - 127.0.0.1:55275 - 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.5ms","level":"info","span":"6ec5292bee49323e","trace":"33a347e6566102f39f02984c2dea7813"} +{"@timestamp":"2026-06-25T17:30:57.936+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas/d539b02a-ea8e-4d6e-8ebb-3d94a1f31cb8 - 127.0.0.1:55278 - 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":"b2d34d812726e84a","trace":"eca7ad3c5e369376a172c8dfbff67e59"} +{"@timestamp":"2026-06-25T17:30:57.936+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas/d539b02a-ea8e-4d6e-8ebb-3d94a1f31cb8/copy-missions - 127.0.0.1:55277 - 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":"771c64fe2b4c0239","trace":"c3fa86ff681184f804898da637c9c45b"} +{"@timestamp":"2026-06-25T17:30:57.936+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:55279 - 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":"0a285bb192ade3a0","trace":"f91633612e1569268b5f436d1560f0d2"} +{"@timestamp":"2026-06-25T17:30:57.937+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/threads-accounts - 127.0.0.1:55280 - 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.9ms","level":"info","span":"0d25b78969ddb628","trace":"736282ea74cf471d142e8f1eca4126f7"} +{"@timestamp":"2026-06-25T17:30:57.939+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas - 127.0.0.1:55285 - 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.1ms","level":"info","span":"abb2e9e78a60b447","trace":"996f7cd66cfb407dea41143e80ebea55"} +{"@timestamp":"2026-06-25T17:30:57.940+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas/d539b02a-ea8e-4d6e-8ebb-3d94a1f31cb8/copy-missions - 127.0.0.1:55287 - 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":"cd8ce20de364a2ee","trace":"0dd73756ab2857d517af7c63d0a58d58"} +{"@timestamp":"2026-06-25T17:30:57.940+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas/d539b02a-ea8e-4d6e-8ebb-3d94a1f31cb8 - 127.0.0.1:55289 - 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.7ms","level":"info","span":"05e73404ad0815f6","trace":"a1d282ffeebb9512ec086b51c76ad0b9"} +{"@timestamp":"2026-06-25T17:30:57.941+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/threads-accounts - 127.0.0.1:55291 - 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.5ms","level":"info","span":"020f06a8d967d281","trace":"fce59e05edd3d9220068d4dc90d95516"} +{"@timestamp":"2026-06-25T17:30:57.941+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:55290 - 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":"7809c4b92f9f91c9","trace":"3d29ed43e55f3533f9d39ebc0f90ae31"} +{"@timestamp":"2026-06-25T17:30:57.943+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas - 127.0.0.1:55294 - 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.0ms","level":"info","span":"20688e0d89c3ddbe","trace":"7b00b63a2e64ae10aa0933913af5b251"} +{"@timestamp":"2026-06-25T17:30:57.944+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas - 127.0.0.1:55295 - 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":"0.8ms","level":"info","span":"91bb232409b87bb8","trace":"6b906e48b510477cd45ed33310c4d29c"} +{"@timestamp":"2026-06-25T17:30:57.946+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas - 127.0.0.1:55296 - 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":"0.8ms","level":"info","span":"ac581a6a34288c03","trace":"2a9b86259eac4d1347448f04e3c14f20"} +{"@timestamp":"2026-06-25T17:30:57.975+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/threads-accounts/a76918ed-69a7-4f45-8ea2-c984d8a85409/connection - 127.0.0.1:55298 - 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":"b82ee1ef135412db","trace":"c6a76cf7ea27a813a4a60d925d20742e"} +{"@timestamp":"2026-06-25T17:30:57.981+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/threads-accounts/a76918ed-69a7-4f45-8ea2-c984d8a85409/connection - 127.0.0.1:55300 - 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":"70c68dc7b205af38","trace":"dccb0fd715ebff61cbbaa992c3cb01a3"} +{"@timestamp":"2026-06-25T17:30:57.984+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/threads-accounts/a76918ed-69a7-4f45-8ea2-c984d8a85409/connection - 127.0.0.1:55302 - 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":"7ba4f89461bc3bc6","trace":"4071dc284ec847524b095aac1578e4b1"} +{"@timestamp":"2026-06-25T17:30:57.987+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas - 127.0.0.1:55304 - 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.3ms","level":"info","span":"8a5325643dfa5d22","trace":"c3b6fd6bfed495c3306b8e1ab40e810c"} +{"@timestamp":"2026-06-25T17:30:57.994+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/threads-accounts/a76918ed-69a7-4f45-8ea2-c984d8a85409/connection - 127.0.0.1:55306 - 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":"db3308545e2198d3","trace":"eca7614390d72379a2a20e00207d42e4"} +{"@timestamp":"2026-06-25T17:30:59.946+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:55309 - 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.2ms","level":"info","span":"3a12f6b9d681fed7","trace":"e3ac5f81f3a84be398d60bd89af93365"} +{"@timestamp":"2026-06-25T17:31:01.046+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2070.2ms)","duration":"2070.2ms","level":"slow","span":"11b5f89014e104e1","trace":"eda812904869cf93bb175f8431f19278"} +{"@timestamp":"2026-06-25T17:31:01.046+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2070.2ms","level":"info","span":"11b5f89014e104e1","trace":"eda812904869cf93bb175f8431f19278"} +{"@timestamp":"2026-06-25T17:31:01.942+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:55311 - 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":"cd14d0819298e6f5","trace":"fb41d4703fe398a0e93d2e36542596b7"} +{"@timestamp":"2026-06-25T17:31:02.451+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/members/me - 127.0.0.1:55319 - 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":"f590d82d383b8975","trace":"682f1840c98d1c7ce975fc7f009c638f"} +{"@timestamp":"2026-06-25T17:31:02.453+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/members/me - 127.0.0.1:55320 - 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.1ms","level":"info","span":"260d074990c5a8b6","trace":"3c7a7656ad9b70ae62fb4734a56112b9"} +{"@timestamp":"2026-06-25T17:31:02.459+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas - 127.0.0.1:55321 - 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.5ms","level":"info","span":"d6ccd931430440be","trace":"26a6a1551b1be8dffa08f16a2bc5a544"} +{"@timestamp":"2026-06-25T17:31:02.460+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas/d539b02a-ea8e-4d6e-8ebb-3d94a1f31cb8 - 127.0.0.1:55324 - 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":"299b095e60854722","trace":"2e19508624657ccca9e5f71fcc2fe1e7"} +{"@timestamp":"2026-06-25T17:31:02.460+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas/d539b02a-ea8e-4d6e-8ebb-3d94a1f31cb8/copy-missions - 127.0.0.1:55323 - 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":"eb4fe2790f17a9c6","trace":"8fcc1419576f5a783804f57878c34857"} +{"@timestamp":"2026-06-25T17:31:02.460+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/threads-accounts - 127.0.0.1:55326 - 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":"786557eaddeaf5d2","trace":"1f8eeda14829f13fe6498a94b02d10ee"} +{"@timestamp":"2026-06-25T17:31:02.461+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:55325 - 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":"def6394282635fc7","trace":"80b043112914c34d6f1442579ade5613"} +{"@timestamp":"2026-06-25T17:31:02.463+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas - 127.0.0.1:55333 - 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.1ms","level":"info","span":"c36b05b6840c3656","trace":"074245184bb2b94c9f45d3740650e441"} +{"@timestamp":"2026-06-25T17:31:02.463+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas/d539b02a-ea8e-4d6e-8ebb-3d94a1f31cb8 - 127.0.0.1:55334 - 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":"0.8ms","level":"info","span":"9ae2637e676f1999","trace":"7bbee3bc35d4387f67b7dcdced9ce6a6"} +{"@timestamp":"2026-06-25T17:31:02.464+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas/d539b02a-ea8e-4d6e-8ebb-3d94a1f31cb8/copy-missions - 127.0.0.1:55335 - 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.5ms","level":"info","span":"cd5599243bac82b5","trace":"85d6616e236bce9a9da9ac3f13d3e710"} +{"@timestamp":"2026-06-25T17:31:02.464+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:55336 - 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.7ms","level":"info","span":"d572a72809faad57","trace":"bf691da31b1c75406a4fcc15f4e121cc"} +{"@timestamp":"2026-06-25T17:31:02.465+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas - 127.0.0.1:55338 - 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":"0.6ms","level":"info","span":"7848a1a7397fbdea","trace":"0cda585155368929a9a43952c96a303f"} +{"@timestamp":"2026-06-25T17:31:02.465+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/threads-accounts - 127.0.0.1:55337 - 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":"86520d0e8cc8438e","trace":"797c4cbcacfed0981188100ecc5a1653"} +{"@timestamp":"2026-06-25T17:31:02.468+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas - 127.0.0.1:55340 - 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.1ms","level":"info","span":"5c1fbc2e0ab14685","trace":"8ed837a6e5953763948bd8cae9905301"} +{"@timestamp":"2026-06-25T17:31:02.471+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas - 127.0.0.1:55342 - 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":"a7860be5759e30e6","trace":"cc4cd12c25607433a490cef57515e18f"} +{"@timestamp":"2026-06-25T17:31:02.501+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/threads-accounts/a76918ed-69a7-4f45-8ea2-c984d8a85409/connection - 127.0.0.1:55344 - 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":"804076bea5a1f541","trace":"c04e6a00f0d3c68e5b9e94ba6803d136"} +{"@timestamp":"2026-06-25T17:31:02.508+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas - 127.0.0.1:55348 - 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":"68023896a9bf8545","trace":"6a068ddf94de0313ecfffd2c83ffca99"} +{"@timestamp":"2026-06-25T17:31:02.509+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/threads-accounts/a76918ed-69a7-4f45-8ea2-c984d8a85409/connection - 127.0.0.1:55346 - 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.0ms","level":"info","span":"0dc3c275a4672e17","trace":"51987cd7f911ce0a17caa223bb72577f"} +{"@timestamp":"2026-06-25T17:31:02.515+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/threads-accounts/a76918ed-69a7-4f45-8ea2-c984d8a85409/connection - 127.0.0.1:55350 - 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":"bf13884fa827bed1","trace":"8e4411e78f82926d11c26357d4f9789a"} +{"@timestamp":"2026-06-25T17:31:02.522+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/threads-accounts/a76918ed-69a7-4f45-8ea2-c984d8a85409/connection - 127.0.0.1:55352 - 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":"81a01a3eba4a754b","trace":"a7d10ab7dc922150c3a3fe82713cd664"} +{"@timestamp":"2026-06-25T17:31:04.468+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:55354 - 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":"3575c4ac7e2c1da7","trace":"50831760b2d840ce3e5083f6a0a4af37"} +{"@timestamp":"2026-06-25T17:31:05.701+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas - 127.0.0.1:55361 - 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":"2ca12b5ee6e1df92","trace":"a5003bcc30c894f9ac57d70d9fb87c5d"} +{"@timestamp":"2026-06-25T17:31:05.704+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/brands/ - 127.0.0.1:55360 - 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.2ms","level":"info","span":"06a14815c58e5951","trace":"4c2a945ce414d9c257aa3952e8603dac"} +{"@timestamp":"2026-06-25T17:31:05.709+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/brands/ - 127.0.0.1:55365 - 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.9ms","level":"info","span":"6cd5323379666c0d","trace":"cef5a557e47ba0a9090c155570507710"} +{"@timestamp":"2026-06-25T17:31:05.710+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/threads-accounts/a76918ed-69a7-4f45-8ea2-c984d8a85409/connection - 127.0.0.1:55363 - 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":"3398337a0bde5c87","trace":"0a3e7a18112c41f1ca97ab700d4962ab"} +{"@timestamp":"2026-06-25T17:31:05.715+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/placement/topics/ - 127.0.0.1:55359 - 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":"17.8ms","level":"info","span":"cee65f36538d0eff","trace":"927e60670d29f47b89148d1750e05ef8"} +{"@timestamp":"2026-06-25T17:31:05.723+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/placement/topics/ - 127.0.0.1:55367 - 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":"fddf9358b7b6bb30","trace":"a96c5f8debc97727f81d5b89a3d1f98c"} +{"@timestamp":"2026-06-25T17:31:06.110+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2060.1ms)","duration":"2060.1ms","level":"slow","span":"da165bfebe449f26","trace":"c54a81f0c5673c639a774b12db9b0b88"} +{"@timestamp":"2026-06-25T17:31:06.110+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2060.1ms","level":"info","span":"da165bfebe449f26","trace":"c54a81f0c5673c639a774b12db9b0b88"} +{"@timestamp":"2026-06-25T17:31:06.464+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:55380 - 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":"2c9273fe735bd60c","trace":"73ba788e5c133779ff7fee945c1ec01b"} +{"@timestamp":"2026-06-25T17:31:06.787+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/members/me - 127.0.0.1:55388 - 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.0ms","level":"info","span":"b28a988bad4fa8c5","trace":"b4d5b47524a770309ddd50a8f0f95991"} +{"@timestamp":"2026-06-25T17:31:06.790+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/members/me - 127.0.0.1:55389 - 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.7ms","level":"info","span":"056038562bd58aa6","trace":"a639448cb9ae36e53a9bac43c0328413"} +{"@timestamp":"2026-06-25T17:31:06.798+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/brands/ - 127.0.0.1:55392 - 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":"62901cf042a6c15a","trace":"a7e5e615be32313dc32518099fcccabd"} +{"@timestamp":"2026-06-25T17:31:06.799+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas - 127.0.0.1:55394 - 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":"9cd703d25683b9fc","trace":"2a4afca6c0c5d93ac953b1cc5b64e594"} +{"@timestamp":"2026-06-25T17:31:06.799+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:55393 - 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.0ms","level":"info","span":"305b9bbe16be35e9","trace":"f8f9be45343402570a145bfe11c13f2c"} +{"@timestamp":"2026-06-25T17:31:06.799+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/threads-accounts - 127.0.0.1:55395 - 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.9ms","level":"info","span":"89f18bfb6eebfdb0","trace":"bd4f44b3d6daedb7763c0696997fbad8"} +{"@timestamp":"2026-06-25T17:31:06.800+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/placement/topics/ - 127.0.0.1:55391 - 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":"88a87954dec11ce1","trace":"23f0a49a7258868c88f6586f92b31bf2"} +{"@timestamp":"2026-06-25T17:31:06.804+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/brands/ - 127.0.0.1:55402 - 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.9ms","level":"info","span":"2e3f33a921286e13","trace":"8891e84abef9bd8062f5cc28358a0f00"} +{"@timestamp":"2026-06-25T17:31:06.805+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas - 127.0.0.1:55403 - 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":"e7a0017f02cc6a8f","trace":"53d0cb527d7ae27ea85a8b9d7eadc6c7"} +{"@timestamp":"2026-06-25T17:31:06.807+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/placement/topics/ - 127.0.0.1:55406 - 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":"1e169418721e50fe","trace":"4691f8943301de04f39a19f058c492d4"} +{"@timestamp":"2026-06-25T17:31:06.807+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/threads-accounts - 127.0.0.1:55405 - 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":"fb87ead57d815315","trace":"113a2d9bcf5cb0910a9d1b10998133f3"} +{"@timestamp":"2026-06-25T17:31:06.807+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:55404 - 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":"a800b51bc9ccdd05","trace":"882c6acf043f066791ecb91d129f2c9a"} +{"@timestamp":"2026-06-25T17:31:06.810+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas - 127.0.0.1:55407 - 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":"a4b80e2720bb300c","trace":"f087b6816f30239a33c6c0930b2b4ec5"} +{"@timestamp":"2026-06-25T17:31:06.838+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/threads-accounts/a76918ed-69a7-4f45-8ea2-c984d8a85409/connection - 127.0.0.1:55409 - 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":"1f2009151093c23a","trace":"23feced1a871fd42af22c7ea21628b3b"} +{"@timestamp":"2026-06-25T17:31:06.838+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas - 127.0.0.1:55411 - 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.5ms","level":"info","span":"8f376760945ea213","trace":"f615ff626e75098105d969f8c64ba337"} +{"@timestamp":"2026-06-25T17:31:06.844+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/threads-accounts/a76918ed-69a7-4f45-8ea2-c984d8a85409/connection - 127.0.0.1:55413 - 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.6ms","level":"info","span":"cd4dccf233bb29d5","trace":"941f9f90bf3d44db6ad3a563727d50a9"} +{"@timestamp":"2026-06-25T17:31:06.851+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/threads-accounts/a76918ed-69a7-4f45-8ea2-c984d8a85409/connection - 127.0.0.1:55415 - 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":"d9a672ab54258f17","trace":"2b7177c3cad783a4304f23db9d291eef"} +{"@timestamp":"2026-06-25T17:31:06.855+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/threads-accounts/a76918ed-69a7-4f45-8ea2-c984d8a85409/connection - 127.0.0.1:55417 - 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":"33be1ab5c1770cc8","trace":"6db71228a0988ff7e507196a6cab0a26"} +{"@timestamp":"2026-06-25T17:31:08.801+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:55420 - 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.4ms","level":"info","span":"b05cf50f858dd528","trace":"df0163829a024e24c95f36e2746a76ac"} +{"@timestamp":"2026-06-25T17:31:09.479+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/auth/logout - 127.0.0.1:55422 - 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.6ms","level":"info","span":"6511056c48eb6664","trace":"cf3c0886fdf1265b3eecaa92634d266d"} +{"@timestamp":"2026-06-25T17:31:11.207+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2094.3ms)","duration":"2094.3ms","level":"slow","span":"c69ffc592e5e910b","trace":"c4f65c6e7add84a1ebc61cd242e25883"} +{"@timestamp":"2026-06-25T17:31:11.208+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2094.3ms","level":"info","span":"c69ffc592e5e910b","trace":"c4f65c6e7add84a1ebc61cd242e25883"} +{"@timestamp":"2026-06-25T17:31:16.292+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2078.8ms)","duration":"2078.8ms","level":"slow","span":"654f6d8d44cdceba","trace":"80abdae74376b55c046de09eec4eaaf3"} +{"@timestamp":"2026-06-25T17:31:16.292+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2078.8ms","level":"info","span":"654f6d8d44cdceba","trace":"80abdae74376b55c046de09eec4eaaf3"} +{"@timestamp":"2026-06-25T17:31:21.398+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2098.0ms)","duration":"2098.0ms","level":"slow","span":"04c66ed148f5aea8","trace":"a2254c41f02b5ea7387120779b02cfbc"} +{"@timestamp":"2026-06-25T17:31:21.398+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2098.0ms","level":"info","span":"04c66ed148f5aea8","trace":"a2254c41f02b5ea7387120779b02cfbc"} +{"@timestamp":"2026-06-25T17:31:26.484+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2079.5ms)","duration":"2079.5ms","level":"slow","span":"2c5bcf866c9a0807","trace":"31fa976dc0be92e5ca985a3b8f7e4d12"} +{"@timestamp":"2026-06-25T17:31:26.484+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2079.5ms","level":"info","span":"2c5bcf866c9a0807","trace":"31fa976dc0be92e5ca985a3b8f7e4d12"} +{"@timestamp":"2026-06-25T17:31:31.588+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2097.6ms)","duration":"2097.6ms","level":"slow","span":"a95aa5c0c8e39189","trace":"39a5e176b834d5c1f8b59f687344f12f"} +{"@timestamp":"2026-06-25T17:31:31.589+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2097.6ms","level":"info","span":"a95aa5c0c8e39189","trace":"39a5e176b834d5c1f8b59f687344f12f"} +{"@timestamp":"2026-06-25T17:31:36.695+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2099.9ms)","duration":"2099.9ms","level":"slow","span":"c45c88f25aa37028","trace":"4644b074b0e7eb5eb03b22b914b40d44"} +{"@timestamp":"2026-06-25T17:31:36.695+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2099.9ms","level":"info","span":"c45c88f25aa37028","trace":"4644b074b0e7eb5eb03b22b914b40d44"} +{"@timestamp":"2026-06-25T17:31:41.784+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2085.0ms)","duration":"2085.0ms","level":"slow","span":"ad09cf5e01b779fb","trace":"cdd16a4dac610045e5fea7b1c3ddef3d"} +{"@timestamp":"2026-06-25T17:31:41.785+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2085.0ms","level":"info","span":"ad09cf5e01b779fb","trace":"cdd16a4dac610045e5fea7b1c3ddef3d"} +{"@timestamp":"2026-06-25T17:31:42.550+08:00","caller":"stat/usage.go:82","content":"CPU: 0m, MEMORY: Alloc=3.4Mi, TotalAlloc=10.8Mi, Sys=22.8Mi, NumGC=8","level":"stat"} +{"@timestamp":"2026-06-25T17:31:42.582+08:00","caller":"load/sheddingstat.go:61","content":"(api) shedding_stat [1m], cpu: 0, total: 118, pass: 118, drop: 0","level":"stat"} +{"@timestamp":"2026-06-25T17:31:42.963+08:00","caller":"stat/metrics.go:210","content":"(haixun-backend) - qps: 2.0/s, drops: 0, avg time: 214.8ms, med: 2.6ms, 90th: 2044.6ms, 99th: 2099.8ms, 99.9th: 2099.8ms","level":"stat"} +{"@timestamp":"2026-06-25T17:31:46.867+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2077.7ms)","duration":"2077.7ms","level":"slow","span":"6ab8d4ee8a0087bf","trace":"3cac9f4fd18a0d0301c09bc511a4191e"} +{"@timestamp":"2026-06-25T17:31:46.867+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2077.7ms","level":"info","span":"6ab8d4ee8a0087bf","trace":"3cac9f4fd18a0d0301c09bc511a4191e"} +{"@timestamp":"2026-06-25T17:31:51.957+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2087.0ms)","duration":"2087.0ms","level":"slow","span":"3ce71abc1ab25c02","trace":"3fdab0afb5165445531bb339273d225f"} +{"@timestamp":"2026-06-25T17:31:51.957+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2087.0ms","level":"info","span":"3ce71abc1ab25c02","trace":"3fdab0afb5165445531bb339273d225f"} +{"@timestamp":"2026-06-25T17:31:57.052+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2091.3ms)","duration":"2091.3ms","level":"slow","span":"27627a4eac5a334f","trace":"57fa85ee4d69821697646a59d5a39f13"} +{"@timestamp":"2026-06-25T17:31:57.053+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2091.3ms","level":"info","span":"27627a4eac5a334f","trace":"57fa85ee4d69821697646a59d5a39f13"} +{"@timestamp":"2026-06-25T17:32:02.155+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2098.2ms)","duration":"2098.2ms","level":"slow","span":"f5780756d4a5c68c","trace":"b7f04f30dc65c57b0bf550e066bee273"} +{"@timestamp":"2026-06-25T17:32:02.156+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2098.2ms","level":"info","span":"f5780756d4a5c68c","trace":"b7f04f30dc65c57b0bf550e066bee273"} +{"@timestamp":"2026-06-25T17:32:07.225+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2066.1ms)","duration":"2066.1ms","level":"slow","span":"663c65e67c709bef","trace":"7de4c2867d569f88ad80c275d9772991"} +{"@timestamp":"2026-06-25T17:32:07.226+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2066.1ms","level":"info","span":"663c65e67c709bef","trace":"7de4c2867d569f88ad80c275d9772991"} +{"@timestamp":"2026-06-25T17:32:12.242+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":"0d7b4a7560c09816","trace":"e8bfe2682b4e73a4b43263aaf038da2c"} +{"@timestamp":"2026-06-25T17:32:12.242+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":"0d7b4a7560c09816","trace":"e8bfe2682b4e73a4b43263aaf038da2c"} +{"@timestamp":"2026-06-25T17:32:17.344+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2096.7ms)","duration":"2096.7ms","level":"slow","span":"ead4d4433e6a7b3b","trace":"a2fb614f0dd6d6dd23287e7c831cb5b9"} +{"@timestamp":"2026-06-25T17:32:17.344+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2096.7ms","level":"info","span":"ead4d4433e6a7b3b","trace":"a2fb614f0dd6d6dd23287e7c831cb5b9"} +{"@timestamp":"2026-06-25T17:32:22.429+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2080.3ms)","duration":"2080.3ms","level":"slow","span":"db06ee273e5a445d","trace":"09d612fbb44ab144261d8484c2be206e"} +{"@timestamp":"2026-06-25T17:32:22.429+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2080.3ms","level":"info","span":"db06ee273e5a445d","trace":"09d612fbb44ab144261d8484c2be206e"} +{"@timestamp":"2026-06-25T17:32:27.516+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2082.5ms)","duration":"2082.5ms","level":"slow","span":"12e7d0db835a4559","trace":"1683ffea836ab1cf3ccedede55f846ce"} +{"@timestamp":"2026-06-25T17:32:27.517+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2082.5ms","level":"info","span":"12e7d0db835a4559","trace":"1683ffea836ab1cf3ccedede55f846ce"} +{"@timestamp":"2026-06-25T17:32:32.529+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2008.3ms)","duration":"2008.3ms","level":"slow","span":"71633f5376ecfa93","trace":"c2114cba9ffe336425ff66da96f4c70d"} +{"@timestamp":"2026-06-25T17:32:32.529+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2008.3ms","level":"info","span":"71633f5376ecfa93","trace":"c2114cba9ffe336425ff66da96f4c70d"} +{"@timestamp":"2026-06-25T17:32:37.619+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2085.1ms)","duration":"2085.1ms","level":"slow","span":"ec573737c77d8639","trace":"e3d884c37a7c8bdb0eb7044a12d42399"} +{"@timestamp":"2026-06-25T17:32:37.619+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2085.1ms","level":"info","span":"ec573737c77d8639","trace":"e3d884c37a7c8bdb0eb7044a12d42399"} +{"@timestamp":"2026-06-25T17:32:42.551+08:00","caller":"stat/usage.go:82","content":"CPU: 0m, MEMORY: Alloc=3.6Mi, TotalAlloc=11.0Mi, Sys=22.8Mi, NumGC=8","level":"stat"} +{"@timestamp":"2026-06-25T17:32:42.582+08:00","caller":"load/sheddingstat.go:61","content":"(api) shedding_stat [1m], cpu: 0, total: 12, pass: 11, drop: 0","level":"stat"} +{"@timestamp":"2026-06-25T17:32:42.709+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2084.8ms)","duration":"2084.8ms","level":"slow","span":"6dd76653010f793b","trace":"c261af1cf03adfe1ff293788c26738f8"} +{"@timestamp":"2026-06-25T17:32:42.709+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2084.8ms","level":"info","span":"6dd76653010f793b","trace":"c261af1cf03adfe1ff293788c26738f8"} +{"@timestamp":"2026-06-25T17:32:42.964+08:00","caller":"stat/metrics.go:210","content":"(haixun-backend) - qps: 0.2/s, drops: 0, avg time: 2072.4ms, med: 2084.7ms, 90th: 2098.1ms, 99th: 2098.1ms, 99.9th: 2098.1ms","level":"stat"} +{"@timestamp":"2026-06-25T17:32:47.786+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2073.1ms)","duration":"2073.1ms","level":"slow","span":"c66a6d108d49e2ba","trace":"75cf868767690979b2a9b2752ba29e91"} +{"@timestamp":"2026-06-25T17:32:47.786+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2073.1ms","level":"info","span":"c66a6d108d49e2ba","trace":"75cf868767690979b2a9b2752ba29e91"} +{"@timestamp":"2026-06-25T17:32:52.890+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2100.5ms)","duration":"2100.5ms","level":"slow","span":"76d74728bd1fba11","trace":"5ff817f5c11917d5eebf4fadbcc52faa"} +{"@timestamp":"2026-06-25T17:32:52.890+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2100.5ms","level":"info","span":"76d74728bd1fba11","trace":"5ff817f5c11917d5eebf4fadbcc52faa"} +{"@timestamp":"2026-06-25T17:32:57.986+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2091.4ms)","duration":"2091.4ms","level":"slow","span":"ae40024330d59976","trace":"3bb8ad99579eaf978378032663ff25fe"} +{"@timestamp":"2026-06-25T17:32:57.986+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2091.4ms","level":"info","span":"ae40024330d59976","trace":"3bb8ad99579eaf978378032663ff25fe"} +{"@timestamp":"2026-06-25T17:33:03.021+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":"8f7609830dfbc72a","trace":"502c18fca2d055ca54d17fa40ba52a34"} +{"@timestamp":"2026-06-25T17:33:03.022+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":"8f7609830dfbc72a","trace":"502c18fca2d055ca54d17fa40ba52a34"} +{"@timestamp":"2026-06-25T17:33:08.029+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2003.4ms)","duration":"2003.4ms","level":"slow","span":"adcb55997fe2d556","trace":"6020e9b6ef12d81c8e62b85392ed6f60"} +{"@timestamp":"2026-06-25T17:33:08.029+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2003.4ms","level":"info","span":"adcb55997fe2d556","trace":"6020e9b6ef12d81c8e62b85392ed6f60"} +{"@timestamp":"2026-06-25T17:33:13.034+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2002.2ms)","duration":"2002.2ms","level":"slow","span":"a768e89de63fcfb2","trace":"5ca4e3870e866ac9a06c700523adedf4"} +{"@timestamp":"2026-06-25T17:33:13.034+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2002.2ms","level":"info","span":"a768e89de63fcfb2","trace":"5ca4e3870e866ac9a06c700523adedf4"} +{"@timestamp":"2026-06-25T17:33:18.092+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2054.1ms)","duration":"2054.1ms","level":"slow","span":"80d74a43a2d943e2","trace":"cc8b48dde391f96686e22641aae76364"} +{"@timestamp":"2026-06-25T17:33:18.092+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2054.1ms","level":"info","span":"80d74a43a2d943e2","trace":"cc8b48dde391f96686e22641aae76364"} +{"@timestamp":"2026-06-25T17:33:23.106+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.1ms)","duration":"2010.1ms","level":"slow","span":"1e6c0bad83977082","trace":"e20cf6f7eb7a72a0e1fd541c70ec5a3e"} +{"@timestamp":"2026-06-25T17:33:23.107+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.1ms","level":"info","span":"1e6c0bad83977082","trace":"e20cf6f7eb7a72a0e1fd541c70ec5a3e"} +{"@timestamp":"2026-06-25T17:33:28.166+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2054.8ms)","duration":"2054.8ms","level":"slow","span":"dd8e290d37298772","trace":"c65be74d49540b646ca4f2a28bdfa4ff"} +{"@timestamp":"2026-06-25T17:33:28.166+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2054.8ms","level":"info","span":"dd8e290d37298772","trace":"c65be74d49540b646ca4f2a28bdfa4ff"} +{"@timestamp":"2026-06-25T17:33:33.206+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2036.3ms)","duration":"2036.3ms","level":"slow","span":"a3b150258e687634","trace":"3cda63e235415958488ed77ad31ef94f"} +{"@timestamp":"2026-06-25T17:33:33.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":"2036.3ms","level":"info","span":"a3b150258e687634","trace":"3cda63e235415958488ed77ad31ef94f"} +{"@timestamp":"2026-06-25T17:33:38.220+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.6ms)","duration":"2009.6ms","level":"slow","span":"50b2976cce40ffca","trace":"c78f86732e57bf74dd28919535aba491"} +{"@timestamp":"2026-06-25T17:33:38.220+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.6ms","level":"info","span":"50b2976cce40ffca","trace":"c78f86732e57bf74dd28919535aba491"} +{"@timestamp":"2026-06-25T17:33:42.551+08:00","caller":"stat/usage.go:82","content":"CPU: 0m, MEMORY: Alloc=3.2Mi, TotalAlloc=11.8Mi, Sys=22.8Mi, NumGC=9","level":"stat"} +{"@timestamp":"2026-06-25T17:33:42.583+08:00","caller":"load/sheddingstat.go:61","content":"(api) shedding_stat [1m], cpu: 0, total: 12, pass: 12, drop: 0","level":"stat"} +{"@timestamp":"2026-06-25T17:33:42.964+08:00","caller":"stat/metrics.go:210","content":"(haixun-backend) - qps: 0.2/s, drops: 0, avg time: 2042.3ms, med: 2054.0ms, 90th: 2100.4ms, 99th: 2100.4ms, 99.9th: 2100.4ms","level":"stat"} +{"@timestamp":"2026-06-25T17:33:43.284+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2058.7ms)","duration":"2058.7ms","level":"slow","span":"d9af7376685e531b","trace":"8f8c36e4db268ccbc125037b980ca964"} +{"@timestamp":"2026-06-25T17:33:43.284+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2058.7ms","level":"info","span":"d9af7376685e531b","trace":"8f8c36e4db268ccbc125037b980ca964"} +{"@timestamp":"2026-06-25T17:33:48.319+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":"5286d37a79fc5cae","trace":"d0c98a2998a5b7a229bbbe6bf9572f0b"} +{"@timestamp":"2026-06-25T17:33:48.319+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":"5286d37a79fc5cae","trace":"d0c98a2998a5b7a229bbbe6bf9572f0b"} +{"@timestamp":"2026-06-25T17:33:53.331+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.5ms)","duration":"2009.5ms","level":"slow","span":"628998740afc0158","trace":"eed13a92127ae10bd5b1248314445789"} +{"@timestamp":"2026-06-25T17:33:53.332+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.5ms","level":"info","span":"628998740afc0158","trace":"eed13a92127ae10bd5b1248314445789"} +{"@timestamp":"2026-06-25T17:33:58.410+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2075.2ms)","duration":"2075.2ms","level":"slow","span":"6f069a1ef9563546","trace":"be08467974b3d47491178c99318c3c5e"} +{"@timestamp":"2026-06-25T17:33:58.411+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2075.2ms","level":"info","span":"6f069a1ef9563546","trace":"be08467974b3d47491178c99318c3c5e"} +{"@timestamp":"2026-06-25T17:34:03.446+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":"f592b115b639fd49","trace":"508156e8fe0fdc6a2fff56f68bc313f3"} +{"@timestamp":"2026-06-25T17:34:03.446+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":"f592b115b639fd49","trace":"508156e8fe0fdc6a2fff56f68bc313f3"} +{"@timestamp":"2026-06-25T17:34:08.498+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2048.2ms)","duration":"2048.2ms","level":"slow","span":"34eb427912169013","trace":"2a3caed7bf8273c9069e03bbb1aed951"} +{"@timestamp":"2026-06-25T17:34:08.499+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2048.2ms","level":"info","span":"34eb427912169013","trace":"2a3caed7bf8273c9069e03bbb1aed951"} +{"@timestamp":"2026-06-25T17:34:13.226+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/auth/login - 127.0.0.1:55487 - 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":"83.2ms","level":"info","span":"de0b846b0d0b03c4","trace":"c660d10bd6c44b171a644f0dc70b971a"} +{"@timestamp":"2026-06-25T17:34:13.236+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/members/me - 127.0.0.1:55489 - 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":"6c957466489aab06","trace":"1e8c4908971c451df4ee47cb9160791e"} +{"@timestamp":"2026-06-25T17:34:13.257+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/job/schedules?page=1&pageSize=100 - 127.0.0.1:55496 - 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":"bad2dcc4e3bfb2f8","trace":"2e36e44a4834890ebea0623ca074b016"} +{"@timestamp":"2026-06-25T17:34:13.257+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas - 127.0.0.1:55498 - 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":"4a1922242b423c1b","trace":"7637c5d70cd66c2a5697c04b812d2d24"} +{"@timestamp":"2026-06-25T17:34:13.258+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:55497 - 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.3ms","level":"info","span":"16aab11bd1b4c035","trace":"7c784b566db4eccfa21b9945240b6fc2"} +{"@timestamp":"2026-06-25T17:34:13.259+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=50 - 127.0.0.1:55495 - 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":"70c19f25af237fab","trace":"32c92af096f8607fdb05fbf9c5a7c960"} +{"@timestamp":"2026-06-25T17:34:13.259+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/threads-accounts - 127.0.0.1:55499 - 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.6ms","level":"info","span":"603161ac3d384acf","trace":"7a74b059192f4518f01e048714e357c5"} +{"@timestamp":"2026-06-25T17:34:13.261+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/job/schedules?page=1&pageSize=100 - 127.0.0.1:55504 - 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.1ms","level":"info","span":"177a9042c244ce1d","trace":"f743752bbb395d9e72700e8b6bb8ec64"} +{"@timestamp":"2026-06-25T17:34:13.261+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas - 127.0.0.1:55506 - 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":"693d09517c51272a","trace":"73ae172771c01b130d1f4b689ef92d6a"} +{"@timestamp":"2026-06-25T17:34:13.262+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/threads-accounts - 127.0.0.1:55509 - 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":"dc5a6a542b42a416","trace":"27eda16b5954803e005e706333a12fb6"} +{"@timestamp":"2026-06-25T17:34:13.262+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=50 - 127.0.0.1:55508 - 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.0ms","level":"info","span":"fa57482c8b72b6f9","trace":"8ec65300c6fa37e09447c2d8e37035d2"} +{"@timestamp":"2026-06-25T17:34:13.263+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas - 127.0.0.1:55511 - 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":"0.6ms","level":"info","span":"466f6152cdfb187c","trace":"2693043b7f13a296f0c6542e470098e8"} +{"@timestamp":"2026-06-25T17:34:13.263+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:55507 - 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.0ms","level":"info","span":"580d9d44dc13a16a","trace":"63db48c572bdcc3eeb9aa09b6590de09"} +{"@timestamp":"2026-06-25T17:34:13.280+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas - 127.0.0.1:55513 - 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.2ms","level":"info","span":"9276cb12d5979c5b","trace":"62455819c00df613fcc8f3f1a287de16"} +{"@timestamp":"2026-06-25T17:34:13.287+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:55515 - 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.7ms","level":"info","span":"973d3515d269fbff","trace":"c3c8d27a51dc0f9aaad906f67f10739d"} +{"@timestamp":"2026-06-25T17:34:13.538+08:00","caller":"handler/loghandler.go:151","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node - slowcall(2034.5ms)","duration":"2034.5ms","level":"slow","span":"b00c0db209e4a57c","trace":"e905c3b542cc69d904c58d440f2bdfbe"} +{"@timestamp":"2026-06-25T17:34:13.538+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - POST /api/v1/internal/workers/jobs/claim - 127.0.0.1:55162 - node","duration":"2034.5ms","level":"info","span":"b00c0db209e4a57c","trace":"e905c3b542cc69d904c58d440f2bdfbe"} +{"@timestamp":"2026-06-25T17:34:15.264+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:55517 - 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.6ms","level":"info","span":"15046a0c56108d0d","trace":"ed13c4023911ef36a7c3ab00b8eb601b"} +{"@timestamp":"2026-06-25T17:34:16.227+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas - 127.0.0.1:55523 - 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":"63bd15555e2ad4f6","trace":"20805b5a6a194019c762c96fdf77481c"} +{"@timestamp":"2026-06-25T17:34:16.230+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/brands/ - 127.0.0.1:55522 - 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":"193a63f058a5a407","trace":"0b3add8c84563b9d8a1cc76d7e923c75"} +{"@timestamp":"2026-06-25T17:34:16.236+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/brands/ - 127.0.0.1:55527 - 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.9ms","level":"info","span":"0d6f0e38bcc544dd","trace":"6e531fba3a04700c3d41dafb0d85de00"} +{"@timestamp":"2026-06-25T17:34:16.237+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:55526 - 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":"9ade351ba23e98b1","trace":"55f3065710880297345462d524e4d22a"} +{"@timestamp":"2026-06-25T17:34:16.243+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/placement/topics/ - 127.0.0.1:55521 - 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":"18.0ms","level":"info","span":"f159c0c225117347","trace":"0637774e9aedef60f5f35dc1b95991fb"} +{"@timestamp":"2026-06-25T17:34:16.250+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/placement/topics/ - 127.0.0.1:55529 - 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.6ms","level":"info","span":"1d41091e46c55919","trace":"bae55464a7aa3a4560a023e14bca3a99"} +{"@timestamp":"2026-06-25T17:34:17.022+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 404 - GET /api/v1/personas/d539b02a-ea8e-4d6e-8ebb-3d94a1f31cb8 - 127.0.0.1:55535 - 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.5ms","level":"info","span":"c9295ac3ec07382e","trace":"18a8409ade0c78bd800a280b03bc8bda"} +{"@timestamp":"2026-06-25T17:34:17.022+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas - 127.0.0.1:55533 - 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":"40baa4e6a7825420","trace":"215a3fefe6bd654286ac03130b39eee8"} +{"@timestamp":"2026-06-25T17:34:17.022+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 404 - GET /api/v1/personas/d539b02a-ea8e-4d6e-8ebb-3d94a1f31cb8/copy-missions - 127.0.0.1:55534 - 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":"89b56c79ceb6d6ca","trace":"ed516227a2db16352b36d3bfe686f560"} +{"@timestamp":"2026-06-25T17:34:17.026+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 404 - GET /api/v1/personas/d539b02a-ea8e-4d6e-8ebb-3d94a1f31cb8 - 127.0.0.1:55539 - 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.3ms","level":"info","span":"008a0d02d1227d76","trace":"2053d1fa6f5e0b35954fc3443a2136c5"} +{"@timestamp":"2026-06-25T17:34:17.027+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 404 - GET /api/v1/personas/d539b02a-ea8e-4d6e-8ebb-3d94a1f31cb8/copy-missions - 127.0.0.1:55541 - 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.3ms","level":"info","span":"7268f09956de0acf","trace":"4e62ffe935e050a7a0a95ffbf10f6d97"} +{"@timestamp":"2026-06-25T17:34:17.027+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas - 127.0.0.1:55540 - 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":"803b24364aedcfc6","trace":"ebdb018aec2678b6c74733974e030e5f"} +{"@timestamp":"2026-06-25T17:34:17.031+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas - 127.0.0.1:55543 - 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":"adeca56146be8aaf","trace":"8f1cc00206ee54dd00b6834049923778"} +{"@timestamp":"2026-06-25T17:34:17.034+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas/51f4d954-b152-4127-bfb9-e47bde06d1f8 - 127.0.0.1:55548 - 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.3ms","level":"info","span":"32049a924f5ae43d","trace":"504423223c86cf78724f872097d80d5a"} +{"@timestamp":"2026-06-25T17:34:17.035+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas - 127.0.0.1:55551 - 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":"0.9ms","level":"info","span":"55c16eef394ba771","trace":"6e9f7fd8bdab24c5a6ade3343550a2e6"} +{"@timestamp":"2026-06-25T17:34:17.037+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:55546 - 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":"231f87d8841e869c","trace":"5eb008225ced180fc32ce5e585876b6e"} +{"@timestamp":"2026-06-25T17:34:17.037+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/personas - 127.0.0.1:55553 - 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.1ms","level":"info","span":"58276bde115780a9","trace":"43b7b943cbc8fca865d4674204d21779"} +{"@timestamp":"2026-06-25T17:34:17.038+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:55550 - 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":"b4e924e714587574","trace":"fc5701e6ccaefe8f4e5d4f43395a79e4"} +{"@timestamp":"2026-06-25T17:34:17.042+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:55556 - 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":"e7df1756a3e8db26","trace":"a9e55bf54acd83a7f916e9c3b40c9d00"} +{"@timestamp":"2026-06-25T17:34:17.048+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:55558 - 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":"09ef1c864b6ca639","trace":"5bcfdf427812c467f8e1df7fd7ef459d"} +{"@timestamp":"2026-06-25T17:34:17.261+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:55560 - 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":"126f4dbdeec6f9ef","trace":"3d743f615986dcbe18559139486ef4b3"} +{"@timestamp":"2026-06-25T17:34:18.582+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.3ms)","duration":"2040.3ms","level":"slow","span":"2d9b54c5b0b86800","trace":"a0bb3d0b3f12e06400af2833e441739c"} +{"@timestamp":"2026-06-25T17:34:18.582+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.3ms","level":"info","span":"2d9b54c5b0b86800","trace":"a0bb3d0b3f12e06400af2833e441739c"} +{"@timestamp":"2026-06-25T17:34:20.207+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:55562 - 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":"c3b1673f60408246","trace":"4e2eaf72dd42b52b9613344d48ee9f5d"} +{"@timestamp":"2026-06-25T17:34:22.204+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:55564 - 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":"8d6b9a718d300d64","trace":"7966693ed54d53352b4e60acbb2f7df9"} +{"@timestamp":"2026-06-25T17:34:23.614+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.4ms)","duration":"2028.4ms","level":"slow","span":"7d9ad1fced3ee7ce","trace":"53f8a448da91d69a57eabef7359056f7"} +{"@timestamp":"2026-06-25T17:34:23.614+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.4ms","level":"info","span":"7d9ad1fced3ee7ce","trace":"53f8a448da91d69a57eabef7359056f7"} +{"@timestamp":"2026-06-25T17:34:24.203+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:55566 - 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.5ms","level":"info","span":"91d37732e5e293da","trace":"de4e83de346a4a7ca03ad1afc9b0d2f1"} +{"@timestamp":"2026-06-25T17:34:26.200+08:00","caller":"handler/loghandler.go:167","content":"[HTTP] 200 - GET /api/v1/jobs?page=1&pageSize=12 - 127.0.0.1:55568 - 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":"c1ce92f78e053dce","trace":"aef13880fae066abcbc1c0c4f8c6c720"} diff --git a/haixun-backend/.run/logs/web.log b/haixun-backend/.run/logs/web.log index b3777aa..4f15193 100644 --- a/haixun-backend/.run/logs/web.log +++ b/haixun-backend/.run/logs/web.log @@ -3,7 +3,7 @@ > vite - VITE v6.4.3 ready in 174 ms + VITE v6.4.3 ready in 130 ms ➜ Local: http://localhost:5173/ ➜ Network: use --host to expose diff --git a/haixun-backend/.run/logs/worker.log b/haixun-backend/.run/logs/worker.log index c39ed1d..88b799e 100644 --- a/haixun-backend/.run/logs/worker.log +++ b/haixun-backend/.run/logs/worker.log @@ -2,4 +2,4 @@ > haixun-master@0.1.0 worker:style-8d > . scripts/playwright-env.sh && npx playwright install chromium && tsx haixun-backend/worker/style-8d-worker.ts -[8d-worker] started id=local-style-8d-node-26520 api=http://127.0.0.1:8890 +[8d-worker] started id=local-style-8d-node-51650 api=http://127.0.0.1:8890 diff --git a/haixun-backend/.run/web.pid b/haixun-backend/.run/web.pid index e117843..5ad3fe9 100644 --- a/haixun-backend/.run/web.pid +++ b/haixun-backend/.run/web.pid @@ -1 +1 @@ -26440 +51572 diff --git a/haixun-backend/.run/worker.pid b/haixun-backend/.run/worker.pid index 273f917..88c636f 100644 --- a/haixun-backend/.run/worker.pid +++ b/haixun-backend/.run/worker.pid @@ -1 +1 @@ -26441 +51573 diff --git a/haixun-backend/generate/api/copy_mission.api b/haixun-backend/generate/api/copy_mission.api index e4646a2..1d1c492 100644 --- a/haixun-backend/generate/api/copy_mission.api +++ b/haixun-backend/generate/api/copy_mission.api @@ -9,11 +9,17 @@ type ( } CopySimilarAccountData { - Username string `json:"username"` - Reason string `json:"reason,omitempty"` - Source string `json:"source,omitempty"` - Confidence string `json:"confidence,omitempty"` - ProfileUrl string `json:"profile_url,omitempty"` + Username string `json:"username"` + Reason string `json:"reason,omitempty"` + Source string `json:"source,omitempty"` + Confidence string `json:"confidence,omitempty"` + ProfileUrl string `json:"profile_url,omitempty"` + AuthorVerified bool `json:"author_verified,omitempty"` + FollowerCount int `json:"follower_count,omitempty"` + EngagementScore int `json:"engagement_score,omitempty"` + LikeCount int `json:"like_count,omitempty"` + ReplyCount int `json:"reply_count,omitempty"` + PostCount int `json:"post_count,omitempty"` } CopyMissionResearchMapData { diff --git a/haixun-backend/generate/api/job.api b/haixun-backend/generate/api/job.api index a5fd4bd..7f30807 100644 --- a/haixun-backend/generate/api/job.api +++ b/haixun-backend/generate/api/job.api @@ -242,4 +242,7 @@ service gateway { @handler disableJobSchedule post /job/schedules/:id/disable (JobScheduleIDPath) returns (JobScheduleData) + + @handler deleteJobSchedule + delete /job/schedules/:id (JobScheduleIDPath) } \ No newline at end of file diff --git a/haixun-backend/generate/api/persona.api b/haixun-backend/generate/api/persona.api index 44a25ef..9958077 100644 --- a/haixun-backend/generate/api/persona.api +++ b/haixun-backend/generate/api/persona.api @@ -88,6 +88,8 @@ type ( SearchTag string `json:"search_tag"` Permalink string `json:"permalink"` Author string `json:"author"` + AuthorVerified bool `json:"author_verified,omitempty"` + FollowerCount int `json:"follower_count,omitempty"` Text string `json:"text"` LikeCount int `json:"like_count"` ReplyCount int `json:"reply_count"` diff --git a/haixun-backend/internal/handler/copy_mission/inspire_copy_mission_handler.go b/haixun-backend/internal/handler/copy_mission/inspire_copy_mission_handler.go index addb22e..7cd3524 100644 --- a/haixun-backend/internal/handler/copy_mission/inspire_copy_mission_handler.go +++ b/haixun-backend/internal/handler/copy_mission/inspire_copy_mission_handler.go @@ -27,4 +27,4 @@ func InspireCopyMissionHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { data, err := l.InspireCopyMission(&req) response.Write(r.Context(), w, data, err) } -} \ No newline at end of file +} diff --git a/haixun-backend/internal/handler/copy_mission/start_copy_mission_copy_draft_job_handler.go b/haixun-backend/internal/handler/copy_mission/start_copy_mission_copy_draft_job_handler.go index 266d08d..e76ce7f 100644 --- a/haixun-backend/internal/handler/copy_mission/start_copy_mission_copy_draft_job_handler.go +++ b/haixun-backend/internal/handler/copy_mission/start_copy_mission_copy_draft_job_handler.go @@ -27,4 +27,4 @@ func StartCopyMissionCopyDraftJobHandler(svcCtx *svc.ServiceContext) http.Handle data, err := l.StartCopyMissionCopyDraftJob(&req) response.Write(r.Context(), w, data, err) } -} \ No newline at end of file +} diff --git a/haixun-backend/internal/handler/copy_mission/start_copy_mission_matrix_job_handler.go b/haixun-backend/internal/handler/copy_mission/start_copy_mission_matrix_job_handler.go index 6b31493..b626e5c 100644 --- a/haixun-backend/internal/handler/copy_mission/start_copy_mission_matrix_job_handler.go +++ b/haixun-backend/internal/handler/copy_mission/start_copy_mission_matrix_job_handler.go @@ -27,4 +27,4 @@ func StartCopyMissionMatrixJobHandler(svcCtx *svc.ServiceContext) http.HandlerFu data, err := l.StartCopyMissionMatrixJob(&req) response.Write(r.Context(), w, data, err) } -} \ No newline at end of file +} diff --git a/haixun-backend/internal/handler/job/delete_job_schedule_handler.go b/haixun-backend/internal/handler/job/delete_job_schedule_handler.go new file mode 100644 index 0000000..ec571a2 --- /dev/null +++ b/haixun-backend/internal/handler/job/delete_job_schedule_handler.go @@ -0,0 +1,33 @@ +// Code scaffolded by goctl. Safe to edit. +// goctl 1.10.1 + +package job + +import ( + "net/http" + + "haixun-backend/internal/logic/job" + "haixun-backend/internal/response" + "haixun-backend/internal/svc" + "haixun-backend/internal/types" + + "github.com/zeromicro/go-zero/rest/httpx" +) + +func DeleteJobScheduleHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req types.JobScheduleIDPath + if err := httpx.Parse(r, &req); err != nil { + response.Write(r.Context(), w, nil, response.WrapRequestError(err)) + return + } + if err := svcCtx.Validator.ValidateAll(&req); err != nil { + response.Write(r.Context(), w, nil, response.WrapRequestError(err)) + return + } + + l := job.NewDeleteJobScheduleLogic(r.Context(), svcCtx) + err := l.DeleteJobSchedule(&req) + response.Write(r.Context(), w, nil, err) + } +} diff --git a/haixun-backend/internal/handler/routes.go b/haixun-backend/internal/handler/routes.go index 2313285..df1dd67 100644 --- a/haixun-backend/internal/handler/routes.go +++ b/haixun-backend/internal/handler/routes.go @@ -226,16 +226,16 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { rest.WithMiddlewares( []rest.Middleware{serverCtx.AuthJWT}, []rest.Route{ - { - Method: http.MethodGet, - Path: "/:personaId/copy-missions", - Handler: copy_mission.ListCopyMissionsHandler(serverCtx), - }, { Method: http.MethodPost, Path: "/:personaId/copy-mission-inspiration", Handler: copy_mission.InspireCopyMissionHandler(serverCtx), }, + { + Method: http.MethodGet, + Path: "/:personaId/copy-missions", + Handler: copy_mission.ListCopyMissionsHandler(serverCtx), + }, { Method: http.MethodPost, Path: "/:personaId/copy-missions", @@ -261,6 +261,11 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { Path: "/:personaId/copy-missions/:id/analyze-jobs", Handler: copy_mission.StartCopyMissionAnalyzeJobHandler(serverCtx), }, + { + Method: http.MethodPost, + Path: "/:personaId/copy-missions/:id/copy-draft-jobs", + Handler: copy_mission.StartCopyMissionCopyDraftJobHandler(serverCtx), + }, { Method: http.MethodGet, Path: "/:personaId/copy-missions/:id/copy-drafts", @@ -276,11 +281,6 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { Path: "/:personaId/copy-missions/:id/matrix-jobs", Handler: copy_mission.StartCopyMissionMatrixJobHandler(serverCtx), }, - { - Method: http.MethodPost, - Path: "/:personaId/copy-missions/:id/copy-draft-jobs", - Handler: copy_mission.StartCopyMissionCopyDraftJobHandler(serverCtx), - }, { Method: http.MethodPost, Path: "/:personaId/copy-missions/:id/scan-jobs", @@ -306,85 +306,6 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { rest.WithPrefix("/api/v1/personas"), ) - server.AddRoutes( - rest.WithMiddlewares( - []rest.Middleware{serverCtx.AuthJWT}, - []rest.Route{ - { - Method: http.MethodGet, - Path: "/job/schedules", - Handler: job.ListJobSchedulesHandler(serverCtx), - }, - { - Method: http.MethodPost, - Path: "/job/schedules", - Handler: job.CreateJobScheduleHandler(serverCtx), - }, - { - Method: http.MethodPut, - Path: "/job/schedules/:id", - Handler: job.UpdateJobScheduleHandler(serverCtx), - }, - { - Method: http.MethodPost, - Path: "/job/schedules/:id/disable", - Handler: job.DisableJobScheduleHandler(serverCtx), - }, - { - Method: http.MethodPost, - Path: "/job/schedules/:id/enable", - Handler: job.EnableJobScheduleHandler(serverCtx), - }, - { - Method: http.MethodGet, - Path: "/job/templates", - Handler: job.ListJobTemplatesHandler(serverCtx), - }, - { - Method: http.MethodGet, - Path: "/job/templates/:type", - Handler: job.GetJobTemplateHandler(serverCtx), - }, - { - Method: http.MethodPut, - Path: "/job/templates/:type", - Handler: job.UpsertJobTemplateHandler(serverCtx), - }, - { - Method: http.MethodPost, - Path: "/jobs", - Handler: job.CreateJobHandler(serverCtx), - }, - { - Method: http.MethodGet, - Path: "/jobs", - Handler: job.ListJobsHandler(serverCtx), - }, - { - Method: http.MethodGet, - Path: "/jobs/:id", - Handler: job.GetJobHandler(serverCtx), - }, - { - Method: http.MethodPost, - Path: "/jobs/:id/cancel", - Handler: job.CancelJobHandler(serverCtx), - }, - { - Method: http.MethodGet, - Path: "/jobs/:id/events", - Handler: job.ListJobEventsHandler(serverCtx), - }, - { - Method: http.MethodPost, - Path: "/jobs/:id/retry", - Handler: job.RetryJobHandler(serverCtx), - }, - }..., - ), - rest.WithPrefix("/api/v1"), - ) - server.AddRoutes( rest.WithMiddlewares( []rest.Middleware{serverCtx.WorkerSecret}, @@ -444,6 +365,90 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { rest.WithPrefix("/api/v1/internal"), ) + server.AddRoutes( + rest.WithMiddlewares( + []rest.Middleware{serverCtx.AuthJWT}, + []rest.Route{ + { + Method: http.MethodGet, + Path: "/job/schedules", + Handler: job.ListJobSchedulesHandler(serverCtx), + }, + { + Method: http.MethodPost, + Path: "/job/schedules", + Handler: job.CreateJobScheduleHandler(serverCtx), + }, + { + Method: http.MethodPut, + Path: "/job/schedules/:id", + Handler: job.UpdateJobScheduleHandler(serverCtx), + }, + { + Method: http.MethodDelete, + Path: "/job/schedules/:id", + Handler: job.DeleteJobScheduleHandler(serverCtx), + }, + { + Method: http.MethodPost, + Path: "/job/schedules/:id/disable", + Handler: job.DisableJobScheduleHandler(serverCtx), + }, + { + Method: http.MethodPost, + Path: "/job/schedules/:id/enable", + Handler: job.EnableJobScheduleHandler(serverCtx), + }, + { + Method: http.MethodGet, + Path: "/job/templates", + Handler: job.ListJobTemplatesHandler(serverCtx), + }, + { + Method: http.MethodGet, + Path: "/job/templates/:type", + Handler: job.GetJobTemplateHandler(serverCtx), + }, + { + Method: http.MethodPut, + Path: "/job/templates/:type", + Handler: job.UpsertJobTemplateHandler(serverCtx), + }, + { + Method: http.MethodPost, + Path: "/jobs", + Handler: job.CreateJobHandler(serverCtx), + }, + { + Method: http.MethodGet, + Path: "/jobs", + Handler: job.ListJobsHandler(serverCtx), + }, + { + Method: http.MethodGet, + Path: "/jobs/:id", + Handler: job.GetJobHandler(serverCtx), + }, + { + Method: http.MethodPost, + Path: "/jobs/:id/cancel", + Handler: job.CancelJobHandler(serverCtx), + }, + { + Method: http.MethodGet, + Path: "/jobs/:id/events", + Handler: job.ListJobEventsHandler(serverCtx), + }, + { + Method: http.MethodPost, + Path: "/jobs/:id/retry", + Handler: job.RetryJobHandler(serverCtx), + }, + }..., + ), + rest.WithPrefix("/api/v1"), + ) + server.AddRoutes( rest.WithMiddlewares( []rest.Middleware{serverCtx.AuthJWT}, diff --git a/haixun-backend/internal/library/matrix/samples.go b/haixun-backend/internal/library/matrix/samples.go index 2810d58..4d6b46d 100644 --- a/haixun-backend/internal/library/matrix/samples.go +++ b/haixun-backend/internal/library/matrix/samples.go @@ -52,4 +52,4 @@ func FormatViralSamples(posts []ViralPostSample) string { } } return strings.TrimSpace(b.String()) -} \ No newline at end of file +} diff --git a/haixun-backend/internal/library/permmatch/match.go b/haixun-backend/internal/library/permmatch/match.go new file mode 100644 index 0000000..2fe7075 --- /dev/null +++ b/haixun-backend/internal/library/permmatch/match.go @@ -0,0 +1,52 @@ +package permmatch + +import ( + "strings" +) + +// MethodAllowed reports whether method is covered by a pipe-separated methods string. +func MethodAllowed(methods, method string) bool { + method = strings.ToUpper(strings.TrimSpace(method)) + if method == "" { + return false + } + for _, item := range strings.Split(methods, "|") { + if strings.ToUpper(strings.TrimSpace(item)) == method { + return true + } + } + return false +} + +// PathAllowed reports whether requestPath matches pattern (exact or trailing * wildcard). +func PathAllowed(pattern, requestPath string) bool { + pattern = strings.TrimSpace(pattern) + requestPath = strings.TrimSpace(requestPath) + if pattern == "" || requestPath == "" { + return false + } + if pattern == requestPath { + return true + } + if strings.HasSuffix(pattern, "*") { + prefix := strings.TrimRight(strings.TrimSuffix(pattern, "*"), "/") + if requestPath == prefix { + return true + } + return strings.HasPrefix(requestPath, prefix+"/") + } + return false +} + +// RequestAllowed checks member permission map against an HTTP request. +func RequestAllowed(permissions map[string]string, method, requestPath string) bool { + if len(permissions) == 0 { + return false + } + for pattern, methods := range permissions { + if PathAllowed(pattern, requestPath) && MethodAllowed(methods, method) { + return true + } + } + return false +} diff --git a/haixun-backend/internal/library/permmatch/match_test.go b/haixun-backend/internal/library/permmatch/match_test.go new file mode 100644 index 0000000..64dbcbd --- /dev/null +++ b/haixun-backend/internal/library/permmatch/match_test.go @@ -0,0 +1,52 @@ +package permmatch + +import "testing" + +func TestMethodAllowed(t *testing.T) { + if !MethodAllowed("GET|POST", "get") { + t.Fatal("expected GET") + } + if MethodAllowed("GET|POST", "PATCH") { + t.Fatal("expected no PATCH") + } +} + +func TestPathAllowed(t *testing.T) { + if !PathAllowed("/api/v1/jobs/*", "/api/v1/jobs/abc/cancel") { + t.Fatal("expected wildcard match") + } + if PathAllowed("/api/v1/jobs/*", "/api/v1/job/schedules") { + t.Fatal("expected no match") + } + if !PathAllowed("/api/v1/members/me", "/api/v1/members/me") { + t.Fatal("expected exact match") + } +} + +func TestRequestAllowed(t *testing.T) { + perms := map[string]string{ + "/api/v1/members/me": "GET|PATCH", + "/api/v1/jobs/*": "GET|POST", + } + if !RequestAllowed(perms, "GET", "/api/v1/members/me") { + t.Fatal("expected member me") + } + if RequestAllowed(perms, "DELETE", "/api/v1/members/me") { + t.Fatal("expected deny delete") + } + if !RequestAllowed(perms, "POST", "/api/v1/jobs/x/cancel") { + t.Fatal("expected job cancel") + } +} + +func TestPathAllowedListRoot(t *testing.T) { + for _, path := range []string{"/api/v1/personas", "/api/v1/jobs", "/api/v1/brands"} { + pattern := path + "/*" + if !PathAllowed(pattern, path) { + t.Fatalf("expected list root match for %s", path) + } + } + if PathAllowed("/api/v1/jobs/*", "/api/v1/job/schedules") { + t.Fatal("expected schedules path to stay blocked") + } +} diff --git a/haixun-backend/internal/library/placement/crawler_exec.go b/haixun-backend/internal/library/placement/crawler_exec.go index 40e7c7c..c74a630 100644 --- a/haixun-backend/internal/library/placement/crawler_exec.go +++ b/haixun-backend/internal/library/placement/crawler_exec.go @@ -22,14 +22,14 @@ type execCrawlerInput struct { } type execCrawlerPost struct { - Text string `json:"text"` - Permalink string `json:"permalink"` - ExternalID string `json:"externalId"` - AuthorName string `json:"authorName"` - LikeCount int `json:"likeCount"` - ReplyCount int `json:"replyCount"` - AuthorVerified bool `json:"authorVerified"` - FollowerCount int `json:"followerCount"` + Text string `json:"text"` + Permalink string `json:"permalink"` + ExternalID string `json:"externalId"` + AuthorName string `json:"authorName"` + LikeCount int `json:"likeCount"` + ReplyCount int `json:"replyCount"` + AuthorVerified bool `json:"authorVerified"` + FollowerCount int `json:"followerCount"` } type execCrawlerOutput struct { diff --git a/haixun-backend/internal/library/placement/discover.go b/haixun-backend/internal/library/placement/discover.go index 349aae4..d317506 100644 --- a/haixun-backend/internal/library/placement/discover.go +++ b/haixun-backend/internal/library/placement/discover.go @@ -106,4 +106,4 @@ func Discover(ctx context.Context, req DiscoverRequest) ([]DiscoverPost, Discove } return nil, "", fmt.Errorf("目前搜尋來源模式無可用管道:%s", m.SearchSourceMode) -} \ No newline at end of file +} diff --git a/haixun-backend/internal/library/placement/discover_route.go b/haixun-backend/internal/library/placement/discover_route.go index 5c69783..cbd9846 100644 --- a/haixun-backend/internal/library/placement/discover_route.go +++ b/haixun-backend/internal/library/placement/discover_route.go @@ -110,4 +110,4 @@ func runCrawlerDiscover(ctx context.Context, req DiscoverRequest) ([]DiscoverPos return nil, fmt.Errorf("crawler keyword is empty") } return req.Crawler(ctx, req.Member, keyword, req.Limit) -} \ No newline at end of file +} diff --git a/haixun-backend/internal/library/placement/discover_route_test.go b/haixun-backend/internal/library/placement/discover_route_test.go index b58b64d..1f801ec 100644 --- a/haixun-backend/internal/library/placement/discover_route_test.go +++ b/haixun-backend/internal/library/placement/discover_route_test.go @@ -4,11 +4,11 @@ import "testing" func TestShouldTryCrawlerFirst_mixedWithBrowser(t *testing.T) { m := MemberContext{ - AllowsCrawler: true, - BrowserConnected: true, - SearchSourceMode: SearchSourceMixed, - AllowsThreadsAPI: true, - ApiConnected: true, + AllowsCrawler: true, + BrowserConnected: true, + SearchSourceMode: SearchSourceMixed, + AllowsThreadsAPI: true, + ApiConnected: true, } if !ShouldTryCrawlerFirst(m) { t.Fatal("mixed + browser should try crawler first to save API") @@ -38,4 +38,4 @@ func TestBuildMemberContextFormalModeKeepsCrawlerMode(t *testing.T) { if !ctx.AllowsThreadsAPI { t.Fatal("threads_crawler should still allow API fallback") } -} \ No newline at end of file +} diff --git a/haixun-backend/internal/library/placement/effective_strategy.go b/haixun-backend/internal/library/placement/effective_strategy.go index 0951a1d..7c7795c 100644 --- a/haixun-backend/internal/library/placement/effective_strategy.go +++ b/haixun-backend/internal/library/placement/effective_strategy.go @@ -13,4 +13,4 @@ func EffectiveExpandStrategy(research ResearchSettings) libkg.ExpandStrategy { func WebSearchAvailable(research ResearchSettings) bool { return !MissingWebSearchKey(research) -} \ No newline at end of file +} diff --git a/haixun-backend/internal/library/placement/effective_strategy_test.go b/haixun-backend/internal/library/placement/effective_strategy_test.go index 92588ad..228950a 100644 --- a/haixun-backend/internal/library/placement/effective_strategy_test.go +++ b/haixun-backend/internal/library/placement/effective_strategy_test.go @@ -35,4 +35,4 @@ func TestWebSearchAvailable(t *testing.T) { if !WebSearchAvailable(ResearchSettings{WebSearchProvider: "brave", BraveAPIKey: "k"}) { t.Fatal("expected available with brave key") } -} \ No newline at end of file +} diff --git a/haixun-backend/internal/library/style8d/prompt.go b/haixun-backend/internal/library/style8d/prompt.go index ae45f6e..473af7e 100644 --- a/haixun-backend/internal/library/style8d/prompt.go +++ b/haixun-backend/internal/library/style8d/prompt.go @@ -101,4 +101,4 @@ func ResolvePersonaBlock(personaText, styleProfileJSON, brief string) string { parts = append(parts, block) } return strings.TrimSpace(strings.Join(parts, "\n\n")) -} \ No newline at end of file +} diff --git a/haixun-backend/internal/library/style8d/prompt_test.go b/haixun-backend/internal/library/style8d/prompt_test.go index 7c09b10..fe48104 100644 --- a/haixun-backend/internal/library/style8d/prompt_test.go +++ b/haixun-backend/internal/library/style8d/prompt_test.go @@ -23,4 +23,4 @@ func TestHasReady8DFromAnalysisOnly(t *testing.T) { if !HasReady8D("", raw) { t.Fatal("expected ready from analysis summary") } -} \ No newline at end of file +} diff --git a/haixun-backend/internal/library/threadspost/limits.go b/haixun-backend/internal/library/threadspost/limits.go index 60e85db..24e11cb 100644 --- a/haixun-backend/internal/library/threadspost/limits.go +++ b/haixun-backend/internal/library/threadspost/limits.go @@ -21,12 +21,12 @@ const ViralSoftWarn = 300 type LengthBand string const ( - BandEmpty LengthBand = "empty" - BandTooShort LengthBand = "too_short" - BandSweet LengthBand = "sweet" - BandLong LengthBand = "long" - BandOverSoft LengthBand = "over_soft" - BandOverHard LengthBand = "over_hard" + BandEmpty LengthBand = "empty" + BandTooShort LengthBand = "too_short" + BandSweet LengthBand = "sweet" + BandLong LengthBand = "long" + BandOverSoft LengthBand = "over_soft" + BandOverHard LengthBand = "over_hard" ) func RuneLen(text string) int { @@ -97,4 +97,4 @@ func PublishHint(text string) string { default: return "" } -} \ No newline at end of file +} diff --git a/haixun-backend/internal/library/threadspost/limits_test.go b/haixun-backend/internal/library/threadspost/limits_test.go index df4166f..629b98e 100644 --- a/haixun-backend/internal/library/threadspost/limits_test.go +++ b/haixun-backend/internal/library/threadspost/limits_test.go @@ -46,4 +46,4 @@ func TestPublishBand(t *testing.T) { t.Fatalf("len %d: got %s want %s", c.len, got, c.want) } } -} \ No newline at end of file +} diff --git a/haixun-backend/internal/library/viral/discover.go b/haixun-backend/internal/library/viral/discover.go index b7e08df..3bdea2d 100644 --- a/haixun-backend/internal/library/viral/discover.go +++ b/haixun-backend/internal/library/viral/discover.go @@ -230,4 +230,4 @@ func sortByEngagement(items []placement.ScanCandidate) { } } } -} \ No newline at end of file +} diff --git a/haixun-backend/internal/library/viral/discover_budget_test.go b/haixun-backend/internal/library/viral/discover_budget_test.go index b88372b..0755cad 100644 --- a/haixun-backend/internal/library/viral/discover_budget_test.go +++ b/haixun-backend/internal/library/viral/discover_budget_test.go @@ -14,4 +14,4 @@ func TestCountMissionQuality(t *testing.T) { if got := countMissionQuality(merged); got != 1 { t.Fatalf("expected 1 quality post, got %d", got) } -} \ No newline at end of file +} diff --git a/haixun-backend/internal/library/viral/discover_graceful_test.go b/haixun-backend/internal/library/viral/discover_graceful_test.go index c94a577..ff840d5 100644 --- a/haixun-backend/internal/library/viral/discover_graceful_test.go +++ b/haixun-backend/internal/library/viral/discover_graceful_test.go @@ -69,4 +69,4 @@ func TestRunDiscover_missionRelaxedFallbackWithoutVerified(t *testing.T) { if out[0].AuthorVerified { t.Fatal("verified should remain false when API omits it") } -} \ No newline at end of file +} diff --git a/haixun-backend/internal/library/viral/flexible_json.go b/haixun-backend/internal/library/viral/flexible_json.go index 6b237cf..78d5d74 100644 --- a/haixun-backend/internal/library/viral/flexible_json.go +++ b/haixun-backend/internal/library/viral/flexible_json.go @@ -138,4 +138,4 @@ func pickRawMessage(root map[string]json.RawMessage, keys ...string) json.RawMes } } return nil -} \ No newline at end of file +} diff --git a/haixun-backend/internal/library/viral/mission_inspire.go b/haixun-backend/internal/library/viral/mission_inspire.go index c08e4d0..9d52d91 100644 --- a/haixun-backend/internal/library/viral/mission_inspire.go +++ b/haixun-backend/internal/library/viral/mission_inspire.go @@ -7,19 +7,19 @@ import ( ) type MissionInspireInput struct { - PersonaDisplayName string - PersonaBrief string - PersonaBlock string - StyleBenchmark string - PersonaAudience string - PersonaContentGoal string - PersonaQuestions []string - PersonaPillars []string - RecentMissionLabels []string - RecentSeedQueries []string - TrendSnippets []MissionInspireTrendSnippet - WebSearchProvider string - LLMOnly bool + PersonaDisplayName string + PersonaBrief string + PersonaBlock string + StyleBenchmark string + PersonaAudience string + PersonaContentGoal string + PersonaQuestions []string + PersonaPillars []string + RecentMissionLabels []string + RecentSeedQueries []string + TrendSnippets []MissionInspireTrendSnippet + WebSearchProvider string + LLMOnly bool } type MissionInspireTrendSnippet struct { @@ -30,11 +30,11 @@ type MissionInspireTrendSnippet struct { } type MissionInspireOutput struct { - Label string - SeedQuery string - Brief string - TrendReason string - TrendKeywords []string + Label string + SeedQuery string + Brief string + TrendReason string + TrendKeywords []string } func BuildMissionInspireSystemPrompt() string { @@ -175,4 +175,4 @@ func ParseMissionInspireOutput(raw string) (MissionInspireOutput, error) { } } return out, nil -} \ No newline at end of file +} diff --git a/haixun-backend/internal/library/viral/mission_inspire_collect.go b/haixun-backend/internal/library/viral/mission_inspire_collect.go index 7e53380..c22fdc1 100644 --- a/haixun-backend/internal/library/viral/mission_inspire_collect.go +++ b/haixun-backend/internal/library/viral/mission_inspire_collect.go @@ -63,4 +63,4 @@ func CollectMissionInspireTrends( } } return out -} \ No newline at end of file +} diff --git a/haixun-backend/internal/library/viral/mission_inspire_test.go b/haixun-backend/internal/library/viral/mission_inspire_test.go index df7f884..0f7e880 100644 --- a/haixun-backend/internal/library/viral/mission_inspire_test.go +++ b/haixun-backend/internal/library/viral/mission_inspire_test.go @@ -22,8 +22,8 @@ func TestParseMissionInspireOutput(t *testing.T) { func TestBuildMissionInspireUserPromptLLMOnly(t *testing.T) { prompt := BuildMissionInspireUserPrompt(MissionInspireInput{ PersonaDisplayName: "測試人設", - PersonaBrief: "職場焦慮", - LLMOnly: true, + PersonaBrief: "職場焦慮", + LLMOnly: true, }) if !strings.Contains(prompt, "未設定 Web Search API key") { t.Fatalf("expected llm-only hint in prompt: %s", prompt) @@ -31,4 +31,4 @@ func TestBuildMissionInspireUserPromptLLMOnly(t *testing.T) { if !strings.Contains(prompt, "無外部趨勢結果") { t.Fatalf("expected empty trend fallback in prompt: %s", prompt) } -} \ No newline at end of file +} diff --git a/haixun-backend/internal/library/viral/mission_research_map_test.go b/haixun-backend/internal/library/viral/mission_research_map_test.go index adfd63c..e6ea412 100644 --- a/haixun-backend/internal/library/viral/mission_research_map_test.go +++ b/haixun-backend/internal/library/viral/mission_research_map_test.go @@ -52,4 +52,4 @@ func TestParseMissionResearchMapOutput_StringSuggestedTags(t *testing.T) { if len(out.SuggestedTags) != 4 { t.Fatalf("expected 4 tags, got %#v", out.SuggestedTags) } -} \ No newline at end of file +} diff --git a/haixun-backend/internal/library/viral/quality.go b/haixun-backend/internal/library/viral/quality.go index bd924db..fdc8902 100644 --- a/haixun-backend/internal/library/viral/quality.go +++ b/haixun-backend/internal/library/viral/quality.go @@ -4,10 +4,10 @@ import "haixun-backend/internal/library/placement" // Mission-quality gates for copy-mission viral patrol (stricter than generic patrol). const ( - MissionQualityMinLikes = 18 - MissionQualityMinEngagement = 50 - MissionVerifiedMinLikes = 10 - MissionVerifiedMinEngagement = 35 + MissionQualityMinLikes = 18 + MissionQualityMinEngagement = 50 + MissionVerifiedMinLikes = 10 + MissionVerifiedMinEngagement = 35 MissionInfluencerMinFollowers = 5000 ) @@ -36,4 +36,4 @@ func MergeAuthorSignals(prev, next placement.ScanCandidate) placement.ScanCandid prev.FollowerCount = next.FollowerCount } return prev -} \ No newline at end of file +} diff --git a/haixun-backend/internal/library/viral/quality_test.go b/haixun-backend/internal/library/viral/quality_test.go index 9de7efb..353a188 100644 --- a/haixun-backend/internal/library/viral/quality_test.go +++ b/haixun-backend/internal/library/viral/quality_test.go @@ -15,4 +15,4 @@ func TestPassesMissionQualityCandidate_unverifiedStricter(t *testing.T) { if !PassesMissionQualityCandidate("轉職面試技巧分享心得", 25, 4, 65, false, 0, nil) { t.Fatal("unverified author should pass with strong engagement") } -} \ No newline at end of file +} diff --git a/haixun-backend/internal/library/viral/reference_accounts.go b/haixun-backend/internal/library/viral/reference_accounts.go index ce47cd9..f71a743 100644 --- a/haixun-backend/internal/library/viral/reference_accounts.go +++ b/haixun-backend/internal/library/viral/reference_accounts.go @@ -198,4 +198,4 @@ func formatReferenceReason(item referenceAuthorAgg) string { return fmt.Sprintf("標籤「%s」高互動作者", item.sampleSearchTag) } return "本次海巡高互動作者" -} \ No newline at end of file +} diff --git a/haixun-backend/internal/library/viral/reference_accounts_fallback_test.go b/haixun-backend/internal/library/viral/reference_accounts_fallback_test.go index e80f1ee..194dfc2 100644 --- a/haixun-backend/internal/library/viral/reference_accounts_fallback_test.go +++ b/haixun-backend/internal/library/viral/reference_accounts_fallback_test.go @@ -21,4 +21,4 @@ func TestBuildReferenceAccountsFromScan_relaxedFallback(t *testing.T) { if got[0].AuthorVerified { t.Fatal("verified must stay false when not provided") } -} \ No newline at end of file +} diff --git a/haixun-backend/internal/library/viral/reference_accounts_test.go b/haixun-backend/internal/library/viral/reference_accounts_test.go index 321881a..9c15abc 100644 --- a/haixun-backend/internal/library/viral/reference_accounts_test.go +++ b/haixun-backend/internal/library/viral/reference_accounts_test.go @@ -55,4 +55,4 @@ func TestBuildReferenceAccountsFromScan_requiresTopicMatch(t *testing.T) { if len(got) != 0 { t.Fatalf("expected no accounts for off-topic post, got %d", len(got)) } -} \ No newline at end of file +} diff --git a/haixun-backend/internal/library/viral/research_map.go b/haixun-backend/internal/library/viral/research_map.go index f102f5c..4d51156 100644 --- a/haixun-backend/internal/library/viral/research_map.go +++ b/haixun-backend/internal/library/viral/research_map.go @@ -18,15 +18,15 @@ type CopyResearchMap struct { } type CopyResearchMapInput struct { - Label string - SeedQuery string - Brief string - Persona string - StyleBenchmark string + Label string + SeedQuery string + Brief string + Persona string + StyleBenchmark string PersonaAudienceSummary string - PersonaContentGoal string - PersonaQuestions []string - PersonaPillars []string + PersonaContentGoal string + PersonaQuestions []string + PersonaPillars []string } var copyMapFenceRE = regexp.MustCompile("(?s)```(?:json)?\\s*([\\s\\S]*?)```") diff --git a/haixun-backend/internal/logic/authz/admin.go b/haixun-backend/internal/logic/authz/admin.go new file mode 100644 index 0000000..82e4c0f --- /dev/null +++ b/haixun-backend/internal/logic/authz/admin.go @@ -0,0 +1,27 @@ +package authz + +import ( + "context" + + "haixun-backend/internal/library/authctx" + app "haixun-backend/internal/library/errors" + "haixun-backend/internal/library/errors/code" + "haixun-backend/internal/svc" +) + +func RequireAdmin(ctx context.Context, svcCtx *svc.ServiceContext) error { + actor, ok := authctx.ActorFromContext(ctx) + if !ok { + return app.For(code.Auth).AuthUnauthorized("missing actor") + } + member, err := svcCtx.Member.GetByUID(ctx, actor.TenantID, actor.UID) + if err != nil { + return err + } + for _, role := range member.Roles { + if role == "admin" { + return nil + } + } + return app.For(code.Auth).AuthForbidden("admin role required") +} diff --git a/haixun-backend/internal/logic/copy_mission/inspire_copy_mission_logic.go b/haixun-backend/internal/logic/copy_mission/inspire_copy_mission_logic.go index 14c4576..7222a19 100644 --- a/haixun-backend/internal/logic/copy_mission/inspire_copy_mission_logic.go +++ b/haixun-backend/internal/logic/copy_mission/inspire_copy_mission_logic.go @@ -151,4 +151,4 @@ func (l *InspireCopyMissionLogic) InspireCopyMission(req *types.PersonaCopyMissi WebSearchUsed: webSearchUsed, Message: message, }, nil -} \ No newline at end of file +} diff --git a/haixun-backend/internal/logic/copy_mission/start_copy_mission_copy_draft_job_logic.go b/haixun-backend/internal/logic/copy_mission/start_copy_mission_copy_draft_job_logic.go index a37266a..30112a3 100644 --- a/haixun-backend/internal/logic/copy_mission/start_copy_mission_copy_draft_job_logic.go +++ b/haixun-backend/internal/logic/copy_mission/start_copy_mission_copy_draft_job_logic.go @@ -74,4 +74,4 @@ func (l *StartCopyMissionCopyDraftJobLogic) StartCopyMissionCopyDraftJob( Status: string(run.Status), Message: "深仿寫已在背景執行", }, nil -} \ No newline at end of file +} diff --git a/haixun-backend/internal/logic/copy_mission/start_copy_mission_matrix_job_logic.go b/haixun-backend/internal/logic/copy_mission/start_copy_mission_matrix_job_logic.go index 6abd97b..d54dbc1 100644 --- a/haixun-backend/internal/logic/copy_mission/start_copy_mission_matrix_job_logic.go +++ b/haixun-backend/internal/logic/copy_mission/start_copy_mission_matrix_job_logic.go @@ -61,8 +61,8 @@ func (l *StartCopyMissionMatrixJobLogic) StartCopyMissionMatrixJob( payload := map[string]any{ "persona_id": personaID, - "copy_mission_id": missionID, - "count": count, + "copy_mission_id": missionID, + "count": count, } run, err := l.svcCtx.Job.CreateRun(l.ctx, jobdom.CreateRunRequest{ TemplateType: "generate-copy-matrix", @@ -80,4 +80,4 @@ func (l *StartCopyMissionMatrixJobLogic) StartCopyMissionMatrixJob( Status: string(run.Status), Message: "內容矩陣產出已在背景執行", }, nil -} \ No newline at end of file +} diff --git a/haixun-backend/internal/logic/job/actor.go b/haixun-backend/internal/logic/job/actor.go index 7921116..8c04841 100644 --- a/haixun-backend/internal/logic/job/actor.go +++ b/haixun-backend/internal/logic/job/actor.go @@ -6,6 +6,8 @@ import ( "haixun-backend/internal/library/authctx" app "haixun-backend/internal/library/errors" "haixun-backend/internal/library/errors/code" + "haixun-backend/internal/logic/authz" + "haixun-backend/internal/svc" ) func actorFrom(ctx context.Context) (tenantID, uid string, err error) { @@ -15,3 +17,7 @@ func actorFrom(ctx context.Context) (tenantID, uid string, err error) { } return actor.TenantID, actor.UID, nil } + +func requireAdmin(ctx context.Context, svcCtx *svc.ServiceContext) error { + return authz.RequireAdmin(ctx, svcCtx) +} diff --git a/haixun-backend/internal/logic/job/delete_job_schedule_logic.go b/haixun-backend/internal/logic/job/delete_job_schedule_logic.go new file mode 100644 index 0000000..6b904aa --- /dev/null +++ b/haixun-backend/internal/logic/job/delete_job_schedule_logic.go @@ -0,0 +1,31 @@ +// Code scaffolded by goctl. Safe to edit. +// goctl 1.10.1 + +package job + +import ( + "context" + + "haixun-backend/internal/svc" + "haixun-backend/internal/types" + + "github.com/zeromicro/go-zero/core/logx" +) + +type DeleteJobScheduleLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +func NewDeleteJobScheduleLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteJobScheduleLogic { + return &DeleteJobScheduleLogic{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +func (l *DeleteJobScheduleLogic) DeleteJobSchedule(req *types.JobScheduleIDPath) error { + return l.svcCtx.Job.DeleteSchedule(l.ctx, req.ID) +} diff --git a/haixun-backend/internal/logic/job/get_job_template_logic.go b/haixun-backend/internal/logic/job/get_job_template_logic.go index dff3cff..107f81d 100644 --- a/haixun-backend/internal/logic/job/get_job_template_logic.go +++ b/haixun-backend/internal/logic/job/get_job_template_logic.go @@ -17,6 +17,9 @@ func NewGetJobTemplateLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Ge } func (l *GetJobTemplateLogic) GetJobTemplate(req *types.JobTemplatePath) (*types.JobTemplateData, error) { + if err := requireAdmin(l.ctx, l.svcCtx); err != nil { + return nil, err + } template, err := l.svcCtx.Job.GetTemplate(l.ctx, req.Type) if err != nil { return nil, err diff --git a/haixun-backend/internal/logic/job/list_job_templates_logic.go b/haixun-backend/internal/logic/job/list_job_templates_logic.go index 443571e..a50d67a 100644 --- a/haixun-backend/internal/logic/job/list_job_templates_logic.go +++ b/haixun-backend/internal/logic/job/list_job_templates_logic.go @@ -17,6 +17,9 @@ func NewListJobTemplatesLogic(ctx context.Context, svcCtx *svc.ServiceContext) * } func (l *ListJobTemplatesLogic) ListJobTemplates() (*types.JobTemplateListData, error) { + if err := requireAdmin(l.ctx, l.svcCtx); err != nil { + return nil, err + } templates, err := l.svcCtx.Job.ListTemplates(l.ctx) if err != nil { return nil, err diff --git a/haixun-backend/internal/logic/job/upsert_job_template_logic.go b/haixun-backend/internal/logic/job/upsert_job_template_logic.go index 771527c..46a46a6 100644 --- a/haixun-backend/internal/logic/job/upsert_job_template_logic.go +++ b/haixun-backend/internal/logic/job/upsert_job_template_logic.go @@ -19,6 +19,9 @@ func NewUpsertJobTemplateLogic(ctx context.Context, svcCtx *svc.ServiceContext) } func (l *UpsertJobTemplateLogic) UpsertJobTemplate(req *types.UpsertJobTemplateReq) (*types.JobTemplateData, error) { + if err := requireAdmin(l.ctx, l.svcCtx); err != nil { + return nil, err + } template, err := l.svcCtx.Job.UpsertTemplate(l.ctx, domusecase.UpsertTemplateRequest{ Type: req.Type, Version: req.Version, diff --git a/haixun-backend/internal/logic/permission/get_permission_catalog_logic.go b/haixun-backend/internal/logic/permission/get_permission_catalog_logic.go index 553575f..0a1fd60 100644 --- a/haixun-backend/internal/logic/permission/get_permission_catalog_logic.go +++ b/haixun-backend/internal/logic/permission/get_permission_catalog_logic.go @@ -3,6 +3,7 @@ package permission import ( "context" + "haixun-backend/internal/logic/authz" domusecase "haixun-backend/internal/model/permission/domain/usecase" "haixun-backend/internal/svc" "haixun-backend/internal/types" @@ -18,6 +19,9 @@ func NewGetPermissionCatalogLogic(ctx context.Context, svcCtx *svc.ServiceContex } func (l *GetPermissionCatalogLogic) GetPermissionCatalog(req *types.PermissionCatalogQuery) (*types.PermissionCatalogData, error) { + if err := authz.RequireAdmin(l.ctx, l.svcCtx); err != nil { + return nil, err + } tree, list, err := l.svcCtx.Permission.Catalog(l.ctx, domusecase.CatalogRequest{ Status: req.Status, Type: req.Type, diff --git a/haixun-backend/internal/middleware/permissionrbac_middleware.go b/haixun-backend/internal/middleware/permissionrbac_middleware.go new file mode 100644 index 0000000..565655a --- /dev/null +++ b/haixun-backend/internal/middleware/permissionrbac_middleware.go @@ -0,0 +1,59 @@ +package middleware + +import ( + "net/http" + + "haixun-backend/internal/library/authctx" + app "haixun-backend/internal/library/errors" + "haixun-backend/internal/library/errors/code" + "haixun-backend/internal/library/permmatch" + memberdomain "haixun-backend/internal/model/member/domain/usecase" + permissiondomain "haixun-backend/internal/model/permission/domain/usecase" + "haixun-backend/internal/response" +) + +// PermissionRBACMiddleware enforces catalog permissions for authenticated members. +// Mount after AuthJWT or MemberAuth so actor is present in context. +type PermissionRBACMiddleware struct { + members memberdomain.UseCase + permissions permissiondomain.UseCase +} + +func NewPermissionRBACMiddleware( + members memberdomain.UseCase, + permissions permissiondomain.UseCase, +) *PermissionRBACMiddleware { + return &PermissionRBACMiddleware{members: members, permissions: permissions} +} + +func (m *PermissionRBACMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + actor, ok := authctx.ActorFromContext(r.Context()) + if !ok { + response.Write(r.Context(), w, nil, app.For(code.Auth).AuthUnauthorized("missing actor")) + return + } + if m.members == nil || m.permissions == nil { + response.Write(r.Context(), w, nil, app.For(code.Permission).SysNotImplemented("permission rbac is not configured")) + return + } + + member, err := m.members.GetByUID(r.Context(), actor.TenantID, actor.UID) + if err != nil { + response.Write(r.Context(), w, nil, err) + return + } + + me, err := m.permissions.Me(r.Context(), member, false) + if err != nil { + response.Write(r.Context(), w, nil, err) + return + } + if !permmatch.RequestAllowed(me.Permissions, r.Method, r.URL.Path) { + response.Write(r.Context(), w, nil, app.For(code.Permission).AuthForbidden("permission denied")) + return + } + + next(w, r) + } +} diff --git a/haixun-backend/internal/model/copy_mission/domain/usecase/usecase.go b/haixun-backend/internal/model/copy_mission/domain/usecase/usecase.go index 42370b6..7db6646 100644 --- a/haixun-backend/internal/model/copy_mission/domain/usecase/usecase.go +++ b/haixun-backend/internal/model/copy_mission/domain/usecase/usecase.go @@ -53,13 +53,13 @@ type MissionSummary struct { } type CreateRequest struct { - TenantID string - OwnerUID string - PersonaID string - Label string - SeedQuery string - Brief string - InitialResearchMap *entity.ResearchMap + TenantID string + OwnerUID string + PersonaID string + Label string + SeedQuery string + Brief string + InitialResearchMap *entity.ResearchMap InitialSelectedTags []string } diff --git a/haixun-backend/internal/model/job/domain/repository/schedule.go b/haixun-backend/internal/model/job/domain/repository/schedule.go index 9e26ced..16fd000 100644 --- a/haixun-backend/internal/model/job/domain/repository/schedule.go +++ b/haixun-backend/internal/model/job/domain/repository/schedule.go @@ -16,6 +16,7 @@ type ScheduleRepository interface { Create(ctx context.Context, schedule *entity.Schedule) (*entity.Schedule, error) Update(ctx context.Context, schedule *entity.Schedule) (*entity.Schedule, error) FindByID(ctx context.Context, id string) (*entity.Schedule, error) + Delete(ctx context.Context, id string) error List(ctx context.Context, filter ScheduleListFilter, offset, limit int64) ([]*entity.Schedule, int64, error) FindDue(ctx context.Context, now int64, limit int64) ([]*entity.Schedule, error) } diff --git a/haixun-backend/internal/model/job/domain/usecase/job.go b/haixun-backend/internal/model/job/domain/usecase/job.go index 4c3ebca..6be688b 100644 --- a/haixun-backend/internal/model/job/domain/usecase/job.go +++ b/haixun-backend/internal/model/job/domain/usecase/job.go @@ -120,6 +120,7 @@ type UseCase interface { UpdateSchedule(ctx context.Context, req UpdateScheduleRequest) (*entity.Schedule, error) EnableSchedule(ctx context.Context, scheduleID string) (*entity.Schedule, error) DisableSchedule(ctx context.Context, scheduleID string) (*entity.Schedule, error) + DeleteSchedule(ctx context.Context, scheduleID string) error RunSchedulerTick(ctx context.Context, holder string) (int, error) RunMaintenance(ctx context.Context) (MaintenanceResult, error) diff --git a/haixun-backend/internal/model/job/repository/mongo_schedule.go b/haixun-backend/internal/model/job/repository/mongo_schedule.go index 51e2a09..26b5575 100644 --- a/haixun-backend/internal/model/job/repository/mongo_schedule.go +++ b/haixun-backend/internal/model/job/repository/mongo_schedule.go @@ -72,6 +72,24 @@ func (r *mongoScheduleRepository) Update(ctx context.Context, schedule *entity.S return &out, nil } +func (r *mongoScheduleRepository) Delete(ctx context.Context, id string) error { + if r.collection == nil { + return app.For(code.Job).DBUnavailable("Mongo is not configured") + } + objectID, err := primitive.ObjectIDFromHex(id) + if err != nil { + return app.For(code.Job).InputInvalidFormat("invalid schedule id") + } + res, err := r.collection.DeleteOne(ctx, bson.M{"_id": objectID}) + if err != nil { + return err + } + if res.DeletedCount == 0 { + return app.For(code.Job).ResNotFound("job schedule not found") + } + return nil +} + func (r *mongoScheduleRepository) FindByID(ctx context.Context, id string) (*entity.Schedule, error) { if r.collection == nil { return nil, app.For(code.Job).DBUnavailable("Mongo is not configured") diff --git a/haixun-backend/internal/model/job/usecase/schedule.go b/haixun-backend/internal/model/job/usecase/schedule.go index 151a42f..902a535 100644 --- a/haixun-backend/internal/model/job/usecase/schedule.go +++ b/haixun-backend/internal/model/job/usecase/schedule.go @@ -125,6 +125,16 @@ func (u *jobUseCase) DisableSchedule(ctx context.Context, scheduleID string) (*e return u.schedules.Update(ctx, schedule) } +func (u *jobUseCase) DeleteSchedule(ctx context.Context, scheduleID string) error { + if strings.TrimSpace(scheduleID) == "" { + return app.For(code.Job).InputMissingRequired("schedule id is required") + } + if _, err := u.schedules.FindByID(ctx, scheduleID); err != nil { + return err + } + return u.schedules.Delete(ctx, scheduleID) +} + func (u *jobUseCase) RunSchedulerTick(ctx context.Context, holder string) (int, error) { ok, err := u.queue.TrySchedulerLock(ctx, holder, 55) if err != nil { diff --git a/haixun-backend/internal/model/job/usecase/test_mocks.go b/haixun-backend/internal/model/job/usecase/test_mocks.go index 9c7e253..e9fc2e1 100644 --- a/haixun-backend/internal/model/job/usecase/test_mocks.go +++ b/haixun-backend/internal/model/job/usecase/test_mocks.go @@ -217,6 +217,21 @@ func (m *memoryScheduleRepo) FindByID(_ context.Context, id string) (*entity.Sch } return nil, nil } +func (m *memoryScheduleRepo) Delete(_ context.Context, id string) error { + m.mu.Lock() + defer m.mu.Unlock() + objectID, err := primitive.ObjectIDFromHex(id) + if err != nil { + return err + } + for i, item := range m.schedules { + if item.ID == objectID { + m.schedules = append(m.schedules[:i], m.schedules[i+1:]...) + return nil + } + } + return nil +} func (m *memoryScheduleRepo) List(_ context.Context, _ domrepo.ScheduleListFilter, _, _ int64) ([]*entity.Schedule, int64, error) { m.mu.Lock() defer m.mu.Unlock() diff --git a/haixun-backend/internal/model/permission/usecase/usecase.go b/haixun-backend/internal/model/permission/usecase/usecase.go index 162777c..2cddd26 100644 --- a/haixun-backend/internal/model/permission/usecase/usecase.go +++ b/haixun-backend/internal/model/permission/usecase/usecase.go @@ -3,6 +3,7 @@ package usecase import ( "context" "sort" + "strings" memberentity "haixun-backend/internal/model/member/domain/entity" "haixun-backend/internal/model/permission/domain/entity" @@ -111,9 +112,14 @@ func (u *permissionUseCase) Me(ctx context.Context, member *memberentity.Member, Permissions: map[string]string{}, } for _, node := range nodes { - if node.HTTPPath != "" && node.HTTPMethods != "" { - result.Permissions[node.HTTPPath] = node.HTTPMethods + if node.HTTPPath == "" || node.HTTPMethods == "" { + continue } + if existing, ok := result.Permissions[node.HTTPPath]; ok { + result.Permissions[node.HTTPPath] = mergeHTTPMethods(existing, node.HTTPMethods) + continue + } + result.Permissions[node.HTTPPath] = node.HTTPMethods } if includeTree { result.Tree = buildTree(nodes) @@ -121,6 +127,25 @@ func (u *permissionUseCase) Me(ctx context.Context, member *memberentity.Member, return result, nil } +func mergeHTTPMethods(existing, next string) string { + seen := map[string]struct{}{} + out := make([]string, 0, 8) + for _, block := range []string{existing, next} { + for _, item := range strings.Split(block, "|") { + method := strings.ToUpper(strings.TrimSpace(item)) + if method == "" { + continue + } + if _, ok := seen[method]; ok { + continue + } + seen[method] = struct{}{} + out = append(out, method) + } + } + return strings.Join(out, "|") +} + func hasRole(roles []string, role string) bool { for _, item := range roles { if item == role { @@ -146,10 +171,20 @@ func filterPermissionsByName(items []*entity.Permission, names []string) []*enti func defaultUserPermissionNames() []string { return []string{ + "auth.logout", "member.me.read", "member.me.update", + "member.placement_settings.read", + "member.placement_settings.update", "permission.me.read", + "setting.manage", "ai.use", + "brand.manage", + "persona.manage", + "placement_topic.manage", + "threads_account.manage", + "job.manage", + "job.schedule.manage", } } @@ -196,14 +231,28 @@ func buildTree(items []domusecase.PermissionNode) []domusecase.PermissionNode { } func defaultPermissions() []*entity.Permission { + allMethods := "GET|POST|PUT|PATCH|DELETE" return []*entity.Permission{ + {Name: "auth.logout", HTTPMethods: "POST", HTTPPath: "/api/v1/auth/logout", Status: entity.StatusOpen, Type: entity.TypeFrontendUser}, + {Name: "member.me.read", HTTPMethods: "GET", HTTPPath: "/api/v1/members/me", Status: entity.StatusOpen, Type: entity.TypeFrontendUser}, {Name: "member.me.update", HTTPMethods: "PATCH", HTTPPath: "/api/v1/members/me", Status: entity.StatusOpen, Type: entity.TypeFrontendUser}, + {Name: "member.placement_settings.read", HTTPMethods: "GET", HTTPPath: "/api/v1/members/me/placement-settings", Status: entity.StatusOpen, Type: entity.TypeFrontendUser}, + {Name: "member.placement_settings.update", HTTPMethods: "PATCH", HTTPPath: "/api/v1/members/me/placement-settings", Status: entity.StatusOpen, Type: entity.TypeFrontendUser}, + {Name: "permission.catalog.read", HTTPMethods: "GET", HTTPPath: "/api/v1/permissions/catalog", Status: entity.StatusOpen, Type: entity.TypeBackendUser}, {Name: "permission.me.read", HTTPMethods: "GET", HTTPPath: "/api/v1/permissions/me", Status: entity.StatusOpen, Type: entity.TypeFrontendUser}, + {Name: "setting.manage", HTTPMethods: "GET|PUT|DELETE", HTTPPath: "/api/v1/settings/*", Status: entity.StatusOpen, Type: entity.TypeBackendUser}, {Name: "ai.use", HTTPMethods: "GET|POST", HTTPPath: "/api/v1/ai/*", Status: entity.StatusOpen, Type: entity.TypeFrontendUser}, - {Name: "job.manage", HTTPMethods: "GET|POST|PUT", HTTPPath: "/api/v1/jobs/*", Status: entity.StatusOpen, Type: entity.TypeBackendUser}, + + {Name: "brand.manage", HTTPMethods: allMethods, HTTPPath: "/api/v1/brands/*", Status: entity.StatusOpen, Type: entity.TypeBackendUser}, + {Name: "persona.manage", HTTPMethods: allMethods, HTTPPath: "/api/v1/personas/*", Status: entity.StatusOpen, Type: entity.TypeBackendUser}, + {Name: "placement_topic.manage", HTTPMethods: allMethods, HTTPPath: "/api/v1/placement/topics/*", Status: entity.StatusOpen, Type: entity.TypeBackendUser}, + {Name: "threads_account.manage", HTTPMethods: "GET|POST|PUT|PATCH", HTTPPath: "/api/v1/threads-accounts/*", Status: entity.StatusOpen, Type: entity.TypeBackendUser}, + + {Name: "job.manage", HTTPMethods: "GET|POST", HTTPPath: "/api/v1/jobs/*", Status: entity.StatusOpen, Type: entity.TypeBackendUser}, + {Name: "job.schedule.manage", HTTPMethods: "GET|POST|PUT|DELETE", HTTPPath: "/api/v1/job/schedules/*", Status: entity.StatusOpen, Type: entity.TypeBackendUser}, {Name: "job.template.manage", HTTPMethods: "GET|PUT", HTTPPath: "/api/v1/job/templates/*", Status: entity.StatusOpen, Type: entity.TypeBackendUser}, } } diff --git a/haixun-backend/internal/model/permission/usecase/usecase_test.go b/haixun-backend/internal/model/permission/usecase/usecase_test.go new file mode 100644 index 0000000..ca9e019 --- /dev/null +++ b/haixun-backend/internal/model/permission/usecase/usecase_test.go @@ -0,0 +1,14 @@ +package usecase + +import "testing" + +func TestMergeHTTPMethods(t *testing.T) { + got := mergeHTTPMethods("GET", "PATCH") + if got != "GET|PATCH" { + t.Fatalf("got %q", got) + } + got = mergeHTTPMethods("GET|POST", "POST|PATCH") + if got != "GET|POST|PATCH" { + t.Fatalf("got %q", got) + } +} \ No newline at end of file diff --git a/haixun-backend/internal/svc/service_context.go b/haixun-backend/internal/svc/service_context.go index 734f094..b63e021 100644 --- a/haixun-backend/internal/svc/service_context.go +++ b/haixun-backend/internal/svc/service_context.go @@ -135,6 +135,9 @@ func NewServiceContext(c config.Config) *ServiceContext { if err := permissionUseCase.EnsureDefaultPermissions(ctx); err != nil { panic(err) } + if err := permissionUseCase.EnsureDefaultRolePermissions(ctx, "default"); err != nil { + panic(err) + } jobTemplateRepository := jobrepo.NewMongoTemplateRepository(mongoClient.Database()) jobRunRepository := jobrepo.NewMongoRunRepository(mongoClient.Database()) diff --git a/haixun-backend/internal/types/types.go b/haixun-backend/internal/types/types.go index 1369cd0..47c7f66 100644 --- a/haixun-backend/internal/types/types.go +++ b/haixun-backend/internal/types/types.go @@ -230,18 +230,6 @@ type CopyMissionData struct { UpdateAt int64 `json:"update_at"` } -type CopyMissionPath struct { - PersonaID string `path:"personaId" validate:"required"` - ID string `path:"id" validate:"required"` -} - -type CopyMissionInspirationSourceData struct { - Query string `json:"query,omitempty"` - Title string `json:"title,omitempty"` - Snippet string `json:"snippet,omitempty"` - URL string `json:"url,omitempty"` -} - type CopyMissionInspirationData struct { Label string `json:"label"` SeedQuery string `json:"seed_query"` @@ -253,6 +241,18 @@ type CopyMissionInspirationData struct { Message string `json:"message"` } +type CopyMissionInspirationSourceData struct { + Query string `json:"query,omitempty"` + Title string `json:"title,omitempty"` + Snippet string `json:"snippet,omitempty"` + URL string `json:"url,omitempty"` +} + +type CopyMissionPath struct { + PersonaID string `path:"personaId" validate:"required"` + ID string `path:"id" validate:"required"` +} + type CopyMissionResearchMapData struct { AudienceSummary string `json:"audience_summary,omitempty"` ContentGoal string `json:"content_goal,omitempty"` @@ -1084,19 +1084,19 @@ type StartCopyMissionAnalyzeJobData struct { Message string `json:"message"` } -type StartCopyMissionScanJobData struct { +type StartCopyMissionCopyDraftJobData struct { JobID string `json:"job_id"` Status string `json:"status"` Message string `json:"message"` } -type StartCopyMissionMatrixJobReq struct { - Count int `json:"count,optional"` +type StartCopyMissionCopyDraftJobHandlerReq struct { + CopyMissionPath + StartCopyMissionCopyDraftJobReq } -type StartCopyMissionMatrixJobHandlerReq struct { - CopyMissionPath - StartCopyMissionMatrixJobReq +type StartCopyMissionCopyDraftJobReq struct { + ScanPostID string `json:"scan_post_id" validate:"required"` } type StartCopyMissionMatrixJobData struct { @@ -1105,16 +1105,16 @@ type StartCopyMissionMatrixJobData struct { Message string `json:"message"` } -type StartCopyMissionCopyDraftJobReq struct { - ScanPostID string `json:"scan_post_id" validate:"required"` -} - -type StartCopyMissionCopyDraftJobHandlerReq struct { +type StartCopyMissionMatrixJobHandlerReq struct { CopyMissionPath - StartCopyMissionCopyDraftJobReq + StartCopyMissionMatrixJobReq } -type StartCopyMissionCopyDraftJobData struct { +type StartCopyMissionMatrixJobReq struct { + Count int `json:"count,optional"` +} + +type StartCopyMissionScanJobData struct { JobID string `json:"job_id"` Status string `json:"status"` Message string `json:"message"` diff --git a/haixun-backend/internal/worker/job/generate_copy_draft.go b/haixun-backend/internal/worker/job/generate_copy_draft.go index 4a1fc53..29c7ea3 100644 --- a/haixun-backend/internal/worker/job/generate_copy_draft.go +++ b/haixun-backend/internal/worker/job/generate_copy_draft.go @@ -207,4 +207,4 @@ func runGenerateCopyDraft(ctx context.Context, step StepContext, deps GenerateCo }, }) return err -} \ No newline at end of file +} diff --git a/haixun-backend/internal/worker/job/generate_copy_matrix.go b/haixun-backend/internal/worker/job/generate_copy_matrix.go index 412da56..ea37f20 100644 --- a/haixun-backend/internal/worker/job/generate_copy_matrix.go +++ b/haixun-backend/internal/worker/job/generate_copy_matrix.go @@ -243,4 +243,4 @@ func intField(payload map[string]any, key string) int { default: return 0 } -} \ No newline at end of file +} diff --git a/haixun-backend/internal/worker/job/scan_viral.go b/haixun-backend/internal/worker/job/scan_viral.go index 0b1d95c..e55840d 100644 --- a/haixun-backend/internal/worker/job/scan_viral.go +++ b/haixun-backend/internal/worker/job/scan_viral.go @@ -11,8 +11,8 @@ import ( libviral "haixun-backend/internal/library/viral" domai "haixun-backend/internal/model/ai/domain/usecase" aiusecase "haixun-backend/internal/model/ai/usecase" - missionentity "haixun-backend/internal/model/copy_mission/domain/entity" copydraftusecase "haixun-backend/internal/model/copy_draft/domain/usecase" + missionentity "haixun-backend/internal/model/copy_mission/domain/entity" missiondomain "haixun-backend/internal/model/copy_mission/domain/usecase" jobdom "haixun-backend/internal/model/job/domain/usecase" personaentity "haixun-backend/internal/model/persona/domain/entity" diff --git a/haixun-backend/web/src/App.tsx b/haixun-backend/web/src/App.tsx index 1c73c58..27a392e 100644 --- a/haixun-backend/web/src/App.tsx +++ b/haixun-backend/web/src/App.tsx @@ -1,15 +1,16 @@ import { BrowserRouter, Navigate, Route, Routes } from 'react-router-dom' import { AuthProvider } from './auth/AuthContext' import { ThemeProvider } from './theme/ThemeContext' +import { AdminRoute } from './components/AdminRoute' import { Layout } from './components/Layout' import { ProtectedRoute } from './components/ProtectedRoute' -import { AiPage } from './pages/AiPage' import { DashboardPage } from './pages/DashboardPage' import { JobDetailPage } from './pages/JobDetailPage' import { JobSchedulesPage } from './pages/JobSchedulesPage' import { JobTemplatesPage } from './pages/JobTemplatesPage' import { JobsPage } from './pages/JobsPage' import { LoginPage } from './pages/LoginPage' +import { EasterEggsPage } from './pages/EasterEggsPage' import { PermissionsPage } from './pages/PermissionsPage' import { LegacyBrandRouteRedirect } from './components/LegacyBrandRouteRedirect' import { BrandDetailPage } from './pages/BrandDetailPage' @@ -59,12 +60,14 @@ export default function App() { } /> } /> } /> - } /> } /> - } /> } /> } /> - } /> + }> + } /> + } /> + } /> + } /> }> } /> diff --git a/haixun-backend/web/src/api/client.ts b/haixun-backend/web/src/api/client.ts index eddbf35..10a4524 100644 --- a/haixun-backend/web/src/api/client.ts +++ b/haixun-backend/web/src/api/client.ts @@ -221,22 +221,3 @@ export async function streamIslanderChat( await consumeAIEventStream(res, onDelta, onDone, onError) } -export async function streamAIChat( - body: Record, - onDelta: (text: string) => void, - onDone: (finishReason?: string) => void, - onError: (msg: string) => void, -) { - const memberToken = storage.getAccessToken() - const providerToken = storage.getAiProviderToken() - const headers: Record = { 'Content-Type': 'application/json' } - if (memberToken) headers['X-Member-Authorization'] = `Bearer ${memberToken}` - if (providerToken) headers.Authorization = `Bearer ${providerToken}` - - const res = await fetch('/api/v1/ai/chat/stream', { - method: 'POST', - headers, - body: JSON.stringify(body), - }) - await consumeAIEventStream(res, onDelta, onDone, onError) -} \ No newline at end of file diff --git a/haixun-backend/web/src/components/AcIcon.tsx b/haixun-backend/web/src/components/AcIcon.tsx index 275faa3..b72a30b 100644 --- a/haixun-backend/web/src/components/AcIcon.tsx +++ b/haixun-backend/web/src/components/AcIcon.tsx @@ -3,73 +3,95 @@ import type { AcAppKey } from '../lib/acAssets' const stroke = { fill: 'none', stroke: 'currentColor', - strokeWidth: 1.75, + strokeWidth: 1.85, strokeLinecap: 'round' as const, strokeLinejoin: 'round' as const, } const paths: Record = { - home: , + home: ( + <> + + + + ), persona: ( <> - - - + + + + + ), + jobs: ( + <> + + + ), - jobs: , schedule: ( <> - - - - ), - ai: ( - <> - - - + + + ), template: ( <> - - + + + ), settings: ( <> - - + + + + ), profile: ( <> - - + + + ), permissions: ( <> - - + + + + ), + easter: ( + <> + + + + ), threads: ( <> - - - + + + + ), more: ( <> - - - + + + + ), } @@ -84,7 +106,7 @@ export function AcIcon({ className?: string }) { const px = size === 'sm' ? 'h-9 w-9' : size === 'lg' ? 'h-12 w-12' : 'h-10 w-10' - const iconPx = size === 'sm' ? 'h-4 w-4' : size === 'lg' ? 'h-6 w-6' : 'h-5 w-5' + const iconPx = size === 'sm' ? 'h-[18px] w-[18px]' : size === 'lg' ? 'h-6 w-6' : 'h-5 w-5' return ( diff --git a/haixun-backend/web/src/components/AccountAiSettings.tsx b/haixun-backend/web/src/components/AccountAiSettings.tsx index 2863be3..9607a2e 100644 --- a/haixun-backend/web/src/components/AccountAiSettings.tsx +++ b/haixun-backend/web/src/components/AccountAiSettings.tsx @@ -4,6 +4,8 @@ import { DEFAULT_AI_CREDENTIALS, getApiKeyStatus, isMaskedKey, + normalizeProviderSettings, + normalizeSupportedProvider, PROVIDER_KEY_LABELS, PROVIDER_OPTIONS, PROVIDER_ORDER, @@ -47,7 +49,15 @@ export function AccountAiSettings({ accountId, compact }: AccountAiSettingsProps setError('') try { const data = await api.get(aiSettingsPath(accountId), { auth: true }) - setSettings(data) + setSettings({ + ...data, + ...normalizeProviderSettings({ + provider: data.provider, + model: data.model, + research_provider: data.research_provider, + research_model: data.research_model, + }), + }) setConfigured(parseConfigured(data.api_keys_configured)) setKeyInputs(data.api_keys ?? {}) } catch (e) { @@ -68,23 +78,37 @@ export function AccountAiSettings({ accountId, compact }: AccountAiSettingsProps setMessage('') try { const apiKeys: ProviderApiKeys = {} - for (const [provider, value] of Object.entries(keyInputs) as [ProviderId, string][]) { - const trimmed = value?.trim() + for (const provider of PROVIDER_ORDER) { + const trimmed = keyInputs[provider]?.trim() if (!trimmed || isMaskedKey(trimmed)) continue apiKeys[provider] = trimmed } + const normalized = normalizeProviderSettings({ + provider: settings.provider, + model: settings.model, + research_provider: settings.research_provider, + research_model: settings.research_model, + }) const data = await api.put( aiSettingsPath(accountId), { - provider: settings.provider, - model: settings.model, - research_provider: settings.research_provider, - research_model: settings.research_model, + provider: normalized.provider, + model: normalized.model, + research_provider: normalized.research_provider, + research_model: normalized.research_model, api_keys: apiKeys, }, { auth: true }, ) - setSettings(data) + setSettings({ + ...data, + ...normalizeProviderSettings({ + provider: data.provider, + model: data.model, + research_provider: data.research_provider, + research_model: data.research_model, + }), + }) setConfigured(parseConfigured(data.api_keys_configured)) setKeyInputs(data.api_keys ?? {}) setMessage('AI 設定已儲存') @@ -95,9 +119,8 @@ export function AccountAiSettings({ accountId, compact }: AccountAiSettingsProps } } - const provider = (settings?.provider as ProviderId) || DEFAULT_AI_CREDENTIALS.provider - const researchProvider = - (settings?.research_provider as ProviderId) || provider + const provider = normalizeSupportedProvider(settings?.provider) || DEFAULT_AI_CREDENTIALS.provider + const researchProvider = normalizeSupportedProvider(settings?.research_provider ?? provider) const providerOption = PROVIDER_OPTIONS.find((item) => item.value === provider) const researchOption = PROVIDER_OPTIONS.find((item) => item.value === researchProvider) const currentProviderConfigured = configured[provider] diff --git a/haixun-backend/web/src/components/AdminRoute.tsx b/haixun-backend/web/src/components/AdminRoute.tsx new file mode 100644 index 0000000..4a94ae6 --- /dev/null +++ b/haixun-backend/web/src/components/AdminRoute.tsx @@ -0,0 +1,21 @@ +import { Navigate, Outlet } from 'react-router-dom' +import { useAuth } from '../auth/AuthContext' +import { isAdminMember } from '../lib/memberRole' + +export function AdminRoute() { + const { member, loading } = useAuth() + + if (loading) { + return ( +
+ 載入中… +
+ ) + } + + if (!isAdminMember(member)) { + return + } + + return +} \ No newline at end of file diff --git a/haixun-backend/web/src/components/AppBrandLink.tsx b/haixun-backend/web/src/components/AppBrandLink.tsx new file mode 100644 index 0000000..5901c92 --- /dev/null +++ b/haixun-backend/web/src/components/AppBrandLink.tsx @@ -0,0 +1,37 @@ +import { Link } from 'react-router-dom' +import { AuthTicketIcon } from './AuthDecor' + +type Props = { + variant?: 'header' | 'sidebar' + className?: string +} + +export function AppBrandLink({ variant = 'header', className = '' }: Props) { + const isSidebar = variant === 'sidebar' + + return ( + + +
+

+ Haixun Patrol +

+

+ 巡樓管理台 +

+
+ + ) +} \ No newline at end of file diff --git a/haixun-backend/web/src/components/AppSidebar.tsx b/haixun-backend/web/src/components/AppSidebar.tsx index e2bed2f..4f92b53 100644 --- a/haixun-backend/web/src/components/AppSidebar.tsx +++ b/haixun-backend/web/src/components/AppSidebar.tsx @@ -1,5 +1,7 @@ import { useMemo } from 'react' import { NavLink, useLocation } from 'react-router-dom' +import { useAuth } from '../auth/AuthContext' +import { isAdminMember } from '../lib/memberRole' import { navGroupsForOnboarding, onboardingGlowClass, shouldGlowNav } from '../lib/onboarding' import { coreWorkflowNavGroup, type AcAppKey } from '../lib/acAssets' import { getActiveTopicId } from '../lib/brandContext' @@ -53,14 +55,16 @@ function SidebarNavItem({ export function AppSidebar() { const { pathname } = useLocation() + const { member } = useAuth() const { isComplete, nextStep } = useOnboarding() + const isAdmin = isAdminMember(member) const groups = useMemo(() => { - const base = navGroupsForOnboarding(isComplete) + const base = navGroupsForOnboarding(isComplete, isAdmin) if (!isComplete) return base const flowGroup = coreWorkflowNavGroup() if (base.length === 0) return [flowGroup] return [flowGroup, ...base] - }, [isComplete]) + }, [isComplete, isAdmin]) return (