Bläddra i källkod

feat tikhub-toktok

gangvy 7 månader sedan
förälder
incheckning
f45d025508
51 ändrade filer med 12973 tillägg och 4 borttagningar
  1. 1177 0
      docs/api/api返回数据结构/4.md
  2. 191 0
      docs/api/api返回数据结构/getuserfroile.md
  3. 0 0
      docs/api/api返回数据结构/user_search.md
  4. 0 0
      docs/task/20260211.md
  5. 84 0
      modules/fmode-tikhub-server/docs/README.md
  6. 87 0
      modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/README.md
  7. 55 0
      modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/build.sh
  8. 11 0
      modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/deno.json
  9. 23 0
      modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/docs/202602005-task.md
  10. 25 0
      modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/docs/config.md
  11. 63 0
      modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/docs/local-debug.md
  12. 178 0
      modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/docs/task.md
  13. 3922 0
      modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/docs/亚马逊需要对接的接口.md
  14. 2119 0
      modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/package-lock.json
  15. 36 0
      modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/package.json
  16. 43 0
      modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/src/api/catalogItems.ts
  17. 97 0
      modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/src/api/customerFeedback.ts
  18. 31 0
      modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/src/api/externalFulfillment.ts
  19. 18 0
      modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/src/api/inventory.ts
  20. 57 0
      modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/src/api/listings.ts
  21. 40 0
      modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/src/api/notifications.ts
  22. 41 0
      modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/src/api/orders.ts
  23. 30 0
      modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/src/api/pricing.ts
  24. 40 0
      modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/src/api/reports.ts
  25. 18 0
      modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/src/api/sales.ts
  26. 27 0
      modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/src/api/sellers.ts
  27. 204 0
      modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/src/client.ts
  28. 33 0
      modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/src/dev-server.ts
  29. 79 0
      modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/src/marketplace-helper.ts
  30. 53 0
      modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/src/middleware/auth.ts
  31. 17 0
      modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/src/mod.ts
  32. 29 0
      modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/src/parse-init.ts
  33. 282 0
      modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/src/routes.ts
  34. 406 0
      modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/src/types.ts
  35. 67 0
      modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/test_auth.ts
  36. 109 0
      modules/fmode-tikhub-server/docs/frontend-example.md
  37. 287 0
      modules/fmode-tikhub-server/docs/hb-hongren/influencer-db.service.ts
  38. 122 0
      modules/fmode-tikhub-server/docs/hb-hongren/tikhub.service.ts
  39. 175 0
      modules/fmode-tikhub-server/docs/hb-hongren/tiktok-cloudfunctions.md
  40. 66 0
      modules/fmode-tikhub-server/docs/router/common/router.ts
  41. 62 0
      modules/fmode-tikhub-server/docs/router/user/router.ts
  42. 113 0
      modules/fmode-tikhub-server/docs/usage.md
  43. 32 0
      modules/fmode-tikhub-server/package.json
  44. 351 0
      modules/fmode-tikhub-server/src/api/users.ts
  45. 68 0
      modules/fmode-tikhub-server/src/client.ts
  46. 5 0
      modules/fmode-tikhub-server/src/mod.ts
  47. 130 0
      modules/fmode-tikhub-server/src/routes.ts
  48. 38 0
      modules/fmode-tikhub-server/src/types.ts
  49. 1820 0
      package-lock.json
  50. 1 0
      package.json
  51. 11 4
      server.ts

+ 1177 - 0
docs/api/api返回数据结构/4.md

@@ -0,0 +1,1177 @@
+# 评论json 数据
+- {
+    "success": true,
+    "data": {
+        "list": [
+            {
+                "cid": "7589062225757225758",
+                "text": "cuanto salenuna mes asi",
+                "create_time": 1766966288,
+                "digg_count": 1,
+                "reply_comment_total": 1,
+                "user": {
+                    "unique_id": "lensiroly",
+                    "nickname": "lensi&roly",
+                    "avatar_url": "https://p16-common-sign.tiktokcdn-us.com/tos-useast8-avt-0068-tx2/e4d24308996bba14799575258f55023e~tplv-tiktokx-cropcenter:100:100.jpg?dr=9640&refresh_token=dd10741d&x-expires=1770915600&x-signature=HMRnx5M5yf%2FzuSDrcEtJPPorl2U%3D&t=4d5b0474&ps=13740610&shp=30310797&shcp=ff37627b&idc=useast8",
+                    "sec_uid": "MS4wLjABAAAAFvHj96jULTJ7i2Im4MXpC-Cp1ZRBiSaffVljjYW1Nmbv_4ceT4ATNNJ-SMlkC9AC"
+                },
+                "raw_data": {
+                    "allow_download_photo": true,
+                    "author_pin": false,
+                    "aweme_id": "7589036947083988279",
+                    "cid": "7589062225757225758",
+                    "collect_stat": 0,
+                    "comment_language": "es",
+                    "comment_post_item_ids": null,
+                    "create_time": 1766966288,
+                    "digg_count": 1,
+                    "fold_status": -1,
+                    "image_list": null,
+                    "is_author_digged": true,
+                    "is_comment_translatable": true,
+                    "is_high_purchase_intent": true,
+                    "label_list": [
+                        {
+                            "text": "Liked by creator",
+                            "type": 20
+                        }
+                    ],
+                    "no_show": false,
+                    "reply_comment": [
+                        {
+                            "allow_download_photo": true,
+                            "author_pin": false,
+                            "aweme_id": "7589036947083988279",
+                            "cid": "7589080226556068621",
+                            "collect_stat": 0,
+                            "comment_language": "es",
+                            "comment_post_item_ids": null,
+                            "create_time": 1766970481,
+                            "digg_count": 0,
+                            "fold_status": 0,
+                            "image_list": null,
+                            "is_author_digged": false,
+                            "is_comment_translatable": true,
+                            "is_high_purchase_intent": true,
+                            "label_list": [
+                                {
+                                    "text": "Creator",
+                                    "type": 1
+                                }
+                            ],
+                            "label_text": "Creator",
+                            "label_type": 1,
+                            "no_show": false,
+                            "reply_comment": null,
+                            "reply_id": "7589062225757225758",
+                            "reply_to_reply_id": "0",
+                            "share_info": {
+                                "acl": {
+                                    "code": 1,
+                                    "extra": "{\"is_share_handler_failed\":\"1\"}"
+                                },
+                                "desc": "",
+                                "title": "",
+                                "url": ""
+                            },
+                            "status": 1,
+                            "stick_position": 0,
+                            "text": "Escríbenos 305-746-9431. Para detalles. Gracias",
+                            "text_extra": [],
+                            "thread_has_more": false,
+                            "thread_id": -1,
+                            "trans_btn_style": 1,
+                            "user": {
+                                "account_labels": null,
+                                "ad_cover_url": null,
+                                "advance_feature_item_order": null,
+                                "advanced_feature_info": null,
+                                "avatar_thumb": {
+                                    "uri": "tos-useast5-avt-0068-tx/fdb9075f6d24284b5621a1c7a2958c49",
+                                    "url_list": [
+                                        "https://p19-common-sign.tiktokcdn-us.com/tos-useast5-avt-0068-tx/fdb9075f6d24284b5621a1c7a2958c49~tplv-tiktokx-cropcenter:100:100.jpg?dr=9640&refresh_token=5932e44d&x-expires=1770915600&x-signature=Gb6yGH9VLDgbgBxtRrFGI64RgnQ%3D&t=4d5b0474&ps=13740610&shp=30310797&shcp=ff37627b&idc=useast8",
+                                        "https://p16-common-sign.tiktokcdn-us.com/tos-useast5-avt-0068-tx/fdb9075f6d24284b5621a1c7a2958c49~tplv-tiktokx-cropcenter:100:100.jpg?dr=9640&refresh_token=36ac9263&x-expires=1770915600&x-signature=PbUYyp%2BKQ3QleDIxbUnQ20cVljs%3D&t=4d5b0474&ps=13740610&shp=30310797&shcp=ff37627b&idc=useast8",
+                                        "https://p19-common-sign.tiktokcdn-us.com/tos-useast5-avt-0068-tx/fdb9075f6d24284b5621a1c7a2958c49~tplv-tiktokx-cropcenter:100:100.jpeg?dr=9640&refresh_token=12a63a7c&x-expires=1770915600&x-signature=fKx0CNPctCqImgphE6lQBUqTEkU%3D&t=4d5b0474&ps=13740610&shp=30310797&shcp=ff37627b&idc=useast8"
+                                    ],
+                                    "url_prefix": null
+                                },
+                                "bold_fields": null,
+                                "can_message_follow_status_list": null,
+                                "can_set_geofencing": null,
+                                "cha_list": null,
+                                "cover_url": null,
+                                "custom_verify": "",
+                                "enterprise_verify_reason": "",
+                                "events": null,
+                                "followers_detail": null,
+                                "geofencing": null,
+                                "homepage_bottom_toast": null,
+                                "item_list": null,
+                                "mutual_relation_avatars": null,
+                                "need_points": null,
+                                "nickname": "dny_partydecoration",
+                                "platform_sync_info": null,
+                                "predicted_age_group": "-1",
+                                "relative_users": null,
+                                "search_highlight": null,
+                                "sec_uid": "MS4wLjABAAAAwOSeDW2rFGcrpPIMtTECj7m9NyQKEIWKhPpvuBYxYKFxbRx6N7IVqbXQQoqKVtGf",
+                                "shield_edit_field_info": null,
+                                "type_label": null,
+                                "uid": "7179361856557319214",
+                                "unique_id": "dny_partydecoration",
+                                "user_profile_guide": null,
+                                "user_tags": null,
+                                "white_cover_url": null
+                            },
+                            "user_buried": false,
+                            "user_digged": 0
+                        }
+                    ],
+                    "reply_comment_total": 1,
+                    "reply_id": "0",
+                    "reply_to_reply_id": "0",
+                    "share_info": {
+                        "acl": {
+                            "code": 1,
+                            "extra": "{\"is_share_handler_failed\":\"1\"}"
+                        },
+                        "desc": "",
+                        "title": "",
+                        "url": ""
+                    },
+                    "sort_extra_score": {
+                        "reply_score": 0.003012,
+                        "show_more_score": 0.37412
+                    },
+                    "sort_tags": "{\"author_digg\":1,\"author_reply\":1,\"top_list\":1}",
+                    "status": 1,
+                    "stick_position": 0,
+                    "text": "cuanto salenuna mes asi",
+                    "text_extra": [],
+                    "thread_has_more": false,
+                    "thread_id": 0,
+                    "trans_btn_style": 1,
+                    "user": {
+                        "account_labels": null,
+                        "ad_cover_url": null,
+                        "advance_feature_item_order": null,
+                        "advanced_feature_info": null,
+                        "avatar_thumb": {
+                            "uri": "tos-useast8-avt-0068-tx2/e4d24308996bba14799575258f55023e",
+                            "url_list": [
+                                "https://p16-common-sign.tiktokcdn-us.com/tos-useast8-avt-0068-tx2/e4d24308996bba14799575258f55023e~tplv-tiktokx-cropcenter:100:100.jpg?dr=9640&refresh_token=dd10741d&x-expires=1770915600&x-signature=HMRnx5M5yf%2FzuSDrcEtJPPorl2U%3D&t=4d5b0474&ps=13740610&shp=30310797&shcp=ff37627b&idc=useast8",
+                                "https://p19-common-sign.tiktokcdn-us.com/tos-useast8-avt-0068-tx2/e4d24308996bba14799575258f55023e~tplv-tiktokx-cropcenter:100:100.jpg?dr=9640&refresh_token=0a164178&x-expires=1770915600&x-signature=FzSlOBxA%2F9VegeRuz8deoat4ml4%3D&t=4d5b0474&ps=13740610&shp=30310797&shcp=ff37627b&idc=useast8",
+                                "https://p16-common-sign.tiktokcdn-us.com/tos-useast8-avt-0068-tx2/e4d24308996bba14799575258f55023e~tplv-tiktokx-cropcenter:100:100.jpeg?dr=9640&refresh_token=725ad56e&x-expires=1770915600&x-signature=nQGCzH0v4%2B5%2Bs873En%2BqmtEhjdY%3D&t=4d5b0474&ps=13740610&shp=30310797&shcp=ff37627b&idc=useast8"
+                            ],
+                            "url_prefix": null
+                        },
+                        "bold_fields": null,
+                        "can_message_follow_status_list": null,
+                        "can_set_geofencing": null,
+                        "cha_list": null,
+                        "cover_url": null,
+                        "custom_verify": "",
+                        "enterprise_verify_reason": "",
+                        "events": null,
+                        "followers_detail": null,
+                        "geofencing": null,
+                        "homepage_bottom_toast": null,
+                        "item_list": null,
+                        "mutual_relation_avatars": null,
+                        "need_points": null,
+                        "nickname": "lensi&roly",
+                        "platform_sync_info": null,
+                        "predicted_age_group": "4",
+                        "relative_users": null,
+                        "search_highlight": null,
+                        "sec_uid": "MS4wLjABAAAAFvHj96jULTJ7i2Im4MXpC-Cp1ZRBiSaffVljjYW1Nmbv_4ceT4ATNNJ-SMlkC9AC",
+                        "shield_edit_field_info": null,
+                        "type_label": null,
+                        "uid": "6772692452979983366",
+                        "unique_id": "lensiroly",
+                        "user_profile_guide": null,
+                        "user_tags": null,
+                        "white_cover_url": null
+                    },
+                    "user_buried": false,
+                    "user_digged": 0
+                }
+            },
+            {
+                "cid": "7590448275989234462",
+                "text": "Lindo 🥰",
+                "create_time": 1767289011,
+                "digg_count": 1,
+                "reply_comment_total": 1,
+                "user": {
+                    "unique_id": "yesenialpez643",
+                    "nickname": "🦋Yesi 🦋",
+                    "avatar_url": "https://p16-common-sign.tiktokcdn-us.com/tos-useast8-avt-0068-tx2/38013d5c18c188611d219f55a654b707~tplv-tiktokx-cropcenter:100:100.jpg?dr=9640&refresh_token=939e6597&x-expires=1770915600&x-signature=N2ui47FfirIf2yF0QMX%2B2VkNetg%3D&t=4d5b0474&ps=13740610&shp=30310797&shcp=ff37627b&idc=useast8",
+                    "sec_uid": "MS4wLjABAAAAqarZnkN5zNoIf5_nPOXk_XWodRoaZ4D9k3PrTidTMeE5wbMnEHO2kcqTbVKESGaP"
+                },
+                "raw_data": {
+                    "allow_download_photo": true,
+                    "author_pin": false,
+                    "aweme_id": "7589036947083988279",
+                    "cid": "7590448275989234462",
+                    "collect_stat": 0,
+                    "comment_language": "pt",
+                    "comment_post_item_ids": null,
+                    "create_time": 1767289011,
+                    "digg_count": 1,
+                    "fold_status": -1,
+                    "image_list": null,
+                    "is_author_digged": true,
+                    "is_comment_translatable": true,
+                    "is_high_purchase_intent": false,
+                    "label_list": [
+                        {
+                            "text": "Liked by creator",
+                            "type": 20
+                        }
+                    ],
+                    "no_show": false,
+                    "reply_comment": [
+                        {
+                            "allow_download_photo": true,
+                            "author_pin": false,
+                            "aweme_id": "7589036947083988279",
+                            "cid": "7590474041174115086",
+                            "collect_stat": 0,
+                            "comment_language": "es",
+                            "comment_post_item_ids": null,
+                            "create_time": 1767294973,
+                            "digg_count": 0,
+                            "fold_status": 0,
+                            "image_list": null,
+                            "is_author_digged": false,
+                            "is_comment_translatable": true,
+                            "is_high_purchase_intent": false,
+                            "label_list": [
+                                {
+                                    "text": "Creator",
+                                    "type": 1
+                                }
+                            ],
+                            "label_text": "Creator",
+                            "label_type": 1,
+                            "no_show": false,
+                            "reply_comment": null,
+                            "reply_id": "7590448275989234462",
+                            "reply_to_reply_id": "0",
+                            "share_info": {
+                                "acl": {
+                                    "code": 1,
+                                    "extra": "{\"is_share_handler_failed\":\"1\"}"
+                                },
+                                "desc": "",
+                                "title": "",
+                                "url": ""
+                            },
+                            "status": 1,
+                            "stick_position": 0,
+                            "text": "Gracias 🥰🥰🥰",
+                            "text_extra": [],
+                            "thread_has_more": false,
+                            "thread_id": -1,
+                            "trans_btn_style": 1,
+                            "user": {
+                                "account_labels": null,
+                                "ad_cover_url": null,
+                                "advance_feature_item_order": null,
+                                "advanced_feature_info": null,
+                                "avatar_thumb": {
+                                    "uri": "tos-useast5-avt-0068-tx/fdb9075f6d24284b5621a1c7a2958c49",
+                                    "url_list": [
+                                        "https://p19-common-sign.tiktokcdn-us.com/tos-useast5-avt-0068-tx/fdb9075f6d24284b5621a1c7a2958c49~tplv-tiktokx-cropcenter:100:100.jpg?dr=9640&refresh_token=5932e44d&x-expires=1770915600&x-signature=Gb6yGH9VLDgbgBxtRrFGI64RgnQ%3D&t=4d5b0474&ps=13740610&shp=30310797&shcp=ff37627b&idc=useast8",
+                                        "https://p16-common-sign.tiktokcdn-us.com/tos-useast5-avt-0068-tx/fdb9075f6d24284b5621a1c7a2958c49~tplv-tiktokx-cropcenter:100:100.jpg?dr=9640&refresh_token=36ac9263&x-expires=1770915600&x-signature=PbUYyp%2BKQ3QleDIxbUnQ20cVljs%3D&t=4d5b0474&ps=13740610&shp=30310797&shcp=ff37627b&idc=useast8",
+                                        "https://p19-common-sign.tiktokcdn-us.com/tos-useast5-avt-0068-tx/fdb9075f6d24284b5621a1c7a2958c49~tplv-tiktokx-cropcenter:100:100.jpeg?dr=9640&refresh_token=12a63a7c&x-expires=1770915600&x-signature=fKx0CNPctCqImgphE6lQBUqTEkU%3D&t=4d5b0474&ps=13740610&shp=30310797&shcp=ff37627b&idc=useast8"
+                                    ],
+                                    "url_prefix": null
+                                },
+                                "bold_fields": null,
+                                "can_message_follow_status_list": null,
+                                "can_set_geofencing": null,
+                                "cha_list": null,
+                                "cover_url": null,
+                                "custom_verify": "",
+                                "enterprise_verify_reason": "",
+                                "events": null,
+                                "followers_detail": null,
+                                "geofencing": null,
+                                "homepage_bottom_toast": null,
+                                "item_list": null,
+                                "mutual_relation_avatars": null,
+                                "need_points": null,
+                                "nickname": "dny_partydecoration",
+                                "platform_sync_info": null,
+                                "predicted_age_group": "-1",
+                                "relative_users": null,
+                                "search_highlight": null,
+                                "sec_uid": "MS4wLjABAAAAwOSeDW2rFGcrpPIMtTECj7m9NyQKEIWKhPpvuBYxYKFxbRx6N7IVqbXQQoqKVtGf",
+                                "shield_edit_field_info": null,
+                                "type_label": null,
+                                "uid": "7179361856557319214",
+                                "unique_id": "dny_partydecoration",
+                                "user_profile_guide": null,
+                                "user_tags": null,
+                                "white_cover_url": null
+                            },
+                            "user_buried": false,
+                            "user_digged": 0
+                        }
+                    ],
+                    "reply_comment_total": 1,
+                    "reply_id": "0",
+                    "reply_to_reply_id": "0",
+                    "share_info": {
+                        "acl": {
+                            "code": 1,
+                            "extra": "{\"is_share_handler_failed\":\"1\"}"
+                        },
+                        "desc": "",
+                        "title": "",
+                        "url": ""
+                    },
+                    "sort_extra_score": {
+                        "reply_score": 0.000512,
+                        "show_more_score": 0.158609
+                    },
+                    "sort_tags": "{\"author_digg\":1,\"author_reply\":1,\"top_list\":1}",
+                    "status": 1,
+                    "stick_position": 0,
+                    "text": "Lindo 🥰",
+                    "text_extra": [],
+                    "thread_has_more": false,
+                    "thread_id": 0,
+                    "trans_btn_style": 1,
+                    "user": {
+                        "account_labels": null,
+                        "ad_cover_url": null,
+                        "advance_feature_item_order": null,
+                        "advanced_feature_info": null,
+                        "avatar_thumb": {
+                            "uri": "tos-useast8-avt-0068-tx2/38013d5c18c188611d219f55a654b707",
+                            "url_list": [
+                                "https://p16-common-sign.tiktokcdn-us.com/tos-useast8-avt-0068-tx2/38013d5c18c188611d219f55a654b707~tplv-tiktokx-cropcenter:100:100.jpg?dr=9640&refresh_token=939e6597&x-expires=1770915600&x-signature=N2ui47FfirIf2yF0QMX%2B2VkNetg%3D&t=4d5b0474&ps=13740610&shp=30310797&shcp=ff37627b&idc=useast8",
+                                "https://p19-common-sign.tiktokcdn-us.com/tos-useast8-avt-0068-tx2/38013d5c18c188611d219f55a654b707~tplv-tiktokx-cropcenter:100:100.jpg?dr=9640&refresh_token=34db19fe&x-expires=1770915600&x-signature=07%2BiE346wUTfE3eQobtK6f0RxGk%3D&t=4d5b0474&ps=13740610&shp=30310797&shcp=ff37627b&idc=useast8",
+                                "https://p16-common-sign.tiktokcdn-us.com/tos-useast8-avt-0068-tx2/38013d5c18c188611d219f55a654b707~tplv-tiktokx-cropcenter:100:100.jpeg?dr=9640&refresh_token=db9e4b76&x-expires=1770915600&x-signature=qm9GucFDFayVLwg8nWgVlaX9F4g%3D&t=4d5b0474&ps=13740610&shp=30310797&shcp=ff37627b&idc=useast8"
+                            ],
+                            "url_prefix": null
+                        },
+                        "bold_fields": null,
+                        "can_message_follow_status_list": null,
+                        "can_set_geofencing": null,
+                        "cha_list": null,
+                        "cover_url": null,
+                        "custom_verify": "",
+                        "enterprise_verify_reason": "",
+                        "events": null,
+                        "followers_detail": null,
+                        "geofencing": null,
+                        "homepage_bottom_toast": null,
+                        "item_list": null,
+                        "mutual_relation_avatars": null,
+                        "need_points": null,
+                        "nickname": "🦋Yesi 🦋",
+                        "platform_sync_info": null,
+                        "predicted_age_group": "",
+                        "relative_users": null,
+                        "search_highlight": null,
+                        "sec_uid": "MS4wLjABAAAAqarZnkN5zNoIf5_nPOXk_XWodRoaZ4D9k3PrTidTMeE5wbMnEHO2kcqTbVKESGaP",
+                        "shield_edit_field_info": null,
+                        "type_label": null,
+                        "uid": "7404511358118904874",
+                        "unique_id": "yesenialpez643",
+                        "user_profile_guide": null,
+                        "user_tags": null,
+                        "white_cover_url": null
+                    },
+                    "user_buried": false,
+                    "user_digged": 0
+                }
+            },
+            {
+                "cid": "7589047945213051661",
+                "text": "es una belleza🥰🥰🥰",
+                "create_time": 1766962968,
+                "digg_count": 1,
+                "reply_comment_total": 1,
+                "user": {
+                    "unique_id": "elianygonzalez96",
+                    "nickname": "ElianyGonzalez🌺",
+                    "avatar_url": "https://p16-common-sign.tiktokcdn-us.com/tos-useast5-avt-0068-tx/986ebc9ba0bc2430a859ab4580ee5eaa~tplv-tiktokx-cropcenter:100:100.jpg?dr=9640&refresh_token=d65b1447&x-expires=1770915600&x-signature=CCRqvxSCeBDKGCNUp4sGZeStLOs%3D&t=4d5b0474&ps=13740610&shp=30310797&shcp=ff37627b&idc=useast8",
+                    "sec_uid": "MS4wLjABAAAA28YmW0dY239MGH0lTzgZyY8uPO3TGkDmNHqATdFkz09zN-smRbgn8dG-y0wKtT2S"
+                },
+                "raw_data": {
+                    "allow_download_photo": true,
+                    "author_pin": false,
+                    "aweme_id": "7589036947083988279",
+                    "cid": "7589047945213051661",
+                    "collect_stat": 0,
+                    "comment_language": "es",
+                    "comment_post_item_ids": null,
+                    "create_time": 1766962968,
+                    "digg_count": 1,
+                    "fold_status": -1,
+                    "image_list": null,
+                    "is_author_digged": true,
+                    "is_comment_translatable": true,
+                    "is_high_purchase_intent": false,
+                    "label_list": [
+                        {
+                            "text": "Liked by creator",
+                            "type": 20
+                        }
+                    ],
+                    "no_show": false,
+                    "reply_comment": [
+                        {
+                            "allow_download_photo": true,
+                            "author_pin": false,
+                            "aweme_id": "7589036947083988279",
+                            "cid": "7589080193064928013",
+                            "collect_stat": 0,
+                            "comment_language": "es",
+                            "comment_post_item_ids": null,
+                            "create_time": 1766970451,
+                            "digg_count": 0,
+                            "fold_status": 0,
+                            "image_list": null,
+                            "is_author_digged": false,
+                            "is_comment_translatable": true,
+                            "is_high_purchase_intent": false,
+                            "label_list": [
+                                {
+                                    "text": "Creator",
+                                    "type": 1
+                                }
+                            ],
+                            "label_text": "Creator",
+                            "label_type": 1,
+                            "no_show": false,
+                            "reply_comment": null,
+                            "reply_id": "7589047945213051661",
+                            "reply_to_reply_id": "0",
+                            "share_info": {
+                                "acl": {
+                                    "code": 1,
+                                    "extra": "{\"is_share_handler_failed\":\"1\"}"
+                                },
+                                "desc": "",
+                                "title": "",
+                                "url": ""
+                            },
+                            "status": 1,
+                            "stick_position": 0,
+                            "text": "Gracias 🥰🥰🥰",
+                            "text_extra": [],
+                            "thread_has_more": false,
+                            "thread_id": -1,
+                            "trans_btn_style": 1,
+                            "user": {
+                                "account_labels": null,
+                                "ad_cover_url": null,
+                                "advance_feature_item_order": null,
+                                "advanced_feature_info": null,
+                                "avatar_thumb": {
+                                    "uri": "tos-useast5-avt-0068-tx/fdb9075f6d24284b5621a1c7a2958c49",
+                                    "url_list": [
+                                        "https://p19-common-sign.tiktokcdn-us.com/tos-useast5-avt-0068-tx/fdb9075f6d24284b5621a1c7a2958c49~tplv-tiktokx-cropcenter:100:100.jpg?dr=9640&refresh_token=5932e44d&x-expires=1770915600&x-signature=Gb6yGH9VLDgbgBxtRrFGI64RgnQ%3D&t=4d5b0474&ps=13740610&shp=30310797&shcp=ff37627b&idc=useast8",
+                                        "https://p16-common-sign.tiktokcdn-us.com/tos-useast5-avt-0068-tx/fdb9075f6d24284b5621a1c7a2958c49~tplv-tiktokx-cropcenter:100:100.jpg?dr=9640&refresh_token=36ac9263&x-expires=1770915600&x-signature=PbUYyp%2BKQ3QleDIxbUnQ20cVljs%3D&t=4d5b0474&ps=13740610&shp=30310797&shcp=ff37627b&idc=useast8",
+                                        "https://p19-common-sign.tiktokcdn-us.com/tos-useast5-avt-0068-tx/fdb9075f6d24284b5621a1c7a2958c49~tplv-tiktokx-cropcenter:100:100.jpeg?dr=9640&refresh_token=12a63a7c&x-expires=1770915600&x-signature=fKx0CNPctCqImgphE6lQBUqTEkU%3D&t=4d5b0474&ps=13740610&shp=30310797&shcp=ff37627b&idc=useast8"
+                                    ],
+                                    "url_prefix": null
+                                },
+                                "bold_fields": null,
+                                "can_message_follow_status_list": null,
+                                "can_set_geofencing": null,
+                                "cha_list": null,
+                                "cover_url": null,
+                                "custom_verify": "",
+                                "enterprise_verify_reason": "",
+                                "events": null,
+                                "followers_detail": null,
+                                "geofencing": null,
+                                "homepage_bottom_toast": null,
+                                "item_list": null,
+                                "mutual_relation_avatars": null,
+                                "need_points": null,
+                                "nickname": "dny_partydecoration",
+                                "platform_sync_info": null,
+                                "predicted_age_group": "-1",
+                                "relative_users": null,
+                                "search_highlight": null,
+                                "sec_uid": "MS4wLjABAAAAwOSeDW2rFGcrpPIMtTECj7m9NyQKEIWKhPpvuBYxYKFxbRx6N7IVqbXQQoqKVtGf",
+                                "shield_edit_field_info": null,
+                                "type_label": null,
+                                "uid": "7179361856557319214",
+                                "unique_id": "dny_partydecoration",
+                                "user_profile_guide": null,
+                                "user_tags": null,
+                                "white_cover_url": null
+                            },
+                            "user_buried": false,
+                            "user_digged": 0
+                        }
+                    ],
+                    "reply_comment_total": 1,
+                    "reply_id": "0",
+                    "reply_to_reply_id": "0",
+                    "share_info": {
+                        "acl": {
+                            "code": 1,
+                            "extra": "{\"is_share_handler_failed\":\"1\"}"
+                        },
+                        "desc": "",
+                        "title": "",
+                        "url": ""
+                    },
+                    "sort_extra_score": {
+                        "reply_score": 0.000176,
+                        "show_more_score": 0.101443
+                    },
+                    "sort_tags": "{\"author_digg\":1,\"author_reply\":1,\"top_list\":1}",
+                    "status": 1,
+                    "stick_position": 0,
+                    "text": "es una belleza🥰🥰🥰",
+                    "text_extra": [],
+                    "thread_has_more": false,
+                    "thread_id": 0,
+                    "trans_btn_style": 1,
+                    "user": {
+                        "account_labels": null,
+                        "ad_cover_url": null,
+                        "advance_feature_item_order": null,
+                        "advanced_feature_info": null,
+                        "avatar_thumb": {
+                            "uri": "tos-useast5-avt-0068-tx/986ebc9ba0bc2430a859ab4580ee5eaa",
+                            "url_list": [
+                                "https://p16-common-sign.tiktokcdn-us.com/tos-useast5-avt-0068-tx/986ebc9ba0bc2430a859ab4580ee5eaa~tplv-tiktokx-cropcenter:100:100.jpg?dr=9640&refresh_token=d65b1447&x-expires=1770915600&x-signature=CCRqvxSCeBDKGCNUp4sGZeStLOs%3D&t=4d5b0474&ps=13740610&shp=30310797&shcp=ff37627b&idc=useast8",
+                                "https://p19-common-sign.tiktokcdn-us.com/tos-useast5-avt-0068-tx/986ebc9ba0bc2430a859ab4580ee5eaa~tplv-tiktokx-cropcenter:100:100.jpg?dr=9640&refresh_token=566cc2ee&x-expires=1770915600&x-signature=NAgBXhvqwCPwT0dh48O5Hw7hM1I%3D&t=4d5b0474&ps=13740610&shp=30310797&shcp=ff37627b&idc=useast8",
+                                "https://p16-common-sign.tiktokcdn-us.com/tos-useast5-avt-0068-tx/986ebc9ba0bc2430a859ab4580ee5eaa~tplv-tiktokx-cropcenter:100:100.jpeg?dr=9640&refresh_token=cc24d135&x-expires=1770915600&x-signature=%2FHLkl8FyoJ4mHcqtmURQcPJRyyE%3D&t=4d5b0474&ps=13740610&shp=30310797&shcp=ff37627b&idc=useast8"
+                            ],
+                            "url_prefix": null
+                        },
+                        "bold_fields": null,
+                        "can_message_follow_status_list": null,
+                        "can_set_geofencing": null,
+                        "cha_list": null,
+                        "cover_url": null,
+                        "custom_verify": "",
+                        "enterprise_verify_reason": "",
+                        "events": null,
+                        "followers_detail": null,
+                        "geofencing": null,
+                        "homepage_bottom_toast": null,
+                        "item_list": null,
+                        "mutual_relation_avatars": null,
+                        "need_points": null,
+                        "nickname": "ElianyGonzalez🌺",
+                        "platform_sync_info": null,
+                        "predicted_age_group": "",
+                        "relative_users": null,
+                        "search_highlight": null,
+                        "sec_uid": "MS4wLjABAAAA28YmW0dY239MGH0lTzgZyY8uPO3TGkDmNHqATdFkz09zN-smRbgn8dG-y0wKtT2S",
+                        "shield_edit_field_info": null,
+                        "type_label": null,
+                        "uid": "7190595357935158315",
+                        "unique_id": "elianygonzalez96",
+                        "user_profile_guide": null,
+                        "user_tags": null,
+                        "white_cover_url": null
+                    },
+                    "user_buried": false,
+                    "user_digged": 0
+                }
+            }
+        ],
+        "comments": [
+            {
+                "cid": "7589062225757225758",
+                "text": "cuanto salenuna mes asi",
+                "create_time": 1766966288,
+                "digg_count": 1,
+                "reply_comment_total": 1,
+                "user": {
+                    "unique_id": "lensiroly",
+                    "nickname": "lensi&roly",
+                    "avatar_url": "https://p16-common-sign.tiktokcdn-us.com/tos-useast8-avt-0068-tx2/e4d24308996bba14799575258f55023e~tplv-tiktokx-cropcenter:100:100.jpg?dr=9640&refresh_token=dd10741d&x-expires=1770915600&x-signature=HMRnx5M5yf%2FzuSDrcEtJPPorl2U%3D&t=4d5b0474&ps=13740610&shp=30310797&shcp=ff37627b&idc=useast8",
+                    "sec_uid": "MS4wLjABAAAAFvHj96jULTJ7i2Im4MXpC-Cp1ZRBiSaffVljjYW1Nmbv_4ceT4ATNNJ-SMlkC9AC"
+                },
+                "raw_data": {
+                    "allow_download_photo": true,
+                    "author_pin": false,
+                    "aweme_id": "7589036947083988279",
+                    "cid": "7589062225757225758",
+                    "collect_stat": 0,
+                    "comment_language": "es",
+                    "comment_post_item_ids": null,
+                    "create_time": 1766966288,
+                    "digg_count": 1,
+                    "fold_status": -1,
+                    "image_list": null,
+                    "is_author_digged": true,
+                    "is_comment_translatable": true,
+                    "is_high_purchase_intent": true,
+                    "label_list": [
+                        {
+                            "text": "Liked by creator",
+                            "type": 20
+                        }
+                    ],
+                    "no_show": false,
+                    "reply_comment": [
+                        {
+                            "allow_download_photo": true,
+                            "author_pin": false,
+                            "aweme_id": "7589036947083988279",
+                            "cid": "7589080226556068621",
+                            "collect_stat": 0,
+                            "comment_language": "es",
+                            "comment_post_item_ids": null,
+                            "create_time": 1766970481,
+                            "digg_count": 0,
+                            "fold_status": 0,
+                            "image_list": null,
+                            "is_author_digged": false,
+                            "is_comment_translatable": true,
+                            "is_high_purchase_intent": true,
+                            "label_list": [
+                                {
+                                    "text": "Creator",
+                                    "type": 1
+                                }
+                            ],
+                            "label_text": "Creator",
+                            "label_type": 1,
+                            "no_show": false,
+                            "reply_comment": null,
+                            "reply_id": "7589062225757225758",
+                            "reply_to_reply_id": "0",
+                            "share_info": {
+                                "acl": {
+                                    "code": 1,
+                                    "extra": "{\"is_share_handler_failed\":\"1\"}"
+                                },
+                                "desc": "",
+                                "title": "",
+                                "url": ""
+                            },
+                            "status": 1,
+                            "stick_position": 0,
+                            "text": "Escríbenos 305-746-9431. Para detalles. Gracias",
+                            "text_extra": [],
+                            "thread_has_more": false,
+                            "thread_id": -1,
+                            "trans_btn_style": 1,
+                            "user": {
+                                "account_labels": null,
+                                "ad_cover_url": null,
+                                "advance_feature_item_order": null,
+                                "advanced_feature_info": null,
+                                "avatar_thumb": {
+                                    "uri": "tos-useast5-avt-0068-tx/fdb9075f6d24284b5621a1c7a2958c49",
+                                    "url_list": [
+                                        "https://p19-common-sign.tiktokcdn-us.com/tos-useast5-avt-0068-tx/fdb9075f6d24284b5621a1c7a2958c49~tplv-tiktokx-cropcenter:100:100.jpg?dr=9640&refresh_token=5932e44d&x-expires=1770915600&x-signature=Gb6yGH9VLDgbgBxtRrFGI64RgnQ%3D&t=4d5b0474&ps=13740610&shp=30310797&shcp=ff37627b&idc=useast8",
+                                        "https://p16-common-sign.tiktokcdn-us.com/tos-useast5-avt-0068-tx/fdb9075f6d24284b5621a1c7a2958c49~tplv-tiktokx-cropcenter:100:100.jpg?dr=9640&refresh_token=36ac9263&x-expires=1770915600&x-signature=PbUYyp%2BKQ3QleDIxbUnQ20cVljs%3D&t=4d5b0474&ps=13740610&shp=30310797&shcp=ff37627b&idc=useast8",
+                                        "https://p19-common-sign.tiktokcdn-us.com/tos-useast5-avt-0068-tx/fdb9075f6d24284b5621a1c7a2958c49~tplv-tiktokx-cropcenter:100:100.jpeg?dr=9640&refresh_token=12a63a7c&x-expires=1770915600&x-signature=fKx0CNPctCqImgphE6lQBUqTEkU%3D&t=4d5b0474&ps=13740610&shp=30310797&shcp=ff37627b&idc=useast8"
+                                    ],
+                                    "url_prefix": null
+                                },
+                                "bold_fields": null,
+                                "can_message_follow_status_list": null,
+                                "can_set_geofencing": null,
+                                "cha_list": null,
+                                "cover_url": null,
+                                "custom_verify": "",
+                                "enterprise_verify_reason": "",
+                                "events": null,
+                                "followers_detail": null,
+                                "geofencing": null,
+                                "homepage_bottom_toast": null,
+                                "item_list": null,
+                                "mutual_relation_avatars": null,
+                                "need_points": null,
+                                "nickname": "dny_partydecoration",
+                                "platform_sync_info": null,
+                                "predicted_age_group": "-1",
+                                "relative_users": null,
+                                "search_highlight": null,
+                                "sec_uid": "MS4wLjABAAAAwOSeDW2rFGcrpPIMtTECj7m9NyQKEIWKhPpvuBYxYKFxbRx6N7IVqbXQQoqKVtGf",
+                                "shield_edit_field_info": null,
+                                "type_label": null,
+                                "uid": "7179361856557319214",
+                                "unique_id": "dny_partydecoration",
+                                "user_profile_guide": null,
+                                "user_tags": null,
+                                "white_cover_url": null
+                            },
+                            "user_buried": false,
+                            "user_digged": 0
+                        }
+                    ],
+                    "reply_comment_total": 1,
+                    "reply_id": "0",
+                    "reply_to_reply_id": "0",
+                    "share_info": {
+                        "acl": {
+                            "code": 1,
+                            "extra": "{\"is_share_handler_failed\":\"1\"}"
+                        },
+                        "desc": "",
+                        "title": "",
+                        "url": ""
+                    },
+                    "sort_extra_score": {
+                        "reply_score": 0.003012,
+                        "show_more_score": 0.37412
+                    },
+                    "sort_tags": "{\"author_digg\":1,\"author_reply\":1,\"top_list\":1}",
+                    "status": 1,
+                    "stick_position": 0,
+                    "text": "cuanto salenuna mes asi",
+                    "text_extra": [],
+                    "thread_has_more": false,
+                    "thread_id": 0,
+                    "trans_btn_style": 1,
+                    "user": {
+                        "account_labels": null,
+                        "ad_cover_url": null,
+                        "advance_feature_item_order": null,
+                        "advanced_feature_info": null,
+                        "avatar_thumb": {
+                            "uri": "tos-useast8-avt-0068-tx2/e4d24308996bba14799575258f55023e",
+                            "url_list": [
+                                "https://p16-common-sign.tiktokcdn-us.com/tos-useast8-avt-0068-tx2/e4d24308996bba14799575258f55023e~tplv-tiktokx-cropcenter:100:100.jpg?dr=9640&refresh_token=dd10741d&x-expires=1770915600&x-signature=HMRnx5M5yf%2FzuSDrcEtJPPorl2U%3D&t=4d5b0474&ps=13740610&shp=30310797&shcp=ff37627b&idc=useast8",
+                                "https://p19-common-sign.tiktokcdn-us.com/tos-useast8-avt-0068-tx2/e4d24308996bba14799575258f55023e~tplv-tiktokx-cropcenter:100:100.jpg?dr=9640&refresh_token=0a164178&x-expires=1770915600&x-signature=FzSlOBxA%2F9VegeRuz8deoat4ml4%3D&t=4d5b0474&ps=13740610&shp=30310797&shcp=ff37627b&idc=useast8",
+                                "https://p16-common-sign.tiktokcdn-us.com/tos-useast8-avt-0068-tx2/e4d24308996bba14799575258f55023e~tplv-tiktokx-cropcenter:100:100.jpeg?dr=9640&refresh_token=725ad56e&x-expires=1770915600&x-signature=nQGCzH0v4%2B5%2Bs873En%2BqmtEhjdY%3D&t=4d5b0474&ps=13740610&shp=30310797&shcp=ff37627b&idc=useast8"
+                            ],
+                            "url_prefix": null
+                        },
+                        "bold_fields": null,
+                        "can_message_follow_status_list": null,
+                        "can_set_geofencing": null,
+                        "cha_list": null,
+                        "cover_url": null,
+                        "custom_verify": "",
+                        "enterprise_verify_reason": "",
+                        "events": null,
+                        "followers_detail": null,
+                        "geofencing": null,
+                        "homepage_bottom_toast": null,
+                        "item_list": null,
+                        "mutual_relation_avatars": null,
+                        "need_points": null,
+                        "nickname": "lensi&roly",
+                        "platform_sync_info": null,
+                        "predicted_age_group": "4",
+                        "relative_users": null,
+                        "search_highlight": null,
+                        "sec_uid": "MS4wLjABAAAAFvHj96jULTJ7i2Im4MXpC-Cp1ZRBiSaffVljjYW1Nmbv_4ceT4ATNNJ-SMlkC9AC",
+                        "shield_edit_field_info": null,
+                        "type_label": null,
+                        "uid": "6772692452979983366",
+                        "unique_id": "lensiroly",
+                        "user_profile_guide": null,
+                        "user_tags": null,
+                        "white_cover_url": null
+                    },
+                    "user_buried": false,
+                    "user_digged": 0
+                }
+            },
+            {
+                "cid": "7590448275989234462",
+                "text": "Lindo 🥰",
+                "create_time": 1767289011,
+                "digg_count": 1,
+                "reply_comment_total": 1,
+                "user": {
+                    "unique_id": "yesenialpez643",
+                    "nickname": "🦋Yesi 🦋",
+                    "avatar_url": "https://p16-common-sign.tiktokcdn-us.com/tos-useast8-avt-0068-tx2/38013d5c18c188611d219f55a654b707~tplv-tiktokx-cropcenter:100:100.jpg?dr=9640&refresh_token=939e6597&x-expires=1770915600&x-signature=N2ui47FfirIf2yF0QMX%2B2VkNetg%3D&t=4d5b0474&ps=13740610&shp=30310797&shcp=ff37627b&idc=useast8",
+                    "sec_uid": "MS4wLjABAAAAqarZnkN5zNoIf5_nPOXk_XWodRoaZ4D9k3PrTidTMeE5wbMnEHO2kcqTbVKESGaP"
+                },
+                "raw_data": {
+                    "allow_download_photo": true,
+                    "author_pin": false,
+                    "aweme_id": "7589036947083988279",
+                    "cid": "7590448275989234462",
+                    "collect_stat": 0,
+                    "comment_language": "pt",
+                    "comment_post_item_ids": null,
+                    "create_time": 1767289011,
+                    "digg_count": 1,
+                    "fold_status": -1,
+                    "image_list": null,
+                    "is_author_digged": true,
+                    "is_comment_translatable": true,
+                    "is_high_purchase_intent": false,
+                    "label_list": [
+                        {
+                            "text": "Liked by creator",
+                            "type": 20
+                        }
+                    ],
+                    "no_show": false,
+                    "reply_comment": [
+                        {
+                            "allow_download_photo": true,
+                            "author_pin": false,
+                            "aweme_id": "7589036947083988279",
+                            "cid": "7590474041174115086",
+                            "collect_stat": 0,
+                            "comment_language": "es",
+                            "comment_post_item_ids": null,
+                            "create_time": 1767294973,
+                            "digg_count": 0,
+                            "fold_status": 0,
+                            "image_list": null,
+                            "is_author_digged": false,
+                            "is_comment_translatable": true,
+                            "is_high_purchase_intent": false,
+                            "label_list": [
+                                {
+                                    "text": "Creator",
+                                    "type": 1
+                                }
+                            ],
+                            "label_text": "Creator",
+                            "label_type": 1,
+                            "no_show": false,
+                            "reply_comment": null,
+                            "reply_id": "7590448275989234462",
+                            "reply_to_reply_id": "0",
+                            "share_info": {
+                                "acl": {
+                                    "code": 1,
+                                    "extra": "{\"is_share_handler_failed\":\"1\"}"
+                                },
+                                "desc": "",
+                                "title": "",
+                                "url": ""
+                            },
+                            "status": 1,
+                            "stick_position": 0,
+                            "text": "Gracias 🥰🥰🥰",
+                            "text_extra": [],
+                            "thread_has_more": false,
+                            "thread_id": -1,
+                            "trans_btn_style": 1,
+                            "user": {
+                                "account_labels": null,
+                                "ad_cover_url": null,
+                                "advance_feature_item_order": null,
+                                "advanced_feature_info": null,
+                                "avatar_thumb": {
+                                    "uri": "tos-useast5-avt-0068-tx/fdb9075f6d24284b5621a1c7a2958c49",
+                                    "url_list": [
+                                        "https://p19-common-sign.tiktokcdn-us.com/tos-useast5-avt-0068-tx/fdb9075f6d24284b5621a1c7a2958c49~tplv-tiktokx-cropcenter:100:100.jpg?dr=9640&refresh_token=5932e44d&x-expires=1770915600&x-signature=Gb6yGH9VLDgbgBxtRrFGI64RgnQ%3D&t=4d5b0474&ps=13740610&shp=30310797&shcp=ff37627b&idc=useast8",
+                                        "https://p16-common-sign.tiktokcdn-us.com/tos-useast5-avt-0068-tx/fdb9075f6d24284b5621a1c7a2958c49~tplv-tiktokx-cropcenter:100:100.jpg?dr=9640&refresh_token=36ac9263&x-expires=1770915600&x-signature=PbUYyp%2BKQ3QleDIxbUnQ20cVljs%3D&t=4d5b0474&ps=13740610&shp=30310797&shcp=ff37627b&idc=useast8",
+                                        "https://p19-common-sign.tiktokcdn-us.com/tos-useast5-avt-0068-tx/fdb9075f6d24284b5621a1c7a2958c49~tplv-tiktokx-cropcenter:100:100.jpeg?dr=9640&refresh_token=12a63a7c&x-expires=1770915600&x-signature=fKx0CNPctCqImgphE6lQBUqTEkU%3D&t=4d5b0474&ps=13740610&shp=30310797&shcp=ff37627b&idc=useast8"
+                                    ],
+                                    "url_prefix": null
+                                },
+                                "bold_fields": null,
+                                "can_message_follow_status_list": null,
+                                "can_set_geofencing": null,
+                                "cha_list": null,
+                                "cover_url": null,
+                                "custom_verify": "",
+                                "enterprise_verify_reason": "",
+                                "events": null,
+                                "followers_detail": null,
+                                "geofencing": null,
+                                "homepage_bottom_toast": null,
+                                "item_list": null,
+                                "mutual_relation_avatars": null,
+                                "need_points": null,
+                                "nickname": "dny_partydecoration",
+                                "platform_sync_info": null,
+                                "predicted_age_group": "-1",
+                                "relative_users": null,
+                                "search_highlight": null,
+                                "sec_uid": "MS4wLjABAAAAwOSeDW2rFGcrpPIMtTECj7m9NyQKEIWKhPpvuBYxYKFxbRx6N7IVqbXQQoqKVtGf",
+                                "shield_edit_field_info": null,
+                                "type_label": null,
+                                "uid": "7179361856557319214",
+                                "unique_id": "dny_partydecoration",
+                                "user_profile_guide": null,
+                                "user_tags": null,
+                                "white_cover_url": null
+                            },
+                            "user_buried": false,
+                            "user_digged": 0
+                        }
+                    ],
+                    "reply_comment_total": 1,
+                    "reply_id": "0",
+                    "reply_to_reply_id": "0",
+                    "share_info": {
+                        "acl": {
+                            "code": 1,
+                            "extra": "{\"is_share_handler_failed\":\"1\"}"
+                        },
+                        "desc": "",
+                        "title": "",
+                        "url": ""
+                    },
+                    "sort_extra_score": {
+                        "reply_score": 0.000512,
+                        "show_more_score": 0.158609
+                    },
+                    "sort_tags": "{\"author_digg\":1,\"author_reply\":1,\"top_list\":1}",
+                    "status": 1,
+                    "stick_position": 0,
+                    "text": "Lindo 🥰",
+                    "text_extra": [],
+                    "thread_has_more": false,
+                    "thread_id": 0,
+                    "trans_btn_style": 1,
+                    "user": {
+                        "account_labels": null,
+                        "ad_cover_url": null,
+                        "advance_feature_item_order": null,
+                        "advanced_feature_info": null,
+                        "avatar_thumb": {
+                            "uri": "tos-useast8-avt-0068-tx2/38013d5c18c188611d219f55a654b707",
+                            "url_list": [
+                                "https://p16-common-sign.tiktokcdn-us.com/tos-useast8-avt-0068-tx2/38013d5c18c188611d219f55a654b707~tplv-tiktokx-cropcenter:100:100.jpg?dr=9640&refresh_token=939e6597&x-expires=1770915600&x-signature=N2ui47FfirIf2yF0QMX%2B2VkNetg%3D&t=4d5b0474&ps=13740610&shp=30310797&shcp=ff37627b&idc=useast8",
+                                "https://p19-common-sign.tiktokcdn-us.com/tos-useast8-avt-0068-tx2/38013d5c18c188611d219f55a654b707~tplv-tiktokx-cropcenter:100:100.jpg?dr=9640&refresh_token=34db19fe&x-expires=1770915600&x-signature=07%2BiE346wUTfE3eQobtK6f0RxGk%3D&t=4d5b0474&ps=13740610&shp=30310797&shcp=ff37627b&idc=useast8",
+                                "https://p16-common-sign.tiktokcdn-us.com/tos-useast8-avt-0068-tx2/38013d5c18c188611d219f55a654b707~tplv-tiktokx-cropcenter:100:100.jpeg?dr=9640&refresh_token=db9e4b76&x-expires=1770915600&x-signature=qm9GucFDFayVLwg8nWgVlaX9F4g%3D&t=4d5b0474&ps=13740610&shp=30310797&shcp=ff37627b&idc=useast8"
+                            ],
+                            "url_prefix": null
+                        },
+                        "bold_fields": null,
+                        "can_message_follow_status_list": null,
+                        "can_set_geofencing": null,
+                        "cha_list": null,
+                        "cover_url": null,
+                        "custom_verify": "",
+                        "enterprise_verify_reason": "",
+                        "events": null,
+                        "followers_detail": null,
+                        "geofencing": null,
+                        "homepage_bottom_toast": null,
+                        "item_list": null,
+                        "mutual_relation_avatars": null,
+                        "need_points": null,
+                        "nickname": "🦋Yesi 🦋",
+                        "platform_sync_info": null,
+                        "predicted_age_group": "",
+                        "relative_users": null,
+                        "search_highlight": null,
+                        "sec_uid": "MS4wLjABAAAAqarZnkN5zNoIf5_nPOXk_XWodRoaZ4D9k3PrTidTMeE5wbMnEHO2kcqTbVKESGaP",
+                        "shield_edit_field_info": null,
+                        "type_label": null,
+                        "uid": "7404511358118904874",
+                        "unique_id": "yesenialpez643",
+                        "user_profile_guide": null,
+                        "user_tags": null,
+                        "white_cover_url": null
+                    },
+                    "user_buried": false,
+                    "user_digged": 0
+                }
+            },
+            {
+                "cid": "7589047945213051661",
+                "text": "es una belleza🥰🥰🥰",
+                "create_time": 1766962968,
+                "digg_count": 1,
+                "reply_comment_total": 1,
+                "user": {
+                    "unique_id": "elianygonzalez96",
+                    "nickname": "ElianyGonzalez🌺",
+                    "avatar_url": "https://p16-common-sign.tiktokcdn-us.com/tos-useast5-avt-0068-tx/986ebc9ba0bc2430a859ab4580ee5eaa~tplv-tiktokx-cropcenter:100:100.jpg?dr=9640&refresh_token=d65b1447&x-expires=1770915600&x-signature=CCRqvxSCeBDKGCNUp4sGZeStLOs%3D&t=4d5b0474&ps=13740610&shp=30310797&shcp=ff37627b&idc=useast8",
+                    "sec_uid": "MS4wLjABAAAA28YmW0dY239MGH0lTzgZyY8uPO3TGkDmNHqATdFkz09zN-smRbgn8dG-y0wKtT2S"
+                },
+                "raw_data": {
+                    "allow_download_photo": true,
+                    "author_pin": false,
+                    "aweme_id": "7589036947083988279",
+                    "cid": "7589047945213051661",
+                    "collect_stat": 0,
+                    "comment_language": "es",
+                    "comment_post_item_ids": null,
+                    "create_time": 1766962968,
+                    "digg_count": 1,
+                    "fold_status": -1,
+                    "image_list": null,
+                    "is_author_digged": true,
+                    "is_comment_translatable": true,
+                    "is_high_purchase_intent": false,
+                    "label_list": [
+                        {
+                            "text": "Liked by creator",
+                            "type": 20
+                        }
+                    ],
+                    "no_show": false,
+                    "reply_comment": [
+                        {
+                            "allow_download_photo": true,
+                            "author_pin": false,
+                            "aweme_id": "7589036947083988279",
+                            "cid": "7589080193064928013",
+                            "collect_stat": 0,
+                            "comment_language": "es",
+                            "comment_post_item_ids": null,
+                            "create_time": 1766970451,
+                            "digg_count": 0,
+                            "fold_status": 0,
+                            "image_list": null,
+                            "is_author_digged": false,
+                            "is_comment_translatable": true,
+                            "is_high_purchase_intent": false,
+                            "label_list": [
+                                {
+                                    "text": "Creator",
+                                    "type": 1
+                                }
+                            ],
+                            "label_text": "Creator",
+                            "label_type": 1,
+                            "no_show": false,
+                            "reply_comment": null,
+                            "reply_id": "7589047945213051661",
+                            "reply_to_reply_id": "0",
+                            "share_info": {
+                                "acl": {
+                                    "code": 1,
+                                    "extra": "{\"is_share_handler_failed\":\"1\"}"
+                                },
+                                "desc": "",
+                                "title": "",
+                                "url": ""
+                            },
+                            "status": 1,
+                            "stick_position": 0,
+                            "text": "Gracias 🥰🥰🥰",
+                            "text_extra": [],
+                            "thread_has_more": false,
+                            "thread_id": -1,
+                            "trans_btn_style": 1,
+                            "user": {
+                                "account_labels": null,
+                                "ad_cover_url": null,
+                                "advance_feature_item_order": null,
+                                "advanced_feature_info": null,
+                                "avatar_thumb": {
+                                    "uri": "tos-useast5-avt-0068-tx/fdb9075f6d24284b5621a1c7a2958c49",
+                                    "url_list": [
+                                        "https://p19-common-sign.tiktokcdn-us.com/tos-useast5-avt-0068-tx/fdb9075f6d24284b5621a1c7a2958c49~tplv-tiktokx-cropcenter:100:100.jpg?dr=9640&refresh_token=5932e44d&x-expires=1770915600&x-signature=Gb6yGH9VLDgbgBxtRrFGI64RgnQ%3D&t=4d5b0474&ps=13740610&shp=30310797&shcp=ff37627b&idc=useast8",
+                                        "https://p16-common-sign.tiktokcdn-us.com/tos-useast5-avt-0068-tx/fdb9075f6d24284b5621a1c7a2958c49~tplv-tiktokx-cropcenter:100:100.jpg?dr=9640&refresh_token=36ac9263&x-expires=1770915600&x-signature=PbUYyp%2BKQ3QleDIxbUnQ20cVljs%3D&t=4d5b0474&ps=13740610&shp=30310797&shcp=ff37627b&idc=useast8",
+                                        "https://p19-common-sign.tiktokcdn-us.com/tos-useast5-avt-0068-tx/fdb9075f6d24284b5621a1c7a2958c49~tplv-tiktokx-cropcenter:100:100.jpeg?dr=9640&refresh_token=12a63a7c&x-expires=1770915600&x-signature=fKx0CNPctCqImgphE6lQBUqTEkU%3D&t=4d5b0474&ps=13740610&shp=30310797&shcp=ff37627b&idc=useast8"
+                                    ],
+                                    "url_prefix": null
+                                },
+                                "bold_fields": null,
+                                "can_message_follow_status_list": null,
+                                "can_set_geofencing": null,
+                                "cha_list": null,
+                                "cover_url": null,
+                                "custom_verify": "",
+                                "enterprise_verify_reason": "",
+                                "events": null,
+                                "followers_detail": null,
+                                "geofencing": null,
+                                "homepage_bottom_toast": null,
+                                "item_list": null,
+                                "mutual_relation_avatars": null,
+                                "need_points": null,
+                                "nickname": "dny_partydecoration",
+                                "platform_sync_info": null,
+                                "predicted_age_group": "-1",
+                                "relative_users": null,
+                                "search_highlight": null,
+                                "sec_uid": "MS4wLjABAAAAwOSeDW2rFGcrpPIMtTECj7m9NyQKEIWKhPpvuBYxYKFxbRx6N7IVqbXQQoqKVtGf",
+                                "shield_edit_field_info": null,
+                                "type_label": null,
+                                "uid": "7179361856557319214",
+                                "unique_id": "dny_partydecoration",
+                                "user_profile_guide": null,
+                                "user_tags": null,
+                                "white_cover_url": null
+                            },
+                            "user_buried": false,
+                            "user_digged": 0
+                        }
+                    ],
+                    "reply_comment_total": 1,
+                    "reply_id": "0",
+                    "reply_to_reply_id": "0",
+                    "share_info": {
+                        "acl": {
+                            "code": 1,
+                            "extra": "{\"is_share_handler_failed\":\"1\"}"
+                        },
+                        "desc": "",
+                        "title": "",
+                        "url": ""
+                    },
+                    "sort_extra_score": {
+                        "reply_score": 0.000176,
+                        "show_more_score": 0.101443
+                    },
+                    "sort_tags": "{\"author_digg\":1,\"author_reply\":1,\"top_list\":1}",
+                    "status": 1,
+                    "stick_position": 0,
+                    "text": "es una belleza🥰🥰🥰",
+                    "text_extra": [],
+                    "thread_has_more": false,
+                    "thread_id": 0,
+                    "trans_btn_style": 1,
+                    "user": {
+                        "account_labels": null,
+                        "ad_cover_url": null,
+                        "advance_feature_item_order": null,
+                        "advanced_feature_info": null,
+                        "avatar_thumb": {
+                            "uri": "tos-useast5-avt-0068-tx/986ebc9ba0bc2430a859ab4580ee5eaa",
+                            "url_list": [
+                                "https://p16-common-sign.tiktokcdn-us.com/tos-useast5-avt-0068-tx/986ebc9ba0bc2430a859ab4580ee5eaa~tplv-tiktokx-cropcenter:100:100.jpg?dr=9640&refresh_token=d65b1447&x-expires=1770915600&x-signature=CCRqvxSCeBDKGCNUp4sGZeStLOs%3D&t=4d5b0474&ps=13740610&shp=30310797&shcp=ff37627b&idc=useast8",
+                                "https://p19-common-sign.tiktokcdn-us.com/tos-useast5-avt-0068-tx/986ebc9ba0bc2430a859ab4580ee5eaa~tplv-tiktokx-cropcenter:100:100.jpg?dr=9640&refresh_token=566cc2ee&x-expires=1770915600&x-signature=NAgBXhvqwCPwT0dh48O5Hw7hM1I%3D&t=4d5b0474&ps=13740610&shp=30310797&shcp=ff37627b&idc=useast8",
+                                "https://p16-common-sign.tiktokcdn-us.com/tos-useast5-avt-0068-tx/986ebc9ba0bc2430a859ab4580ee5eaa~tplv-tiktokx-cropcenter:100:100.jpeg?dr=9640&refresh_token=cc24d135&x-expires=1770915600&x-signature=%2FHLkl8FyoJ4mHcqtmURQcPJRyyE%3D&t=4d5b0474&ps=13740610&shp=30310797&shcp=ff37627b&idc=useast8"
+                            ],
+                            "url_prefix": null
+                        },
+                        "bold_fields": null,
+                        "can_message_follow_status_list": null,
+                        "can_set_geofencing": null,
+                        "cha_list": null,
+                        "cover_url": null,
+                        "custom_verify": "",
+                        "enterprise_verify_reason": "",
+                        "events": null,
+                        "followers_detail": null,
+                        "geofencing": null,
+                        "homepage_bottom_toast": null,
+                        "item_list": null,
+                        "mutual_relation_avatars": null,
+                        "need_points": null,
+                        "nickname": "ElianyGonzalez🌺",
+                        "platform_sync_info": null,
+                        "predicted_age_group": "",
+                        "relative_users": null,
+                        "search_highlight": null,
+                        "sec_uid": "MS4wLjABAAAA28YmW0dY239MGH0lTzgZyY8uPO3TGkDmNHqATdFkz09zN-smRbgn8dG-y0wKtT2S",
+                        "shield_edit_field_info": null,
+                        "type_label": null,
+                        "uid": "7190595357935158315",
+                        "unique_id": "elianygonzalez96",
+                        "user_profile_guide": null,
+                        "user_tags": null,
+                        "white_cover_url": null
+                    },
+                    "user_buried": false,
+                    "user_digged": 0
+                }
+            }
+        ],
+        "cursor": 20,
+        "has_more": false
+    },
+    "timestamp": "2026-02-11T17:48:50.922Z"
+}

+ 191 - 0
docs/api/api返回数据结构/getuserfroile.md

@@ -0,0 +1,191 @@
+# getUserProfile接口
+- 返回的json原数据
+- [
+        {
+            "unique_id": "eventdecorations",
+            "nickname": "One Stop Party Decor",
+            "avatar_url": "https://p16-common-sign.tiktokcdn-us.com/tos-maliva-avt-0068/7328492571123040261~tplv-tiktokx-cropcenter:100:100.webp?biz_tag=tiktok_user.user_cover&dr=9640&idc=useast8&ps=13740610&refresh_token=1a32b26a&shcp=c1333099&shp=30310797&t=4d5b0474&x-expires=1770908400&x-signature=7AKrARwkmT5isrgeggKNxXhc1%2Fc%3D",
+            "signature": "Making your dreams into a reality 🤍\nLocated in Houston Tx.",
+            "follower_count": 698400,
+            "total_favorited": 8920091,
+            "aweme_count": 0,
+            "engagement_rate": "1277.22%",
+            "sec_uid": "MS4wLjABAAAAh2vbftpSBhB0JEuxTo1-uaF6at3nVbphB_026v-SlbC5HXFrqskjSkX7LxUyRE50",
+            "user_info": {
+                "uid": "6821888979579814918",
+                "nickname": "One Stop Party Decor",
+                "signature": "Making your dreams into a reality 🤍\nLocated in Houston Tx.",
+                "avatar_thumb": {
+                    "uri": "tos-maliva-avt-0068/7328492571123040261",
+                    "url_list": [
+                        "https://p16-common-sign.tiktokcdn-us.com/tos-maliva-avt-0068/7328492571123040261~tplv-tiktokx-cropcenter:100:100.webp?biz_tag=tiktok_user.user_cover&dr=9640&idc=useast8&ps=13740610&refresh_token=1a32b26a&shcp=c1333099&shp=30310797&t=4d5b0474&x-expires=1770908400&x-signature=7AKrARwkmT5isrgeggKNxXhc1%2Fc%3D",
+                        "https://p19-common-sign.tiktokcdn-us.com/tos-maliva-avt-0068/7328492571123040261~tplv-tiktokx-cropcenter:100:100.webp?biz_tag=tiktok_user.user_cover&dr=9640&idc=useast8&ps=13740610&refresh_token=1a68d5d2&shcp=c1333099&shp=30310797&t=4d5b0474&x-expires=1770908400&x-signature=0Urk%2BrkGD%2BV1xOURL5h3IaoYj%2BU%3D",
+                        "https://p16-common-sign.tiktokcdn-us.com/tos-maliva-avt-0068/7328492571123040261~tplv-tiktokx-cropcenter:100:100.jpeg?biz_tag=tiktok_user.user_cover&dr=9640&idc=useast8&ps=13740610&refresh_token=902049bc&shcp=c1333099&shp=30310797&t=4d5b0474&x-expires=1770908400&x-signature=2jcrU0vTGRmNQnza80LPp0VvTjI%3D"
+                    ],
+                    "width": 720,
+                    "height": 720,
+                    "url_prefix": null
+                },
+                "follow_status": 0,
+                "follower_count": 698400,
+                "total_favorited": 8920091,
+                "custom_verify": "",
+                "unique_id": "eventdecorations",
+                "room_id": 0,
+                "enterprise_verify_reason": "",
+                "followers_detail": null,
+                "platform_sync_info": null,
+                "geofencing": null,
+                "cover_url": null,
+                "item_list": null,
+                "type_label": null,
+                "ad_cover_url": null,
+                "relative_users": null,
+                "cha_list": null,
+                "sec_uid": "MS4wLjABAAAAh2vbftpSBhB0JEuxTo1-uaF6at3nVbphB_026v-SlbC5HXFrqskjSkX7LxUyRE50",
+                "need_points": null,
+                "homepage_bottom_toast": null,
+                "can_set_geofencing": null,
+                "white_cover_url": null,
+                "user_tags": null,
+                "bold_fields": null,
+                "search_highlight": null,
+                "mutual_relation_avatars": null,
+                "room_id_str": "0",
+                "events": null,
+                "advance_feature_item_order": null,
+                "advanced_feature_info": null,
+                "user_profile_guide": null,
+                "shield_edit_field_info": null,
+                "can_message_follow_status_list": null,
+                "account_labels": null
+            }
+        },
+        {
+            "unique_id": "events_.decoration",
+            "nickname": "Events_.decoration(by meyssi)",
+            "avatar_url": "https://p16-common-sign.tiktokcdn-us.com/tos-alisg-avt-0068/91841c0c25c09c2d38f15ca7fdaaca37~tplv-tiktokx-cropcenter:100:100.webp?biz_tag=tiktok_user.user_cover&dr=9640&idc=useast8&ps=13740610&refresh_token=b70ad480&shcp=c1333099&shp=30310797&t=4d5b0474&x-expires=1770908400&x-signature=YpoSs%2BaVVoDJHXIcO0qm1ZAagS0%3D",
+            "signature": "Organisatrice d'événements \nPrise de rendez-vous sur Instagram/ \n0663119336📲📞\nInstagram :Events_.decoration \nالحجز عبر الانستاغرام 🇩🇿📍ام البواقي",
+            "follower_count": 2388,
+            "total_favorited": 13053,
+            "aweme_count": 0,
+            "engagement_rate": "546.61%",
+            "sec_uid": "MS4wLjABAAAAeWcCD6ynuVQYaofCWsk7KTtJZUDVog5ZAWZpSGacc9dEh3KrFjG7JC6PcoDzw89T",
+            "user_info": {
+                "uid": "7334067617761068037",
+                "nickname": "Events_.decoration(by meyssi)",
+                "signature": "Organisatrice d'événements \nPrise de rendez-vous sur Instagram/ \n0663119336📲📞\nInstagram :Events_.decoration \nالحجز عبر الانستاغرام 🇩🇿📍ام البواقي",
+                "avatar_thumb": {
+                    "uri": "tos-alisg-avt-0068/91841c0c25c09c2d38f15ca7fdaaca37",
+                    "url_list": [
+                        "https://p16-common-sign.tiktokcdn-us.com/tos-alisg-avt-0068/91841c0c25c09c2d38f15ca7fdaaca37~tplv-tiktokx-cropcenter:100:100.webp?biz_tag=tiktok_user.user_cover&dr=9640&idc=useast8&ps=13740610&refresh_token=b70ad480&shcp=c1333099&shp=30310797&t=4d5b0474&x-expires=1770908400&x-signature=YpoSs%2BaVVoDJHXIcO0qm1ZAagS0%3D",
+                        "https://p19-common-sign.tiktokcdn-us.com/tos-alisg-avt-0068/91841c0c25c09c2d38f15ca7fdaaca37~tplv-tiktokx-cropcenter:100:100.webp?biz_tag=tiktok_user.user_cover&dr=9640&idc=useast8&ps=13740610&refresh_token=81931729&shcp=c1333099&shp=30310797&t=4d5b0474&x-expires=1770908400&x-signature=vzyuqFzdr8tR3Qv5GoWHyi101a0%3D",
+                        "https://p16-common-sign.tiktokcdn-us.com/tos-alisg-avt-0068/91841c0c25c09c2d38f15ca7fdaaca37~tplv-tiktokx-cropcenter:100:100.jpeg?biz_tag=tiktok_user.user_cover&dr=9640&idc=useast8&ps=13740610&refresh_token=d60893c9&shcp=c1333099&shp=30310797&t=4d5b0474&x-expires=1770908400&x-signature=CDtZF3odFVPivLc%2FYB%2FeOIpMezs%3D"
+                    ],
+                    "width": 720,
+                    "height": 720,
+                    "url_prefix": null
+                },
+                "follow_status": 0,
+                "follower_count": 2388,
+                "total_favorited": 13053,
+                "custom_verify": "",
+                "unique_id": "events_.decoration",
+                "room_id": 0,
+                "enterprise_verify_reason": "",
+                "followers_detail": null,
+                "platform_sync_info": null,
+                "geofencing": null,
+                "cover_url": null,
+                "item_list": null,
+                "type_label": null,
+                "ad_cover_url": null,
+                "relative_users": null,
+                "cha_list": null,
+                "sec_uid": 
+
+
+- {
+    "success": true,
+    "data": {
+        "aweme_count": 0,
+        "bio_link": null,
+        "is_verified": true,
+        "raw_data": {
+            "userInfo": {
+                "user": {
+                    "id": "6821888979579814918",
+                    "shortId": "",
+                    "uniqueId": "eventdecorations",
+                    "nickname": "One Stop Party Decor",
+                    "avatarLarger": "https://p16-common-sign.tiktokcdn-us.com/tos-maliva-avt-0068/7328492571123040261~tplv-tiktokx-cropcenter:1080:1080.jpeg?dr=9640&refresh_token=e820ddea&x-expires=1770994800&x-signature=hGoJ0iPZ9d%2FV7GAM%2BG2p7Y3GuDQ%3D&t=4d5b0474&ps=13740610&shp=a5d48078&shcp=81f88b70&idc=useast8",
+                    "avatarMedium": "https://p16-common-sign.tiktokcdn-us.com/tos-maliva-avt-0068/7328492571123040261~tplv-tiktokx-cropcenter:720:720.jpeg?dr=9640&refresh_token=22f305a2&x-expires=1770994800&x-signature=HStaTvyZIKQSlbsK%2B2NdWZ87RhU%3D&t=4d5b0474&ps=13740610&shp=a5d48078&shcp=81f88b70&idc=useast8",
+                    "avatarThumb": "https://p16-common-sign.tiktokcdn-us.com/tos-maliva-avt-0068/7328492571123040261~tplv-tiktokx-cropcenter:100:100.jpeg?dr=9640&refresh_token=bb8358ee&x-expires=1770994800&x-signature=kRa65j9VzrGT3ElZ8m%2FTwvvkOZk%3D&t=4d5b0474&ps=13740610&shp=a5d48078&shcp=81f88b70&idc=useast8",
+                    "signature": "Making your dreams into a reality 🤍\nLocated in Houston Tx.",
+                    "createTime": 1588345641,
+                    "verified": false,
+                    "secUid": "MS4wLjABAAAAh2vbftpSBhB0JEuxTo1-uaF6at3nVbphB_026v-SlbC5HXFrqskjSkX7LxUyRE50",
+                    "ftc": false,
+                    "relation": 0,
+                    "openFavorite": false,
+                    "commentSetting": 0,
+                    "commerceUserInfo": {
+                        "commerceUser": false
+                    },
+                    "duetSetting": 0,
+                    "stitchSetting": 0,
+                    "privateAccount": false,
+                    "secret": false,
+                    "isADVirtual": false,
+                    "roomId": "",
+                    "uniqueIdModifyTime": 0,
+                    "ttSeller": false,
+                    "downloadSetting": 0,
+                    "profileTab": {
+                        "showMusicTab": false,
+                        "showQuestionTab": true,
+                        "showPlayListTab": false
+                    },
+                    "followingVisibility": 1,
+                    "recommendReason": "",
+                    "nowInvitationCardUrl": "",
+                    "nickNameModifyTime": 0,
+                    "isEmbedBanned": false,
+                    "canExpPlaylist": true,
+                    "profileEmbedPermission": 1,
+                    "language": "en",
+                    "eventList": [],
+                    "suggestAccountBind": false,
+                    "isOrganization": 0,
+                    "UserStoryStatus": 0
+                },
+                "stats": {
+                    "followerCount": 698400,
+                    "followingCount": 707,
+                    "heart": 8900000,
+                    "heartCount": 8900000,
+                    "videoCount": 206,
+                    "diggCount": 0,
+                    "friendCount": 65
+                },
+                "statsV2": {
+                    "followerCount": "698421",
+                    "followingCount": "707",
+                    "heart": "8920091",
+                    "heartCount": "8920091",
+                    "videoCount": "206",
+                    "diggCount": "0",
+                    "friendCount": "65"
+                },
+                "itemList": []
+            },
+            "shareMeta": {
+                "title": "One Stop Party Decor on TikTok",
+                "desc": "@eventdecorations 698.4k Followers, 707 Following, 8.9m Likes - Watch awesome short videos created by One Stop Party Decor"
+            },
+            "statusCode": 0,
+            "statusMsg": "",
+            "needFix": false
+        }
+    },
+    "timestamp": "2026-02-11T15:11:33.622Z"

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
docs/api/api返回数据结构/user_search.md


+ 0 - 0
docs/task/20260211.md


+ 84 - 0
modules/fmode-tikhub-server/docs/README.md

@@ -0,0 +1,84 @@
+
+# fmode-tikhub-server 模块说明
+
+## 1. 模块概述
+
+`fmode-tikhub-server` 是一个独立的后端集成模块,旨在为项目提供 **TikTok 数据采集与交互能力**。它封装了 TikHub 第三方 API,通过标准化的 RESTful 接口对外提供服务,屏蔽了底层 API Key 管理、签名机制和请求细节。
+
+### 核心作用
+
+1.  **API 代理与封装**:将 TikHub 的原始 API 封装为本地服务接口,前端无需直接与第三方服务交互,避免了 API Key 泄露风险。
+2.  **统一鉴权**:内置了 API Key 和 Token 管理,确保请求的合法性。
+3.  **简化调用**:提供了语义化的接口(如 `/search`, `/users/:id`),比原始 API 更易于理解和使用。
+4.  **易于集成**:设计为 Express 中间件/路由模块,可插拔式地集成到任何 Node.js/Express 主应用中。
+
+---
+
+## 2. 接口能力
+
+模块当前开放了以下核心能力:
+
+- **用户搜索** (`/search`): 根据关键词搜索 TikTok 用户。
+- **用户详情** (`/users/:uniqueId`): 获取指定用户的详细 Profile 信息。
+- **作品列表** (`/users/:secUid/posts`): 分页获取用户的视频作品。
+- **评论采集** (`/posts/:videoId/comments`): 获取指定视频的评论数据。
+
+---
+
+## 3. 前端集成指南
+
+### 3.1 前置条件
+
+在主应用的前端调用此模块前,请确保:
+
+1.  **后端服务已启动**:主应用(如 `server.ts`)必须已启动,并且已挂载了本模块的路由。
+    - 默认挂载路径:`/api/tikhub`
+    - 检查方法:访问 `http://localhost:10003/api/tikhub/search?keyword=test` 应返回 JSON 数据。
+2.  **网络连通性**:前端环境(浏览器或 App)能够访问到后端服务的端口(默认 10003)。
+
+### 3.2 调用注意事项
+
+1.  **Base URL 配置**:
+    - 前端请求的 Base URL 应指向主应用的地址,例如 `http://localhost:10003/api/tikhub`(开发环境)或 `https://api.yourdomain.com/api/tikhub`(生产环境)。
+    - **不要**直接请求 `api.tikhub.io`,否则会跨域且暴露 API Key。
+
+2.  **参数传递**:
+    - 所有接口均为 `GET` 请求。
+    - 参数通常通过 Query String(如 `?keyword=abc`)或 URL Path(如 `/users/elonmusk`)传递。
+    - 分页参数 `cursor` 初始传 `0`,后续请求使用上一次接口返回的 `cursor` 值。
+
+3.  **跨域 (CORS)**:
+    - 如果前端与后端部署在不同域名/端口,后端主应用必须配置 CORS 中间件允许前端域名的访问。
+
+### 3.3 示例代码 (Frontend Service)
+
+建议在前端项目中封装一个 Service 层:
+
+```typescript
+// services/tiktok.service.ts
+
+// 根据环境动态配置 API 地址
+const API_ROOT = import.meta.env.VITE_API_URL || 'http://localhost:10003';
+const TIKHUB_BASE = `${API_ROOT}/api/tikhub`;
+
+export const TikTokService = {
+  // 搜索用户
+  async search(keyword: string) {
+    const res = await fetch(`${TIKHUB_BASE}/search?keyword=${encodeURIComponent(keyword)}`);
+    return await res.json();
+  },
+
+  // 获取详情
+  async getProfile(uniqueId: string) {
+    const res = await fetch(`${TIKHUB_BASE}/users/${uniqueId}`);
+    return await res.json();
+  }
+};
+```
+
+---
+
+## 4. 维护与扩展
+
+- **API Key 更换**:如果 Key 失效,请在 `src/client.ts` 中更新 `API_KEY` 常量,或在初始化路由时传入新 Key。
+- **新增接口**:如需支持更多 TikHub 功能,请参考 `src/api/users.ts` 扩展新的方法,并在 `src/routes.ts` 中添加对应路由。

+ 87 - 0
modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/README.md

@@ -0,0 +1,87 @@
+# @fmode/amazon-sp-api
+
+Amazon Selling Partner API (SP-API) 集成模块,支持多店铺动态配置(基于 Parse Server)。
+
+## 功能特性
+
+- **多店铺支持**:基于 `Shop` 表(Parse Server)动态获取 SP-API 配置。
+- **动态路由**:请求头携带 `shopId` 和 `marketplaceId`,自动路由到正确的 API 端点和店铺配置。
+- **Token 管理**:自动处理 LWA Token 的获取、缓存(数据库持久化)和刷新。
+- **完整接口覆盖**:包括 Orders, Listings, Reports, Notifications, Catalog, Sales, Inventory, Pricing, ExternalFulfillment, CustomerFeedback。
+- **企业级特性**:请求重试(429 处理)、日志记录、类型安全。
+
+## 安装
+
+```bash
+npm install @fmode/amazon-sp-api
+```
+
+## 初始化
+
+在应用启动时,初始化 Parse 配置:
+
+```typescript
+import express from 'express';
+import { createSpApiRouter, initParse } from '@fmode/amazon-sp-api';
+
+const app = express();
+
+// 1. 初始化 Parse (连接到配置中心)
+initParse({
+  appId: 'msq-voc', // 或从环境变量读取
+  serverURL: 'https://server-msq.fmode.cn/parse',
+  // masterKey: 'YOUR_MASTER_KEY' // 如果需要 Server-to-Server 权限
+});
+
+// 2. 挂载 SP-API 路由
+// 注意:不再需要传入静态 client,模块会自动处理动态配置
+app.use('/api/amazon', createSpApiRouter());
+
+app.listen(3000, () => {
+  console.log('Server started on port 3000');
+});
+```
+
+## 请求示例
+
+前端或调用方需要在 Header 中携带 `shopId` 和 `marketplaceId`。
+
+### 获取订单列表
+
+```http
+GET /api/amazon/orders?CreatedAfter=2023-10-01T00:00:00Z
+Headers:
+  shopId: <PARSE_SHOP_OBJECT_ID>
+  marketplaceId: ATVPDKIKX0DER
+```
+
+### 响应格式
+
+```json
+{
+  "success": true,
+  "data": {
+    "payload": {
+      "Orders": [...]
+    }
+  },
+  "timestamp": "2023-10-27T10:00:00.000Z"
+}
+```
+
+## 数据库配置 (Parse Shop 表)
+
+在 Parse Dashboard 中,`Shop` 表需要包含以下字段:
+
+| Field | Type | Description |
+|Data Type| Field Name | Description |
+|---|---|---|
+| Object | `config` | 包含 SP-API 凭证: `{ "clientId": "...", "clientSecret": "...", "refreshToken": "..." }` |
+| String | `sp_api_access_token` | (自动维护) 当前有效的 Access Token |
+| Date | `sp_api_token_expires_at` | (自动维护) Token 过期时间 |
+
+## 错误处理
+
+- **400 Bad Request**: 缺少 `shopId` 头,或 `shopId` 无效,或 Shop 配置不完整。
+- **429 Too Many Requests**: 模块会自动重试(指数退避),如果重试多次仍失败,返回错误。
+- **500 Internal Server Error**: 服务器内部错误或 SP-API 异常。

+ 55 - 0
modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/build.sh

@@ -0,0 +1,55 @@
+#!/bin/bash
+
+###############################################################################
+# fmode-amazon-sp-api 构建脚本
+###############################################################################
+
+set -e
+
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+RED='\033[0;31m'
+NC='\033[0m'
+
+echo -e "${GREEN}Building fmode-amazon-sp-api...${NC}"
+
+if [ ! -f "package.json" ]; then
+    echo -e "${RED}Error: package.json not found${NC}"
+    exit 1
+fi
+
+VERSION=$(node -p "require('./package.json').version" 2>/dev/null || echo "1.0.0")
+DIST_DIR="dist/${VERSION}"
+
+echo -e "${YELLOW}Creating ${DIST_DIR}...${NC}"
+rm -rf "${DIST_DIR}"
+mkdir -p "${DIST_DIR}"
+
+if [ ! -d "node_modules" ]; then
+    echo -e "${YELLOW}Installing dependencies...${NC}"
+    npm install
+fi
+
+echo -e "${YELLOW}Bundling ESM...${NC}"
+npx esbuild src/mod.ts \
+    --bundle \
+    --format=esm \
+    --platform=node \
+    --target=node16 \
+    --outfile="${DIST_DIR}/fmode-amazon-sp-api.js" \
+    --sourcemap \
+    --external:express \
+    --external:axios
+
+echo -e "${YELLOW}Bundling CJS...${NC}"
+npx esbuild src/mod.ts \
+    --bundle \
+    --format=cjs \
+    --platform=node \
+    --target=node16 \
+    --outfile="${DIST_DIR}/fmode-amazon-sp-api.cjs" \
+    --sourcemap \
+    --external:express \
+    --external:axios
+
+echo -e "${GREEN}Build complete!${NC}"

+ 11 - 0
modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/deno.json

@@ -0,0 +1,11 @@
+{
+  "imports": {
+    "axios": "npm:axios@^1.6.0",
+    "express": "npm:express@4.21.2"
+  },
+  "compilerOptions": {
+    "allowJs": true,
+    "lib": ["deno.window"],
+    "strict": false
+  }
+}

+ 23 - 0
modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/docs/202602005-task.md

@@ -0,0 +1,23 @@
+## 任务 202602005
+#### 角色
+- 资深的node.js+ express.js + typescript + postgresql 开发人员,
+
+
+#### 任务描述
+- 我需要完成一个亚马逊多店铺的接口对接模块,每个店铺需要对接的接口的配置信息在数据库的shop 表的config字段中,目前fmode-amazon-sp-api 完成了接对接和鉴权相关功能,但是目前需要将配置信息通过parse查询配置信息,然后使用配置信息进行请求。
+- 1、现在fmode-amazon-sp-api模块完成了亚马逊官方文档的相关接口的对接,但是配置信息是写在代码中的,现在需要将配置信息通过Parse查询配置信息,
+- 2、在每个请求中,需要根据店铺的ID查询对应的配置信息,然后使用配置信息进行请求。所以请求头上需要添加一个shopId字段,用于标识当前请求的店铺ID,然后在路由中根据shopId查询对应的配置信息。
+- 3、如果请求头中没有添加shopId字段,或者添加了错误的shopId字段,需要返回400错误。
+- 4、需要在模块中加入parse 的初始化,对应配置信息放在config.json文件中,然后使用parse.initialize()方法进行初始化。appId: 'msq-voc',serverURL: 'https://server-msq.fmode.cn/parse',
+- 5、在通过请求亚马逊的接口时获取的临时token 需要保存在数据库中,需要添加一个字段,用于保存临时token,并添加一个字段,用于保存临时token的过期时间,需要和shopId关联起来,然后根据shopId查询对应的临时token和过期时间,如果过期了,需要重新获取。
+- 6、在client.ts 文件中baseUrl 是写在代码里面的现在需要从Marketplace获取endpoint字段,前端所有请求中都需要携带marketplaceId字段放在请求头中,然后根据marketplaceId查询对应的endpoint字段,然后替换baseUrl字段的值。
+
+
+#### 完成要求
+- 1、模块时一个拔插式的模块,它可以复用在其他的VOC系统,只是在启动时更换对应parse的appId和serverURL
+- 2、在modules\fmode-amazon-sp-api 目录下输出一个REDME.md文件,描述模块的功能和实现方式,并给出使用示例。
+
+
+
+
+

+ 25 - 0
modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/docs/config.md

@@ -0,0 +1,25 @@
+### 亚马逊配置信息
+- LWA凭证:amzn1.application-oa2-client.afc01e883d4143bcae466247ac5fe692
+
+- AK:AKIA6PQCIWKZRY53YSWS,SK:6pu3F5XX5tS9panWwiRhjTHsDFm1LFwLdVmQkpFv
+
+- arn: aws:iam::995363435187:user/moshengqi
+
+- Client ID:amzn1.application-oa2-client.afc01e883d4143bcae466247ac5fe692
+
+- Client Secret :amzn1.oa2-cs.v1.eb3330dce4b5c835b0d22e855c8db1082632b99ed7226db7114557b3c562d038
+
+
+- 墨西哥, 加拿大, 美国, 巴西 Refresh Token
+  - Refresh Token: Atzr|IwEBIC8ubYHG1E9Li2-_madN1smv4tKNgv9-SZTkW-OxlyeiXVQMbRon_7-ON9s2spa-fBsi6G1KXdk8eSO2SlyM-5BxMUufTTclXDCmDZWHg6wmi6HPwRceGDbWhqjIuPWseNj4uyBBjUcd9F6O_RYzEsu1yss67RcZikPI7t_i-tUhF5W8l2Ufj2Sl9JXGycxNoJKbZzApPVVLwja_JaiAlqhZX4JYM1gfK9dgynJNgcPhnVZRJ0vNOkurQPtSp6_Yix6itvMhN5QeXZwJdd5xW1T7C6TJXsHJLiEPfTeJIEj7m7UwgdDS7YvdryIL8cMl5Gc
+
+- 阿联酋, 德国, 沙特阿拉伯, 英国
+  - Refresh Token: Atzr|IwEBINGdC2C2UYyvQX2pCx_JwxmTFt8E2cB6-kaxktvIeVc5A2R34uQWFfbAKu-1oTD3LFeUAIaqOSG4H8843_5sDQLMO4Zw6T5a7Cbcbt9iALPOIl6McOyHWhr1aDxYZ4W1AcxOrLXoIhbOwSBijCm_IqRGdzHrhS6iyZV_RDDBdE5-wEDL3n7v8VrDcyQ1PWMktxe0VP6AR1UMCa2L1Bgw17iOE5jX0gW67BelEbAUbolwZbKavO-IfK-OlElR3WUL9FNDSTdpIXmy5DDLwgMHUuGjLE0n2FNnINn-1xOuQwSdMVEhSsPhjfL2JMvWjVTQRW0
+
+
+### 问题清单
+
+1、在gongzuo-server中完成了开发如何应用到 实际的应用中
+2、比如现在开发的fmode-amazon-sp-api模块,他是如何读取parse的配置的 
+3、不同应用之间可能数据清洗需要的结果不一致如何解决
+4、fmode/studio 中

+ 63 - 0
modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/docs/local-debug.md

@@ -0,0 +1,63 @@
+# 本地调试指南
+
+## 启动调试服务器
+
+我们提供了一个本地开发服务器 `src/dev-server.ts`,它会连接到 Parse Server 并使用真实的店铺认证流程。
+
+### 1. 启动 Parse Server (先决条件)
+
+确保你有一个正在运行的 Parse Server 实例,并且其中包含了有效的 `Shop` 数据。
+
+### 2. 启动 SP-API 开发服务器
+
+使用以下命令启动本地服务器,并配置 Parse 连接信息:
+
+**PowerShell:**
+
+```powershell
+$env:PARSE_APP_ID="你的AppId"
+$env:PARSE_SERVER_URL="http://localhost:1337/parse"
+$env:PARSE_MASTER_KEY="你的MasterKey"
+npm run dev
+```
+
+**Bash:**
+
+```bash
+PARSE_APP_ID="你的AppId" \
+PARSE_SERVER_URL="http://localhost:1337/parse" \
+PARSE_MASTER_KEY="你的MasterKey" \
+npm run dev
+```
+
+服务器默认运行在 `http://localhost:3333`。
+
+### 3. 测试接口
+
+由于使用了真实的认证中间件,你在请求时必须携带 `shop-objectid` 头部。
+
+**Curl 示例:**
+
+```bash
+curl -H "shop-objectid: YOUR_SHOP_OBJECT_ID" "http://localhost:3333/sp-api/orders?CreatedAfter=2023-10-01&marketplaceIds=ATVPDKIKX0DER"
+```
+
+**Postman 配置:**
+
+1.  Method: GET
+2.  URL: `http://localhost:3333/sp-api/orders`
+3.  Headers:
+    *   Key: `shop-objectid`
+    *   Value: `你的店铺在Parse中的ObjectId`
+4.  Query Params:
+    *   `marketplaceIds`: `ATVPDKIKX0DER`
+    *   `CreatedAfter`: `2023-10-01`
+
+## 原理说明
+
+`src/dev-server.ts` 现在会初始化 Parse SDK,并使用默认的 `shopAuthMiddleware`。
+当收到请求时,中间件会:
+1.  读取 `shop-objectid` 头部。
+2.  查询 Parse 数据库获取对应的 Shop 对象。
+3.  读取 Shop 对象中的配置 (ClientId, ClientSecret 等)。
+4.  如果配置有效,则发起 SP-API 请求。

+ 178 - 0
modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/docs/task.md

@@ -0,0 +1,178 @@
+# 任务:亚马逊 SP-API 模块开发
+
+## 任务概述
+
+开发 `fmode-amazon-sp-api` 模块,集成亚马逊 Selling Partner API (SP-API),首期对接客户反馈 (Customer Feedback) 相关接口。
+**注意:本模块采用 2023 年 10 月 2 日后的简化授权流程,无需 AWS IAM 配置,仅需 LWA Access Token。**
+
+## API 规格
+
+### Customer Feedback (客户反馈)
+
+- **API 名称**: `getItemReviewTopics`
+- **功能**: 检索某件商品的十个最正面和十个最负面的评论主题。
+- **API 地址**: `https://sellingpartnerapi-na.amazon.com/customerFeedback/2024-06-01/items/{asin}/reviews/topics`
+- **请求方法**: `GET`
+- **认证方式**: LWA (Login with Amazon) Access Token (无需 AWS Signature V4)
+- **参考文档**: `docs/亚马逊需要对接的接口.md`
+
+## 模块结构
+
+```
+modules/fmode-amazon-sp-api/
+├── src/
+│   ├── mod.ts           # 主模块导出
+│   ├── client.ts        # SP-API 基础客户端(处理 LWA 认证)
+│   ├── api/
+│   │   └── customerFeedback.ts # Customer Feedback API 实现
+│   ├── types.ts         # 类型定义
+│   └── routes.ts        # Express 路由定义
+├── docs/
+│   ├── task.md          # 任务文档
+│   ├── config.md        # 配置说明
+│   └── 亚马逊需要对接的接口.md # 需求文档
+├── deno.json            # Deno 配置
+├── package.json         # 包元数据
+└── README.md            # 项目文档
+```
+
+## 实现方案
+
+### 文件 1: src/types.ts - 类型定义
+
+定义 SP-API 配置接口、请求参数接口和响应数据接口。
+
+```typescript
+export interface SpApiConfig {
+  clientId: string;
+  clientSecret: string;
+  refreshToken: string;
+  region: string; // e.g., 'us-east-1'
+  sandbox?: boolean; // 可选:是否使用沙箱
+}
+
+export interface GetItemReviewTopicsParams {
+  asin: string;
+  itemName?: string;
+  marketplaceId: string;
+  countryCode?: string;
+  dateRange?: {
+    startDate: string;
+    endDate: string;
+  };
+}
+```
+
+### 文件 2: src/client.ts - 基础客户端
+
+实现 `SpApiClient` 类,封装 SP-API 通用逻辑:
+
+- **认证**: 实现 LWA (Login with Amazon) Token 获取与自动刷新机制。
+- **签名**: **无需 AWS Signature V4 签名**。
+- **请求**: 封装统一的 HTTP 请求方法,自动在 Header 中添加 `x-amz-access-token`。
+
+### 文件 3: src/api/customerFeedback.ts - 业务 API
+
+实现 `CustomerFeedbackApi` 类,继承或使用 `SpApiClient`:
+
+- `async getItemReviewTopics(params: GetItemReviewTopicsParams)`: 调用 `getItemReviewTopics` 接口。
+
+### 文件 4: src/routes.ts - Express 路由
+
+实现以下端点:
+
+| 方法 | 路径 | 描述 |
+|------|------|------|
+| GET | /customerFeedback/reviews/topics | 获取商品评论主题 |
+| GET | /health | 健康状态检查 |
+
+### 文件 5: src/mod.ts - 模块导出
+
+导出所有公共接口:
+- `SpApiClient` 类
+- `CustomerFeedbackApi` 类
+- `createSpApiRoutes` 函数
+- 类型定义
+
+## 配置文件
+
+- `package.json`: 依赖管理 (axios)
+- `deno.json`: Deno 兼容性配置
+
+## 集成步骤
+
+在 `api/routes.ts` 中添加:
+
+```typescript
+import { createSpApiRoutes } from "../modules/fmode-amazon-sp-api/src/mod.ts";
+
+const spApiConfig = {
+  clientId: process.env.SP_API_CLIENT_ID,
+  clientSecret: process.env.SP_API_CLIENT_SECRET,
+  refreshToken: process.env.SP_API_REFRESH_TOKEN,
+  region: "us-east-1"
+};
+
+router.use("/amazon-sp-api", createSpApiRoutes(spApiConfig));
+```
+
+## 测试方案
+
+### 1. 测试端点可用性
+
+```bash
+curl http://localhost:10003/api/amazon-sp-api/health
+```
+
+### 2. 测试获取评论主题
+
+```bash
+curl "http://localhost:10003/api/amazon-sp-api/customerFeedback/reviews/topics?asin=B0BT5K9B2T&marketplaceId=ATVPDKIKX0DER"
+```
+
+## 错误处理
+
+- **参数验证**: 验证 `asin` 和 `marketplaceId` 等必填参数。
+- **API 错误**: 捕获 SP-API 返回的错误 (如 403, 429),并转换为标准化错误响应。
+- **认证错误**: 处理 Token 过期情况。
+
+## 日志格式
+
+```
+[SP-API] Request: GET /customerFeedback/2024-06-01/items/B0BT5K9B2T/reviews/topics
+[SP-API] Params: {"marketplaceId": "ATVPDKIKX0DER"}
+[SP-API] Response: 200 OK
+```
+
+## 安全考虑
+
+- 敏感信息 (Client Secret) 必须通过环境变量或安全配置传递,禁止硬编码。
+- 确保 Access Token 不被泄露。
+- 遵循 Amazon 的数据保护政策 (DPP)。
+
+## 响应格式
+
+**成功响应**:
+```json
+{
+  "success": true,
+  "data": {
+    "asin": "B0BT5K9B2T",
+    "topics": {
+      "positiveTopics": [...],
+      "negativeTopics": [...]
+    }
+  }
+}
+```
+
+**错误响应**:
+```json
+{
+  "success": false,
+  "error": {
+    "code": "InvalidInput",
+    "message": "The ASIN is invalid."
+  }
+}
+```

+ 3922 - 0
modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/docs/亚马逊需要对接的接口.md

@@ -0,0 +1,3922 @@
+# 接口文档地址
+- https://developer-docs.amazon.com/sp-api/lang-zh_CN/reference/welcome-to-api-references
+
+## Customer Feedback【客户反馈】
+- 1. getItemReviewTopics【检索某件商品的十个最正面和十个最负面的评论主题。】
+  - 接口地址:https://sellingpartnerapi-na.amazon.com/customerFeedback/2024-06-01/items/{asin}/reviews/topics
+  - 请求方法:GET
+  ```json
+  {
+    "asin": "B0BT5K9B2T",
+    "itemName": "LANTEFUL 10 Tiers Tall Shoe Rack 20-25 Pairs Boots Organizer Storage Sturdy Narrow Shoe Shelf for Entryway Closets with Hooks Black",
+    "marketplaceId": "ATVPDKIKX0DER",
+    "countryCode": "US",
+    "dateRange": {
+      "startDate": "2024-03-22T08:35:43.717Z",
+      "endDate": "2024-09-22T08:35:43.717Z"
+    },
+    "topics": {
+      "positiveTopics": [
+        {
+          "topic": "Assembly/Installation",
+          "asinMetrics": {
+            "numberOfMentions": 36,
+            "occurrencePercentage": 17.31,
+            "starRatingImpact": 4
+          },
+          "parentAsinMetrics": {
+            "numberOfMentions": 107,
+            "occurrencePercentage": 18.01,
+            "starRatingImpact": 3
+          },
+          "browseNodeMetrics": {
+            "occurrencePercentage": {
+              "allProducts": 16.41
+            },
+            "starRatingImpact": {
+              "allProducts": 1.5
+            }
+          },
+          "childAsinMetrics": {
+            "mostMentions": {
+              "asin": "B08576RCY4",
+              "numberOfMentions": 36
+            },
+            "leastMentions": {
+              "asin": "B08576DCY4",
+              "numberOfMentions": 4
+            }
+          },
+          "reviewSnippets": [
+            "Easy installation!",
+            "Easy to install.",
+            "Easy to assemble"
+          ],
+          "subtopics": [
+            {
+              "subtopic": "Simple and easy to assemble",
+              "metrics": {
+                "numberOfMentions": 9,
+                "occurrencePercentage": 25.01
+              },
+              "reviewSnippets": [
+                "Easy installation!",
+                "Not difficult to assemble.",
+                "Easy to assemble"
+              ]
+            },
+            {
+              "subtopic": "easy to put together and quick",
+              "metrics": {
+                "numberOfMentions": 5,
+                "occurrencePercentage": 13.01
+              },
+              "reviewSnippets": [
+                "Easy to put together!!.",
+                "Putting the product togetherwas alot however this saved so much room in my closet",
+                "love that it came with the one little tool you needed to putit all together!"
+              ]
+            },
+            {
+              "subtopic": "quick and easy to install",
+              "metrics": {
+                "numberOfMentions": 3,
+                "occurrencePercentage": 9.01
+              },
+              "reviewSnippets": [
+                "Easy installation!",
+                "Easy to install.",
+                "install was straight forward."
+              ]
+            }
+          ]
+        },
+        {
+          "topic": "Strong",
+          "asinMetrics": {
+            "numberOfMentions": 36,
+            "occurrencePercentage": 17.31,
+            "starRatingImpact": 4.7
+          },
+          "parentAsinMetrics": {
+            "numberOfMentions": 107,
+            "occurrencePercentage": 18.01,
+            "starRatingImpact": 3.8
+          },
+          "browseNodeMetrics": {
+            "occurrencePercentage": {
+              "allProducts": 16.41
+            },
+            "starRatingImpact": {
+              "allProducts": 1.5
+            }
+          },
+          "childAsinMetrics": {
+            "mostMentions": {
+              "asin": "B08576RCY4",
+              "numberOfMentions": 36
+            },
+            "leastMentions": {
+              "asin": "B08576DCY4",
+              "numberOfMentions": 4
+            }
+          },
+          "reviewSnippets": [
+            "Sturdy and strong.",
+            "Fairly sturdy.",
+            "very sturdy"
+          ],
+          "subtopics": [
+            {
+              "subtopic": "sturdy and well built",
+              "metrics": {
+                "numberOfMentions": 9,
+                "occurrencePercentage": 25.01
+              },
+              "reviewSnippets": [
+                "very sturdy",
+                "Sturdiness is ok.",
+                "Sturdier than expected."
+              ]
+            },
+            {
+              "subtopic": "durable and sturdy",
+              "metrics": {
+                "numberOfMentions": 4,
+                "occurrencePercentage": 12.01
+              },
+              "reviewSnippets": [
+                "This IS sturdy.",
+                "Sturdiness construction.",
+                "it much sturdier than the other two brands that I have purchased."
+              ]
+            },
+            {
+              "subtopic": "strong and durable",
+              "metrics": {
+                "numberOfMentions": 2,
+                "occurrencePercentage": 6.01
+              },
+              "reviewSnippets": [
+                "This IS sturdy.",
+                "Sturdiness construction.",
+                "it much sturdier than the other two brands that I have purchased."
+              ]
+            }
+          ]
+        }
+      ],
+      "negativeTopics": [
+        {
+          "topic": "Assembly/Installation",
+          "asinMetrics": {
+            "numberOfMentions": 36,
+            "occurrencePercentage": 17.31,
+            "starRatingImpact": -2.3
+          },
+          "parentAsinMetrics": {
+            "numberOfMentions": 107,
+            "occurrencePercentage": 18.01,
+            "starRatingImpact": -2.6
+          },
+          "browseNodeMetrics": {
+            "occurrencePercentage": {
+              "allProducts": 16.41
+            },
+            "starRatingImpact": {
+              "allProducts": -1.5
+            }
+          },
+          "childAsinMetrics": {
+            "mostMentions": {
+              "asin": "B08576RCY4",
+              "numberOfMentions": 36
+            },
+            "leastMentions": {
+              "asin": "B08576DCY4",
+              "numberOfMentions": 4
+            }
+          },
+          "reviewSnippets": [
+            "Difficult to assemble",
+            "Difficult to assemble",
+            "impossible to put together"
+          ],
+          "subtopics": [
+            {
+              "subtopic": "difficult to put together",
+              "metrics": {
+                "numberOfMentions": 9,
+                "occurrencePercentage": 25.01
+              },
+              "reviewSnippets": [
+                "impossible to put together",
+                "Had a little hard time putting together either."
+              ]
+            },
+            {
+              "subtopic": "hard to assemble",
+              "metrics": {
+                "numberOfMentions": 6,
+                "occurrencePercentage": 18.01
+              },
+              "reviewSnippets": [
+                "impossible to put together",
+                "Had a little hard time putting together either."
+              ]
+            }
+          ]
+        }
+      ]
+    }
+  }
+  ```
+
+- 2. getItemBrowseNode 【该API返回请求ASIN的关联浏览节点。浏览节点是浏览树中的一个位置,用于亚马逊零售网站上的导航、产品分类和网站内容。】
+  - 接口地址:https://sellingpartnerapi-na.amazon.com/customerFeedback/2024-06-01/items/{asin}/browseNode
+  - 请求方法:GET
+  ```json
+  {
+    "browseNodeId": "37584319",
+    "displayName": "Shoes & Jewelry"
+  }
+  ```
+
+- 3. getBrowseNodeReviewTopics 【检索浏览节点中十个最正面和十个最负面的评论主题。】
+  - 接口地址:https://sellingpartnerapi-na.amazon.com/customerFeedback/2024-06-01/browseNodes/{browseNodeId}/reviews/topics
+  - 请求方法:GET
+  ```json
+  {
+    "browseNodeId": "123456789",
+    "displayName": "Shoe rack",
+    "marketplaceId": "US",
+    "countryCode": "US",
+    "dateRange": {
+      "startDate": "2024-03-22T08:35:43.717Z",
+      "endDate": "2024-03-22T08:35:43.717Z"
+    },
+    "topics": {
+      "positiveTopics": [
+        {
+          "topic": "Assembly/Installation",
+          "browseNodeMetrics": {
+            "occurrencePercentage": {
+              "allProducts": 27.5,
+              "topTwentyFivePercentProducts": 13.5
+            },
+            "starRatingImpact": {
+              "topTwentyFivePercentProducts": 1.85,
+              "allProducts": 1.1
+            }
+          },
+          "reviewSnippets": [
+            "Easy installation!",
+            "Easy to install.",
+            "Easy to assemble"
+          ],
+          "subtopics": [
+            {
+              "subtopic": "Simple and easy to assemble",
+              "metrics": {
+                "occurrencePercentage": 45.47
+              },
+              "reviewSnippets": [
+                "Easy installation!",
+                "Not difficult to assemble.",
+                "Easy to assemble"
+              ]
+            },
+            {
+              "subtopic": "easy to put together and quick",
+              "metrics": {
+                "occurrencePercentage": 24.47
+              },
+              "reviewSnippets": [
+                "Easy to put together!!.",
+                "Putting the product togetherwas alot however this saved so much room in my closet",
+                "love that it came with the one little tool you needed to putit all together!"
+              ]
+            },
+            {
+              "subtopic": "quick and easy to install",
+              "metrics": {
+                "occurrencePercentage": 14.47
+              },
+              "reviewSnippets": [
+                "Easy installation!",
+                "Easy to install.",
+                "install was straight forward."
+              ]
+            }
+          ]
+        },
+        {
+          "topic": "Strong",
+          "browseNodeMetrics": {
+            "occurrencePercentage": {
+              "allProducts": 27.3,
+              "topTwentyFivePercentProducts": 13.5
+            },
+            "starRatingImpact": {
+              "topTwentyFivePercentProducts": 1.85,
+              "allProducts": 1.1
+            }
+          },
+          "reviewSnippets": [
+            "Sturdy and strong.",
+            "Fairly sturdy.",
+            "very sturdy"
+          ],
+          "subtopics": [
+            {
+              "subtopic": "sturdy and well built",
+              "metrics": {
+                "occurrencePercentage": 24.47
+              },
+              "reviewSnippets": [
+                "very sturdy",
+                "Sturdiness is ok.",
+                "Sturdier than expected."
+              ]
+            },
+            {
+              "subtopic": "durable and sturdy",
+              "metrics": {
+                "occurrencePercentage": 18.47
+              },
+              "reviewSnippets": [
+                "This IS sturdy.",
+                "Sturdiness construction.",
+                "it much sturdier than the other two brands that I have purchased."
+              ]
+            },
+            {
+              "subtopic": "strong and durable",
+              "metrics": {
+                "occurrencePercentage": 11.47
+              },
+              "reviewSnippets": [
+                "This IS sturdy.",
+                "Sturdiness construction.",
+                "it much sturdier than the other two brands that I have purchased."
+              ]
+            }
+          ]
+        }
+      ],
+      "negativeTopics": [
+        {
+          "topic": "Assembly/Installation",
+          "browseNodeMetrics": {
+            "occurrencePercentage": {
+              "allProducts": 22,
+              "topTwentyFivePercentProducts": 13.5
+            },
+            "starRatingImpact": {
+              "topTwentyFivePercentProducts": -1.85,
+              "allProducts": -1.1
+            }
+          },
+          "reviewSnippets": [
+            "Difficult to assemble",
+            "Difficult to assemble",
+            "impossible to put together"
+          ],
+          "subtopics": [
+            {
+              "subtopic": "difficult to put together",
+              "metrics": {
+                "occurrencePercentage": 24.47
+              },
+              "reviewSnippets": [
+                "impossible to put together",
+                "Had a little hard time putting together either."
+              ]
+            },
+            {
+              "subtopic": "hard to assemble",
+              "metrics": {
+                "occurrencePercentage": 21.47
+              },
+              "reviewSnippets": [
+                "impossible to put together",
+                "Had a little hard time putting together either."
+              ]
+            }
+          ]
+        }
+      ]
+    }
+  }
+  ```
+- 4. getItemReviewTrends 【检索过去六个月商品的正面和负面评价趋势。】
+  - 接口地址:https://sellingpartnerapi-na.amazon.com/customerFeedback/2024-06-01/items/{asin}/reviews/trends
+  - 请求方法:GET
+  ```json
+  {
+    "asin": "B0BN5K9B2T",
+    "itemName": "LANTEFUL 10 Tiers Tall Shoe Rack 20-25 Pairs Boots Organizer Storage Sturdy Narrow Shoe Shelf for Entryway Closets with Hooks Black",
+    "marketplaceId": "ATVPDKIKX0DER",
+    "countryCode": "US",
+    "dateRange": {
+      "startDate": "2024-03-22T08:35:43.717Z",
+      "endDate": "2024-03-22T08:35:43.717Z"
+    },
+    "reviewTrends": {
+      "positiveTopics": [
+        {
+          "topic": "Quality-Overall ",
+          "trendMetrics": [
+            {
+              "dateRange": {
+                "startDate": "2024-06-01T00:00:00.000Z",
+                "endDate": "2024-06-30T00:00:00.000Z"
+              },
+              "asinMetrics": {
+                "occurrencePercentage": 27.58
+              },
+              "parentAsinMetrics": {
+                "occurrencePercentage": 27.58
+              },
+              "browseNodeMetrics": {
+                "occurrencePercentage": {
+                  "allProducts": 13.6
+                }
+              }
+            },
+            {
+              "dateRange": {
+                "startDate": "2024-05-01T00:00:00.000Z",
+                "endDate": "2024-05-30T00:00:00.000Z"
+              },
+              "asinMetrics": {
+                "occurrencePercentage": 27.58
+              },
+              "parentAsinMetrics": {
+                "occurrencePercentage": 27.58
+              },
+              "browseNodeMetrics": {
+                "occurrencePercentage": {
+                  "allProducts": 13.6
+                }
+              }
+            },
+            {
+              "dateRange": {
+                "startDate": "2024-04-01T00:00:00.000Z",
+                "endDate": "2024-04-30T00:00:00.000Z"
+              },
+              "asinMetrics": {
+                "occurrencePercentage": 27.58
+              },
+              "parentAsinMetrics": {
+                "occurrencePercentage": 27.58
+              },
+              "browseNodeMetrics": {
+                "occurrencePercentage": {
+                  "allProducts": 13.6
+                }
+              }
+            },
+            {
+              "dateRange": {
+                "startDate": "2024-03-01T00:00:00.000Z",
+                "endDate": "2024-03-30T00:00:00.000Z"
+              },
+              "asinMetrics": {
+                "occurrencePercentage": 27.58
+              },
+              "parentAsinMetrics": {
+                "occurrencePercentage": 27.58
+              },
+              "browseNodeMetrics": {
+                "occurrencePercentage": {
+                  "allProducts": 13.6
+                }
+              }
+            },
+            {
+              "dateRange": {
+                "startDate": "2024-02-01T00:00:00.000Z",
+                "endDate": "2024-02-30T00:00:00.000Z"
+              },
+              "asinMetrics": {
+                "occurrencePercentage": 27.58
+              },
+              "parentAsinMetrics": {
+                "occurrencePercentage": 27.58
+              },
+              "browseNodeMetrics": {
+                "occurrencePercentage": {
+                  "allProducts": 13.6
+                }
+              }
+            },
+            {
+              "dateRange": {
+                "startDate": "2024-01-01T00:00:00.000Z",
+                "endDate": "2024-01-30T00:00:00.000Z"
+              },
+              "asinMetrics": {
+                "occurrencePercentage": 27.58
+              },
+              "parentAsinMetrics": {
+                "occurrencePercentage": 27.58
+              },
+              "browseNodeMetrics": {
+                "occurrencePercentage": {
+                  "allProducts": 13.6
+                }
+              }
+            }
+          ]
+        },
+        {
+          "topic": "Assembly/Installation ",
+          "trendMetrics": [
+            {
+              "dateRange": {
+                "startDate": "2024-06-01T00:00:00.000Z",
+                "endDate": "2024-06-30T00:00:00.000Z"
+              },
+              "asinMetrics": {
+                "occurrencePercentage": 27.58
+              },
+              "parentAsinMetrics": {
+                "occurrencePercentage": 27.58
+              },
+              "browseNodeMetrics": {
+                "occurrencePercentage": {
+                  "allProducts": 13.6
+                }
+              }
+            },
+            {
+              "dateRange": {
+                "startDate": "2024-05-01T00:00:00.000Z",
+                "endDate": "2024-05-30T00:00:00.000Z"
+              },
+              "asinMetrics": {
+                "occurrencePercentage": 27.58
+              },
+              "parentAsinMetrics": {
+                "occurrencePercentage": 27.58
+              },
+              "browseNodeMetrics": {
+                "occurrencePercentage": {
+                  "allProducts": 13.6
+                }
+              }
+            },
+            {
+              "dateRange": {
+                "startDate": "2024-04-01T00:00:00.000Z",
+                "endDate": "2024-04-30T00:00:00.000Z"
+              },
+              "asinMetrics": {
+                "occurrencePercentage": 27.58
+              },
+              "parentAsinMetrics": {
+                "occurrencePercentage": 27.58
+              },
+              "browseNodeMetrics": {
+                "occurrencePercentage": {
+                  "allProducts": 13.6
+                }
+              }
+            },
+            {
+              "dateRange": {
+                "startDate": "2024-03-01T00:00:00.000Z",
+                "endDate": "2024-03-30T00:00:00.000Z"
+              },
+              "asinMetrics": {
+                "occurrencePercentage": 27.58
+              },
+              "parentAsinMetrics": {
+                "occurrencePercentage": 27.58
+              },
+              "browseNodeMetrics": {
+                "occurrencePercentage": {
+                  "allProducts": 13.6
+                }
+              }
+            },
+            {
+              "dateRange": {
+                "startDate": "2024-02-01T00:00:00.000Z",
+                "endDate": "2024-02-30T00:00:00.000Z"
+              },
+              "asinMetrics": {
+                "occurrencePercentage": 27.58
+              },
+              "parentAsinMetrics": {
+                "occurrencePercentage": 27.58
+              },
+              "browseNodeMetrics": {
+                "occurrencePercentage": {
+                  "allProducts": 13.6
+                }
+              }
+            },
+            {
+              "dateRange": {
+                "startDate": "2024-01-01T00:00:00.000Z",
+                "endDate": "2024-01-30T00:00:00.000Z"
+              },
+              "asinMetrics": {
+                "occurrencePercentage": 27.58
+              },
+              "parentAsinMetrics": {
+                "occurrencePercentage": 27.58
+              },
+              "browseNodeMetrics": {
+                "occurrencePercentage": {
+                  "allProducts": 13.6
+                }
+              }
+            }
+          ]
+        }
+      ],
+      "negativeTopics": [
+        {
+          "topic": "Weak and fragile",
+          "trendMetrics": [
+            {
+              "dateRange": {
+                "startDate": "2024-06-01T00:00:00.000Z",
+                "endDate": "2024-06-30T00:00:00.000Z"
+              },
+              "asinMetrics": {
+                "occurrencePercentage": 27.58
+              },
+              "parentAsinMetrics": {
+                "occurrencePercentage": 27.58
+              },
+              "browseNodeMetrics": {
+                "occurrencePercentage": {
+                  "allProducts": 13.6
+                }
+              }
+            },
+            {
+              "dateRange": {
+                "startDate": "2024-05-01T00:00:00.000Z",
+                "endDate": "2024-05-30T00:00:00.000Z"
+              },
+              "asinMetrics": {
+                "occurrencePercentage": 27.58
+              },
+              "parentAsinMetrics": {
+                "occurrencePercentage": 27.58
+              },
+              "browseNodeMetrics": {
+                "occurrencePercentage": {
+                  "allProducts": 13.6
+                }
+              }
+            },
+            {
+              "dateRange": {
+                "startDate": "2024-04-01T00:00:00.000Z",
+                "endDate": "2024-04-30T00:00:00.000Z"
+              },
+              "asinMetrics": {
+                "occurrencePercentage": 27.58
+              },
+              "parentAsinMetrics": {
+                "occurrencePercentage": 27.58
+              },
+              "browseNodeMetrics": {
+                "occurrencePercentage": {
+                  "allProducts": 13.6
+                }
+              }
+            },
+            {
+              "dateRange": {
+                "startDate": "2024-03-01T00:00:00.000Z",
+                "endDate": "2024-03-30T00:00:00.000Z"
+              },
+              "asinMetrics": {
+                "occurrencePercentage": 27.58
+              },
+              "parentAsinMetrics": {
+                "occurrencePercentage": 27.58
+              },
+              "browseNodeMetrics": {
+                "occurrencePercentage": {
+                  "allProducts": 13.6
+                }
+              }
+            },
+            {
+              "dateRange": {
+                "startDate": "2024-02-01T00:00:00.000Z",
+                "endDate": "2024-02-30T00:00:00.000Z"
+              },
+              "asinMetrics": {
+                "occurrencePercentage": 27.58
+              },
+              "parentAsinMetrics": {
+                "occurrencePercentage": 27.58
+              },
+              "browseNodeMetrics": {
+                "occurrencePercentage": {
+                  "allProducts": 13.6
+                }
+              }
+            },
+            {
+              "dateRange": {
+                "startDate": "2024-01-01T00:00:00.000Z",
+                "endDate": "2024-01-30T00:00:00.000Z"
+              },
+              "asinMetrics": {
+                "occurrencePercentage": 27.58
+              },
+              "parentAsinMetrics": {
+                "occurrencePercentage": 27.58
+              },
+              "browseNodeMetrics": {
+                "occurrencePercentage": {
+                  "allProducts": 13.6
+                }
+              }
+            }
+          ]
+        }
+      ]
+    }
+  }
+  ```
+
+- 5. getBrowseNodeReviewTrends 【检索过去六个月浏览节点的正面和负面评价趋势。】
+  - 接口地址:https://sellingpartnerapi-na.amazon.com/customerFeedback/2024-06-01/browseNodes/{browseNodeId}/reviews/trends
+  - 请求方法:GET
+  ```json
+  {
+    "browseNodeId": "123456789",
+    "displayName": "Shoe rack",
+    "marketplaceId": "ATVPDKIKX0DER",
+    "countryCode": "US",
+    "dateRange": {
+      "startDate": "2024-03-22T08:35:43.717Z",
+      "endDate": "2024-03-22T08:35:43.717Z"
+    },
+    "reviewTrends": {
+      "positiveTopics": [
+        {
+          "topic": "Quality-Overall ",
+          "trendMetrics": [
+            {
+              "dateRange": {
+                "startDate": "2024-06-01T00:00:00.000Z",
+                "endDate": "2024-06-30T00:00:00.000Z"
+              },
+              "browseNodeMetrics": {
+                "occurrencePercentage": {
+                  "topTwentyFivePercentProducts": 27.58,
+                  "allProducts": 15.78
+                }
+              }
+            },
+            {
+              "dateRange": {
+                "startDate": "2024-07-01T00:00:00.000Z",
+                "endDate": "2024-07-30T00:00:00.000Z"
+              },
+              "browseNodeMetrics": {
+                "occurrencePercentage": {
+                  "topTwentyFivePercentProducts": 27.58,
+                  "allProducts": 15.78
+                }
+              }
+            },
+            {
+              "dateRange": {
+                "startDate": "2024-08-01T00:00:00.000Z",
+                "endDate": "2024-08-30T00:00:00.000Z"
+              },
+              "browseNodeMetrics": {
+                "occurrencePercentage": {
+                  "topTwentyFivePercentProducts": 27.58,
+                  "allProducts": 15.78
+                }
+              }
+            },
+            {
+              "dateRange": {
+                "startDate": "2024-09-01T00:00:00.000Z",
+                "endDate": "2024-09-30T00:00:00.000Z"
+              },
+              "browseNodeMetrics": {
+                "occurrencePercentage": {
+                  "topTwentyFivePercentProducts": 27.58,
+                  "allProducts": 15.78
+                }
+              }
+            },
+            {
+              "dateRange": {
+                "startDate": "2024-10-01T00:00:00.000Z",
+                "endDate": "2024-10-30T00:00:00.000Z"
+              },
+              "browseNodeMetrics": {
+                "occurrencePercentage": {
+                  "topTwentyFivePercentProducts": 27.58,
+                  "allProducts": 15.78
+                }
+              }
+            },
+            {
+              "dateRange": {
+                "startDate": "2024-10-01T00:00:00.000Z",
+                "endDate": "2024-10-30T00:00:00.000Z"
+              },
+              "browseNodeMetrics": {
+                "occurrencePercentage": {
+                  "topTwentyFivePercentProducts": 27.58,
+                  "allProducts": 15.78
+                }
+              }
+            }
+          ]
+        },
+        {
+          "topic": "Assembly/Installation ",
+          "trendMetrics": [
+            {
+              "dateRange": {
+                "startDate": "2024-06-01T00:00:00.000Z",
+                "endDate": "2024-06-30T00:00:00.000Z"
+              },
+              "browseNodeMetrics": {
+                "occurrencePercentage": {
+                  "topTwentyFivePercentProducts": 27.58,
+                  "allProducts": 15.78
+                }
+              }
+            },
+            {
+              "dateRange": {
+                "startDate": "2024-07-01T00:00:00.000Z",
+                "endDate": "2024-07-30T00:00:00.000Z"
+              },
+              "browseNodeMetrics": {
+                "occurrencePercentage": {
+                  "topTwentyFivePercentProducts": 27.58,
+                  "allProducts": 15.78
+                }
+              }
+            },
+            {
+              "dateRange": {
+                "startDate": "2024-08-01T00:00:00.000Z",
+                "endDate": "2024-08-30T00:00:00.000Z"
+              },
+              "browseNodeMetrics": {
+                "occurrencePercentage": {
+                  "topTwentyFivePercentProducts": 27.58,
+                  "allProducts": 15.78
+                }
+              }
+            },
+            {
+              "dateRange": {
+                "startDate": "2024-09-01T00:00:00.000Z",
+                "endDate": "2024-09-30T00:00:00.000Z"
+              },
+              "browseNodeMetrics": {
+                "occurrencePercentage": {
+                  "topTwentyFivePercentProducts": 27.58,
+                  "allProducts": 15.78
+                }
+              }
+            },
+            {
+              "dateRange": {
+                "startDate": "2024-10-01T00:00:00.000Z",
+                "endDate": "2024-10-30T00:00:00.000Z"
+              },
+              "browseNodeMetrics": {
+                "occurrencePercentage": {
+                  "topTwentyFivePercentProducts": 27.58,
+                  "allProducts": 15.78
+                }
+              }
+            },
+            {
+              "dateRange": {
+                "startDate": "2024-10-01T00:00:00.000Z",
+                "endDate": "2024-10-30T00:00:00.000Z"
+              },
+              "browseNodeMetrics": {
+                "occurrencePercentage": {
+                  "topTwentyFivePercentProducts": 27.58,
+                  "allProducts": 15.78
+                }
+              }
+            }
+          ]
+        }
+      ],
+      "negativeTopics": [
+        {
+          "topic": "Weak fragile ",
+          "trendMetrics": [
+            {
+              "dateRange": {
+                "startDate": "2024-06-01T00:00:00.000Z",
+                "endDate": "2024-06-30T00:00:00.000Z"
+              },
+              "browseNodeMetrics": {
+                "occurrencePercentage": {
+                  "topTwentyFivePercentProducts": 27.58,
+                  "allProducts": 15.78
+                }
+              }
+            },
+            {
+              "dateRange": {
+                "startDate": "2024-07-01T00:00:00.000Z",
+                "endDate": "2024-07-30T00:00:00.000Z"
+              },
+              "browseNodeMetrics": {
+                "occurrencePercentage": {
+                  "topTwentyFivePercentProducts": 27.58,
+                  "allProducts": 15.78
+                }
+              }
+            },
+            {
+              "dateRange": {
+                "startDate": "2024-08-01T00:00:00.000Z",
+                "endDate": "2024-08-30T00:00:00.000Z"
+              },
+              "browseNodeMetrics": {
+                "occurrencePercentage": {
+                  "topTwentyFivePercentProducts": 27.58,
+                  "allProducts": 15.78
+                }
+              }
+            },
+            {
+              "dateRange": {
+                "startDate": "2024-09-01T00:00:00.000Z",
+                "endDate": "2024-09-30T00:00:00.000Z"
+              },
+              "browseNodeMetrics": {
+                "occurrencePercentage": {
+                  "topTwentyFivePercentProducts": 27.58,
+                  "allProducts": 15.78
+                }
+              }
+            },
+            {
+              "dateRange": {
+                "startDate": "2024-10-01T00:00:00.000Z",
+                "endDate": "2024-10-30T00:00:00.000Z"
+              },
+              "browseNodeMetrics": {
+                "occurrencePercentage": {
+                  "topTwentyFivePercentProducts": 27.58,
+                  "allProducts": 15.78
+                }
+              }
+            },
+            {
+              "dateRange": {
+                "startDate": "2024-10-01T00:00:00.000Z",
+                "endDate": "2024-10-30T00:00:00.000Z"
+              },
+              "browseNodeMetrics": {
+                "occurrencePercentage": {
+                  "topTwentyFivePercentProducts": 27.58,
+                  "allProducts": 15.78
+                }
+              }
+            }
+          ]
+        },
+        {
+          "topic": "Value for Money ",
+          "trendMetrics": [
+            {
+              "dateRange": {
+                "startDate": "2024-06-01T00:00:00.000Z",
+                "endDate": "2024-06-30T00:00:00.000Z"
+              },
+              "browseNodeMetrics": {
+                "occurrencePercentage": {
+                  "topTwentyFivePercentProducts": 27.58,
+                  "allProducts": 15.78
+                }
+              }
+            },
+            {
+              "dateRange": {
+                "startDate": "2024-07-01T00:00:00.000Z",
+                "endDate": "2024-07-30T00:00:00.000Z"
+              },
+              "browseNodeMetrics": {
+                "occurrencePercentage": {
+                  "topTwentyFivePercentProducts": 27.58,
+                  "allProducts": 15.78
+                }
+              }
+            },
+            {
+              "dateRange": {
+                "startDate": "2024-08-01T00:00:00.000Z",
+                "endDate": "2024-08-30T00:00:00.000Z"
+              },
+              "browseNodeMetrics": {
+                "occurrencePercentage": {
+                  "topTwentyFivePercentProducts": 27.58,
+                  "allProducts": 15.78
+                }
+              }
+            },
+            {
+              "dateRange": {
+                "startDate": "2024-09-01T00:00:00.000Z",
+                "endDate": "2024-09-30T00:00:00.000Z"
+              },
+              "browseNodeMetrics": {
+                "occurrencePercentage": {
+                  "topTwentyFivePercentProducts": 27.58,
+                  "allProducts": 15.78
+                }
+              }
+            },
+            {
+              "dateRange": {
+                "startDate": "2024-10-01T00:00:00.000Z",
+                "endDate": "2024-10-30T00:00:00.000Z"
+              },
+              "browseNodeMetrics": {
+                "occurrencePercentage": {
+                  "topTwentyFivePercentProducts": 27.58,
+                  "allProducts": 15.78
+                }
+              }
+            },
+            {
+              "dateRange": {
+                "startDate": "2024-10-01T00:00:00.000Z",
+                "endDate": "2024-10-30T00:00:00.000Z"
+              },
+              "browseNodeMetrics": {
+                "occurrencePercentage": {
+                  "topTwentyFivePercentProducts": 27.58,
+                  "allProducts": 15.78
+                }
+              }
+            }
+          ]
+        }
+      ]
+    }
+  }
+  ```
+
+- 6. getBrowseNodeReturnTopics 【检索客户在浏览节点退货时提到的主题。】
+  - 接口地址:https://sellingpartnerapi-na.amazon.com/customerFeedback/2024-06-01/browseNodes/{browseNodeId}/returns/topics
+  - 请求方法:GET
+  ```json
+  {
+    "browseNodeId": "123456789",
+    "displayName": "Shoe rack",
+    "marketplaceId": "ATVPDKIKX0DER",
+    "countryCode": "US",
+    "dateRange": {
+      "startDate": "2024-03-22T08:35:43.717Z",
+      "endDate": "2024-03-22T08:35:43.717Z"
+    },
+    "topics": [
+      {
+        "topic": "Weak fragile ",
+        "browseNodeMetrics": {
+          "occurrencePercentage": {
+            "allProducts": 27.5
+          }
+        }
+      },
+      {
+        "topic": "Assembly/Installation ",
+        "browseNodeMetrics": {
+          "occurrencePercentage": {
+            "allProducts": 23.5
+          }
+        }
+      },
+      {
+        "topic": "Quality-Overall ",
+        "browseNodeMetrics": {
+          "occurrencePercentage": {
+            "allProducts": 17.5
+          }
+        }
+      },
+      {
+        "topic": "Value for Money ",
+        "browseNodeMetrics": {
+          "occurrencePercentage": {
+            "allProducts": 10.5
+          }
+        }
+      },
+      {
+        "topic": "Storage Capacity ",
+        "browseNodeMetrics": {
+          "occurrencePercentage": {
+            "allProducts": 7.5
+          }
+        }
+      }
+    ]
+  }
+  ```
+
+- 7. getBrowseNodeReturnTrends 【检索客户在浏览节点退货时提到的话题趋势。】
+  - 接口地址:https://sellingpartnerapi-na.amazon.com/customerFeedback/2024-06-01/browseNodes/{browseNodeId}/returns/trends
+  - 请求方法:GET
+  ```json
+  {
+    "browseNodeId": "123456789",
+    "displayName": "Shoe rack",
+    "marketplaceId": "ATVPDKIKX0DER",
+    "countryCode": "US",
+    "dateRange": {
+      "startDate": "2024-03-22T08:35:43.717Z",
+      "endDate": "2024-03-22T08:35:43.717Z"
+    },
+    "returnTrends": [
+      {
+        "topic": "Weak fragile ",
+        "trendMetrics": [
+          {
+            "dateRange": {
+              "startDate": "2024-06-01T00:00:00.000Z",
+              "endDate": "2024-06-30T00:00:00.000Z"
+            },
+            "browseNodeMetrics": {
+              "occurrencePercentage": {
+                "allProducts": 27.54
+              }
+            }
+          },
+          {
+            "dateRange": {
+              "startDate": "2024-07-01T00:00:00.000Z",
+              "endDate": "2024-07-30T00:00:00.000Z"
+            },
+            "browseNodeMetrics": {
+              "occurrencePercentage": {
+                "allProducts": 27.54
+              }
+            }
+          },
+          {
+            "dateRange": {
+              "startDate": "2024-08-01T00:00:00.000Z",
+              "endDate": "2024-08-30T00:00:00.000Z"
+            },
+            "browseNodeMetrics": {
+              "occurrencePercentage": {
+                "allProducts": 27.54
+              }
+            }
+          },
+          {
+            "dateRange": {
+              "startDate": "2024-09-01T00:00:00.000Z",
+              "endDate": "2024-09-30T00:00:00.000Z"
+            },
+            "browseNodeMetrics": {
+              "occurrencePercentage": {
+                "allProducts": 27.54
+              }
+            }
+          },
+          {
+            "dateRange": {
+              "startDate": "2024-10-01T00:00:00.000Z",
+              "endDate": "2024-10-30T00:00:00.000Z"
+            },
+            "browseNodeMetrics": {
+              "occurrencePercentage": {
+                "allProducts": 27.54
+              }
+            }
+          },
+          {
+            "dateRange": {
+              "startDate": "2024-10-01T00:00:00.000Z",
+              "endDate": "2024-10-30T00:00:00.000Z"
+            },
+            "browseNodeMetrics": {
+              "occurrencePercentage": {
+                "allProducts": 27.54
+              }
+            }
+          }
+        ]
+      },
+      {
+        "topic": "Value for Money ",
+        "trendMetrics": [
+          {
+            "dateRange": {
+              "startDate": "2024-06-01T00:00:00.000Z",
+              "endDate": "2024-06-30T00:00:00.000Z"
+            },
+            "browseNodeMetrics": {
+              "occurrencePercentage": {
+                "allProducts": 27.54
+              }
+            }
+          },
+          {
+            "dateRange": {
+              "startDate": "2024-07-01T00:00:00.000Z",
+              "endDate": "2024-07-30T00:00:00.000Z"
+            },
+            "browseNodeMetrics": {
+              "occurrencePercentage": {
+                "allProducts": 27.54
+              }
+            }
+          },
+          {
+            "dateRange": {
+              "startDate": "2024-08-01T00:00:00.000Z",
+              "endDate": "2024-08-30T00:00:00.000Z"
+            },
+            "browseNodeMetrics": {
+              "occurrencePercentage": {
+                "allProducts": 27.54
+              }
+            }
+          },
+          {
+            "dateRange": {
+              "startDate": "2024-09-01T00:00:00.000Z",
+              "endDate": "2024-09-30T00:00:00.000Z"
+            },
+            "browseNodeMetrics": {
+              "occurrencePercentage": {
+                "allProducts": 27.54
+              }
+            }
+          },
+          {
+            "dateRange": {
+              "startDate": "2024-10-01T00:00:00.000Z",
+              "endDate": "2024-10-30T00:00:00.000Z"
+            },
+            "browseNodeMetrics": {
+              "occurrencePercentage": {
+                "allProducts": 27.54
+              }
+            }
+          },
+          {
+            "dateRange": {
+              "startDate": "2024-10-01T00:00:00.000Z",
+              "endDate": "2024-10-30T00:00:00.000Z"
+            },
+            "browseNodeMetrics": {
+              "occurrencePercentage": {
+                "allProducts": 27.54
+              }
+            }
+          }
+        ]
+      }
+    ]
+  }
+  ```
+
+
+## Order 【订单】
+- 1. getOrders 【获取订单列表】
+  - 接口地址:https://sellingpartnerapi-na.amazon.com/orders/v0/orders
+  - 请求方法:GET
+  ```json
+    {
+      "payload": {
+        "NextToken": "2YgYW55IGNhcm5hbCBwbGVhc3VyZS4",
+        "Orders": [
+          {
+            "AmazonOrderId": "902-3159896-1390916",
+            "PurchaseDate": "2017-01-20T19:49:35Z",
+            "LastUpdateDate": "2017-01-20T19:49:35Z",
+            "OrderStatus": "Pending",
+            "FulfillmentChannel": "MFN",
+            "NumberOfItemsShipped": 0,
+            "NumberOfItemsUnshipped": 0,
+            "PaymentMethod": "Other",
+            "PaymentMethodDetails": [
+              "CreditCard",
+              "GiftCertificate"
+            ],
+            "MarketplaceId": "ATVPDKIKX0DER",
+            "ShipmentServiceLevelCategory": "Standard",
+            "OrderType": "StandardOrder",
+            "EarliestShipDate": "2017-01-20T19:51:16Z",
+            "LatestShipDate": "2017-01-25T19:49:35Z",
+            "IsBusinessOrder": false,
+            "IsPrime": false,
+            "IsAccessPointOrder": false,
+            "IsGlobalExpressEnabled": false,
+            "IsPremiumOrder": false,
+            "IsSoldByAB": false,
+            "IsIBA": false,
+            "ShippingAddress": {
+              "Name": "Michigan address",
+              "AddressLine1": "1 Cross St.",
+              "City": "Canton",
+              "StateOrRegion": "MI",
+              "PostalCode": "48817",
+              "CountryCode": "US"
+            },
+            "BuyerInfo": {
+              "BuyerName": "John Doe",
+              "BuyerTaxInfo": {
+                "CompanyLegalName": "A Company Name"
+              },
+              "PurchaseOrderNumber": "1234567890123"
+            }
+          }
+        ]
+      }
+    }
+  ```
+
+
+  ## 销售
+  - 1. getOrderMetrics 【回报给定买方类型的给定区间内汇总订单指标】
+    - 接口地址:https://sellingpartnerapi-na.amazon.com/sales/v1/orderMetrics
+    - 请求方法:GET
+    ```json
+    {
+      "payload": [
+        {
+          "interval": "string",
+          "unitCount": 0,
+          "orderItemCount": 0,
+          "orderCount": 0,
+          "averageUnitPrice": {
+            "currencyCode": "string",
+            "amount": "string"
+          },
+          "totalSales": {
+            "currencyCode": "string",
+            "amount": "string"
+          }
+        }
+      ],
+      "errors": [
+        {
+          "code": "string",
+          "message": "string",
+          "details": "string"
+        }
+      ]
+    }
+    ```
+## Listings 【列表,搜索并返回卖方合作伙伴商品及其相关详情列表。】
+- 2. searchListingsItems 【列出所有列表项】
+  - 接口地址:https://sellingpartnerapi-na.amazon.com/listings/2021-08-01/items/{sellerId}
+  - 请求方法:GET
+  ```json
+  {
+    "numberOfResults": 3,
+    "pagination": {
+      "nextToken": "xsdflkj324lkjsdlkj3423klkjsdfkljlk2j34klj2l3k4jlksdjl234",
+      "previousToken": "ilkjsdflkj234lkjds234234lkjl234lksjdflkj234234lkjsfsdflkj333d"
+    },
+    "items": [
+      {
+        "sku": "GM-ZDPI-9B4E",
+        "summaries": [
+          {
+            "marketplaceId": "ATVPDKIKX0DER",
+            "asin": "B071VG5N9D",
+            "productType": "LUGGAGE",
+            "conditionType": "new_new",
+            "status": [
+              "BUYABLE"
+            ],
+            "itemName": "Hardside Carry-On Spinner Suitcase Luggage",
+            "createdDate": "2021-02-01T00:00:00Z",
+            "lastUpdatedDate": "2021-03-01T00:00:00Z",
+            "mainImage": {
+              "link": "https://www.example.com/luggage.png",
+              "height": 500,
+              "width": 500
+            }
+          }
+        ],
+        "attributes": {
+          "total_hdmi_ports": [
+            {
+              "value": 4,
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "resolution": [
+            {
+              "language_tag": "en_US",
+              "value": "4K",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "item_weight": [
+            {
+              "unit": "pounds",
+              "value": 107.6,
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "product_subcategory": [
+            {
+              "value": "50400120",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "bullet_point": [
+            {
+              "language_tag": "en_US",
+              "value": "SMART TV WITH UNIVERSAL GUIDE: Simple On-screen Guide is an easy way to find streaming content and live TV shows",
+              "marketplace_id": "ATVPDKIKX0DER"
+            },
+            {
+              "language_tag": "en_US",
+              "value": "100% COLOR VOLUME WITH QUANTUM DOTS: Powered by Quantum dots, Samsung’s 4K QLED TV offers over a billion shades of brilliant color and 100% color volume for exceptional depth of detail that can draw you in to the picture for the best 4K TV experience",
+              "marketplace_id": "ATVPDKIKX0DER"
+            },
+            {
+              "language_tag": "en_US",
+              "value": "QUANTUM PROCESSOR 4K: Intelligently powered processor instantly upscales content to 4K for sharp detail and refined color",
+              "marketplace_id": "ATVPDKIKX0DER"
+            },
+            {
+              "language_tag": "en_US",
+              "value": "QUANTUM HDR 4X: 4K depth of detail with high dynamic range powered by HDR10+ delivers the lightest to darkest colors, scene by scene, for amazing picture realism",
+              "marketplace_id": "ATVPDKIKX0DER"
+            },
+            {
+              "language_tag": "en_US",
+              "value": "AMBIENT MODE: Customizes and complements your living space by turning a blank screen of this big screen TV into enticing visuals including décor, info, photos and artwork",
+              "marketplace_id": "ATVPDKIKX0DER"
+            },
+            {
+              "language_tag": "en_US",
+              "value": "SMART TV FEATURES: OneRemote to control all compatible devices, Bixby voice command, on-screen universal guide, SmartThings to control compatible home appliances and devices, smart speaker expandability with Alexa and Google Assistant compatibility, and more",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "item_dimensions": [
+            {
+              "width": {
+                "unit": "inches",
+                "value": 72.4
+              },
+              "length": {
+                "unit": "inches",
+                "value": 2.4
+              },
+              "height": {
+                "unit": "inches",
+                "value": 41.4
+              },
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "brand": [
+            {
+              "language_tag": "en_US",
+              "value": "SAMSUNG",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "generic_keyword": [
+            {
+              "language_tag": "en_US",
+              "value": "oled 65 inch smart tv qled 75 inch tv 85 inch 4k tv smart tv 4k tv 65 inch qled 82 inch tv 8k tv oled 65 smart tv qled 65 inch 80 inch tv 4k 4k tv shield tv 2018",
+              "marketplace_id": "ATVPDKIKX0DER"
+            },
+            {
+              "language_tag": "en_US",
+              "value": "q9fn qled 2018 oled 4k 4k oled toshiba antenna dvd player outdoor tv kitchen tv fire tv firetv hdtv hd tv android shield tv gaming deals tv ears dvr speakers digital tv antenna tv android tv frame mount",
+              "marketplace_id": "ATVPDKIKX0DER"
+            },
+            {
+              "language_tag": "en_US",
+              "value": "4k hdr tv 70 tv shield tv 90 inch tv gaming tv 75 tv 65 inch 4k tv tv 65 inch smart tv 4k 65 inch 4k tv 65 inch 4k tv 4k tv uhd tv uhd tv 4k",
+              "marketplace_id": "ATVPDKIKX0DER"
+            },
+            {
+              "language_tag": "en_US",
+              "value": "smart tv 4k tv tv tv oled tv 65 inch smart tv 4k tv 65 inch smart tv shield tv 2018 tv 4k oled tv 65 4k tv 4k smart tv 4k hdr tv shield tv gaming tv 65 inch 4k tv tv 65 inch smart tv 4k 65 inch 4k tv 65 inch 4k tv 4k tv uhd tv uhd tv 4k",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "control_method": [
+            {
+              "value": "voice",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "item_package_dimensions": [
+            {
+              "length": {
+                "unit": "centimeters",
+                "value": 26.67
+              },
+              "width": {
+                "unit": "centimeters",
+                "value": 121.92
+              },
+              "height": {
+                "unit": "centimeters",
+                "value": 203.2
+              },
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "image_aspect_ratio": [
+            {
+              "language_tag": "en_US",
+              "value": "16:9",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "part_number": [
+            {
+              "value": "QN82Q60RAFXZA",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "includes_remote": [
+            {
+              "value": true,
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "style": [
+            {
+              "language_tag": "en_US",
+              "value": "TV only",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "item_type_name": [
+            {
+              "language_tag": "en_US",
+              "value": "TV",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "battery": [
+            {
+              "cell_composition": [
+                {
+                  "value": "alkaline"
+                }
+              ],
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "image_contrast_ratio": [
+            {
+              "language_tag": "en_US",
+              "value": "QLED 4K",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "manufacturer": [
+            {
+              "language_tag": "en_US",
+              "value": "Samsung",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "number_of_boxes": [
+            {
+              "value": 1,
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "total_usb_ports": [
+            {
+              "value": 2,
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "model_number": [
+            {
+              "value": "QN82Q60RAFXZA",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "supplier_declared_dg_hz_regulation": [
+            {
+              "value": "not_applicable",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "num_batteries": [
+            {
+              "quantity": 2,
+              "type": "aaa",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "california_proposition_65": [
+            {
+              "compliance_type": "on_product_combined_cancer_reproductive",
+              "marketplace_id": "ATVPDKIKX0DER"
+            },
+            {
+              "compliance_type": "chemical",
+              "chemical_names": [
+                "di_2_ethylhexyl_phthalate_dehp"
+              ],
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "display": [
+            {
+              "resolution_maximum": [
+                {
+                  "unit": "pixels",
+                  "language_tag": "en_US",
+                  "value": "3840 x 2160"
+                }
+              ],
+              "size": [
+                {
+                  "unit": "inches",
+                  "value": 82
+                }
+              ],
+              "type": [
+                {
+                  "language_tag": "en_US",
+                  "value": "QLED"
+                }
+              ],
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "item_name": [
+            {
+              "language_tag": "en_US",
+              "value": "Samsung QN82Q60RAFXZA Flat 82-Inch QLED 4K Q60 Series (2019) Ultra HD Smart TV with HDR and Alexa Compatibility",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "list_price": [
+            {
+              "currency": "USD",
+              "value": 3799.99,
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "batteries_required": [
+            {
+              "value": false,
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "includes_rechargable_battery": [
+            {
+              "value": false,
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "product_site_launch_date": [
+            {
+              "value": "2019-03-11T08:00:01.000Z",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "product_category": [
+            {
+              "value": "50400100",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "batteries_included": [
+            {
+              "value": false,
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "connectivity_technology": [
+            {
+              "language_tag": "en_US",
+              "value": "Bluetooth",
+              "marketplace_id": "ATVPDKIKX0DER"
+            },
+            {
+              "language_tag": "en_US",
+              "value": "USB",
+              "marketplace_id": "ATVPDKIKX0DER"
+            },
+            {
+              "language_tag": "en_US",
+              "value": "Wireless",
+              "marketplace_id": "ATVPDKIKX0DER"
+            },
+            {
+              "language_tag": "en_US",
+              "value": "HDMI",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "included_components": [
+            {
+              "language_tag": "en_US",
+              "value": "QLED Standard Smart Remote",
+              "marketplace_id": "ATVPDKIKX0DER"
+            },
+            {
+              "language_tag": "en_US",
+              "value": "Power Cable",
+              "marketplace_id": "ATVPDKIKX0DER"
+            },
+            {
+              "language_tag": "en_US",
+              "value": "Stand",
+              "marketplace_id": "ATVPDKIKX0DER"
+            },
+            {
+              "language_tag": "en_US",
+              "value": "Samsung Smart Control",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "specification_met": [
+            {
+              "language_tag": "en_US",
+              "value": "",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "parental_control_technology": [
+            {
+              "value": "V-Chip",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "power_consumption": [
+            {
+              "unit": "watts",
+              "value": 120,
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "cpsia_cautionary_statement": [
+            {
+              "value": "no_warning_applicable",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "item_type_keyword": [
+            {
+              "value": "qled-televisions",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "number_of_items": [
+            {
+              "value": 1,
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "warranty_description": [
+            {
+              "language_tag": "en_US",
+              "value": "1 year manufacturer",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "max_resolution": [
+            {
+              "unit": "pixels",
+              "value": 8.3,
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "color": [
+            {
+              "language_tag": "en_US",
+              "value": "Black",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "screen_surface_description": [
+            {
+              "language_tag": "en_US",
+              "value": "Flat",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "item_package_weight": [
+            {
+              "unit": "kilograms",
+              "value": 62.142,
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "speaker_type": [
+            {
+              "language_tag": "en_US",
+              "value": "2CH",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "supported_internet_services": [
+            {
+              "language_tag": "en_US",
+              "value": "Amazon Instant Video",
+              "marketplace_id": "ATVPDKIKX0DER"
+            },
+            {
+              "language_tag": "en_US",
+              "value": "YouTube",
+              "marketplace_id": "ATVPDKIKX0DER"
+            },
+            {
+              "language_tag": "en_US",
+              "value": "Netflix",
+              "marketplace_id": "ATVPDKIKX0DER"
+            },
+            {
+              "language_tag": "en_US",
+              "value": "Hulu",
+              "marketplace_id": "ATVPDKIKX0DER"
+            },
+            {
+              "language_tag": "en_US",
+              "value": "Browser",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "tuner_technology": [
+            {
+              "language_tag": "en_US",
+              "value": "Analog Tuner",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "controller_type": [
+            {
+              "language_tag": "en_US",
+              "value": "SmartThings",
+              "marketplace_id": "ATVPDKIKX0DER"
+            },
+            {
+              "language_tag": "en_US",
+              "value": "Voice Control",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "special_feature": [
+            {
+              "language_tag": "en_US",
+              "value": "100% Color Volume with Quantum Dot; Quantum Processor 4K; Ambient Mode; Quantum HDR 4X; Real Game Enhancer",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "wireless_communication_technology": [
+            {
+              "language_tag": "en_US",
+              "value": "Wi-Fi::Wi-Fi Direct::Bluetooth",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "model_year": [
+            {
+              "value": 2019,
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "power_source_type": [
+            {
+              "language_tag": "en_US",
+              "value": "Corded Electric",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "street_date": [
+            {
+              "value": "2019-03-21T00:00:01Z",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "mounting_type": [
+            {
+              "language_tag": "en_US",
+              "value": "Table Mount",
+              "marketplace_id": "ATVPDKIKX0DER"
+            },
+            {
+              "language_tag": "en_US",
+              "value": "Wall Mount",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "refresh_rate": [
+            {
+              "unit": "hertz",
+              "language_tag": "en_US",
+              "value": "120",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ]
+        },
+        "offers": [
+          {
+            "marketplaceId": "ATVPDKIKX0DER",
+            "offerType": "B2C",
+            "price": {
+              "currencyCode": "USD",
+              "amount": "100.00"
+            }
+          }
+        ],
+        "fulfillmentAvailability": [
+          {
+            "fulfillmentChannelCode": "DEFAULT",
+            "quantity": 100
+          }
+        ],
+        "issues": [
+          {
+            "code": "90220",
+            "message": "'size' is required but not supplied.",
+            "severity": "ERROR",
+            "attributeNames": [
+              "size"
+            ],
+            "categories": [
+              "MISSING_ATTRIBUTE"
+            ]
+          },
+          {
+            "code": "18027",
+            "message": "We believe the main image has text, logo, graphic or watermark which is not permitted for this product type. Submit a compliant image to lift the suppression. Also refer to Product image requirements.",
+            "severity": "ERROR",
+            "categories": [
+              "INVALID_IMAGE"
+            ],
+            "enforcements": {
+              "actions": [
+                {
+                  "action": "SEARCH_SUPPRESSED"
+                }
+              ],
+              "exemption": {
+                "status": "EXEMPT_UNTIL_EXPIRY_DATE",
+                "expiryDate": "2025-05-28T00:36:48.914Z"
+              }
+            }
+          },
+          {
+            "code": "99300",
+            "message": "Product Detail Page Rules Violation (Inaccurate information on product detail page)",
+            "severity": "ERROR",
+            "categories": [],
+            "enforcements": {
+              "actions": [
+                {
+                  "action": "ATTRIBUTE_SUPPRESSED"
+                }
+              ],
+              "exemption": {
+                "status": "EXEMPT"
+              }
+            }
+          },
+          {
+            "code": "18155",
+            "message": "The 'minimum price' is greater than the selling price (excluding shipping) for the listing. Review and update your price and/or minimum price.",
+            "severity": "ERROR",
+            "categories": [
+              "INVALID_PRICE"
+            ],
+            "enforcements": {
+              "actions": [
+                {
+                  "action": "LISTING_SUPPRESSED"
+                }
+              ],
+              "exemption": {
+                "status": "NOT_EXEMPT"
+              }
+            }
+          },
+          {
+            "code": "18742",
+            "message": "Restricted Products Policy Violation",
+            "severity": "ERROR",
+            "categories": [],
+            "enforcements": {
+              "actions": [
+                {
+                  "action": "CATALOG_ITEM_REMOVED"
+                }
+              ],
+              "exemption": {
+                "status": "NOT_EXEMPT"
+              }
+            }
+          }
+        ]
+      }
+    ]
+  }
+  ```
+- 3. getListingsItem 【获取列表项详情】
+  - 接口地址: https://sellingpartnerapi-na.amazon.com/externalFulfillment/2024-09-11/returns
+  - 请求方法:get
+  ```json
+    {
+    "sku": "GM-ZDPI-9B4E",
+    "summaries": [
+      {
+        "marketplaceId": "ATVPDKIKX0DER",
+        "asin": "B071VG5N9D",
+        "productType": "LUGGAGE",
+        "conditionType": "new_new",
+        "status": [
+          "BUYABLE"
+        ],
+        "itemName": "Hardside Carry-On Spinner Suitcase Luggage",
+        "createdDate": "2021-02-01T00:00:00Z",
+        "lastUpdatedDate": "2021-03-01T00:00:00Z",
+        "mainImage": {
+          "link": "https://www.example.com/luggage.png",
+          "height": 500,
+          "width": 500
+        }
+      }
+    ],
+    "offers": [
+      {
+        "marketplaceId": "ATVPDKIKX0DER",
+        "offerType": "B2C",
+        "price": {
+          "currencyCode": "USD",
+          "amount": "100.00"
+        },
+        "audience": {
+          "value": "ALL",
+          "displayName": "Sell on Amazon"
+        }
+      }
+    ],
+    "fulfillmentAvailability": [
+      {
+        "fulfillmentChannelCode": "DEFAULT",
+        "quantity": 100
+      }
+    ],
+    "issues": [
+      {
+        "code": "90220",
+        "message": "'size' is required but not supplied.",
+        "severity": "ERROR",
+        "attributeNames": [
+          "size"
+        ],
+        "categories": [
+          "MISSING_ATTRIBUTE"
+        ]
+      },
+      {
+        "code": "18027",
+        "message": "We believe the main image has text, logo, graphic, or watermark that is not permitted for this product type. Submit a compliant image to lift the suppression. Also refer to Product image requirements.",
+        "severity": "ERROR",
+        "categories": [
+          "INVALID_IMAGE"
+        ],
+        "enforcements": {
+          "actions": [
+            {
+              "action": "SEARCH_SUPPRESSED"
+            }
+          ],
+          "exemption": {
+            "status": "EXEMPT_UNTIL_EXPIRY_DATE",
+            "expiryDate": "2025-05-28T00:36:48.914Z"
+          }
+        }
+      },
+      {
+        "code": "99300",
+        "message": "Product Detail Page Rules Violation (Inaccurate information on product detail page)",
+        "severity": "ERROR",
+        "categories": [],
+        "enforcements": {
+          "actions": [
+            {
+              "action": "ATTRIBUTE_SUPPRESSED"
+            }
+          ],
+          "exemption": {
+            "status": "EXEMPT"
+          }
+        }
+      },
+      {
+        "code": "18155",
+        "message": "The 'minimum price' is greater than the selling price (excluding shipping) for the listing. Review and update your price and/or minimum price.",
+        "severity": "ERROR",
+        "categories": [
+          "INVALID_PRICE"
+        ],
+        "enforcements": {
+          "actions": [
+            {
+              "action": "LISTING_SUPPRESSED"
+            }
+          ],
+          "exemption": {
+            "status": "NOT_EXEMPT"
+          }
+        }
+      },
+      {
+        "code": "18742",
+        "message": "Restricted Products Policy Violation",
+        "severity": "ERROR",
+        "categories": [],
+        "enforcements": {
+          "actions": [
+            {
+              "action": "CATALOG_ITEM_REMOVED"
+            }
+          ],
+          "exemption": {
+            "status": "NOT_EXEMPT"
+          }
+        }
+      }
+    ]
+  }
+  ```
+
+## External Fulfillment 【外部履行】
+- 1. listReturns 【退货列表】
+  - 接口地址:https://sellingpartnerapi-na.amazon.com/externalFulfillment/2024-09-11/returns
+  - 请求方法:GET
+  ```json
+  {
+    "nextToken": "eyJsYXN0VXBkYXRlZFRpbWVzdGFtcCI6eyJzIjpu",
+    "returns": [
+      {
+        "marketplaceChannelDetails": {
+          "channelSku": "SerialYT1",
+          "customerOrderId": "107-7727827-8514700",
+          "returnLocationId": "EDD5",
+          "shipmentId": "D39CwZpdj",
+          "marketplaceChannel": {
+            "marketplaceName": "AMAZON_IN",
+            "channelName": "FBA"
+          }
+        },
+        "otpDetails": {
+          "otp": "703669",
+          "validTill": 1612933142
+        },
+        "packageDeliveryMode": "WITH_OTP",
+        "creationDateTime": "2021-03-18T15:23:58Z",
+        "fulfillmentLocationId": "450a6661-1a97-4abd-99e4-45714bcf4439",
+        "id": "ee39cdd9-9caa-47b6-a5b1-7b6a1c6d43d1",
+        "lastUpdatedDateTime": "2021-03-18T20:13:08Z",
+        "merchantSku": "SerialYT1",
+        "numberOfUnits": 1,
+        "returnMetadata": {
+          "invoiceInformation": {
+            "id": "EDD5-101"
+          },
+          "returnReason": "Missed",
+          "rmaId": "Test",
+          "fulfillmentOrderId": "10d1db2e-a546-4d64-a5da-577d5bf9420d"
+        },
+        "returnShippingInfo": {
+          "forwardTrackingInfo": {
+            "carrierName": "ATS",
+            "trackingId": "156121269663"
+          }
+        },
+        "returnType": "CUSTOMER",
+        "status": "CARRIER_NOTIFIED_TO_PICK_UP_FROM_CUSTOMER"
+      },
+      {
+        "marketplaceChannelDetails": {
+          "channelSku": "FBA_PERF_BOOK",
+          "customerOrderId": "107-7727827-8514701",
+          "returnLocationId": "EDD5",
+          "shipmentId": "DXPQ1wCCZ",
+          "marketplaceChannel": {
+            "marketplaceName": "AMAZON_IN$",
+            "channelName": "FBA"
+          }
+        },
+        "otpDetails": {
+          "otp": "703669",
+          "validTill": 1612933142
+        },
+        "packageDeliveryMode": "WITH_OTP",
+        "creationDateTime": "2021-04-02T10:13:50Z",
+        "fulfillmentLocationId": "450a6661-1a97-4abd-99e4-45714bcf4439",
+        "id": "add6bfb3-8d15-4c79-8bcf-810fe12b163c",
+        "lastUpdatedDateTime": "2021-04-02T15:23:58Z",
+        "merchantSku": "FBA_PERF_BOOK",
+        "numberOfUnits": 1,
+        "returnMetadata": {
+          "invoiceInformation": {
+            "id": "EDD5-92"
+          },
+          "returnReason": "Missed",
+          "rmaId": "Test"
+        },
+        "returnShippingInfo": {
+          "forwardTrackingInfo": {
+            "carrierName": "ATS",
+            "trackingId": "151987126617"
+          },
+          "reverseTrackingInfo": {
+            "carrierName": "ATS",
+            "trackingId": "19124773658"
+          }
+        },
+        "returnType": "CUSTOMER",
+        "status": "CREATED"
+      }
+    ]
+  }
+  ```
+
+- 2. getReturn 【获取退货详情】
+  - 接口地址:https://sellingpartnerapi-na.amazon.com/externalFulfillment/2024-09-11/returns/{returnId}
+  - 请求方法:GET
+  ```json
+  {
+    "marketplaceChannelDetails": {
+      "channelSku": "SerialYT1",
+      "customerOrderId": "107-7727827-8514700",
+      "returnLocationId": "EDD5",
+      "shipmentId": "D39CwZpdj",
+      "marketplaceChannel": {
+        "marketplaceName": "AMAZON_IN$",
+        "channelName": "FBA"
+      }
+    },
+    "otpDetails": {
+      "otp": "703669",
+      "validTill": 1612933142
+    },
+    "packageDeliveryMode": "WITH_OTP",
+    "creationDateTime": "2021-05-08T01:23:33Z",
+    "fulfillmentLocationId": "450a6661-1a97-4abd-99e4-45714bcf4439",
+    "id": "ee39cdd9-9caa-47b6-a5b1-7b6a1c6d43d1",
+    "lastUpdatedDateTime": "2021-05-08T10:23:38Z",
+    "merchantSku": "SerialYT1",
+    "numberOfUnits": 1,
+    "returnMetadata": {
+      "invoiceInformation": {
+        "id": "EDD5-101"
+      },
+      "returnReason": "Missed",
+      "rmaId": "Test"
+    },
+    "returnShippingInfo": {
+      "forwardTrackingInfo": {
+        "carrierName": "ATS",
+        "trackingId": "156121269663"
+      },
+      "reverseTrackingInfo": {
+        "carrierName": "ATS",
+        "trackingId": "19124773658"
+      }
+    },
+    "returnType": "CUSTOMER",
+    "status": "CARRIER_NOTIFIED_TO_PICK_UP_FROM_CUSTOMER"
+  }
+  ```
+
+## Catalog Items 【类目】
+- 1. listCatalogCategories 【列表目录分类- 返回商品所属的父类别,基于指定的ASIN或SellerSKU。】
+  - 接口地址:https://sellingpartnerapi-na.amazon.com/catalog/v0/categories
+  - 请求方法:GET
+  ```json
+  {
+    "payload": [
+      {
+        "ProductCategoryId": "string",
+        "ProductCategoryName": "string",
+        "parent": {}
+      }
+    ],
+    "errors": [
+      {
+        "code": "string",
+        "message": "string",
+        "details": "string"
+      }
+    ]
+  }
+  ```
+- 2. searchCatalogItems 【搜索亚马逊目录商品及相关信息列表。您可以按标识符或关键词搜索。】
+  - 接口地址:https://sellingpartnerapi-na.amazon.com/catalog/2022-04-01/items
+  - 请求方法:GET
+  ```json
+  {
+    "numberOfResults": 1,
+    "pagination": {
+      "nextToken": "xsdflkj324lkjsdlkj3423klkjsdfkljlk2j34klj2l3k4jlksdjl234",
+      "previousToken": "ilkjsdflkj234lkjds234234lkjl234lksjdflkj234234lkjsfsdflkj333d"
+    },
+    "refinements": {
+      "brands": [
+        {
+          "numberOfResults": 1,
+          "brandName": "SAMSUNG"
+        }
+      ],
+      "classifications": [
+        {
+          "numberOfResults": 1,
+          "displayName": "Electronics",
+          "classificationId": "493964"
+        }
+      ]
+    },
+    "items": [
+      {
+        "asin": "B07N4M94X4",
+        "attributes": {
+          "total_hdmi_ports": [
+            {
+              "value": 4,
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "resolution": [
+            {
+              "language_tag": "en_US",
+              "value": "4K",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "item_weight": [
+            {
+              "unit": "pounds",
+              "value": 107.6,
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "product_subcategory": [
+            {
+              "value": "50400120",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "bullet_point": [
+            {
+              "language_tag": "en_US",
+              "value": "SMART TV WITH UNIVERSAL GUIDE: Simple on-screen Guide is an easy way to find streaming content and live TV shows",
+              "marketplace_id": "ATVPDKIKX0DER"
+            },
+            {
+              "language_tag": "en_US",
+              "value": "100% COLOR VOLUME WITH QUANTUM DOTS: Powered by Quantum dots, Samsung’s 4K QLED TV offers over a billion shades of brilliant color and 100% color volume for exceptional depth of detail that will draw you in to the picture for the best 4K TV experience",
+              "marketplace_id": "ATVPDKIKX0DER"
+            },
+            {
+              "language_tag": "en_US",
+              "value": "QUANTUM PROCESSOR 4K: Intelligently powered processor instantly upscales content to 4K for sharp detail and refined color",
+              "marketplace_id": "ATVPDKIKX0DER"
+            },
+            {
+              "language_tag": "en_US",
+              "value": "QUANTUM HDR 4X: 4K depth of detail with high dynamic range powered by HDR10+ delivers the lightest to darkest colors, scene by scene, for amazing picture realism",
+              "marketplace_id": "ATVPDKIKX0DER"
+            },
+            {
+              "language_tag": "en_US",
+              "value": "AMBIENT MODE: Customizes and complements your living space by turning a blank screen of this big screen TV into enticing visuals including décor, info, photos and artwork",
+              "marketplace_id": "ATVPDKIKX0DER"
+            },
+            {
+              "language_tag": "en_US",
+              "value": "SMART TV FEATURES: OneRemote to control all compatible devices, Bixby voice command, on-screen universal guide, SmartThings to control compatible home appliances and devices, smart speaker expandability with Alexa and Google Assistant compatibility, and more",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "item_dimensions": [
+            {
+              "width": {
+                "unit": "inches",
+                "value": 72.4
+              },
+              "length": {
+                "unit": "inches",
+                "value": 2.4
+              },
+              "height": {
+                "unit": "inches",
+                "value": 41.4
+              },
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "brand": [
+            {
+              "language_tag": "en_US",
+              "value": "SAMSUNG",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "control_method": [
+            {
+              "value": "voice",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "item_package_dimensions": [
+            {
+              "length": {
+                "unit": "centimeters",
+                "value": 26.67
+              },
+              "width": {
+                "unit": "centimeters",
+                "value": 121.92
+              },
+              "height": {
+                "unit": "centimeters",
+                "value": 203.2
+              },
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "image_aspect_ratio": [
+            {
+              "language_tag": "en_US",
+              "value": "16:9",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "part_number": [
+            {
+              "value": "QN82Q60RAFXZA",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "includes_remote": [
+            {
+              "value": true,
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "style": [
+            {
+              "language_tag": "en_US",
+              "value": "TV only",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "item_type_name": [
+            {
+              "language_tag": "en_US",
+              "value": "TV",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "battery": [
+            {
+              "cell_composition": [
+                {
+                  "value": "alkaline"
+                }
+              ],
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "image_contrast_ratio": [
+            {
+              "language_tag": "en_US",
+              "value": "QLED 4K",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "manufacturer": [
+            {
+              "language_tag": "en_US",
+              "value": "Samsung",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "number_of_boxes": [
+            {
+              "value": 1,
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "total_usb_ports": [
+            {
+              "value": 2,
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "model_number": [
+            {
+              "value": "QN82Q60RAFXZA",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "supplier_declared_dg_hz_regulation": [
+            {
+              "value": "not_applicable",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "num_batteries": [
+            {
+              "quantity": 2,
+              "type": "aaa",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "california_proposition_65": [
+            {
+              "compliance_type": "on_product_combined_cancer_reproductive",
+              "marketplace_id": "ATVPDKIKX0DER"
+            },
+            {
+              "compliance_type": "chemical",
+              "chemical_names": [
+                "di_2_ethylhexyl_phthalate_dehp"
+              ],
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "display": [
+            {
+              "resolution_maximum": [
+                {
+                  "unit": "pixels",
+                  "language_tag": "en_US",
+                  "value": "3840 x 2160"
+                }
+              ],
+              "size": [
+                {
+                  "unit": "inches",
+                  "value": 82
+                }
+              ],
+              "type": [
+                {
+                  "language_tag": "en_US",
+                  "value": "QLED"
+                }
+              ],
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "item_name": [
+            {
+              "language_tag": "en_US",
+              "value": "Samsung QN82Q60RAFXZA Flat 82-Inch QLED 4K Q60 Series (2019) Ultra HD Smart TV with HDR and Alexa Compatibility",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "list_price": [
+            {
+              "currency": "USD",
+              "value": 3799.99,
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "batteries_required": [
+            {
+              "value": false,
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "includes_rechargable_battery": [
+            {
+              "value": false,
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "product_site_launch_date": [
+            {
+              "value": "2019-03-11T08:00:01.000Z",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "product_category": [
+            {
+              "value": "50400100",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "batteries_included": [
+            {
+              "value": false,
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "connectivity_technology": [
+            {
+              "language_tag": "en_US",
+              "value": "Bluetooth",
+              "marketplace_id": "ATVPDKIKX0DER"
+            },
+            {
+              "language_tag": "en_US",
+              "value": "USB",
+              "marketplace_id": "ATVPDKIKX0DER"
+            },
+            {
+              "language_tag": "en_US",
+              "value": "Wireless",
+              "marketplace_id": "ATVPDKIKX0DER"
+            },
+            {
+              "language_tag": "en_US",
+              "value": "HDMI",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "included_components": [
+            {
+              "language_tag": "en_US",
+              "value": "QLED Standard Smart Remote",
+              "marketplace_id": "ATVPDKIKX0DER"
+            },
+            {
+              "language_tag": "en_US",
+              "value": "Power Cable",
+              "marketplace_id": "ATVPDKIKX0DER"
+            },
+            {
+              "language_tag": "en_US",
+              "value": "Stand",
+              "marketplace_id": "ATVPDKIKX0DER"
+            },
+            {
+              "language_tag": "en_US",
+              "value": "Samsung Smart Control",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "specification_met": [
+            {
+              "language_tag": "en_US",
+              "value": "",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "parental_control_technology": [
+            {
+              "value": "V-Chip",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "power_consumption": [
+            {
+              "unit": "watts",
+              "value": 120,
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "cpsia_cautionary_statement": [
+            {
+              "value": "no_warning_applicable",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "item_type_keyword": [
+            {
+              "value": "qled-televisions",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "number_of_items": [
+            {
+              "value": 1,
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "warranty_description": [
+            {
+              "language_tag": "en_US",
+              "value": "1 year manufacturer",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "max_resolution": [
+            {
+              "unit": "pixels",
+              "value": 8.3,
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "color": [
+            {
+              "language_tag": "en_US",
+              "value": "Black",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "screen_surface_description": [
+            {
+              "language_tag": "en_US",
+              "value": "Flat",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "item_package_weight": [
+            {
+              "unit": "kilograms",
+              "value": 62.142,
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "speaker_type": [
+            {
+              "language_tag": "en_US",
+              "value": "2CH",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "supported_internet_services": [
+            {
+              "language_tag": "en_US",
+              "value": "Amazon Instant Video",
+              "marketplace_id": "ATVPDKIKX0DER"
+            },
+            {
+              "language_tag": "en_US",
+              "value": "YouTube",
+              "marketplace_id": "ATVPDKIKX0DER"
+            },
+            {
+              "language_tag": "en_US",
+              "value": "Netflix",
+              "marketplace_id": "ATVPDKIKX0DER"
+            },
+            {
+              "language_tag": "en_US",
+              "value": "Hulu",
+              "marketplace_id": "ATVPDKIKX0DER"
+            },
+            {
+              "language_tag": "en_US",
+              "value": "Browser",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "tuner_technology": [
+            {
+              "language_tag": "en_US",
+              "value": "Analog Tuner",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "controller_type": [
+            {
+              "language_tag": "en_US",
+              "value": "SmartThings",
+              "marketplace_id": "ATVPDKIKX0DER"
+            },
+            {
+              "language_tag": "en_US",
+              "value": "Voice Control",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "special_feature": [
+            {
+              "language_tag": "en_US",
+              "value": "100% Color Volume with Quantum Dot; Quantum Processor 4K; Ambient Mode; Quantum HDR 4X; Real Game Enhancer",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "wireless_communication_technology": [
+            {
+              "language_tag": "en_US",
+              "value": "Wi-Fi::Wi-Fi Direct::Bluetooth",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "model_year": [
+            {
+              "value": 2019,
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "power_source_type": [
+            {
+              "language_tag": "en_US",
+              "value": "Corded Electric",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "street_date": [
+            {
+              "value": "2019-03-21T00:00:01Z",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "mounting_type": [
+            {
+              "language_tag": "en_US",
+              "value": "Table Mount",
+              "marketplace_id": "ATVPDKIKX0DER"
+            },
+            {
+              "language_tag": "en_US",
+              "value": "Wall Mount",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ],
+          "refresh_rate": [
+            {
+              "unit": "hertz",
+              "language_tag": "en_US",
+              "value": "120",
+              "marketplace_id": "ATVPDKIKX0DER"
+            }
+          ]
+        },
+        "classifications": [
+          {
+            "marketplaceId": "ATVPDKIKX0DER",
+            "classifications": [
+              {
+                "displayName": "QLED TVs",
+                "classificationId": "21489946011",
+                "parent": {
+                  "displayName": "Televisions",
+                  "classificationId": "172659",
+                  "parent": {
+                    "displayName": "Television & Video",
+                    "classificationId": "1266092011",
+                    "parent": {
+                      "displayName": "Electronics",
+                      "classificationId": "172282"
+                    }
+                  }
+                }
+              }
+            ]
+          }
+        ],
+        "dimensions": [
+          {
+            "marketplaceId": "ATVPDKIKX0DER",
+            "item": {
+              "height": {
+                "unit": "inches",
+                "value": 41.4
+              },
+              "length": {
+                "unit": "inches",
+                "value": 2.4
+              },
+              "weight": {
+                "unit": "pounds",
+                "value": 107.6
+              },
+              "width": {
+                "unit": "inches",
+                "value": 72.4
+              }
+            },
+            "package": {
+              "height": {
+                "unit": "inches",
+                "value": 10.49999998929
+              },
+              "length": {
+                "unit": "inches",
+                "value": 79.9999999184
+              },
+              "weight": {
+                "unit": "kilograms",
+                "value": 62.142
+              },
+              "width": {
+                "unit": "inches",
+                "value": 47.99999995104
+              }
+            }
+          }
+        ],
+        "identifiers": [
+          {
+            "marketplaceId": "ATVPDKIKX0DER",
+            "identifiers": [
+              {
+                "identifier": "0887276302195",
+                "identifierType": "EAN"
+              },
+              {
+                "identifier": "00887276302195",
+                "identifierType": "GTIN"
+              },
+              {
+                "identifier": "887276302195",
+                "identifierType": "UPC"
+              }
+            ]
+          }
+        ],
+        "images": [
+          {
+            "marketplaceId": "ATVPDKIKX0DER",
+            "images": [
+              {
+                "variant": "MAIN",
+                "link": "https://m.media-amazon.com/images/I/91uohwV+k3L.jpg",
+                "height": 1707,
+                "width": 2560
+              },
+              {
+                "variant": "MAIN",
+                "link": "https://m.media-amazon.com/images/I/51DZzp3w3vL.jpg",
+                "height": 333,
+                "width": 500
+              },
+              {
+                "variant": "PT01",
+                "link": "https://m.media-amazon.com/images/I/81w2rTVShlL.jpg",
+                "height": 2560,
+                "width": 2560
+              },
+              {
+                "variant": "PT01",
+                "link": "https://m.media-amazon.com/images/I/41Px9eq9tkL.jpg",
+                "height": 500,
+                "width": 500
+              },
+              {
+                "variant": "PT02",
+                "link": "https://m.media-amazon.com/images/I/51NTNhdhPyL.jpg",
+                "height": 375,
+                "width": 500
+              },
+              {
+                "variant": "PT03",
+                "link": "https://m.media-amazon.com/images/I/51o4zpL+A3L.jpg",
+                "height": 375,
+                "width": 500
+              },
+              {
+                "variant": "PT04",
+                "link": "https://m.media-amazon.com/images/I/71ux2k9GAZL.jpg",
+                "height": 1000,
+                "width": 1000
+              },
+              {
+                "variant": "PT04",
+                "link": "https://m.media-amazon.com/images/I/61UUX63yw1L.jpg",
+                "height": 500,
+                "width": 500
+              },
+              {
+                "variant": "PT05",
+                "link": "https://m.media-amazon.com/images/I/61LwHkljX-L.jpg",
+                "height": 1000,
+                "width": 1000
+              },
+              {
+                "variant": "PT05",
+                "link": "https://m.media-amazon.com/images/I/51wJTQty3PL.jpg",
+                "height": 500,
+                "width": 500
+              },
+              {
+                "variant": "PT06",
+                "link": "https://m.media-amazon.com/images/I/61uvoB4VvoL.jpg",
+                "height": 1000,
+                "width": 1000
+              },
+              {
+                "variant": "PT06",
+                "link": "https://m.media-amazon.com/images/I/51ZexIO628L.jpg",
+                "height": 500,
+                "width": 500
+              },
+              {
+                "variant": "PT07",
+                "link": "https://m.media-amazon.com/images/I/7121MGd2ncL.jpg",
+                "height": 1000,
+                "width": 1000
+              },
+              {
+                "variant": "PT07",
+                "link": "https://m.media-amazon.com/images/I/61QK+JBMrGL.jpg",
+                "height": 500,
+                "width": 500
+              },
+              {
+                "variant": "PT08",
+                "link": "https://m.media-amazon.com/images/I/61ECcGlG4IL.jpg",
+                "height": 1080,
+                "width": 1920
+              },
+              {
+                "variant": "PT08",
+                "link": "https://m.media-amazon.com/images/I/31TxwfqvB5L.jpg",
+                "height": 281,
+                "width": 500
+              }
+            ]
+          }
+        ],
+        "productTypes": [
+          {
+            "marketplaceId": "ATVPDKIKX0DER",
+            "productType": "TELEVISION"
+          }
+        ],
+        "salesRanks": [
+          {
+            "marketplaceId": "ATVPDKIKX0DER",
+            "classificationRanks": [
+              {
+                "classificationId": "21489946011",
+                "title": "QLED TVs",
+                "link": "http://www.amazon.com/gp/bestsellers/electronics/21489946011",
+                "rank": 113
+              }
+            ],
+            "displayGroupRanks": [
+              {
+                "websiteDisplayGroup": "ce_display_on_website",
+                "title": "Electronics",
+                "link": "http://www.amazon.com/gp/bestsellers/electronics",
+                "rank": 72855
+              }
+            ]
+          }
+        ],
+        "summaries": [
+          {
+            "marketplaceId": "ATVPDKIKX0DER",
+            "brand": "SAMSUNG",
+            "browseClassification": {
+              "displayName": "QLED TVs",
+              "classificationId": "21489946011"
+            },
+            "color": "Black",
+            "itemClassification": "BASE_PRODUCT",
+            "itemName": "Samsung QN82Q60RAFXZA Flat 82-Inch QLED 4K Q60 Series (2019) Ultra HD Smart TV with HDR and Alexa Compatibility",
+            "manufacturer": "Samsung",
+            "modelNumber": "QN82Q60RAFXZA",
+            "packageQuantity": 1,
+            "partNumber": "QN82Q60RAFXZA",
+            "size": "82-Inch",
+            "style": "TV only",
+            "websiteDisplayGroup": "home_theater_display_on_website",
+            "websiteDisplayGroupName": "Home Theater"
+          }
+        ],
+        "relationships": [
+          {
+            "marketplaceId": "ATVPDKIKX0DER",
+            "relationships": [
+              {
+                "type": "VARIATION",
+                "parentAsins": [
+                  "B08J7TQ9FL"
+                ],
+                "variationTheme": {
+                  "attributes": [
+                    "color",
+                    "size"
+                  ],
+                  "theme": "SIZE_NAME/COLOR_NAME"
+                }
+              }
+            ]
+          }
+        ],
+        "vendorDetails": [
+          {
+            "marketplaceId": "ATVPDKIKX0DER",
+            "brandCode": "SAMF9",
+            "manufacturerCode": "SAMF9",
+            "manufacturerCodeParent": "SAMF9",
+            "productCategory": {
+              "displayName": "Televisions",
+              "value": "50400100"
+            },
+            "productGroup": "Home Entertainment",
+            "productSubcategory": {
+              "displayName": "Plasma TVs",
+              "value": "50400120"
+            },
+            "replenishmentCategory": "OBSOLETE"
+          }
+        ]
+      }
+    ]
+  }
+  ```
+- 3. getCatalogItem 【获取类目信息】
+  - 接口地址:https://sellingpartnerapi-na.amazon.com/catalog/2022-04-01/items/{asin}
+  - 请求方法:GET
+```json
+  {
+  "asin": "B07N4M94X4",
+  "attributes": {
+    "total_hdmi_ports": [
+      {
+        "value": 4,
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "resolution": [
+      {
+        "language_tag": "en_US",
+        "value": "4K",
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "item_weight": [
+      {
+        "unit": "pounds",
+        "value": 107.6,
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "product_subcategory": [
+      {
+        "value": "50400120",
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "bullet_point": [
+      {
+        "language_tag": "en_US",
+        "value": "SMART TV WITH UNIVERSAL GUIDE: Simple on-screen Guide is an easy way to find streaming content and live TV shows",
+        "marketplace_id": "ATVPDKIKX0DER"
+      },
+      {
+        "language_tag": "en_US",
+        "value": "100% COLOR VOLUME WITH QUANTUM DOTS: Powered by Quantum dots, Samsung’s 4K QLED TV offers over a billion shades of brilliant color and 100% color volume for exceptional depth of detail that will draw you in to the picture for the best 4K TV experience",
+        "marketplace_id": "ATVPDKIKX0DER"
+      },
+      {
+        "language_tag": "en_US",
+        "value": "QUANTUM PROCESSOR 4K: Intelligently powered processor instantly upscales content to 4K for sharp detail and refined color",
+        "marketplace_id": "ATVPDKIKX0DER"
+      },
+      {
+        "language_tag": "en_US",
+        "value": "QUANTUM HDR 4X: 4K depth of detail with high dynamic range powered by HDR10+ delivers the lightest to darkest colors, scene by scene, for amazing picture realism",
+        "marketplace_id": "ATVPDKIKX0DER"
+      },
+      {
+        "language_tag": "en_US",
+        "value": "AMBIENT MODE: Customizes and complements your living space by turning a blank screen of this big screen TV into enticing visuals including décor, info, photos and artwork",
+        "marketplace_id": "ATVPDKIKX0DER"
+      },
+      {
+        "language_tag": "en_US",
+        "value": "SMART TV FEATURES: OneRemote to control all compatible devices, Bixby voice command, on-screen universal guide, SmartThings to control compatible home appliances and devices, smart speaker expandability with Alexa and Google Assistant compatibility, and more",
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "item_dimensions": [
+      {
+        "width": {
+          "unit": "inches",
+          "value": 72.4
+        },
+        "length": {
+          "unit": "inches",
+          "value": 2.4
+        },
+        "height": {
+          "unit": "inches",
+          "value": 41.4
+        },
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "brand": [
+      {
+        "language_tag": "en_US",
+        "value": "SAMSUNG",
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "control_method": [
+      {
+        "value": "voice",
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "item_package_dimensions": [
+      {
+        "length": {
+          "unit": "centimeters",
+          "value": 26.67
+        },
+        "width": {
+          "unit": "centimeters",
+          "value": 121.92
+        },
+        "height": {
+          "unit": "centimeters",
+          "value": 203.2
+        },
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "image_aspect_ratio": [
+      {
+        "language_tag": "en_US",
+        "value": "16:9",
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "part_number": [
+      {
+        "value": "QN82Q60RAFXZA",
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "includes_remote": [
+      {
+        "value": true,
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "style": [
+      {
+        "language_tag": "en_US",
+        "value": "TV only",
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "item_type_name": [
+      {
+        "language_tag": "en_US",
+        "value": "TV",
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "battery": [
+      {
+        "cell_composition": [
+          {
+            "value": "alkaline"
+          }
+        ],
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "image_contrast_ratio": [
+      {
+        "language_tag": "en_US",
+        "value": "QLED 4K",
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "manufacturer": [
+      {
+        "language_tag": "en_US",
+        "value": "Samsung",
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "number_of_boxes": [
+      {
+        "value": 1,
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "total_usb_ports": [
+      {
+        "value": 2,
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "model_number": [
+      {
+        "value": "QN82Q60RAFXZA",
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "supplier_declared_dg_hz_regulation": [
+      {
+        "value": "not_applicable",
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "num_batteries": [
+      {
+        "quantity": 2,
+        "type": "aaa",
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "california_proposition_65": [
+      {
+        "compliance_type": "on_product_combined_cancer_reproductive",
+        "marketplace_id": "ATVPDKIKX0DER"
+      },
+      {
+        "compliance_type": "chemical",
+        "chemical_names": [
+          "di_2_ethylhexyl_phthalate_dehp"
+        ],
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "display": [
+      {
+        "resolution_maximum": [
+          {
+            "unit": "pixels",
+            "language_tag": "en_US",
+            "value": "3840 x 2160"
+          }
+        ],
+        "size": [
+          {
+            "unit": "inches",
+            "value": 82
+          }
+        ],
+        "type": [
+          {
+            "language_tag": "en_US",
+            "value": "QLED"
+          }
+        ],
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "item_name": [
+      {
+        "language_tag": "en_US",
+        "value": "Samsung QN82Q60RAFXZA Flat 82-Inch QLED 4K Q60 Series (2019) Ultra HD Smart TV with HDR and Alexa Compatibility",
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "list_price": [
+      {
+        "currency": "USD",
+        "value": 3799.99,
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "batteries_required": [
+      {
+        "value": false,
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "includes_rechargable_battery": [
+      {
+        "value": false,
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "product_site_launch_date": [
+      {
+        "value": "2019-03-11T08:00:01.000Z",
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "product_category": [
+      {
+        "value": "50400100",
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "batteries_included": [
+      {
+        "value": false,
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "connectivity_technology": [
+      {
+        "language_tag": "en_US",
+        "value": "Bluetooth",
+        "marketplace_id": "ATVPDKIKX0DER"
+      },
+      {
+        "language_tag": "en_US",
+        "value": "USB",
+        "marketplace_id": "ATVPDKIKX0DER"
+      },
+      {
+        "language_tag": "en_US",
+        "value": "Wireless",
+        "marketplace_id": "ATVPDKIKX0DER"
+      },
+      {
+        "language_tag": "en_US",
+        "value": "HDMI",
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "included_components": [
+      {
+        "language_tag": "en_US",
+        "value": "QLED Standard Smart Remote",
+        "marketplace_id": "ATVPDKIKX0DER"
+      },
+      {
+        "language_tag": "en_US",
+        "value": "Power Cable",
+        "marketplace_id": "ATVPDKIKX0DER"
+      },
+      {
+        "language_tag": "en_US",
+        "value": "Stand",
+        "marketplace_id": "ATVPDKIKX0DER"
+      },
+      {
+        "language_tag": "en_US",
+        "value": "Samsung Smart Control",
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "specification_met": [
+      {
+        "language_tag": "en_US",
+        "value": "",
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "parental_control_technology": [
+      {
+        "value": "V-Chip",
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "power_consumption": [
+      {
+        "unit": "watts",
+        "value": 120,
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "cpsia_cautionary_statement": [
+      {
+        "value": "no_warning_applicable",
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "item_type_keyword": [
+      {
+        "value": "qled-televisions",
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "number_of_items": [
+      {
+        "value": 1,
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "warranty_description": [
+      {
+        "language_tag": "en_US",
+        "value": "1 year manufacturer",
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "max_resolution": [
+      {
+        "unit": "pixels",
+        "value": 8.3,
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "color": [
+      {
+        "language_tag": "en_US",
+        "value": "Black",
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "screen_surface_description": [
+      {
+        "language_tag": "en_US",
+        "value": "Flat",
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "item_package_weight": [
+      {
+        "unit": "kilograms",
+        "value": 62.142,
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "speaker_type": [
+      {
+        "language_tag": "en_US",
+        "value": "2CH",
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "supported_internet_services": [
+      {
+        "language_tag": "en_US",
+        "value": "Amazon Instant Video",
+        "marketplace_id": "ATVPDKIKX0DER"
+      },
+      {
+        "language_tag": "en_US",
+        "value": "YouTube",
+        "marketplace_id": "ATVPDKIKX0DER"
+      },
+      {
+        "language_tag": "en_US",
+        "value": "Netflix",
+        "marketplace_id": "ATVPDKIKX0DER"
+      },
+      {
+        "language_tag": "en_US",
+        "value": "Hulu",
+        "marketplace_id": "ATVPDKIKX0DER"
+      },
+      {
+        "language_tag": "en_US",
+        "value": "Browser",
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "tuner_technology": [
+      {
+        "language_tag": "en_US",
+        "value": "Analog Tuner",
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "controller_type": [
+      {
+        "language_tag": "en_US",
+        "value": "SmartThings",
+        "marketplace_id": "ATVPDKIKX0DER"
+      },
+      {
+        "language_tag": "en_US",
+        "value": "Voice Control",
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "special_feature": [
+      {
+        "language_tag": "en_US",
+        "value": "100% Color Volume with Quantum Dot; Quantum Processor 4K; Ambient Mode; Quantum HDR 4X; Real Game Enhancer",
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "wireless_communication_technology": [
+      {
+        "language_tag": "en_US",
+        "value": "Wi-Fi::Wi-Fi Direct::Bluetooth",
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "model_year": [
+      {
+        "value": 2019,
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "power_source_type": [
+      {
+        "language_tag": "en_US",
+        "value": "Corded Electric",
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "street_date": [
+      {
+        "value": "2019-03-21T00:00:01Z",
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "mounting_type": [
+      {
+        "language_tag": "en_US",
+        "value": "Table Mount",
+        "marketplace_id": "ATVPDKIKX0DER"
+      },
+      {
+        "language_tag": "en_US",
+        "value": "Wall Mount",
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ],
+    "refresh_rate": [
+      {
+        "unit": "hertz",
+        "language_tag": "en_US",
+        "value": "120",
+        "marketplace_id": "ATVPDKIKX0DER"
+      }
+    ]
+  },
+  "classifications": [
+    {
+      "marketplaceId": "ATVPDKIKX0DER",
+      "classifications": [
+        {
+          "displayName": "QLED TVs",
+          "classificationId": "21489946011",
+          "parent": {
+            "displayName": "Televisions",
+            "classificationId": "172659",
+            "parent": {
+              "displayName": "Television & Video",
+              "classificationId": "1266092011",
+              "parent": {
+                "displayName": "Electronics",
+                "classificationId": "172282"
+              }
+            }
+          }
+        }
+      ]
+    }
+  ],
+  "dimensions": [
+    {
+      "marketplaceId": "ATVPDKIKX0DER",
+      "item": {
+        "height": {
+          "unit": "inches",
+          "value": 41.4
+        },
+        "length": {
+          "unit": "inches",
+          "value": 2.4
+        },
+        "weight": {
+          "unit": "pounds",
+          "value": 107.6
+        },
+        "width": {
+          "unit": "inches",
+          "value": 72.4
+        }
+      },
+      "package": {
+        "height": {
+          "unit": "inches",
+          "value": 10.49999998929
+        },
+        "length": {
+          "unit": "inches",
+          "value": 79.9999999184
+        },
+        "weight": {
+          "unit": "kilograms",
+          "value": 62.142
+        },
+        "width": {
+          "unit": "inches",
+          "value": 47.99999995104
+        }
+      }
+    }
+  ],
+  "identifiers": [
+    {
+      "marketplaceId": "ATVPDKIKX0DER",
+      "identifiers": [
+        {
+          "identifier": "0887276302195",
+          "identifierType": "EAN"
+        },
+        {
+          "identifier": "00887276302195",
+          "identifierType": "GTIN"
+        },
+        {
+          "identifier": "887276302195",
+          "identifierType": "UPC"
+        }
+      ]
+    }
+  ],
+  "images": [
+    {
+      "marketplaceId": "ATVPDKIKX0DER",
+      "images": [
+        {
+          "variant": "MAIN",
+          "link": "https://m.media-amazon.com/images/I/91uohwV+k3L.jpg",
+          "height": 1707,
+          "width": 2560
+        },
+        {
+          "variant": "MAIN",
+          "link": "https://m.media-amazon.com/images/I/51DZzp3w3vL.jpg",
+          "height": 333,
+          "width": 500
+        },
+        {
+          "variant": "PT01",
+          "link": "https://m.media-amazon.com/images/I/81w2rTVShlL.jpg",
+          "height": 2560,
+          "width": 2560
+        },
+        {
+          "variant": "PT01",
+          "link": "https://m.media-amazon.com/images/I/41Px9eq9tkL.jpg",
+          "height": 500,
+          "width": 500
+        },
+        {
+          "variant": "PT02",
+          "link": "https://m.media-amazon.com/images/I/51NTNhdhPyL.jpg",
+          "height": 375,
+          "width": 500
+        },
+        {
+          "variant": "PT03",
+          "link": "https://m.media-amazon.com/images/I/51o4zpL+A3L.jpg",
+          "height": 375,
+          "width": 500
+        },
+        {
+          "variant": "PT04",
+          "link": "https://m.media-amazon.com/images/I/71ux2k9GAZL.jpg",
+          "height": 1000,
+          "width": 1000
+        },
+        {
+          "variant": "PT04",
+          "link": "https://m.media-amazon.com/images/I/61UUX63yw1L.jpg",
+          "height": 500,
+          "width": 500
+        },
+        {
+          "variant": "PT05",
+          "link": "https://m.media-amazon.com/images/I/61LwHkljX-L.jpg",
+          "height": 1000,
+          "width": 1000
+        },
+        {
+          "variant": "PT05",
+          "link": "https://m.media-amazon.com/images/I/51wJTQty3PL.jpg",
+          "height": 500,
+          "width": 500
+        },
+        {
+          "variant": "PT06",
+          "link": "https://m.media-amazon.com/images/I/61uvoB4VvoL.jpg",
+          "height": 1000,
+          "width": 1000
+        },
+        {
+          "variant": "PT06",
+          "link": "https://m.media-amazon.com/images/I/51ZexIO628L.jpg",
+          "height": 500,
+          "width": 500
+        },
+        {
+          "variant": "PT07",
+          "link": "https://m.media-amazon.com/images/I/7121MGd2ncL.jpg",
+          "height": 1000,
+          "width": 1000
+        },
+        {
+          "variant": "PT07",
+          "link": "https://m.media-amazon.com/images/I/61QK+JBMrGL.jpg",
+          "height": 500,
+          "width": 500
+        },
+        {
+          "variant": "PT08",
+          "link": "https://m.media-amazon.com/images/I/61ECcGlG4IL.jpg",
+          "height": 1080,
+          "width": 1920
+        },
+        {
+          "variant": "PT08",
+          "link": "https://m.media-amazon.com/images/I/31TxwfqvB5L.jpg",
+          "height": 281,
+          "width": 500
+        }
+      ]
+    }
+  ],
+  "productTypes": [
+    {
+      "marketplaceId": "ATVPDKIKX0DER",
+      "productType": "TELEVISION"
+    }
+  ],
+  "salesRanks": [
+    {
+      "marketplaceId": "ATVPDKIKX0DER",
+      "classificationRanks": [
+        {
+          "classificationId": "21489946011",
+          "title": "QLED TVs",
+          "link": "http://www.amazon.com/gp/bestsellers/electronics/21489946011",
+          "rank": 113
+        }
+      ],
+      "displayGroupRanks": [
+        {
+          "websiteDisplayGroup": "ce_display_on_website",
+          "title": "Electronics",
+          "link": "http://www.amazon.com/gp/bestsellers/electronics",
+          "rank": 72855
+        }
+      ]
+    }
+  ],
+  "summaries": [
+    {
+      "marketplaceId": "ATVPDKIKX0DER",
+      "brand": "SAMSUNG",
+      "browseClassification": {
+        "displayName": "QLED TVs",
+        "classificationId": "21489946011"
+      },
+      "color": "Black",
+      "itemClassification": "BASE_PRODUCT",
+      "itemName": "Samsung QN82Q60RAFXZA Flat 82-Inch QLED 4K Q60 Series (2019) Ultra HD Smart TV with HDR and Alexa Compatibility",
+      "manufacturer": "Samsung",
+      "modelNumber": "QN82Q60RAFXZA",
+      "packageQuantity": 1,
+      "partNumber": "QN82Q60RAFXZA",
+      "size": "82-Inch",
+      "style": "TV only",
+      "websiteDisplayGroup": "home_theater_display_on_website",
+      "websiteDisplayGroupName": "Home Theater"
+    }
+  ],
+  "relationships": [
+    {
+      "marketplaceId": "ATVPDKIKX0DER",
+      "relationships": [
+        {
+          "type": "VARIATION",
+          "parentAsins": [
+            "B08J7TQ9FL"
+          ],
+          "variationTheme": {
+            "attributes": [
+              "color",
+              "size"
+            ],
+            "theme": "SIZE_NAME/COLOR_NAME"
+          }
+        }
+      ]
+    }
+  ],
+  "vendorDetails": [
+    {
+      "marketplaceId": "ATVPDKIKX0DER",
+      "brandCode": "SAMF9",
+      "manufacturerCode": "SAMF9",
+      "manufacturerCodeParent": "SAMF9",
+      "productCategory": {
+        "displayName": "Televisions",
+        "value": "50400100"
+      },
+      "productGroup": "Home Entertainment",
+      "productSubcategory": {
+        "displayName": "Plasma TVs",
+        "value": "50400120"
+      },
+      "replenishmentCategory": "OBSOLETE"
+    }
+  ]
+}
+```
+

+ 2119 - 0
modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/package-lock.json

@@ -0,0 +1,2119 @@
+{
+  "name": "@fmode/amazon-sp-api",
+  "version": "1.0.0",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "@fmode/amazon-sp-api",
+      "version": "1.0.0",
+      "license": "MIT",
+      "dependencies": {
+        "@types/parse": "^3.0.9",
+        "axios": "^1.6.0",
+        "express": "^4.18.2",
+        "parse": "^8.1.0"
+      },
+      "devDependencies": {
+        "@types/express": "^5.0.6",
+        "@types/node": "^20.0.0",
+        "esbuild": "^0.19.0",
+        "tsx": "^4.21.0"
+      }
+    },
+    "node_modules/@babel/runtime": {
+      "version": "7.28.6",
+      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz",
+      "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/runtime-corejs3": {
+      "version": "7.29.0",
+      "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.29.0.tgz",
+      "integrity": "sha512-TgUkdp71C9pIbBcHudc+gXZnihEDOjUAmXO1VO4HHGES7QLZcShR0stfKIxLSNIYx2fqhmJChOjm/wkF8wv4gA==",
+      "license": "MIT",
+      "dependencies": {
+        "core-js-pure": "^3.48.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@esbuild/aix-ppc64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz",
+      "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "aix"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/android-arm": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz",
+      "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/android-arm64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz",
+      "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/android-x64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz",
+      "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/darwin-arm64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz",
+      "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/darwin-x64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz",
+      "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/freebsd-arm64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz",
+      "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/freebsd-x64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz",
+      "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-arm": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz",
+      "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-arm64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz",
+      "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-ia32": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz",
+      "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-loong64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz",
+      "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==",
+      "cpu": [
+        "loong64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-mips64el": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz",
+      "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==",
+      "cpu": [
+        "mips64el"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-ppc64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz",
+      "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-riscv64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz",
+      "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-s390x": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz",
+      "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-x64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz",
+      "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/netbsd-arm64": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz",
+      "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/netbsd-x64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz",
+      "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/openbsd-arm64": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz",
+      "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/openbsd-x64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz",
+      "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/openharmony-arm64": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz",
+      "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openharmony"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/sunos-x64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz",
+      "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "sunos"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/win32-arm64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz",
+      "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/win32-ia32": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz",
+      "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/win32-x64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz",
+      "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@types/body-parser": {
+      "version": "1.19.6",
+      "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz",
+      "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/connect": "*",
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/connect": {
+      "version": "3.4.38",
+      "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
+      "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/express": {
+      "version": "5.0.6",
+      "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz",
+      "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/body-parser": "*",
+        "@types/express-serve-static-core": "^5.0.0",
+        "@types/serve-static": "^2"
+      }
+    },
+    "node_modules/@types/express-serve-static-core": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz",
+      "integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*",
+        "@types/qs": "*",
+        "@types/range-parser": "*",
+        "@types/send": "*"
+      }
+    },
+    "node_modules/@types/http-errors": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz",
+      "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/node": {
+      "version": "20.19.32",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.32.tgz",
+      "integrity": "sha512-Ez8QE4DMfhjjTsES9K2dwfV258qBui7qxUsoaixZDiTzbde4U12e1pXGNu/ECsUIOi5/zoCxAQxIhQnaUQ2VvA==",
+      "license": "MIT",
+      "dependencies": {
+        "undici-types": "~6.21.0"
+      }
+    },
+    "node_modules/@types/parse": {
+      "version": "3.0.9",
+      "resolved": "https://registry.npmjs.org/@types/parse/-/parse-3.0.9.tgz",
+      "integrity": "sha512-DGTHygc7krgmNAK8h42giwmAofCd9uv2++RD+zw6OmWI7AEnlTYZwEuWsx22SA2CSMQrZW8P2INHLpQbnQFUng==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/qs": {
+      "version": "6.14.0",
+      "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz",
+      "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/range-parser": {
+      "version": "1.2.7",
+      "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz",
+      "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/send": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz",
+      "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/serve-static": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz",
+      "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/http-errors": "*",
+        "@types/node": "*"
+      }
+    },
+    "node_modules/accepts": {
+      "version": "1.3.8",
+      "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+      "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+      "license": "MIT",
+      "dependencies": {
+        "mime-types": "~2.1.34",
+        "negotiator": "0.6.3"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/array-flatten": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+      "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
+      "license": "MIT"
+    },
+    "node_modules/asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+      "license": "MIT"
+    },
+    "node_modules/axios": {
+      "version": "1.13.4",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.4.tgz",
+      "integrity": "sha512-1wVkUaAO6WyaYtCkcYCOx12ZgpGf9Zif+qXa4n+oYzK558YryKqiL6UWwd5DqiH3VRW0GYhTZQ/vlgJrCoNQlg==",
+      "license": "MIT",
+      "dependencies": {
+        "follow-redirects": "^1.15.6",
+        "form-data": "^4.0.4",
+        "proxy-from-env": "^1.1.0"
+      }
+    },
+    "node_modules/body-parser": {
+      "version": "1.20.4",
+      "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz",
+      "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==",
+      "license": "MIT",
+      "dependencies": {
+        "bytes": "~3.1.2",
+        "content-type": "~1.0.5",
+        "debug": "2.6.9",
+        "depd": "2.0.0",
+        "destroy": "~1.2.0",
+        "http-errors": "~2.0.1",
+        "iconv-lite": "~0.4.24",
+        "on-finished": "~2.4.1",
+        "qs": "~6.14.0",
+        "raw-body": "~2.5.3",
+        "type-is": "~1.6.18",
+        "unpipe": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8",
+        "npm": "1.2.8000 || >= 1.4.16"
+      }
+    },
+    "node_modules/bytes": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+      "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/call-bind-apply-helpers": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+      "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "function-bind": "^1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/call-bound": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+      "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.2",
+        "get-intrinsic": "^1.3.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/combined-stream": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+      "license": "MIT",
+      "dependencies": {
+        "delayed-stream": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/content-disposition": {
+      "version": "0.5.4",
+      "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
+      "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
+      "license": "MIT",
+      "dependencies": {
+        "safe-buffer": "5.2.1"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/content-type": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
+      "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/cookie": {
+      "version": "0.7.2",
+      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
+      "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/cookie-signature": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz",
+      "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==",
+      "license": "MIT"
+    },
+    "node_modules/core-js-pure": {
+      "version": "3.48.0",
+      "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.48.0.tgz",
+      "integrity": "sha512-1slJgk89tWC51HQ1AEqG+s2VuwpTRr8ocu4n20QUcH1v9lAN0RXen0Q0AABa/DK1I7RrNWLucplOHMx8hfTGTw==",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/core-js"
+      }
+    },
+    "node_modules/crypto-js": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
+      "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
+      "license": "MIT"
+    },
+    "node_modules/debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "license": "MIT",
+      "dependencies": {
+        "ms": "2.0.0"
+      }
+    },
+    "node_modules/delayed-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/depd": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+      "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/destroy": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
+      "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8",
+        "npm": "1.2.8000 || >= 1.4.16"
+      }
+    },
+    "node_modules/dunder-proto": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+      "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.1",
+        "es-errors": "^1.3.0",
+        "gopd": "^1.2.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/ee-first": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+      "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
+      "license": "MIT"
+    },
+    "node_modules/encodeurl": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
+      "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/es-define-property": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+      "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-errors": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+      "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-object-atoms": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+      "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-set-tostringtag": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+      "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.6",
+        "has-tostringtag": "^1.0.2",
+        "hasown": "^2.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/esbuild": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz",
+      "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "bin": {
+        "esbuild": "bin/esbuild"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "optionalDependencies": {
+        "@esbuild/aix-ppc64": "0.19.12",
+        "@esbuild/android-arm": "0.19.12",
+        "@esbuild/android-arm64": "0.19.12",
+        "@esbuild/android-x64": "0.19.12",
+        "@esbuild/darwin-arm64": "0.19.12",
+        "@esbuild/darwin-x64": "0.19.12",
+        "@esbuild/freebsd-arm64": "0.19.12",
+        "@esbuild/freebsd-x64": "0.19.12",
+        "@esbuild/linux-arm": "0.19.12",
+        "@esbuild/linux-arm64": "0.19.12",
+        "@esbuild/linux-ia32": "0.19.12",
+        "@esbuild/linux-loong64": "0.19.12",
+        "@esbuild/linux-mips64el": "0.19.12",
+        "@esbuild/linux-ppc64": "0.19.12",
+        "@esbuild/linux-riscv64": "0.19.12",
+        "@esbuild/linux-s390x": "0.19.12",
+        "@esbuild/linux-x64": "0.19.12",
+        "@esbuild/netbsd-x64": "0.19.12",
+        "@esbuild/openbsd-x64": "0.19.12",
+        "@esbuild/sunos-x64": "0.19.12",
+        "@esbuild/win32-arm64": "0.19.12",
+        "@esbuild/win32-ia32": "0.19.12",
+        "@esbuild/win32-x64": "0.19.12"
+      }
+    },
+    "node_modules/escape-html": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+      "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
+      "license": "MIT"
+    },
+    "node_modules/etag": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+      "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/express": {
+      "version": "4.22.1",
+      "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz",
+      "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==",
+      "license": "MIT",
+      "dependencies": {
+        "accepts": "~1.3.8",
+        "array-flatten": "1.1.1",
+        "body-parser": "~1.20.3",
+        "content-disposition": "~0.5.4",
+        "content-type": "~1.0.4",
+        "cookie": "~0.7.1",
+        "cookie-signature": "~1.0.6",
+        "debug": "2.6.9",
+        "depd": "2.0.0",
+        "encodeurl": "~2.0.0",
+        "escape-html": "~1.0.3",
+        "etag": "~1.8.1",
+        "finalhandler": "~1.3.1",
+        "fresh": "~0.5.2",
+        "http-errors": "~2.0.0",
+        "merge-descriptors": "1.0.3",
+        "methods": "~1.1.2",
+        "on-finished": "~2.4.1",
+        "parseurl": "~1.3.3",
+        "path-to-regexp": "~0.1.12",
+        "proxy-addr": "~2.0.7",
+        "qs": "~6.14.0",
+        "range-parser": "~1.2.1",
+        "safe-buffer": "5.2.1",
+        "send": "~0.19.0",
+        "serve-static": "~1.16.2",
+        "setprototypeof": "1.2.0",
+        "statuses": "~2.0.1",
+        "type-is": "~1.6.18",
+        "utils-merge": "1.0.1",
+        "vary": "~1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.10.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/finalhandler": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz",
+      "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==",
+      "license": "MIT",
+      "dependencies": {
+        "debug": "2.6.9",
+        "encodeurl": "~2.0.0",
+        "escape-html": "~1.0.3",
+        "on-finished": "~2.4.1",
+        "parseurl": "~1.3.3",
+        "statuses": "~2.0.2",
+        "unpipe": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/follow-redirects": {
+      "version": "1.15.11",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
+      "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/RubenVerborgh"
+        }
+      ],
+      "license": "MIT",
+      "engines": {
+        "node": ">=4.0"
+      },
+      "peerDependenciesMeta": {
+        "debug": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/form-data": {
+      "version": "4.0.5",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
+      "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
+      "license": "MIT",
+      "dependencies": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.8",
+        "es-set-tostringtag": "^2.1.0",
+        "hasown": "^2.0.2",
+        "mime-types": "^2.1.12"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/forwarded": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+      "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/fresh": {
+      "version": "0.5.2",
+      "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+      "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/fsevents": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+      "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+      }
+    },
+    "node_modules/function-bind": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+      "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/get-intrinsic": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+      "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.2",
+        "es-define-property": "^1.0.1",
+        "es-errors": "^1.3.0",
+        "es-object-atoms": "^1.1.1",
+        "function-bind": "^1.1.2",
+        "get-proto": "^1.0.1",
+        "gopd": "^1.2.0",
+        "has-symbols": "^1.1.0",
+        "hasown": "^2.0.2",
+        "math-intrinsics": "^1.1.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/get-proto": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+      "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+      "license": "MIT",
+      "dependencies": {
+        "dunder-proto": "^1.0.1",
+        "es-object-atoms": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/get-tsconfig": {
+      "version": "4.13.6",
+      "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz",
+      "integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "resolve-pkg-maps": "^1.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
+      }
+    },
+    "node_modules/gopd": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+      "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/has-symbols": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+      "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/has-tostringtag": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+      "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+      "license": "MIT",
+      "dependencies": {
+        "has-symbols": "^1.0.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/hasown": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+      "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+      "license": "MIT",
+      "dependencies": {
+        "function-bind": "^1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/http-errors": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
+      "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
+      "license": "MIT",
+      "dependencies": {
+        "depd": "~2.0.0",
+        "inherits": "~2.0.4",
+        "setprototypeof": "~1.2.0",
+        "statuses": "~2.0.2",
+        "toidentifier": "~1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/iconv-lite": {
+      "version": "0.4.24",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+      "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+      "license": "MIT",
+      "dependencies": {
+        "safer-buffer": ">= 2.1.2 < 3"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/idb-keyval": {
+      "version": "6.2.2",
+      "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.2.tgz",
+      "integrity": "sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg==",
+      "license": "Apache-2.0"
+    },
+    "node_modules/inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+      "license": "ISC"
+    },
+    "node_modules/ipaddr.js": {
+      "version": "1.9.1",
+      "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+      "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/math-intrinsics": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+      "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/media-typer": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+      "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/merge-descriptors": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
+      "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/methods": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+      "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mime": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+      "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+      "license": "MIT",
+      "bin": {
+        "mime": "cli.js"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/mime-db": {
+      "version": "1.52.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mime-types": {
+      "version": "2.1.35",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+      "license": "MIT",
+      "dependencies": {
+        "mime-db": "1.52.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+      "license": "MIT"
+    },
+    "node_modules/negotiator": {
+      "version": "0.6.3",
+      "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+      "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/object-inspect": {
+      "version": "1.13.4",
+      "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
+      "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/on-finished": {
+      "version": "2.4.1",
+      "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+      "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+      "license": "MIT",
+      "dependencies": {
+        "ee-first": "1.1.1"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/parse": {
+      "version": "8.1.0",
+      "resolved": "https://registry.npmjs.org/parse/-/parse-8.1.0.tgz",
+      "integrity": "sha512-QJvYHrNXXKfo8F9wUPB5eeT8VOTc9tObWuf07dAKgQi3375gC3bejyxIezOh3c6SOmCVpvBL2Ro0jspQbeEgkA==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@babel/runtime": "7.28.6",
+        "@babel/runtime-corejs3": "7.29.0",
+        "crypto-js": "4.2.0",
+        "idb-keyval": "6.2.2",
+        "react-native-crypto-js": "1.0.0",
+        "ws": "8.19.0"
+      },
+      "engines": {
+        "node": ">=20.19.0 <21 || >=22.12.0 <23 || >=24.1.0 <25"
+      }
+    },
+    "node_modules/parseurl": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+      "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/path-to-regexp": {
+      "version": "0.1.12",
+      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
+      "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
+      "license": "MIT"
+    },
+    "node_modules/proxy-addr": {
+      "version": "2.0.7",
+      "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+      "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+      "license": "MIT",
+      "dependencies": {
+        "forwarded": "0.2.0",
+        "ipaddr.js": "1.9.1"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/proxy-from-env": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+      "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
+      "license": "MIT"
+    },
+    "node_modules/qs": {
+      "version": "6.14.1",
+      "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz",
+      "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==",
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "side-channel": "^1.1.0"
+      },
+      "engines": {
+        "node": ">=0.6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/range-parser": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+      "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/raw-body": {
+      "version": "2.5.3",
+      "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz",
+      "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==",
+      "license": "MIT",
+      "dependencies": {
+        "bytes": "~3.1.2",
+        "http-errors": "~2.0.1",
+        "iconv-lite": "~0.4.24",
+        "unpipe": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/react-native-crypto-js": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/react-native-crypto-js/-/react-native-crypto-js-1.0.0.tgz",
+      "integrity": "sha512-FNbLuG/HAdapQoybeZSoes1PWdOj0w242gb+e1R0hicf3Gyj/Mf8M9NaED2AnXVOX01b2FXomwUiw1xP1K+8sA==",
+      "license": "MIT"
+    },
+    "node_modules/resolve-pkg-maps": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
+      "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
+      "dev": true,
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
+      }
+    },
+    "node_modules/safe-buffer": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+      "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/safer-buffer": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+      "license": "MIT"
+    },
+    "node_modules/send": {
+      "version": "0.19.2",
+      "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz",
+      "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==",
+      "license": "MIT",
+      "dependencies": {
+        "debug": "2.6.9",
+        "depd": "2.0.0",
+        "destroy": "1.2.0",
+        "encodeurl": "~2.0.0",
+        "escape-html": "~1.0.3",
+        "etag": "~1.8.1",
+        "fresh": "~0.5.2",
+        "http-errors": "~2.0.1",
+        "mime": "1.6.0",
+        "ms": "2.1.3",
+        "on-finished": "~2.4.1",
+        "range-parser": "~1.2.1",
+        "statuses": "~2.0.2"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/send/node_modules/ms": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+      "license": "MIT"
+    },
+    "node_modules/serve-static": {
+      "version": "1.16.3",
+      "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz",
+      "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==",
+      "license": "MIT",
+      "dependencies": {
+        "encodeurl": "~2.0.0",
+        "escape-html": "~1.0.3",
+        "parseurl": "~1.3.3",
+        "send": "~0.19.1"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/setprototypeof": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+      "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+      "license": "ISC"
+    },
+    "node_modules/side-channel": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
+      "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "object-inspect": "^1.13.3",
+        "side-channel-list": "^1.0.0",
+        "side-channel-map": "^1.0.1",
+        "side-channel-weakmap": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/side-channel-list": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
+      "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "object-inspect": "^1.13.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/side-channel-map": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
+      "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.2",
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.5",
+        "object-inspect": "^1.13.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/side-channel-weakmap": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+      "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.2",
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.5",
+        "object-inspect": "^1.13.3",
+        "side-channel-map": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/statuses": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
+      "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/toidentifier": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+      "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.6"
+      }
+    },
+    "node_modules/tsx": {
+      "version": "4.21.0",
+      "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz",
+      "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "esbuild": "~0.27.0",
+        "get-tsconfig": "^4.7.5"
+      },
+      "bin": {
+        "tsx": "dist/cli.mjs"
+      },
+      "engines": {
+        "node": ">=18.0.0"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.3"
+      }
+    },
+    "node_modules/tsx/node_modules/@esbuild/aix-ppc64": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz",
+      "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "aix"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tsx/node_modules/@esbuild/android-arm": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz",
+      "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tsx/node_modules/@esbuild/android-arm64": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz",
+      "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tsx/node_modules/@esbuild/android-x64": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz",
+      "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tsx/node_modules/@esbuild/darwin-arm64": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz",
+      "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tsx/node_modules/@esbuild/darwin-x64": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz",
+      "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tsx/node_modules/@esbuild/freebsd-arm64": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz",
+      "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tsx/node_modules/@esbuild/freebsd-x64": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz",
+      "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tsx/node_modules/@esbuild/linux-arm": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz",
+      "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tsx/node_modules/@esbuild/linux-arm64": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz",
+      "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tsx/node_modules/@esbuild/linux-ia32": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz",
+      "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tsx/node_modules/@esbuild/linux-loong64": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz",
+      "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==",
+      "cpu": [
+        "loong64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tsx/node_modules/@esbuild/linux-mips64el": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz",
+      "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==",
+      "cpu": [
+        "mips64el"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tsx/node_modules/@esbuild/linux-ppc64": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz",
+      "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tsx/node_modules/@esbuild/linux-riscv64": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz",
+      "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tsx/node_modules/@esbuild/linux-s390x": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz",
+      "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tsx/node_modules/@esbuild/linux-x64": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz",
+      "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tsx/node_modules/@esbuild/netbsd-x64": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz",
+      "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tsx/node_modules/@esbuild/openbsd-x64": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz",
+      "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tsx/node_modules/@esbuild/sunos-x64": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz",
+      "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "sunos"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tsx/node_modules/@esbuild/win32-arm64": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz",
+      "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tsx/node_modules/@esbuild/win32-ia32": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz",
+      "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tsx/node_modules/@esbuild/win32-x64": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz",
+      "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tsx/node_modules/esbuild": {
+      "version": "0.27.3",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz",
+      "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "bin": {
+        "esbuild": "bin/esbuild"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "optionalDependencies": {
+        "@esbuild/aix-ppc64": "0.27.3",
+        "@esbuild/android-arm": "0.27.3",
+        "@esbuild/android-arm64": "0.27.3",
+        "@esbuild/android-x64": "0.27.3",
+        "@esbuild/darwin-arm64": "0.27.3",
+        "@esbuild/darwin-x64": "0.27.3",
+        "@esbuild/freebsd-arm64": "0.27.3",
+        "@esbuild/freebsd-x64": "0.27.3",
+        "@esbuild/linux-arm": "0.27.3",
+        "@esbuild/linux-arm64": "0.27.3",
+        "@esbuild/linux-ia32": "0.27.3",
+        "@esbuild/linux-loong64": "0.27.3",
+        "@esbuild/linux-mips64el": "0.27.3",
+        "@esbuild/linux-ppc64": "0.27.3",
+        "@esbuild/linux-riscv64": "0.27.3",
+        "@esbuild/linux-s390x": "0.27.3",
+        "@esbuild/linux-x64": "0.27.3",
+        "@esbuild/netbsd-arm64": "0.27.3",
+        "@esbuild/netbsd-x64": "0.27.3",
+        "@esbuild/openbsd-arm64": "0.27.3",
+        "@esbuild/openbsd-x64": "0.27.3",
+        "@esbuild/openharmony-arm64": "0.27.3",
+        "@esbuild/sunos-x64": "0.27.3",
+        "@esbuild/win32-arm64": "0.27.3",
+        "@esbuild/win32-ia32": "0.27.3",
+        "@esbuild/win32-x64": "0.27.3"
+      }
+    },
+    "node_modules/type-is": {
+      "version": "1.6.18",
+      "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+      "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+      "license": "MIT",
+      "dependencies": {
+        "media-typer": "0.3.0",
+        "mime-types": "~2.1.24"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/undici-types": {
+      "version": "6.21.0",
+      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
+      "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+      "license": "MIT"
+    },
+    "node_modules/unpipe": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+      "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/utils-merge": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+      "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4.0"
+      }
+    },
+    "node_modules/vary": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+      "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/ws": {
+      "version": "8.19.0",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
+      "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10.0.0"
+      },
+      "peerDependencies": {
+        "bufferutil": "^4.0.1",
+        "utf-8-validate": ">=5.0.2"
+      },
+      "peerDependenciesMeta": {
+        "bufferutil": {
+          "optional": true
+        },
+        "utf-8-validate": {
+          "optional": true
+        }
+      }
+    }
+  }
+}

+ 36 - 0
modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/package.json

@@ -0,0 +1,36 @@
+{
+  "name": "@fmode/amazon-sp-api",
+  "version": "1.0.0",
+  "description": "Amazon Selling Partner API 模块",
+  "type": "module",
+  "main": "src/mod.ts",
+  "exports": {
+    ".": "./src/mod.ts",
+    "./client": "./src/client.ts",
+    "./types": "./src/types.ts",
+    "./api/customerFeedback": "./src/api/customerFeedback.ts"
+  },
+  "scripts": {
+    "test": "echo \"No tests yet\"",
+    "dev": "tsx src/dev-server.ts"
+  },
+  "keywords": [
+    "amazon",
+    "sp-api",
+    "fmode"
+  ],
+  "author": "fmode team",
+  "license": "MIT",
+  "dependencies": {
+    "@types/parse": "^3.0.9",
+    "axios": "^1.6.0",
+    "express": "^4.18.2",
+    "parse": "^8.1.0"
+  },
+  "devDependencies": {
+    "@types/express": "^5.0.6",
+    "@types/node": "^20.0.0",
+    "esbuild": "^0.19.0",
+    "tsx": "^4.21.0"
+  }
+}

+ 43 - 0
modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/src/api/catalogItems.ts

@@ -0,0 +1,43 @@
+import { SpApiClient } from '../client.ts';
+import { ListCatalogCategoriesParams, SearchCatalogItemsParams, GetCatalogItemParams } from '../types.ts';
+
+export class CatalogItemsApi {
+  constructor(private client: SpApiClient) {}
+
+  /**
+   * 列表目录分类
+   * GET /catalog/v0/categories
+   */
+  async listCatalogCategories(params: ListCatalogCategoriesParams) {
+    return this.client.request({
+      method: 'GET',
+      path: '/catalog/v0/categories',
+      query: params as any
+    });
+  }
+
+  /**
+   * 搜索亚马逊目录商品
+   * GET /catalog/2022-04-01/items
+   */
+  async searchCatalogItems(params: SearchCatalogItemsParams) {
+    return this.client.request({
+      method: 'GET',
+      path: '/catalog/2022-04-01/items',
+      query: params as any
+    });
+  }
+
+  /**
+   * 获取类目信息
+   * GET /catalog/2022-04-01/items/{asin}
+   */
+  async getCatalogItem(params: GetCatalogItemParams) {
+    const { asin, ...queryParams } = params;
+    return this.client.request({
+      method: 'GET',
+      path: `/catalog/2022-04-01/items/${asin}`,
+      query: queryParams as any
+    });
+  }
+}

+ 97 - 0
modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/src/api/customerFeedback.ts

@@ -0,0 +1,97 @@
+import { SpApiClient } from '../client.ts';
+import { GetItemReviewTopicsParams } from '../types.ts';
+
+export class CustomerFeedbackApi {
+  constructor(private client: SpApiClient) {}
+
+  /**
+   * 检索某件商品的十个最正面和十个最负面的评论主题。
+   * GET /customerFeedback/2024-06-01/items/{asin}/reviews/topics
+   */
+  async getItemReviewTopics(params: GetItemReviewTopicsParams) {
+    const { asin, ...queryParams } = params;
+    return this.client.request({
+      method: 'GET',
+      path: `/customerFeedback/2024-06-01/items/${asin}/reviews/topics`,
+      query: queryParams
+    });
+  }
+
+  /**
+   * 该API返回请求ASIN的关联浏览节点。
+   * GET /customerFeedback/2024-06-01/items/{asin}/browseNode
+   */
+  async getItemBrowseNode(params: { asin: string } & import('../types.ts').BaseRequestParams) {
+    const { asin, ...queryParams } = params;
+    return this.client.request({
+      method: 'GET',
+      path: `/customerFeedback/2024-06-01/items/${asin}/browseNode`,
+      query: queryParams
+    });
+  }
+
+  /**
+   * 检索浏览节点中十个最正面和十个最负面的评论主题。
+   * GET /customerFeedback/2024-06-01/browseNodes/{browseNodeId}/reviews/topics
+   */
+  async getBrowseNodeReviewTopics(params: { browseNodeId: string } & import('../types.ts').BaseRequestParams) {
+    const { browseNodeId, ...queryParams } = params;
+    return this.client.request({
+      method: 'GET',
+      path: `/customerFeedback/2024-06-01/browseNodes/${browseNodeId}/reviews/topics`,
+      query: queryParams
+    });
+  }
+
+  /**
+   * 检索过去六个月商品的正面和负面评价趋势。
+   * GET /customerFeedback/2024-06-01/items/{asin}/reviews/trends
+   */
+  async getItemReviewTrends(params: { asin: string } & import('../types.ts').BaseRequestParams) {
+    const { asin, ...queryParams } = params;
+    return this.client.request({
+      method: 'GET',
+      path: `/customerFeedback/2024-06-01/items/${asin}/reviews/trends`,
+      query: queryParams
+    });
+  }
+
+  /**
+   * 检索过去六个月浏览节点的正面和负面评价趋势。
+   * GET /customerFeedback/2024-06-01/browseNodes/{browseNodeId}/reviews/trends
+   */
+  async getBrowseNodeReviewTrends(params: { browseNodeId: string } & import('../types.ts').BaseRequestParams) {
+    const { browseNodeId, ...queryParams } = params;
+    return this.client.request({
+      method: 'GET',
+      path: `/customerFeedback/2024-06-01/browseNodes/${browseNodeId}/reviews/trends`,
+      query: queryParams
+    });
+  }
+
+  /**
+   * 检索客户在浏览节点退货时提到的主题。
+   * GET /customerFeedback/2024-06-01/browseNodes/{browseNodeId}/returns/topics
+   */
+  async getBrowseNodeReturnTopics(params: { browseNodeId: string } & import('../types.ts').BaseRequestParams) {
+    const { browseNodeId, ...queryParams } = params;
+    return this.client.request({
+      method: 'GET',
+      path: `/customerFeedback/2024-06-01/browseNodes/${browseNodeId}/returns/topics`,
+      query: queryParams
+    });
+  }
+
+  /**
+   * 检索客户在浏览节点退货时提到的话题趋势。
+   * GET /customerFeedback/2024-06-01/browseNodes/{browseNodeId}/returns/trends
+   */
+  async getBrowseNodeReturnTrends(params: { browseNodeId: string } & import('../types.ts').BaseRequestParams) {
+    const { browseNodeId, ...queryParams } = params;
+    return this.client.request({
+      method: 'GET',
+      path: `/customerFeedback/2024-06-01/browseNodes/${browseNodeId}/returns/trends`,
+      query: queryParams
+    });
+  }
+}

+ 31 - 0
modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/src/api/externalFulfillment.ts

@@ -0,0 +1,31 @@
+import { SpApiClient } from '../client.ts';
+import { ListReturnsParams } from '../types.ts';
+
+export class ExternalFulfillmentApi {
+  constructor(private client: SpApiClient) {}
+
+  /**
+   * 退货列表
+   * GET /externalFulfillment/2024-09-11/returns
+   */
+  async listReturns(params: ListReturnsParams) {
+    // 注意: 如果 sellerId 在某些版本中是路径的一部分,请查看文档。
+    // 这里文档说是 /returns,所以它可能使用查询参数。
+    return this.client.request({
+      method: 'GET',
+      path: '/externalFulfillment/2024-09-11/returns',
+      query: params as any
+    });
+  }
+
+  /**
+   * 获取退货详情
+   * GET /externalFulfillment/2024-09-11/returns/{returnId}
+   */
+  async getReturn(returnId: string) {
+    return this.client.request({
+      method: 'GET',
+      path: `/externalFulfillment/2024-09-11/returns/${returnId}`
+    });
+  }
+}

+ 18 - 0
modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/src/api/inventory.ts

@@ -0,0 +1,18 @@
+import { SpApiClient } from '../client.ts';
+import { GetInventorySummariesParams } from '../types.ts';
+
+export class InventoryApi {
+  constructor(private client: SpApiClient) {}
+
+  /**
+   * 获取库存摘要
+   * GET /fba/inventory/v1/summaries
+   */
+  async getInventorySummaries(params: GetInventorySummariesParams) {
+    return this.client.request({
+      method: 'GET',
+      path: '/fba/inventory/v1/summaries',
+      query: params as any
+    });
+  }
+}

+ 57 - 0
modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/src/api/listings.ts

@@ -0,0 +1,57 @@
+import { SpApiClient } from '../client.ts';
+import { SearchListingsItemsParams, GetListingsItemParams } from '../types.ts';
+
+export class ListingsApi {
+  constructor(private client: SpApiClient) {}
+
+  /**
+   * 列出所有列表项
+   * GET /listings/2021-08-01/items/{sellerId}
+   */
+  async searchListingsItems(params: SearchListingsItemsParams) {
+    const { sellerId, ...queryParams } = params;
+    return this.client.request({
+      method: 'GET',
+      path: `/listings/2021-08-01/items/${sellerId}`,
+      query: queryParams as any
+    });
+  }
+
+  /**
+   * 获取列表项详情
+   * GET /listings/2021-08-01/items/{sellerId}/{sku}
+   */
+  async getListingsItem(params: GetListingsItemParams) {
+    const { sellerId, sku, ...queryParams } = params;
+    return this.client.request({
+      method: 'GET',
+      path: `/listings/2021-08-01/items/${sellerId}/${sku}`,
+      query: queryParams as any
+    });
+  }
+
+  /**
+   * Put (创建/更新) 列表项
+   * PUT /listings/2021-08-01/items/{sellerId}/{sku}
+   */
+  async putListingsItem(sellerId: string, sku: string, marketplaceIds: string[], body: any) {
+    return this.client.request({
+      method: 'PUT',
+      path: `/listings/2021-08-01/items/${sellerId}/${sku}`,
+      query: { marketplaceIds: marketplaceIds.join(',') },
+      body
+    });
+  }
+
+  /**
+   * 删除列表项
+   * DELETE /listings/2021-08-01/items/{sellerId}/{sku}
+   */
+  async deleteListingsItem(sellerId: string, sku: string, marketplaceIds: string[]) {
+    return this.client.request({
+      method: 'DELETE',
+      path: `/listings/2021-08-01/items/${sellerId}/${sku}`,
+      query: { marketplaceIds: marketplaceIds.join(',') }
+    });
+  }
+}

+ 40 - 0
modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/src/api/notifications.ts

@@ -0,0 +1,40 @@
+import { SpApiClient } from '../client.ts';
+import { CreateSubscriptionParams } from '../types.ts';
+
+export class NotificationsApi {
+  constructor(private client: SpApiClient) {}
+
+  /**
+   * 创建订阅
+   * POST /notifications/v1/subscriptions/{notificationType}
+   */
+  async createSubscription(notificationType: string, params: Omit<CreateSubscriptionParams, 'notificationType'>) {
+    return this.client.request({
+      method: 'POST',
+      path: `/notifications/v1/subscriptions/${notificationType}`,
+      body: params
+    });
+  }
+
+  /**
+   * 获取订阅
+   * GET /notifications/v1/subscriptions/{notificationType}/{subscriptionId}
+   */
+  async getSubscription(notificationType: string, subscriptionId: string) {
+    return this.client.request({
+      method: 'GET',
+      path: `/notifications/v1/subscriptions/${notificationType}/${subscriptionId}`
+    });
+  }
+
+  /**
+   * 删除订阅
+   * DELETE /notifications/v1/subscriptions/{notificationType}/{subscriptionId}
+   */
+  async deleteSubscription(notificationType: string, subscriptionId: string) {
+    return this.client.request({
+      method: 'DELETE',
+      path: `/notifications/v1/subscriptions/${notificationType}/${subscriptionId}`
+    });
+  }
+}

+ 41 - 0
modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/src/api/orders.ts

@@ -0,0 +1,41 @@
+import { SpApiClient } from '../client.ts';
+import { GetOrdersParams } from '../types.ts';
+
+export class OrdersApi {
+  constructor(private client: SpApiClient) {}
+
+  /**
+   * 获取订单列表
+   * GET /orders/v0/orders
+   */
+  async getOrders(params: GetOrdersParams) {
+    return this.client.request({
+      method: 'GET',
+      path: '/orders/v0/orders',
+      query: params as any
+    });
+  }
+
+  /**
+   * 获取订单详情
+   * GET /orders/v0/orders/{orderId}
+   */
+  async getOrder(orderId: string) {
+    return this.client.request({
+      method: 'GET',
+      path: `/orders/v0/orders/${orderId}`
+    });
+  }
+
+  /**
+   * 获取订单商品
+   * GET /orders/v0/orders/{orderId}/orderItems
+   */
+  async getOrderItems(orderId: string, nextToken?: string) {
+    return this.client.request({
+      method: 'GET',
+      path: `/orders/v0/orders/${orderId}/orderItems`,
+      query: nextToken ? { NextToken: nextToken } : undefined
+    });
+  }
+}

+ 30 - 0
modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/src/api/pricing.ts

@@ -0,0 +1,30 @@
+import { SpApiClient } from '../client.ts';
+import { GetPricingParams } from '../types.ts';
+
+export class PricingApi {
+  constructor(private client: SpApiClient) {}
+
+  /**
+   * 获取定价信息
+   * GET /products/pricing/v0/price
+   */
+  async getPricing(params: GetPricingParams) {
+    return this.client.request({
+      method: 'GET',
+      path: '/products/pricing/v0/price',
+      query: params as any
+    });
+  }
+
+  /**
+   * 获取竞争性定价
+   * GET /products/pricing/v0/competitivePrice
+   */
+  async getCompetitivePricing(params: GetPricingParams) {
+    return this.client.request({
+      method: 'GET',
+      path: '/products/pricing/v0/competitivePrice',
+      query: params as any
+    });
+  }
+}

+ 40 - 0
modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/src/api/reports.ts

@@ -0,0 +1,40 @@
+import { SpApiClient } from '../client.ts';
+import { CreateReportParams } from '../types.ts';
+
+export class ReportsApi {
+  constructor(private client: SpApiClient) {}
+
+  /**
+   * 创建报告
+   * POST /reports/2021-06-30/reports
+   */
+  async createReport(params: CreateReportParams) {
+    return this.client.request({
+      method: 'POST',
+      path: '/reports/2021-06-30/reports',
+      body: params
+    });
+  }
+
+  /**
+   * 获取报告
+   * GET /reports/2021-06-30/reports/{reportId}
+   */
+  async getReport(reportId: string) {
+    return this.client.request({
+      method: 'GET',
+      path: `/reports/2021-06-30/reports/${reportId}`
+    });
+  }
+
+  /**
+   * 获取报告文档
+   * GET /reports/2021-06-30/documents/{reportDocumentId}
+   */
+  async getReportDocument(reportDocumentId: string) {
+    return this.client.request({
+      method: 'GET',
+      path: `/reports/2021-06-30/documents/${reportDocumentId}`
+    });
+  }
+}

+ 18 - 0
modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/src/api/sales.ts

@@ -0,0 +1,18 @@
+import { SpApiClient } from '../client.ts';
+import { GetOrderMetricsParams } from '../types.ts';
+
+export class SalesApi {
+  constructor(private client: SpApiClient) {}
+
+  /**
+   * 回报给定买方类型的给定区间内汇总订单指标
+   * GET /sales/v1/orderMetrics
+   */
+  async getOrderMetrics(params: GetOrderMetricsParams) {
+    return this.client.request({
+      method: 'GET',
+      path: '/sales/v1/orderMetrics',
+      query: params as any
+    });
+  }
+}

+ 27 - 0
modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/src/api/sellers.ts

@@ -0,0 +1,27 @@
+import { SpApiClient } from '../client.ts';
+
+export class SellersApi {
+  constructor(private client: SpApiClient) {}
+
+  /**
+   * 返回卖家账户及其市场的信息
+   * GET /sellers/v1/account
+   */
+  async getAccount() {
+    return this.client.request({
+      method: 'GET',
+      path: '/sellers/v1/account'
+    });
+  }
+
+  /**
+   * 返回卖家可以列出商品的市场列表以及卖家在这些市场的参与信息
+   * GET /sellers/v1/marketplaceParticipations
+   */
+  async getMarketplaceParticipations() {
+    return this.client.request({
+      method: 'GET',
+      path: '/sellers/v1/marketplaceParticipations'
+    });
+  }
+}

+ 204 - 0
modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/src/client.ts

@@ -0,0 +1,204 @@
+
+import axios from 'axios';
+import { SpApiConfig, LwaTokenResponse, ShopConfig } from './types.ts';
+import { getEndpointByMarketplaceId, getEndpointByRegion } from './marketplace-helper.ts';
+
+/**
+ * Amazon Selling Partner API 基础客户端
+ * 处理 LWA 认证 (简化模式,无需 AWS V4 签名)
+ */
+export class SpApiClient {
+  // 配置在构造函数中现在是可选的,但可以传递用于遗留/静态用法
+  // 然而,对于基于 Parse 的多租户用法,我们依赖于方法参数。
+  // 为了保持向后兼容性并支持新的动态模式,我们可以使用混合方法。
+  
+  // 对于动态用法,我们将主要不使用 this.config。
+  private staticConfig?: SpApiConfig;
+
+  constructor(config?: SpApiConfig) {
+    this.staticConfig = config;
+  }
+
+  /**
+   * 获取基础 URL
+   */
+  private getBaseUrl(marketplaceId?: string, isSandbox?: boolean, region?: string): string {
+    if (marketplaceId) {
+      return getEndpointByMarketplaceId(marketplaceId, isSandbox);
+    }
+    
+    // 如果提供了区域配置,则使用区域
+    if (region) {
+      return getEndpointByRegion(region, isSandbox);
+    }
+
+    // 回退到静态配置 (遗留模式)
+    if (this.staticConfig) {
+       const REGION_ENDPOINTS: Record<string, string> = {
+        'us-east-1': 'https://sellingpartnerapi-na.amazon.com',
+        'eu-west-1': 'https://sellingpartnerapi-eu.amazon.com',
+        'us-west-2': 'https://sellingpartnerapi-fe.amazon.com'
+      };
+      const SANDBOX_ENDPOINTS: Record<string, string> = {
+        'us-east-1': 'https://sandbox.sellingpartnerapi-na.amazon.com',
+        'eu-west-1': 'https://sandbox.sellingpartnerapi-eu.amazon.com',
+        'us-west-2': 'https://sandbox.sellingpartnerapi-fe.amazon.com'
+      };
+      const endpoints = this.staticConfig.sandbox ? SANDBOX_ENDPOINTS : REGION_ENDPOINTS;
+      return endpoints[this.staticConfig.region] || endpoints['us-east-1'];
+    }
+
+    throw new Error('无法确定 SP-API 端点,MarketplaceId 为必填项。');
+  }
+
+  /**
+   * 获取 LWA Access Token
+   * 仅使用 config 获取 token
+   */
+  private async getAccessToken(context?: { config: ShopConfig }): Promise<string> {
+    // 1. 动态上下文模式
+    if (context) {
+      const { config } = context;
+      // 刷新 Token
+      return this.refreshAccessToken(config.clientId, config.clientSecret, config.refreshToken);
+    }
+    // 2. 静态配置模式 (遗留/内存)
+    if (this.staticConfig) {
+      return this.refreshAccessToken(this.staticConfig.clientId, this.staticConfig.clientSecret, this.staticConfig.refreshToken);
+    }
+
+    throw new Error('缺少 SpApiClient 配置。');
+  }
+
+  /**
+   * 刷新 Token 逻辑
+   */
+  private async refreshAccessToken(clientId: string, clientSecret: string, refreshToken: string): Promise<string> {
+    try {
+      console.log('[SP-API] 正在刷新 Access Token...');
+      const response = await axios.post<LwaTokenResponse>(
+        'https://api.amazon.com/auth/o2/token',
+        new URLSearchParams({
+          grant_type: 'refresh_token',
+          refresh_token: refreshToken,
+          client_id: clientId,
+          client_secret: clientSecret
+        }),
+        {
+          headers: {
+            'Content-Type': 'application/x-www-form-urlencoded'
+          }
+        }
+      );
+
+      const { access_token } = response.data;
+      
+      // 注意:这里不再将 token 保存回数据库,因为我们不再传递 parseShop 对象。
+      // 如果需要缓存,应在上层或使用内存缓存实现。
+      
+      return access_token;
+    } catch (error) {
+      console.error('[SP-API] 刷新 token 失败:', error);
+      throw error;
+    }
+  }
+
+  /**
+   * 发送 SP-API 请求
+   */
+  async request<T>(options: {
+    method: 'GET' | 'POST' | 'PUT' | 'DELETE';
+    path: string;
+    query?: Record<string, any>;
+    body?: any;
+    headers?: Record<string, string>;
+    context?: { // 动态模式的可选上下文
+        shopId: string;
+        marketplaceId?: string;
+        config: ShopConfig;
+    }
+  }): Promise<T> {
+    const accessToken = await this.getAccessToken(options.context);
+    // 确定用于端点选择的 MarketplaceId
+    // 优先级: 上下文中显式指定 -> 查询参数中显式指定 -> 配置默认值 (如果是静态的)
+    let marketplaceId = options.context?.marketplaceId;
+    if (!marketplaceId && options.query) {
+        // 尝试在查询参数中查找 marketplaceId (不区分大小写)
+        const key = Object.keys(options.query).find(k => k.toLowerCase() === 'marketplaceids' || k.toLowerCase() === 'marketplaceid');
+        if (key) {
+            const val = options.query[key];
+            marketplaceId = Array.isArray(val) ? val[0] : (val as string)?.split(',')[0];
+        }
+    }
+
+    const isSandbox = options.context?.config.sandbox || this.staticConfig?.sandbox;
+    // 尝试从配置中获取 Region
+    const region = options.context?.config.region || this.staticConfig?.region;
+    
+    const baseUrl = this.getBaseUrl(marketplaceId, isSandbox, region);
+    const url = new URL(options.path, baseUrl);
+    if (options.query) {
+      Object.entries(options.query).forEach(([key, value]) => {
+        if (value !== undefined && value !== null) {
+          url.searchParams.append(key, String(value));
+        }
+      });
+    }
+    const headers = {
+      'x-amz-access-token': accessToken,
+      'Content-Type': 'application/json',
+      'User-Agent': 'fmode-amazon-sp-api/1.0',
+    };
+
+    let retries = 0;
+    const maxRetries = 3;
+
+    while (retries < maxRetries) {
+      try {
+        console.log(`[SP-API] 请求: ${options.method} ${url}`, { query: options.query, shopId: options.context?.shopId });
+        const response = await axios({
+          method: options.method,
+          url: url.toString(),
+          data: options.body,
+          headers
+        });
+        return response.data;
+      } catch (error: any) {
+        if (axios.isAxiosError(error)) {
+          console.log(error.response?.data);
+          console.error(`[SP-API] 错误: ${options.method} ${url}`, {
+            status: error.response?.status,
+            data: error.response?.data,
+            message: error.message
+          });
+
+          // 处理 429 Too Many Requests
+          if (error.response?.status === 429) {
+            retries++;
+            if (retries >= maxRetries) throw error;
+            
+            // 等待 Retry-After 或指数退避
+            const retryAfter = parseInt(error.response.headers['retry-after'] || '0', 10);
+            const delay = retryAfter ? retryAfter * 1000 : Math.pow(2, retries) * 1000;
+            
+            console.log(`[SP-API] 速率限制。${delay}ms 后重试... (尝试 ${retries}/${maxRetries})`);
+            await new Promise(resolve => setTimeout(resolve, delay));
+            continue;
+          }
+          
+          // 处理 5xx 服务器错误 (可选,但有利于稳定性)
+          if (error.response?.status && error.response.status >= 500) {
+             retries++;
+             if (retries >= maxRetries) throw error;
+             const delay = Math.pow(2, retries) * 1000;
+             console.log(`[SP-API] 服务器错误。${delay}ms 后重试... (尝试 ${retries}/${maxRetries})`);
+             await new Promise(resolve => setTimeout(resolve, delay));
+             continue;
+          }
+        }
+        throw error;
+      }
+    }
+    throw new Error('超过最大重试次数');
+  }
+}

+ 33 - 0
modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/src/dev-server.ts

@@ -0,0 +1,33 @@
+
+import express from 'express';
+import { createSpApiRouter } from './routes.ts';
+import { initParse } from './parse-init.ts';
+
+const app = express();
+app.use(express.json());
+
+// Initialize Parse SDK
+// Ensure these environment variables are set when running the dev server
+initParse({
+  appId: 'msq-voc',
+  serverURL: 'https://server-msq.fmode.cn/parse'
+});
+
+const port = process.env.PORT || 3000;
+
+// Mount the SP-API router
+// Uses the default shopAuthMiddleware which checks for 'shop-objectid' header and queries Parse
+app.use('/sp-api', createSpApiRouter());
+
+app.get('/', (req, res) => {
+    res.send('SP-API Dev Server Running. Try /sp-api/orders');
+});
+
+app.listen(port, () => {
+  console.log(`Dev server running at http://localhost:${port}`);
+  console.log(`Ensure you have a running Parse Server and valid Shop ObjectId.`);
+  console.log(`Test URL: http://localhost:${port}/sp-api/orders?CreatedAfter=2023-01-01&marketplaceIds=ATVPDKIKX0DER`);
+  console.log(`Required Header: shop-objectid: <YOUR_SHOP_OBJECT_ID>`);
+});
+
+// curl -H "shop-objectid: mdHNWzZmcA" "http://localhost:3000/sp-api/sellers/account"

+ 79 - 0
modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/src/marketplace-helper.ts

@@ -0,0 +1,79 @@
+/**
+ * 亚马逊 Marketplace ID 到区域端点的映射
+ * 参考: https://developer-docs.amazon.com/sp-api/docs/sp-api-endpoints
+ */
+
+const ENDPOINTS = {
+  NA: 'https://sellingpartnerapi-na.amazon.com',
+  EU: 'https://sellingpartnerapi-eu.amazon.com',
+  FE: 'https://sellingpartnerapi-fe.amazon.com'
+};
+
+const SANDBOX_ENDPOINTS = {
+  NA: 'https://sandbox.sellingpartnerapi-na.amazon.com',
+  EU: 'https://sandbox.sellingpartnerapi-eu.amazon.com',
+  FE: 'https://sandbox.sellingpartnerapi-fe.amazon.com'
+};
+
+const MARKETPLACE_REGION_MAP: Record<string, keyof typeof ENDPOINTS> = {
+  // 北美
+  'A2EUQ1WTGCTBG2': 'NA', // 加拿大
+  'ATVPDKIKX0DER': 'NA', // 美国
+  'A1AM78C64UM0Y8': 'NA', // 墨西哥
+  'A2Q3Y263D00KWC': 'NA', // 巴西
+
+  // 欧洲
+  'A1RKKUPIHCS9HS': 'EU', // 西班牙
+  'A1F83G8C2ARO7P': 'EU', // 英国
+  'A13V1IB3VIYZZH': 'EU', // 法国
+  'A1805IZSGTT6HS': 'EU', // 比利时
+  'A1PA6795UKMFR9': 'EU', // 德国
+  'APJ6JRA9NG5V4': 'EU', // 意大利
+  'A2NODRKZP88ZB9': 'EU', // 瑞典
+  'AE08WJ6EMKS27': 'EU', // 埃及
+  'A1C3SOZRARQ6R3': 'EU', // 波兰
+  'AMEN7PMS3EDWL': 'EU', // 比利时 (重复? 检查中...) - 实际上 BE 是 A1805IZSGTT6HS.
+  'A33AVAJ2PDY3EV': 'EU', // 土耳其
+  'A17E79C6D8DWNP': 'EU', // 沙特阿拉伯
+  'A2VIGQ35RCS4UG': 'EU', // 阿联酋
+  'ARBP9OOSHTCHU': 'EU', // 埃及 (重复?)
+  'A21TJRUUN4KGV': 'EU', // 印度
+
+  // 远东
+  'A1VC38T7YXB528': 'FE', // 日本
+  'A39IBJ37TRP1C6': 'FE', // 澳大利亚
+  'A19VAU5U5O7RUS': 'FE', // 新加坡
+};
+
+/**
+ * 根据 Marketplace ID 获取 SP-API 端点
+ */
+export const getEndpointByMarketplaceId = (marketplaceId: string, isSandbox = false): string => {
+  const region = MARKETPLACE_REGION_MAP[marketplaceId] || 'NA'; // 如果未知则默认为 NA
+  const endpoints = isSandbox ? SANDBOX_ENDPOINTS : ENDPOINTS;
+  return endpoints[region];
+};
+
+/**
+ * 根据区域代码获取 SP-API 端点
+ * @param regionCode e.g., 'us-east-1', 'eu-west-1', 'us-west-2' or 'NA', 'EU', 'FE'
+ */
+export const getEndpointByRegion = (regionCode: string, isSandbox = false): string => {
+  const endpoints = isSandbox ? SANDBOX_ENDPOINTS : ENDPOINTS;
+  
+  // Map AWS regions to SP-API regions
+  const awsRegionMap: Record<string, keyof typeof ENDPOINTS> = {
+    'us-east-1': 'NA',
+    'eu-west-1': 'EU',
+    'us-west-2': 'FE'
+  };
+
+  const regionKey = awsRegionMap[regionCode] || (regionCode as keyof typeof ENDPOINTS);
+  
+  if (endpoints[regionKey]) {
+    return endpoints[regionKey];
+  }
+  
+  // Default to NA if unknown
+  return endpoints.NA;
+};

+ 53 - 0
modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/src/middleware/auth.ts

@@ -0,0 +1,53 @@
+import { Request, Response, NextFunction } from 'express';
+import { getParse } from '../parse-init.ts';
+import { ShopConfig } from '../types.ts';
+
+export const shopAuthMiddleware = async (req: Request, res: Response, next: NextFunction) => {
+  try {
+    const shopId = req.headers['shop-objectid'] as string || req.headers['x-shop-objectid'] as string;
+    if (!shopId) {
+      return res.status(400).json({
+        success: false,
+        message: '缺少必需的 header: shop-objectId'
+      });
+    }
+
+    const Parse = getParse();
+    const query = new Parse.Query('Shop');
+    let shop: Parse.Object;
+    
+    try {
+      shop = await query.get(shopId);
+      console.log('[SP-API] 认证中间件:', shop.get('name'));
+    } catch (error) {
+      return res.status(400).json({
+        success: false,
+        message: `无效的 shopId: ${shopId}`
+      });
+    }
+
+    const config = shop.get('config').SpApiConfig as ShopConfig;
+    console.log('[SP-API] 认证中间件:', config);
+    if (!config || !config.clientId || !config.clientSecret || !config.refreshToken) {
+      return res.status(400).json({
+        success: false,
+        message: '缺少亚马逊配置信息'
+      });
+    }
+
+    req.spApiContext = {
+      shopId,
+      marketplaceId: shop.get('marketplaceId'),
+      config
+    };
+
+    next();
+  } catch (error: any) {
+    console.error('[SP-API] 认证中间件错误:', error);
+    res.status(500).json({
+      success: false,
+      message: '店铺认证期间发生内部服务器错误',
+      error: error.message
+    });
+  }
+};

+ 17 - 0
modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/src/mod.ts

@@ -0,0 +1,17 @@
+export * from './client.ts';
+export * from './types.ts';
+export * from './routes.ts';
+export * from './parse-init.ts';
+
+export * from './api/customerFeedback.ts';
+export * from './api/orders.ts';
+export * from './api/sales.ts';
+export * from './api/listings.ts';
+export * from './api/externalFulfillment.ts';
+export * from './api/catalogItems.ts';
+export * from './api/inventory.ts';
+export * from './api/pricing.ts';
+export * from './api/reports.ts';
+export * from './api/notifications.ts';
+export * from './api/sellers.ts';
+

+ 29 - 0
modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/src/parse-init.ts

@@ -0,0 +1,29 @@
+import Parse from 'parse/node';
+import { ParseInitConfig } from './types.ts';
+
+let isInitialized = false;
+
+/**
+ * 初始化 Parse SDK
+ */
+export const initParse = (config: ParseInitConfig) => {
+  if (isInitialized) {
+    console.warn('[SP-API] Parse already initialized, skipping...');
+    return;
+  }
+
+  Parse.initialize(config.appId, config.javascriptKey, config.masterKey);
+  Parse.serverURL = config.serverURL;
+  isInitialized = true;
+  console.log(`[SP-API] Parse initialized with appId: ${config.appId} at ${config.serverURL}`);
+};
+
+/**
+ * 获取 Parse 实例 (确保已初始化)
+ */
+export const getParse = () => {
+  if (!isInitialized) {
+    throw new Error('[SP-API] Parse not initialized! Call initParse() first.');
+  }
+  return Parse;
+};

+ 282 - 0
modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/src/routes.ts

@@ -0,0 +1,282 @@
+import { Router, Request, Response, NextFunction } from 'express';
+import { SpApiClient } from './client.ts';
+import { shopAuthMiddleware } from './middleware/auth.ts';
+import { CustomerFeedbackApi } from './api/customerFeedback.ts';
+import { OrdersApi } from './api/orders.ts';
+import { SalesApi } from './api/sales.ts';
+import { ListingsApi } from './api/listings.ts';
+import { ExternalFulfillmentApi } from './api/externalFulfillment.ts';
+import { CatalogItemsApi } from './api/catalogItems.ts';
+// import { InventoryApi } from './api/inventory.ts';
+// import { PricingApi } from './api/pricing.ts';
+// import { ReportsApi } from './api/reports.ts';
+// import { NotificationsApi } from './api/notifications.ts';
+import { SellersApi } from './api/sellers.ts';
+
+// Unified Response Format
+const sendResponse = (res: Response, data: any) => {
+  res.json({
+    success: true,
+    data,
+    timestamp: new Date().toISOString()
+  });
+};
+
+const asyncHandler = (fn: (req: Request, res: Response, next: NextFunction) => Promise<any>) => 
+  (req: Request, res: Response, next: NextFunction) => {
+    Promise.resolve(fn(req, res, next)).catch(next);
+  };
+
+/**
+ * 创建 SP-API 路由 (动态配置版)
+ * 不需要传入静态 client,而是使用中间件动态配置
+ */
+export const createSpApiRouter = (staticClient?: SpApiClient) => {
+  const router = Router();
+  
+  // Apply Shop Authentication Middleware to all routes
+  router.use(shopAuthMiddleware);
+
+  // Helper to inject client with context
+  const getClient = (req: Request): SpApiClient => {
+    // We reuse a singleton instance or static methods if possible, 
+    // but since SpApiClient is now stateless-ish (config via method params), 
+    // we can use a single instance.
+    // However, to keep it clean, let's use the passed staticClient or create a new one.
+    // The important part is passing the context to the API methods.
+    return staticClient || new SpApiClient();
+  };
+
+  // Helper to wrap API calls with context injection
+  // Since we modified SpApiClient.request to take context, 
+  // but the individual API classes (e.g. OrdersApi) wraps the client.request call,
+  // we need to pass the context down.
+  // OPTION: We can modify the individual API classes to accept context in their methods?
+  // OR: We can use a Proxy or Factory to inject context into the client before passing to API.
+  
+  // Better approach for minimal refactor of API classes:
+  // Create a "Scoped Client" that pre-fills the context.
+  
+  const createScopedApi = <T>(ApiClass: new (client: SpApiClient) => T, req: Request): T => {
+    const baseClient = getClient(req);
+    
+    // Create a proxy client that injects context into request()
+    const scopedClient = {
+      request: async (options: any) => {
+        return baseClient.request({
+          ...options,
+          context: {
+            shopId: req.spApiContext?.shopId,
+            marketplaceId: req.spApiContext?.marketplaceId,
+            config: req.spApiContext?.config
+          }
+        });
+      }
+    } as SpApiClient;
+
+    return new ApiClass(scopedClient);
+  };
+
+  // --- Customer Feedback ---
+  router.get('/customerFeedback/items/:asin/reviews/topics', asyncHandler(async (req, res) => {
+    const api = createScopedApi(CustomerFeedbackApi, req);
+    const result = await api.getItemReviewTopics({ ...req.query, asin: req.params.asin } as any);
+    sendResponse(res, result);
+  }));
+  
+  router.get('/customerFeedback/items/:asin/browseNode', asyncHandler(async (req, res) => {
+    const api = createScopedApi(CustomerFeedbackApi, req);
+    const result = await api.getItemBrowseNode({ ...req.query, asin: req.params.asin } as any);
+    sendResponse(res, result);
+  }));
+
+  router.get('/customerFeedback/browseNodes/:browseNodeId/reviews/topics', asyncHandler(async (req, res) => {
+    const api = createScopedApi(CustomerFeedbackApi, req);
+    const result = await api.getBrowseNodeReviewTopics({ ...req.query, browseNodeId: req.params.browseNodeId } as any);
+    sendResponse(res, result);
+  }));
+
+  router.get('/customerFeedback/items/:asin/reviews/trends', asyncHandler(async (req, res) => {
+    const api = createScopedApi(CustomerFeedbackApi, req);
+    const result = await api.getItemReviewTrends({ ...req.query, asin: req.params.asin } as any);
+    sendResponse(res, result);
+  }));
+
+  router.get('/customerFeedback/browseNodes/:browseNodeId/reviews/trends', asyncHandler(async (req, res) => {
+    const api = createScopedApi(CustomerFeedbackApi, req);
+    const result = await api.getBrowseNodeReviewTrends({ ...req.query, browseNodeId: req.params.browseNodeId } as any);
+    sendResponse(res, result);
+  }));
+
+  router.get('/customerFeedback/browseNodes/:browseNodeId/returns/topics', asyncHandler(async (req, res) => {
+    const api = createScopedApi(CustomerFeedbackApi, req);
+    const result = await api.getBrowseNodeReturnTopics({ ...req.query, browseNodeId: req.params.browseNodeId } as any);
+    sendResponse(res, result);
+  }));
+
+  router.get('/customerFeedback/browseNodes/:browseNodeId/returns/trends', asyncHandler(async (req, res) => {
+    const api = createScopedApi(CustomerFeedbackApi, req);
+    const result = await api.getBrowseNodeReturnTrends({ ...req.query, browseNodeId: req.params.browseNodeId } as any);
+    sendResponse(res, result);
+  }));
+  
+  // --- Orders ---
+  router.get('/orders', asyncHandler(async (req, res) => {
+    const api = createScopedApi(OrdersApi, req);
+    const result = await api.getOrders(req.query as any);
+    console.log('result', result);
+    sendResponse(res, result);
+  }));
+  
+  router.get('/orders/:orderId', asyncHandler(async (req:any, res) => {
+    const api = createScopedApi(OrdersApi, req);
+    const result = await api.getOrder(req.params.orderId);
+    sendResponse(res, result);
+  }));
+  
+  router.get('/orders/:orderId/items', asyncHandler(async (req:any, res) => {
+    const api = createScopedApi(OrdersApi, req);
+    const result = await api.getOrderItems(req.params.orderId, req.query.NextToken as string);
+    sendResponse(res, result);
+  }));
+
+  // --- Sales ---
+  router.get('/sales/orderMetrics', asyncHandler(async (req:any, res) => {
+    const api = createScopedApi(SalesApi, req);
+    const result = await api.getOrderMetrics(req.query as any);
+    sendResponse(res, result);
+  }));
+
+  // --- Listings ---
+  router.get('/listings/items/:sellerId', asyncHandler(async (req:any, res) => {
+    const api = createScopedApi(ListingsApi, req);
+    const result = await api.searchListingsItems({ ...req.query, sellerId: req.params.sellerId } as any);
+    sendResponse(res, result);
+  }));
+  
+  router.get('/listings/items/:sellerId/:sku', asyncHandler(async (req:any, res) => {
+    const api = createScopedApi(ListingsApi, req);
+    const result = await api.getListingsItem({ ...req.query, sellerId: req.params.sellerId, sku: req.params.sku } as any);
+    sendResponse(res, result);
+  }));
+  
+  router.put('/listings/items/:sellerId/:sku', asyncHandler(async (req:any, res) => {
+    const api = createScopedApi(ListingsApi, req);
+    const marketplaceIds = (req.query.marketplaceIds as string)?.split(',') || [];
+    const result = await api.putListingsItem(req.params.sellerId, req.params.sku, marketplaceIds, req.body);
+    sendResponse(res, result);
+  }));
+  
+  router.delete('/listings/items/:sellerId/:sku', asyncHandler(async (req:any, res) => {
+    const api = createScopedApi(ListingsApi, req);
+    const marketplaceIds = (req.query.marketplaceIds as string)?.split(',') || [];
+    const result = await api.deleteListingsItem(req.params.sellerId, req.params.sku, marketplaceIds);
+    sendResponse(res, result);
+  }));
+
+  // --- External Fulfillment ---
+  router.get('/externalFulfillment/returns', asyncHandler(async (req, res) => {
+    const api = createScopedApi(ExternalFulfillmentApi, req);
+    const result = await api.listReturns(req.query as any);
+    sendResponse(res, result);
+  }));
+  
+  router.get('/externalFulfillment/returns/:returnId', asyncHandler(async (req:any, res) => {
+    const api = createScopedApi(ExternalFulfillmentApi, req);
+    const result = await api.getReturn(req.params.returnId);
+    sendResponse(res, result);
+  }));
+
+  // --- Catalog Items ---
+  router.get('/catalog/categories', asyncHandler(async (req, res) => {
+    const api = createScopedApi(CatalogItemsApi, req);
+    const result = await api.listCatalogCategories(req.query as any);
+    sendResponse(res, result);
+  }));
+
+  router.get('/catalog/items', asyncHandler(async (req, res) => {
+    const api = createScopedApi(CatalogItemsApi, req);
+    const result = await api.searchCatalogItems(req.query as any);
+    sendResponse(res, result);
+  }));
+
+  router.get('/catalog/items/:asin', asyncHandler(async (req, res) => {
+    const api = createScopedApi(CatalogItemsApi, req);
+    const result = await api.getCatalogItem({ ...req.query, asin: req.params.asin } as any);
+    sendResponse(res, result);
+  }));
+
+  // // --- Inventory ---
+  // router.get('/inventory/summaries', asyncHandler(async (req, res) => {
+  //   const api = createScopedApi(InventoryApi, req);
+  //   const result = await api.getInventorySummaries(req.query as any);
+  //   sendResponse(res, result);
+  // }));
+
+  // // --- Pricing ---
+  // router.get('/pricing/price', asyncHandler(async (req, res) => {
+  //   const api = createScopedApi(PricingApi, req);
+  //   const result = await api.getPricing(req.query as any);
+  //   sendResponse(res, result);
+  // }));
+
+  // router.get('/pricing/competitivePrice', asyncHandler(async (req, res) => {
+  //   const api = createScopedApi(PricingApi, req);
+  //   const result = await api.getCompetitivePricing(req.query as any);
+  //   sendResponse(res, result);
+  // }));
+
+  // // --- Reports ---
+  // router.post('/reports', asyncHandler(async (req, res) => {
+  //   const api = createScopedApi(ReportsApi, req);
+  //   const result = await api.createReport(req.body);
+  //   sendResponse(res, result);
+  // }));
+
+  // router.get('/reports/:reportId', asyncHandler(async (req, res) => {
+  //   const api = createScopedApi(ReportsApi, req);
+  //   const result = await api.getReport(req.params.reportId);
+  //   sendResponse(res, result);
+  // }));
+
+  // router.get('/reports/documents/:reportDocumentId', asyncHandler(async (req, res) => {
+  //   const api = createScopedApi(ReportsApi, req);
+  //   const result = await api.getReportDocument(req.params.reportDocumentId);
+  //   sendResponse(res, result);
+  // }));
+
+  // // --- Notifications ---
+  // router.post('/notifications/subscriptions/:notificationType', asyncHandler(async (req, res) => {
+  //   const api = createScopedApi(NotificationsApi, req);
+  //   const result = await api.createSubscription(req.params.notificationType, req.body);
+  //   sendResponse(res, result);
+  // }));
+
+  // router.get('/notifications/subscriptions/:notificationType/:subscriptionId', asyncHandler(async (req, res) => {
+  //   const api = createScopedApi(NotificationsApi, req);
+  //   const result = await api.getSubscription(req.params.notificationType, req.params.subscriptionId);
+  //   sendResponse(res, result);
+  // }));
+
+  // router.delete('/notifications/subscriptions/:notificationType/:subscriptionId', asyncHandler(async (req, res) => {
+  //   const api = createScopedApi(NotificationsApi, req);
+  //   const result = await api.deleteSubscription(req.params.notificationType, req.params.subscriptionId);
+  //   sendResponse(res, result);
+  // }));
+
+  // --- Sellers ---
+  router.get('/sellers/account', asyncHandler(async (req, res) => {
+    const api = createScopedApi(SellersApi, req);
+    const result: any = await api.getAccount();
+    console.log(result.data.errors);
+    sendResponse(res, result);
+  }));
+
+  router.get('/sellers/marketplaceParticipations', asyncHandler(async (req, res) => {
+    const api = createScopedApi(SellersApi, req);
+    const result = await api.getMarketplaceParticipations();
+    sendResponse(res, result);
+  }));
+
+  return router;
+};

+ 406 - 0
modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/src/types.ts

@@ -0,0 +1,406 @@
+import Parse from 'parse/node'; // 引入 Parse SDK
+/**
+ * Parse Server 初始化配置
+ */
+export interface ParseInitConfig {
+  appId: string;
+  serverURL: string;
+  masterKey?: string;
+  javascriptKey?: string;
+}
+
+/**
+ * 数据库中的 Shop 配置对象结构 (存储在 config 字段中)
+ */
+export interface ShopConfig {
+  clientId: string;
+  clientSecret: string;
+  refreshToken: string;
+  sandbox?: boolean;
+  region?: string; // e.g., 'us-east-1', 'eu-west-1', 'us-west-2'
+}
+
+/**
+ * 扩展 Express Request 接口
+ */
+declare global {
+  namespace Express {
+    interface Request {
+      spApiContext?: {
+        shopId: string;
+        marketplaceId?: string;
+        config: ShopConfig;
+        // parseShop: Parse.Object; // Removed as per requirement
+      };
+    }
+  }
+}
+
+/**
+ * SP-API 配置 (Legacy - for direct usage without Parse)
+ */
+export interface SpApiConfig {
+  clientId: string;
+  clientSecret: string;
+  refreshToken: string;
+  region: string; // e.g., 'us-east-1', 'eu-west-1'
+  sandbox?: boolean; // 是否使用沙箱环境
+}
+
+/**
+ * LWA Token 响应
+ */
+export interface LwaTokenResponse {
+  access_token: string;
+  refresh_token: string;
+  token_type: string;
+  expires_in: number;
+}
+
+/**
+ * 基础请求参数
+ */
+export interface BaseRequestParams {
+  marketplaceId?: string;
+  marketplaceIds?: string[];
+}
+
+/**
+ * Customer Feedback: GetItemReviewTopics 请求参数
+ */
+export interface GetItemReviewTopicsParams extends BaseRequestParams {
+  asin: string;
+  itemName?: string;
+  countryCode?: string;
+  dateRange?: {
+    startDate: string;
+    endDate: string;
+  };
+}
+
+/**
+ * Orders: GetOrders 请求参数
+ */
+export interface GetOrdersParams {
+  MarketplaceIds: string[];
+  CreatedAfter?: string;
+  CreatedBefore?: string;
+  LastUpdatedAfter?: string;
+  LastUpdatedBefore?: string;
+  OrderStatuses?: string[];
+  FulfillmentChannels?: string[];
+  PaymentMethods?: string[];
+  BuyerEmail?: string;
+  SellerOrderId?: string;
+  MaxResultsPerPage?: number;
+  EasyShipShipmentStatuses?: string[];
+  ElectronicInvoiceStatuses?: string[];
+  NextToken?: string;
+  AmazonOrderIds?: string[];
+  ActualFulfillmentSupplySourceId?: string;
+  IsISPU?: boolean;
+  StoreChainStoreId?: string;
+}
+
+/**
+ * Sales: GetOrderMetrics 请求参数
+ */
+export interface GetOrderMetricsParams {
+  marketplaceIds: string[];
+  interval: string; // e.g., "2018-09-01T00:00:00-07:00--2018-09-04T00:00:00-07:00"
+  granularity: 'Hour' | 'Day' | 'Week' | 'Month' | 'Year' | 'Total';
+  granularityTimeZone?: string;
+  buyerType?: 'B2B' | 'B2C' | 'All';
+  fulfillmentNetwork?: string;
+  firstDayOfWeek?: 'Monday' | 'Sunday';
+  asin?: string;
+  sku?: string;
+}
+
+/**
+ * Listings: SearchListingsItems 请求参数
+ */
+export interface SearchListingsItemsParams {
+  sellerId: string;
+  marketplaceIds: string[];
+  issueLocale?: string;
+  includedData?: string[]; // e.g., ["summaries", "attributes", "issues", "offers", "fulfillmentAvailability", "procurement"]
+  identifiers?: string[];
+  identifiersType?: 'ASIN' | 'SKU';
+  asin?: string[]; // 文档示例似乎有 asin, 但 searchListingsItems 通常用 identifiers
+  pageToken?: string;
+  pageSize?: number;
+  keywords?: string[]; // 搜索关键词
+}
+
+/**
+ * Listings: GetListingsItem 请求参数
+ */
+export interface GetListingsItemParams {
+  sellerId: string;
+  sku: string;
+  marketplaceIds: string[];
+  issueLocale?: string;
+  includedData?: string[];
+}
+
+/**
+ * External Fulfillment: ListReturns 请求参数
+ */
+export interface ListReturnsParams {
+  sellerId?: string; // 客户端封装有时需要
+  marketplaceIds?: string[]; // 通常可选或可推断
+  creationDate?: string;
+  lastUpdatedDate?: string;
+  nextToken?: string;
+}
+
+/**
+ * Catalog Items: ListCatalogCategories 请求参数
+ */
+export interface ListCatalogCategoriesParams {
+  MarketplaceId: string;
+  ASIN?: string;
+  SellerSKU?: string;
+}
+
+/**
+ * Catalog Items: SearchCatalogItems 请求参数
+ */
+export interface SearchCatalogItemsParams {
+  marketplaceIds: string[];
+  identifiers?: string[];
+  identifiersType?: 'ASIN' | 'EAN' | 'GTIN' | 'ISBN' | 'JAN' | 'MINSAN' | 'SKU' | 'UPC';
+  keywords?: string[];
+  brandNames?: string[];
+  classificationIds?: string[];
+  pageSize?: number;
+  pageToken?: string;
+  keywordsLocale?: string;
+}
+
+/**
+ * Catalog Items: GetCatalogItem 请求参数
+ */
+export interface GetCatalogItemParams {
+  asin: string;
+  marketplaceIds: string[];
+  includedData?: string[];
+  locale?: string;
+}
+
+/**
+ * Inventory: GetInventorySummaries 请求参数 (Standard SP-API)
+ */
+export interface GetInventorySummariesParams {
+  granularityType: 'Marketplace';
+  granularityId: string;
+  marketplaceIds: string[];
+  details?: boolean;
+  startDateTime?: string;
+  sellerSkus?: string[];
+  nextToken?: string;
+}
+
+/**
+ * Pricing: GetPricing 请求参数 (Standard SP-API)
+ */
+export interface GetPricingParams {
+  MarketplaceId: string;
+  Asins?: string[];
+  Skus?: string[];
+  ItemType: 'Asin' | 'Sku';
+  ItemCondition?: 'New' | 'Used' | 'Collectible' | 'Refurbished' | 'Club';
+  OfferType?: 'B2C' | 'B2B';
+}
+
+/**
+ * Reports: CreateReport 请求参数 (Standard SP-API)
+ */
+export interface CreateReportParams {
+  reportType: string;
+  dataStartTime?: string;
+  dataEndTime?: string;
+  marketplaceIds: string[];
+  reportOptions?: Record<string, string>;
+}
+
+/**
+ * Notifications: CreateSubscription 请求参数 (Standard SP-API)
+ */
+export interface CreateSubscriptionParams {
+  payloadVersion: string;
+  destinationId: string;
+  notificationType: string; // 例如:'ORDER_CHANGE'
+}
+
+// --- 响应类型定义 (简化版,根据需要扩展) ---
+
+export interface SpApiResponse<T> {
+  payload?: T;
+  errors?: SpApiError[];
+}
+
+export interface SpApiError {
+  code: string;
+  message: string;
+  details?: string;
+}
+
+// Customer Feedback 响应
+export interface ReviewTopic {
+  topic: string;
+  asinMetrics?: any;
+  reviewSnippets?: string[];
+  subtopics?: any[];
+}
+
+export interface GetItemReviewTopicsResponse {
+  asin: string;
+  topics: {
+    positiveTopics: ReviewTopic[];
+    negativeTopics: ReviewTopic[];
+  };
+}
+
+// Orders 响应
+export interface Order {
+  AmazonOrderId: string;
+  PurchaseDate: string;
+  LastUpdateDate: string;
+  OrderStatus: string;
+  FulfillmentChannel?: string;
+  SalesChannel?: string;
+  OrderTotal?: {
+    CurrencyCode: string;
+    Amount: string;
+  };
+  NumberOfItemsShipped?: number;
+  NumberOfItemsUnshipped?: number;
+  PaymentMethod?: string;
+  MarketplaceId?: string;
+  BuyerInfo?: {
+    BuyerEmail?: string;
+    BuyerName?: string;
+  };
+  // ... 其他字段
+}
+
+export interface GetOrdersResponse {
+  Orders: Order[];
+  NextToken?: string;
+  LastUpdatedBefore?: string;
+  CreatedBefore?: string;
+}
+
+// Sales 响应
+export interface OrderMetricsInterval {
+  interval: string;
+  unitCount: number;
+  orderItemCount: number;
+  orderCount: number;
+  averageUnitPrice: {
+    currencyCode: string;
+    amount: string;
+  };
+  totalSales: {
+    currencyCode: string;
+    amount: string;
+  };
+}
+
+export type GetOrderMetricsResponse = OrderMetricsInterval[];
+
+// Listings 响应
+export interface ItemSummary {
+  marketplaceId: string;
+  asin: string;
+  productType: string;
+  conditionType?: string;
+  status: string[];
+  itemName: string;
+  createdDate: string;
+  lastUpdatedDate: string;
+  mainImage?: {
+    link: string;
+    height: number;
+    width: number;
+  };
+}
+
+export interface ListingsItem {
+  sku: string;
+  summaries?: ItemSummary[];
+  attributes?: any;
+  issues?: any[];
+  offers?: any[];
+}
+
+export interface SearchListingsItemsResponse {
+  numberOfResults: number;
+  pagination?: {
+    nextToken?: string;
+    previousToken?: string;
+  };
+  items: ListingsItem[];
+}
+
+// --- Sellers 响应 ---
+
+export interface Marketplace {
+  id: string;
+  name: string;
+  countryCode: string;
+  defaultCurrencyCode: string;
+  defaultLanguageCode: string;
+  domainName: string;
+}
+
+export interface Participation {
+  isParticipating: boolean;
+  hasSuspendedListings: boolean;
+}
+
+export interface MarketplaceParticipation {
+  marketplace: Marketplace;
+  participation: Participation;
+}
+
+export type GetMarketplaceParticipationsResponse = MarketplaceParticipation[];
+
+export interface Business {
+  name: string;
+  registeredBusinessAddress: {
+    addressLine1: string;
+    addressLine2?: string;
+    city?: string;
+    stateOrProvinceCode?: string;
+    countryCode: string;
+    postalCode?: string;
+  };
+  companyRegistrationNumber?: string;
+  companyTaxIdentificationNumber?: string;
+  nonLatinName?: string;
+}
+
+export interface PrimaryContact {
+  name: string;
+  address: {
+    addressLine1: string;
+    addressLine2?: string;
+    city?: string;
+    stateOrProvinceCode?: string;
+    countryCode: string;
+    postalCode?: string;
+  };
+  nonLatinName?: string;
+}
+
+export interface GetAccountResponse {
+  marketplaceParticipationList: MarketplaceParticipation[];
+  businessType: 'CHARITY' | 'CRAFTSMAN' | 'NATURAL_PERSON_COMPANY' | 'PUBLIC_LISTED' | 'PRIVATE_LIMITED' | 'SOLE_PROPRIETORSHIP' | 'STATE_OWNED' | 'INDIVIDUAL';
+  buyingRole: 'BUYING_PROFESSIONAL' | 'BUYING_RESELLER';
+  sellingPlan: 'PROFESSIONAL' | 'INDIVIDUAL';
+  business?: Business;
+  primaryContact?: PrimaryContact;
+}

+ 67 - 0
modules/fmode-tikhub-server/docs/fmode-amazon-sp-api/test_auth.ts

@@ -0,0 +1,67 @@
+
+import { SpApiClient } from './src/client.ts';
+import { SpApiConfig } from './src/types.ts';
+
+// 从环境变量读取配置,或者使用占位符
+const config: SpApiConfig = {
+  clientId:'amzn1.application-oa2-client.afc01e883d4143bcae466247ac5fe692',
+  clientSecret:'amzn1.oa2-cs.v1.eb3330dce4b5c835b0d22e855c8db1082632b99ed7226db7114557b3c562d038',
+  refreshToken:'Atzr|IwEBIC8ubYHG1E9Li2-_madN1smv4tKNgv9-SZTkW-OxlyeiXVQMbRon_7-ON9s2spa-fBsi6G1KXdk8eSO2SlyM-5BxMUufTTclXDCmDZWHg6wmi6HPwRceGDbWhqjIuPWseNj4uyBBjUcd9F6O_RYzEsu1yss67RcZikPI7t_i-tUhF5W8l2Ufj2Sl9JXGycxNoJKbZzApPVVLwja_JaiAlqhZX4JYM1gfK9dgynJNgcPhnVZRJ0vNOkurQPtSp6_Yix6itvMhN5QeXZwJdd5xW1T7C6TJXsHJLiEPfTeJIEj7m7UwgdDS7YvdryIL8cMl5Gc',
+  region: 'us-east-1'
+};
+
+
+
+console.log('Initializing SP-API Client with config:', {
+  ...config
+});
+
+const client = new SpApiClient(config);
+
+async function testAuth() {
+  try {
+    console.log('Attempting to fetch Access Token...');
+    // 通过访问私有方法或者触发请求来测试
+    // 由于 getAccessToken 是私有的,我们通过 request 方法触发它
+    // 我们故意请求一个不存在的端点,如果能走到 404,说明 Auth 成功了
+    // 如果是 400/401 且来自 Amazon Auth Server,说明 Auth 失败
+    await client.request({
+      method: 'GET',
+      path: '/orders/v0/orders',
+      query: {
+        MarketplaceIds: ['ATVPDKIKX0DER'], // getOrders expects MarketplaceIds as a list
+        CreatedAfter: '2023-10-01T00:00:00Z'
+      }
+    });
+    console.log('Request executed successfully.');
+  } catch (error: any) {
+    console.log('Test finished with error.');
+    
+    if (error.response) {
+      console.log('HTTP Status:', error.response.status);
+      console.log('Response Data:', JSON.stringify(error.response.data, null, 2));
+      
+      // 分析错误来源
+      if (error.config && error.config.url && error.config.url.includes('api.amazon.com/auth/o2/token')) {
+         console.error('❌ LWA Authentication Failed: Unable to get Access Token.');
+      } else if (error.response.status === 403) {
+         console.log('✅ Access Token acquired successfully! (But API request forbidden/invalid as expected with dummy creds)');
+      } else if (error.response.status === 404) {
+         console.log('✅ Access Token acquired successfully! (API endpoint not found)');
+      } else if (error.response.status === 401) {
+          console.log('❌ API Unauthorized: Access Token might be invalid or expired immediately.');
+      } else {
+          // 如果是我们预期的 token 请求错误
+          if (error.message.includes('refresh_token')) {
+              console.error('❌ LWA Token Error:', error.message);
+          }
+      }
+    } else {
+      console.error('Error Message:', error.message);
+    }
+  }
+}
+
+testAuth();
+
+

+ 109 - 0
modules/fmode-tikhub-server/docs/frontend-example.md

@@ -0,0 +1,109 @@
+
+# 示例:前端调用 TikHub 接口
+
+本文档提供如何从前端(Web 或其他客户端)直接调用本地或远程服务的示例。
+
+## 1. 启动服务
+
+首先确保服务端已经启动:
+
+```bash
+# 在项目根目录
+npm run dev
+# 或
+bun server.ts
+```
+
+假设服务运行在 `http://localhost:10003` (根据 config.json 或默认端口)。
+
+## 2. Curl 调用示例
+
+您可以在终端中使用 `curl` 命令直接测试接口。
+
+### 搜索用户
+```bash
+curl "http://localhost:10003/api/tikhub/search?keyword=elonmusk"
+```
+
+### 获取用户详情
+```bash
+curl "http://localhost:10003/api/tikhub/users/elonmusk"
+```
+
+### 获取用户视频
+```bash
+# 注意替换 secUid 为实际值
+curl "http://localhost:10003/api/tikhub/users/MS4wLjABAAAA.../posts?count=10"
+```
+
+### 获取视频评论
+```bash
+# 注意替换 videoId 为实际值
+curl "http://localhost:10003/api/tikhub/posts/7281234567890/comments"
+```
+
+## 3. 前端 fetch 调用示例
+
+在您的前端代码(Vue, React, Angular 等)中,可以直接使用 `fetch` 或 `axios` 调用。
+
+### 封装 API 服务类 (Frontend Service)
+
+```typescript
+// frontend-api.ts
+
+const API_BASE_URL = 'http://localhost:10003/api/tikhub'; // 生产环境请替换为真实域名
+
+export const TikHubApi = {
+  /**
+   * 搜索用户
+   */
+  async searchUsers(keyword: string) {
+    const res = await fetch(`${API_BASE_URL}/search?keyword=${encodeURIComponent(keyword)}`);
+    return res.json();
+  },
+
+  /**
+   * 获取用户详情
+   */
+  async getUserProfile(uniqueId: string) {
+    const res = await fetch(`${API_BASE_URL}/users/${uniqueId}`);
+    return res.json();
+  },
+
+  /**
+   * 获取用户作品
+   */
+  async getUserPosts(secUid: string, count = 20) {
+    const res = await fetch(`${API_BASE_URL}/users/${secUid}/posts?count=${count}`);
+    return res.json();
+  },
+
+  /**
+   * 获取视频评论
+   */
+  async getPostComments(videoId: string, count = 50) {
+    const res = await fetch(`${API_BASE_URL}/posts/${videoId}/comments?count=${count}`);
+    return res.json();
+  }
+};
+```
+
+### 在组件中使用
+
+```typescript
+import { TikHubApi } from './frontend-api';
+
+async function handleSearch() {
+  try {
+    const result = await TikHubApi.searchUsers('cat');
+    console.log('Search Results:', result.data);
+  } catch (err) {
+    console.error('Search failed:', err);
+  }
+}
+```
+
+## 4. 常见问题
+
+- **CORS 跨域问题**: 如果前端和服务端不在同一个域,请确保服务端配置了 CORS 中间件(`server.ts` 中可能需要添加 `cors` 包)。
+- **端口**: 默认端口可能为 `3000` 或 `10003`,请检查服务端启动日志。

+ 287 - 0
modules/fmode-tikhub-server/docs/hb-hongren/influencer-db.service.ts

@@ -0,0 +1,287 @@
+import { Injectable } from '@angular/core';
+import { FmodeParse, FmodeObject, FmodeQuery } from 'fmode-ng/core';
+
+@Injectable({
+  providedIn: 'root'
+})
+export class InfluencerDbService {
+  private Parse: any;
+
+  constructor() {
+    // 使用已初始化的 Parse 实例
+    this.Parse = FmodeParse.with("nova");
+  }
+
+  /**
+   * 保存或更新红人信息到数据库
+   * @param influencerData 红人数据
+   */
+  async saveInfluencer(influencerData: any): Promise<any> {
+    try {
+      // 查询是否已存在(根据 platform + unique_id)
+      const query = new this.Parse.Query("Influencers");
+      query.equalTo("platform", influencerData.platform);
+      query.equalTo("unique_id", influencerData.unique_id);
+      
+      let influencer = await query.first();
+      
+      if (!influencer) {
+        // 创建新记录
+        influencer = new this.Parse.Object("Influencers");
+      }
+      
+      // 设置字段
+      influencer.set("platform", influencerData.platform);
+      influencer.set("unique_id", influencerData.unique_id);
+      influencer.set("sec_uid", influencerData.sec_uid);
+      influencer.set("nickname", influencerData.nickname);
+      influencer.set("avatar_url", influencerData.avatar_url);
+      influencer.set("signature", influencerData.signature);
+      influencer.set("verification_type", influencerData.verification_type || "none");
+      influencer.set("follower_count", influencerData.follower_count);
+      influencer.set("following_count", influencerData.following_count);
+      influencer.set("heart_count", influencerData.heart_count);
+      influencer.set("video_count", influencerData.video_count);
+      
+      // 计算互动率
+      if (influencerData.follower_count > 0) {
+        const engagementRate = (influencerData.heart_count / influencerData.follower_count * 100).toFixed(2);
+        influencer.set("engagement_rate", parseFloat(engagementRate));
+      }
+      
+      influencer.set("tags", influencerData.tags || []);
+      influencer.set("data_source", influencerData.data_source || "search");
+      influencer.set("is_archived", false);
+      
+      // 保存
+      const savedInfluencer = await influencer.save();
+      console.log('Influencer saved:', savedInfluencer.id);
+      return savedInfluencer;
+    } catch (error) {
+      console.error('Error saving influencer:', error);
+      throw error;
+    }
+  }
+
+  /**
+   * 批量保存红人信息
+   * @param influencersData 红人数据数组
+   */
+  async saveInfluencers(influencersData: any[]): Promise<any[]> {
+    const results = [];
+    for (const data of influencersData) {
+      try {
+        const saved = await this.saveInfluencer(data);
+        results.push(saved);
+      } catch (error) {
+        console.error('Error saving influencer:', data.unique_id, error);
+      }
+    }
+    return results;
+  }
+
+  /**
+   * 查询红人列表
+   * @param options 查询选项
+   */
+  async getInfluencers(options: {
+    platform?: string;
+    limit?: number;
+    skip?: number;
+    sortBy?: string;
+  } = {}): Promise<any[]> {
+    try {
+      const query = new this.Parse.Query("Influencers");
+      
+      // 过滤条件
+      if (options.platform) {
+        query.equalTo("platform", options.platform);
+      }
+      
+      query.equalTo("is_archived", false);
+      
+      // 排序
+      if (options.sortBy === 'followers') {
+        query.descending("follower_count");
+      } else if (options.sortBy === 'engagement') {
+        query.descending("engagement_rate");
+      } else {
+        query.descending("createdAt");
+      }
+      
+      // 分页
+      if (options.limit) {
+        query.limit(options.limit);
+      }
+      if (options.skip) {
+        query.skip(options.skip);
+      }
+      
+      const results = await query.find();
+      return results;
+    } catch (error) {
+      console.error('Error fetching influencers:', error);
+      throw error;
+    }
+  }
+
+  /**
+   * 根据 ID 获取红人详情
+   * @param influencerId 红人 ID
+   */
+  async getInfluencerById(influencerId: string): Promise<any> {
+    try {
+      const query = new this.Parse.Query("Influencers");
+      const influencer = await query.get(influencerId);
+      return influencer;
+    } catch (error) {
+      console.error('Error fetching influencer:', error);
+      throw error;
+    }
+  }
+
+  /**
+   * 保存红人作品
+   * @param influencerId 红人 ID
+   * @param postsData 作品数据数组
+   */
+  async saveInfluencerPosts(influencerId: string, postsData: any[]): Promise<any[]> {
+    const results = [];
+    
+    for (const postData of postsData) {
+      try {
+        // 查询是否已存在
+        const query = new this.Parse.Query("InfluencerPosts");
+        query.equalTo("platform_post_id", postData.platform_post_id);
+        
+        let post = await query.first();
+        
+        if (!post) {
+          post = new this.Parse.Object("InfluencerPosts");
+        }
+        
+        // 设置字段
+        post.set("influencer_id", influencerId);
+        post.set("platform_post_id", postData.platform_post_id);
+        post.set("desc", postData.desc);
+        post.set("cover_url", postData.cover_url);
+        post.set("video_url", postData.video_url);
+        post.set("duration", postData.duration);
+        post.set("create_time", postData.create_time);
+        post.set("play_count", postData.play_count);
+        post.set("digg_count", postData.digg_count);
+        post.set("comment_count", postData.comment_count);
+        post.set("share_count", postData.share_count);
+        post.set("collect_count", postData.collect_count || 0);
+        
+        // 计算互动率
+        if (postData.play_count > 0) {
+          const engagementRate = ((postData.digg_count + postData.comment_count + postData.share_count) / postData.play_count * 100).toFixed(2);
+          post.set("engagement_rate", parseFloat(engagementRate));
+        }
+        
+        post.set("ai_keywords", postData.ai_keywords || []);
+        post.set("is_representative", postData.is_representative || false);
+        
+        const savedPost = await post.save();
+        results.push(savedPost);
+      } catch (error) {
+        console.error('Error saving post:', postData.platform_post_id, error);
+      }
+    }
+    
+    return results;
+  }
+
+  /**
+   * 获取红人的作品列表
+   * @param influencerId 红人 ID
+   */
+  async getInfluencerPosts(influencerId: string): Promise<any[]> {
+    try {
+      const query = new this.Parse.Query("InfluencerPosts");
+      query.equalTo("influencer_id", influencerId);
+      query.descending("create_time");
+      query.limit(20);
+      
+      const results = await query.find();
+      return results;
+    } catch (error) {
+      console.error('Error fetching posts:', error);
+      throw error;
+    }
+  }
+
+  /**
+   * 保存作品评论
+   * @param postId 作品 ID
+   * @param commentsData 评论数据数组
+   */
+  async savePostComments(postId: string, commentsData: any[]): Promise<any[]> {
+    const results = [];
+    
+    // 只保存前50条热门评论
+    const topComments = commentsData.slice(0, 50);
+    
+    for (const commentData of topComments) {
+      try {
+        // 查询是否已存在
+        const query = new this.Parse.Query("InfluencerPostComments");
+        query.equalTo("platform_comment_id", commentData.platform_comment_id);
+        
+        let comment = await query.first();
+        
+        if (!comment) {
+          comment = new this.Parse.Object("InfluencerPostComments");
+        }
+        
+        // 设置字段
+        comment.set("post_id", postId);
+        comment.set("platform_comment_id", commentData.platform_comment_id);
+        comment.set("user_id", commentData.user_id);
+        comment.set("user_nickname", commentData.user_nickname);
+        comment.set("user_avatar", commentData.user_avatar);
+        comment.set("content", commentData.content);
+        comment.set("digg_count", commentData.digg_count);
+        comment.set("reply_count", commentData.reply_count);
+        comment.set("create_time", commentData.create_time);
+        comment.set("is_hot", commentData.digg_count > 100); // 点赞数超过100认为是热门评论
+        
+        const savedComment = await comment.save();
+        results.push(savedComment);
+      } catch (error) {
+        console.error('Error saving comment:', commentData.platform_comment_id, error);
+      }
+    }
+    
+    return results;
+  }
+
+  /**
+   * 保存搜索历史
+   * @param searchData 搜索数据
+   */
+  async saveSearchHistory(searchData: {
+    user_id?: string;
+    keyword: string;
+    result_count: number;
+    search_type: string;
+  }): Promise<any> {
+    try {
+      const searchHistory = new this.Parse.Object("InfluencerSearchHistory");
+      
+      if (searchData.user_id) {
+        searchHistory.set("user_id", searchData.user_id);
+      }
+      searchHistory.set("keyword", searchData.keyword);
+      searchHistory.set("result_count", searchData.result_count);
+      searchHistory.set("search_type", searchData.search_type);
+      
+      const saved = await searchHistory.save();
+      return saved;
+    } catch (error) {
+      console.error('Error saving search history:', error);
+      // 不抛出错误,避免阻塞主流程
+    }
+  }
+}

+ 122 - 0
modules/fmode-tikhub-server/docs/hb-hongren/tikhub.service.ts

@@ -0,0 +1,122 @@
+import { Injectable } from '@angular/core';
+import { FmodeParse } from "fmode-ng/core";
+
+@Injectable({
+  providedIn: 'root'
+})
+export class TikHubService {
+  private Parse: any;
+
+  // Cloud Function IDs
+  private readonly FUNCTIONS = {
+    SEARCH_USER: 'BHmiTlSNA4',
+    GET_PROFILE: 'UfDBWNeH90',
+    GET_POSTS: 'rZ5HSTDpMm',
+    GET_COMMENTS: '6sjEatiEN8'
+  };
+
+  constructor() {
+    this.Parse = FmodeParse.initialize({
+      appId: "ncloudmaster",
+      serverURL: "https://server.fmode.cn/parse"
+    });
+  }
+
+  /**
+   * Search for TikTok users
+   * @param keyword Search keyword
+   * @param cursor Pagination cursor
+   */
+  async searchUsers(keyword: string, cursor: number = 0): Promise<any> {
+    try {
+      const result = await this.Parse.Cloud.function({
+        id: this.FUNCTIONS.SEARCH_USER,
+        keyword,
+        cursor
+      });
+
+      if (result.code === 200 && result.success) {
+        return result.data;
+      } else {
+        throw new Error(result.error || 'Search failed');
+      }
+    } catch (error) {
+      console.error('TikHub Search Error:', error);
+      throw error;
+    }
+  }
+
+  /**
+   * Get user profile details
+   * @param uniqueId User handle (e.g. @username)
+   */
+  async getUserProfile(uniqueId: string): Promise<any> {
+    try {
+      const result = await this.Parse.Cloud.function({
+        id: this.FUNCTIONS.GET_PROFILE,
+        unique_id: uniqueId
+      });
+
+      if (result.code === 200 && result.success) {
+        return result.data;
+      } else {
+        throw new Error(result.error || 'Get Profile failed');
+      }
+    } catch (error) {
+      console.error('TikHub Profile Error:', error);
+      throw error;
+    }
+  }
+
+  /**
+   * Get user posts
+   * @param secUid User secure UID
+   * @param count Number of posts
+   * @param cursor Pagination cursor
+   */
+  async getUserPosts(secUid: string, count: number = 20, cursor: number = 0): Promise<any> {
+    try {
+      const result = await this.Parse.Cloud.function({
+        id: this.FUNCTIONS.GET_POSTS,
+        sec_user_id: secUid,
+        count,
+        cursor
+      });
+
+      if (result.code === 200 && result.success) {
+        return result.data;
+      } else {
+        throw new Error(result.error || 'Get Posts failed');
+      }
+    } catch (error) {
+      console.error('TikHub Posts Error:', error);
+      throw error;
+    }
+  }
+
+  /**
+   * Get video comments
+   * @param videoId Video ID
+   * @param count Number of comments
+   * @param cursor Pagination cursor
+   */
+  async getPostComments(videoId: string, count: number = 50, cursor: number = 0): Promise<any> {
+    try {
+      const result = await this.Parse.Cloud.function({
+        id: this.FUNCTIONS.GET_COMMENTS,
+        video_id: videoId,
+        count,
+        cursor
+      });
+
+      if (result.code === 200 && result.success) {
+        return result.data;
+      } else {
+        throw new Error(result.error || 'Get Comments failed');
+      }
+    } catch (error) {
+      console.error('TikHub Comments Error:', error);
+      throw error;
+    }
+  }
+}

+ 175 - 0
modules/fmode-tikhub-server/docs/hb-hongren/tiktok-cloudfunctions.md

@@ -0,0 +1,175 @@
+# TikTok 红人博主库云函数
+
+本文档定义了用于红人博主库模块的 TikTok 数据采集云函数。
+
+## API 配置
+- **Base URL**: `https://api.tikhub.io/api/v1`
+- **API Key**: `tKIbAsEM8X+GmE2vHqGW7D/ICwK1Q5V4viKFrWiPB6HholGdLFqZJmmyNw==`
+
+## 云函数列表
+
+### 1. 搜索用户 (tiktok_searchUsers)
+用于在红人库首页搜索 TikTok 用户。
+
+```javascript
+async function handler(request, response) {
+  console.log('🚀 执行 TikTok 用户搜索...');
+  
+  const API_KEY = 'tKIbAsEM8X+GmE2vHqGW7D/ICwK1Q5V4viKFrWiPB6HholGdLFqZJmmyNw==';
+  const BASE_URL = 'https://api.tikhub.io/api/v1';
+
+  try {
+    // 1. 获取参数
+    let keyword = request.params?.keyword || request.body?.keyword;
+    let cursor = request.params?.cursor || request.body?.cursor || 0;
+
+    if (!keyword) {
+      response.json({ code: 400, success: false, error: '缺少关键词参数 (keyword)' });
+      return;
+    }
+
+    // 2. 调用 TikHub API
+    const url = `${BASE_URL}/tiktok/web/fetch_search_user?keyword=${encodeURIComponent(keyword)}&cursor=${cursor}`;
+    
+    // 注意:这里假设云函数环境支持 fetch,如果不支持需使用 axios 或其他库
+    const apiRes = await fetch(url, {
+      method: 'GET',
+      headers: {
+        'Authorization': `Bearer ${API_KEY}`, // 假设 API Key 放在 Header,或者根据 TikHub 文档放在 query param
+        // 根据文档 URL 示例,TikHub 似乎通常不需要复杂的 Auth Header,或者 Key 就在 URL 中?
+        // 实际上通常商业 API 需要 Header。如果 TikHub 是开放 API,可能直接调用。
+        // *根据任务描述的 API Key,通常用于 Authorization 头或 X-Api-Key 头*
+        // 这里假设作为 Bearer Token 或 X-Api-Key
+        'X-Api-Key': API_KEY 
+      }
+    });
+
+    const data = await apiRes.json();
+
+    // 3. 处理返回结果
+    // 假设 TikHub 返回结构包含 user_list
+    response.json({ 
+      code: 200, 
+      success: true, 
+      data: data 
+    });
+
+  } catch (error) {
+    console.error('❌ 搜索失败:', error.message);
+    response.json({ code: 500, success: false, error: error.message });
+  }
+}
+```
+
+### 2. 获取用户详情 (tiktok_getUserProfile)
+获取指定用户的详细信息(包括 sec_uid)。
+
+```javascript
+async function handler(request, response) {
+  console.log('🚀 获取 TikTok 用户详情...');
+  
+  const API_KEY = 'tKIbAsEM8X+GmE2vHqGW7D/ICwK1Q5V4viKFrWiPB6HholGdLFqZJmmyNw==';
+  const BASE_URL = 'https://api.tikhub.io/api/v1';
+
+  try {
+    let uniqueId = request.params?.unique_id || request.body?.unique_id;
+
+    if (!uniqueId) {
+      response.json({ code: 400, success: false, error: '缺少 unique_id 参数' });
+      return;
+    }
+
+    const url = `${BASE_URL}/tiktok/web/fetch_user_profile?unique_id=${uniqueId}`;
+    const apiRes = await fetch(url, {
+      headers: { 'X-Api-Key': API_KEY }
+    });
+    const data = await apiRes.json();
+
+    response.json({ code: 200, success: true, data: data });
+
+  } catch (error) {
+    console.error('❌ 获取详情失败:', error.message);
+    response.json({ code: 500, success: false, error: error.message });
+  }
+}
+```
+
+### 3. 获取用户作品列表 (tiktok_getUserPosts)
+获取用户的视频作品列表。
+
+```javascript
+async function handler(request, response) {
+  console.log('🚀 获取 TikTok 用户作品...');
+  
+  const API_KEY = 'tKIbAsEM8X+GmE2vHqGW7D/ICwK1Q5V4viKFrWiPB6HholGdLFqZJmmyNw==';
+  const BASE_URL = 'https://api.tikhub.io/api/v1';
+
+  try {
+    let secUid = request.params?.sec_user_id || request.body?.sec_user_id;
+    let count = request.params?.count || request.body?.count || 20;
+    let cursor = request.params?.cursor || request.body?.cursor || 0;
+
+    if (!secUid) {
+      response.json({ code: 400, success: false, error: '缺少 sec_user_id 参数' });
+      return;
+    }
+
+    const url = `${BASE_URL}/tiktok/web/fetch_user_posts?sec_user_id=${secUid}&count=${count}&cursor=${cursor}`;
+    const apiRes = await fetch(url, {
+      headers: { 'X-Api-Key': API_KEY }
+    });
+    const data = await apiRes.json();
+
+    response.json({ code: 200, success: true, data: data });
+
+  } catch (error) {
+    console.error('❌ 获取作品失败:', error.message);
+    response.json({ code: 500, success: false, error: error.message });
+  }
+}
+```
+
+### 4. 获取视频评论 (tiktok_getPostComments)
+获取视频的评论列表。
+
+```javascript
+async function handler(request, response) {
+  console.log('🚀 获取 TikTok 视频评论...');
+  
+  const API_KEY = 'tKIbAsEM8X+GmE2vHqGW7D/ICwK1Q5V4viKFrWiPB6HholGdLFqZJmmyNw==';
+  const BASE_URL = 'https://api.tikhub.io/api/v1';
+
+  try {
+    let videoId = request.params?.video_id || request.body?.video_id;
+    let count = request.params?.count || request.body?.count || 50;
+    let cursor = request.params?.cursor || request.body?.cursor || 0;
+
+    if (!videoId) {
+      response.json({ code: 400, success: false, error: '缺少 video_id 参数' });
+      return;
+    }
+
+    const url = `${BASE_URL}/tiktok/web/fetch_post_comment?aweme_id=${videoId}&count=${count}&cursor=${cursor}`;
+    const apiRes = await fetch(url, {
+      headers: { 'X-Api-Key': API_KEY }
+    });
+    const data = await apiRes.json();
+
+    response.json({ code: 200, success: true, data: data });
+
+  } catch (error) {
+    console.error('❌ 获取评论失败:', error.message);
+    response.json({ code: 500, success: false, error: error.message });
+  }
+}
+```
+
+## 部署说明
+请将以上代码部署为对应的云函数,并记录 Function ID。
+
+| 函数名 | 建议 Function ID | 描述 |
+| :--- | :--- | :--- |
+| tiktok_searchUsers | `tiktok_search` | 搜索用户 |
+| tiktok_getUserProfile | `tiktok_profile` | 获取用户详情 |
+| tiktok_getUserPosts | `tiktok_posts` | 获取用户作品 |
+| tiktok_getPostComments | `tiktok_comments` | 获取作品评论 |

+ 66 - 0
modules/fmode-tikhub-server/docs/router/common/router.ts

@@ -0,0 +1,66 @@
+import express from 'express';
+import multer from 'multer';
+import * as XLSX from 'xlsx';
+
+const router = express.Router();
+const upload = multer({ storage: multer.memoryStorage() });
+
+// 批量导入接口
+router.post('/import', upload.single('file'), (req: any, res: any) => {
+  try {
+    if (!req.file) {
+      return res.status(400).json({ code: 400, message: 'No file uploaded' });
+    }
+    // 读取 Excel 文件
+    const workbook = XLSX.read(req.file.buffer, { type: 'buffer' });
+    // 获取第一个工作表
+    const sheetName = workbook.SheetNames[0];
+    const worksheet = workbook.Sheets[sheetName];
+    // 转换为 JSON
+    const jsonData = XLSX.utils.sheet_to_json(worksheet);
+
+    res.json({
+      code: 200,
+      message: 'Import successful',
+      data: jsonData,
+      count: jsonData.length
+    });
+  } catch (error: any) {
+    console.error('Import error:', error);
+    res.status(500).json({ code: 500, message: 'Import failed', error: error.message });
+  }
+});
+
+// 批量导出接口
+router.get('/export', (req: any, res: any) => {
+  try {
+    // 模拟数据 (实际应从数据库获取)
+    const data = [
+      { id: 1, name: 'Alice', email: 'alice@example.com', role: 'admin' },
+      { id: 2, name: 'Bob', email: 'bob@example.com', role: 'user' },
+      { id: 3, name: 'Charlie', email: 'charlie@example.com', role: 'user' }
+    ];
+
+    // 创建工作簿和工作表
+    const workbook = XLSX.utils.book_new();
+    const worksheet = XLSX.utils.json_to_sheet(data);
+    
+    // 将工作表添加到工作簿
+    XLSX.utils.book_append_sheet(workbook, worksheet, 'Users');
+
+    // 生成 Buffer
+    const buffer = XLSX.write(workbook, { type: 'buffer', bookType: 'xlsx' });
+
+    // 设置响应头
+    res.setHeader('Content-Disposition', 'attachment; filename="export.xlsx"');
+    res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
+    
+    // 发送文件
+    res.send(buffer);
+  } catch (error: any) {
+    console.error('Export error:', error);
+    res.status(500).json({ code: 500, message: 'Export failed', error: error.message });
+  }
+});
+
+export default router;

+ 62 - 0
modules/fmode-tikhub-server/docs/router/user/router.ts

@@ -0,0 +1,62 @@
+
+import express from 'express';
+const Parse = require('parse');
+
+const router = express.Router();
+
+// 修改密码接口
+router.post('/change-password', async (req: any, res) => {
+  try {
+    // 1. 基础参数校验:旧密码、新密码非空
+    const { oldPassword, newPassword } = req.body;
+    if (!oldPassword || !newPassword) {
+      return res.status(400).json({
+        success: false,
+        message: '参数错误:旧密码和新密码不能为空'
+      });
+    }
+
+    // 2. 获取当前认证用户(Parse自动挂载,未认证则req.user为undefined)
+    const currentUser = req.user;
+    if (!currentUser) {
+      return res.status(401).json({
+        success: false,
+        message: '未授权:请先登录'
+      });
+    }
+
+    // 3. 验证旧密码:通过用户名+旧密码重新登录,验证身份
+    // currentUser.get('username') 若用户用邮箱注册,可替换为 currentUser.get('email')
+    await Parse.User.logIn(currentUser.get('username'), oldPassword);
+
+    // 4. 核心操作:设置新密码(Parse官方方法)
+    currentUser.setPassword(newPassword);
+
+    // 5. 保存用户信息(完成密码持久化,useMasterKey: false 遵循普通用户权限)
+    await currentUser.save(null, { useMasterKey: false });
+
+    // 6. 返回成功响应
+    res.status(200).json({
+      success: true,
+      message: '密码修改成功,请重新登录'
+    });
+  } catch (error: any) {
+    // 统一异常处理:根据错误类型返回对应信息
+    console.error('修改密码失败:', error); // 服务端打印错误,方便调试
+    if (error.code === 101) {
+      // Parse错误码101:用户名/密码错误(旧密码验证失败)
+      return res.status(401).json({
+        success: false,
+        message: '旧密码错误,请重新输入'
+      });
+    }
+    // 其他错误(如数据库错误、参数不合法等)
+    res.status(500).json({
+      success: false,
+      message: '服务器内部错误,修改密码失败',
+      error: process.env.NODE_ENV === 'development' ? error.message : '' // 开发环境返回错误详情
+    });
+  }
+});
+
+export default router; 

+ 113 - 0
modules/fmode-tikhub-server/docs/usage.md

@@ -0,0 +1,113 @@
+
+# @fmode/tikhub-server 使用指南
+
+本模块封装了 TikHub API 的调用,支持在 Node.js/Express 服务端直接集成 TikTok 数据采集能力。
+
+## 1. 安装
+
+确保已安装依赖:
+
+```bash
+npm install axios express
+# 或者
+bun install axios express
+```
+
+## 2. 快速开始
+
+### 方式一:作为 Express 路由挂载 (推荐)
+
+最简单的使用方式是将模块提供的路由直接挂载到您的 Express 应用中。
+
+```typescript
+import express from 'express';
+import { createTikHubRouter } from '../modules/fmode-tikhub-server/src/mod';
+
+const app = express();
+
+// 挂载路由到 /api/tikhub 路径下
+// 默认使用内置的 API Key 和 Base URL
+app.use('/api/tikhub', createTikHubRouter());
+
+// 如果需要自定义配置 (例如更换 API Key)
+/*
+app.use('/api/tikhub', createTikHubRouter({
+  apiKey: 'YOUR_CUSTOM_API_KEY',
+  serverURL: 'https://api.tikhub.io/api/v1' // 可选
+}));
+*/
+
+app.listen(3000, () => {
+  console.log('Server running on http://localhost:3000');
+});
+```
+
+#### API 端点说明
+
+挂载后,您可以通过以下端点访问:
+
+| 功能 | HTTP 方法 | 路径 | 参数 (Query) | 示例 |
+| :--- | :--- | :--- | :--- | :--- |
+| **搜索用户** | GET | `/search` | `keyword` (必填), `cursor` | `/api/tikhub/search?keyword=cat` |
+| **用户详情** | GET | `/users/:uniqueId` | 无 | `/api/tikhub/users/tiktok` |
+| **用户作品** | GET | `/users/:secUid/posts` | `count`, `cursor` | `/api/tikhub/users/MS4wLjAB.../posts` |
+| **视频评论** | GET | `/posts/:videoId/comments` | `count`, `cursor` | `/api/tikhub/posts/728.../comments` |
+
+---
+
+### 方式二:在代码中直接调用 (Service 模式)
+
+如果您需要在自己的业务逻辑中调用 TikHub API(而不是直接暴露 HTTP 接口),可以使用 `TikHubClient` 和 `TikHubUsersApi` 类。
+
+```typescript
+import { TikHubClient, TikHubUsersApi } from '../modules/fmode-tikhub-server/src/mod';
+
+async function main() {
+  // 1. 初始化客户端
+  const client = new TikHubClient({
+    // apiKey: '...' // 可选
+  });
+
+  // 2. 创建 API 实例
+  const usersApi = new TikHubUsersApi(client);
+
+  try {
+    // 示例:搜索用户
+    const searchResult = await usersApi.searchUsers({
+      keyword: 'elon musk',
+      cursor: 0
+    });
+    console.log('搜索结果:', searchResult);
+
+    // 示例:获取用户详情
+    const profile = await usersApi.getUserProfile({
+      uniqueId: 'elonmusk' // TikTok handle
+    });
+    console.log('用户详情:', profile);
+
+  } catch (error) {
+    console.error('API 调用失败:', error);
+  }
+}
+
+main();
+```
+
+## 3. 类型定义
+
+模块导出了完整的 TypeScript 类型定义,方便开发使用。
+
+```typescript
+import { 
+  SearchUsersParams, 
+  GetUserProfileParams, 
+  TikHubResponse 
+} from '../modules/fmode-tikhub-server/src/types';
+```
+
+## 4. 配置说明
+
+`TikHubConfig` 接口支持以下选项:
+
+- `apiKey`: (可选) TikHub API Key。如果不传,将使用模块内置的默认 Key。
+- `serverURL`: (可选) TikHub API 基础地址。默认为 `https://api.tikhub.io/api/v1`。

+ 32 - 0
modules/fmode-tikhub-server/package.json

@@ -0,0 +1,32 @@
+
+{
+  "name": "@fmode/tikhub-server",
+  "version": "1.0.0",
+  "description": "TikHub API 集成模块",
+  "type": "module",
+  "main": "src/mod.ts",
+  "exports": {
+    ".": "./src/mod.ts",
+    "./client": "./src/client.ts",
+    "./types": "./src/types.ts",
+    "./api/users": "./src/api/users.ts"
+  },
+  "scripts": {
+    "test": "echo \"No tests yet\""
+  },
+  "keywords": [
+    "tikhub",
+    "tiktok",
+    "fmode"
+  ],
+  "author": "fmode team",
+  "license": "MIT",
+  "dependencies": {
+    "axios": "^1.6.0",
+    "express": "^4.18.2"
+  },
+  "devDependencies": {
+    "@types/express": "^5.0.0",
+    "@types/node": "^20.0.0"
+  }
+}

+ 351 - 0
modules/fmode-tikhub-server/src/api/users.ts

@@ -0,0 +1,351 @@
+
+import { TikHubClient } from '../client.ts';
+import { 
+  SearchUsersParams, 
+  SearchVideosParams,
+  GetUserProfileParams, 
+  GetUserPostsParams, 
+  GetPostCommentsParams,
+  GetPostCommentRepliesParams
+} from '../types.ts';
+
+export class TikHubUsersApi {
+  private client: TikHubClient;
+
+  constructor(client: TikHubClient) {
+    this.client = client;
+  }
+
+  /**
+   * 搜索 TikTok 用户
+   * API: /tiktok/web/fetch_search_user
+   * 
+   * 数据清洗规则:
+   * 1. 始终返回数组格式的用户列表
+   * 2. 提取核心字段:unique_id, nickname, avatar_url, signature, follower_count, total_favorited
+   * 3. 补充计算字段:engagement_rate (互动率), aweme_count (作品数)
+   */
+  async searchUsers(params: SearchUsersParams) {
+    const response = await this.client.get('/tiktok/web/fetch_search_user', {
+      keyword: params.keyword,
+      cursor: params.cursor || 0
+    });
+
+    // 数据清洗逻辑
+    let users: any[] = [];
+    const rawData = response.data; // TikHub 原始响应通常在 data 字段中
+
+    // 1. 提取用户列表
+    if (rawData?.user_list && Array.isArray(rawData.user_list)) {
+      users = rawData.user_list.map((item: any) => item.user_info);
+    } else if (rawData?.user_info) {
+      // 某些情况下可能直接返回单个对象
+      users = [rawData.user_info];
+    }
+
+    // 2. 格式化用户数据
+    const formattedUsers = users.map(user => {
+      const followerCount = user.follower_count || 0;
+      const totalFavorited = user.total_favorited || 0;
+      // 注意:搜索接口可能不返回 aweme_count (作品数),如不存在则默认为 0
+      const awemeCount = user.aweme_count || user.video_count || 0;
+
+      // 计算互动率
+      // 公式: (总获赞数 / 粉丝数) * 100
+      let engagementRate = 0;
+      if (followerCount > 0) {
+        engagementRate = (totalFavorited / followerCount) * 100;
+      }
+
+      return {
+        unique_id: user.unique_id,
+        nickname: user.nickname,
+        avatar_url: user.avatar_thumb?.url_list?.[0] || user.avatar_medium?.url_list?.[0],
+        signature: user.signature,
+        follower_count: followerCount,
+        total_favorited: totalFavorited,
+        aweme_count: awemeCount,
+        engagement_rate: parseFloat(engagementRate.toFixed(2)) + '%', // 格式化为百分比字符串,如 "12.34%"
+        sec_uid: user.sec_uid,
+        // 保留原始信息供参考
+        user_info: user 
+      };
+    });
+
+    return formattedUsers;
+  }
+
+  /**
+   * 搜索视频 (作为获取用户作品的替代方案)
+   * API: /tiktok/web/fetch_search_video
+   */
+  async searchVideos(params: SearchUsersParams) {
+    const response = await this.client.get('/tiktok/web/fetch_search_video', {
+      keyword: params.keyword,
+      cursor: params.cursor || 0
+    });
+
+    const rawData = response.data;
+    
+    // 清洗 Video 数据
+    let posts: any[] = [];
+    if (rawData?.data && Array.isArray(rawData.data)) {
+      posts = rawData.data.map((item: any) => item.item);
+    } else if (rawData?.item_list && Array.isArray(rawData.item_list)) {
+      posts = rawData.item_list;
+    }
+
+    const formattedPosts = posts
+      .filter(post => post) // 过滤空值
+      .map(post => ({
+        aweme_id: String(post.id || post.aweme_id), // 强制转为字符串
+        desc: post.desc,
+        create_time: post.createTime || post.create_time,
+        video_url: post.video?.playAddr || post.video?.play_addr?.url_list?.[0],
+        cover_url: post.video?.cover || post.video?.cover?.url_list?.[0],
+        duration: post.video?.duration,
+        statistics: {
+          digg_count: post.stats?.diggCount || post.statistics?.digg_count || 0,
+          comment_count: post.stats?.commentCount || post.statistics?.comment_count || 0,
+          share_count: post.stats?.shareCount || post.statistics?.share_count || 0,
+          play_count: post.stats?.playCount || post.statistics?.play_count || 0,
+          collect_count: post.stats?.collectCount || post.statistics?.collect_count || 0
+        },
+        author: {
+          unique_id: post.author?.uniqueId || post.author?.unique_id,
+          nickname: post.author?.nickname,
+          avatar_url: post.author?.avatarThumb || post.author?.avatar_thumb?.url_list?.[0]
+        },
+        music: {
+          id: post.music?.id,
+          title: post.music?.title,
+          author: post.music?.author,
+          cover_url: post.music?.coverThumb || post.music?.cover_thumb?.url_list?.[0]
+        },
+        // 分页游标
+        cursor: rawData?.cursor || 0,
+        has_more: rawData?.has_more === 1 || rawData?.has_more === true,
+        // 保留原始信息
+        raw_data: post
+      }));
+
+    return {
+      list: formattedPosts,
+      cursor: rawData?.cursor || 0,
+      has_more: rawData?.has_more === 1 || rawData?.has_more === true
+    };
+  }
+
+  /**
+   * 获取用户详情
+   * API: /tiktok/web/fetch_user_profile
+   */
+  async getUserProfile(params: GetUserProfileParams) {
+    const response = await this.client.get('/tiktok/web/fetch_user_profile', {
+      uniqueId: params.uniqueId
+    });
+    
+    // TikHub 返回的结构可能是嵌套的,例如 data.userInfo.user
+    // 参考 user_search.md 中的真实返回结构
+    const rawData = response.data;
+    if (!rawData) return null;
+
+    // 1. 尝试多层级提取 user_info 核心对象
+    // 根据提供的 getuserfroile.md,结构是 data.userInfo.user
+    // 但搜索列表返回的是 user_info,所以这里要做兼容
+    let user = rawData.userInfo?.user || rawData.user || rawData.user_info;
+
+    // 2. 提取统计数据 (stats)
+    // 根据文档,统计数据可能在 data.userInfo.stats (数字) 或 statsV2 (字符串)
+    const stats = rawData.userInfo?.stats || user?.stats || {};
+    
+    // 3. 提取字段(优先使用外层 stats,因为更全)
+    const followerCount = stats.followerCount || user?.follower_count || 0;
+    const followingCount = stats.followingCount || user?.following_count || 0;
+    const totalFavorited = stats.heartCount || stats.heart || user?.total_favorited || 0;
+    const videoCount = stats.videoCount || user?.videoCount || user?.aweme_count || 0;
+
+    // 4. 计算互动率
+    let engagementRate = '0%';
+    if (followerCount > 0) {
+      const rate = (Number(totalFavorited) / Number(followerCount)) * 100;
+      engagementRate = rate.toFixed(2) + '%';
+    }
+
+    return {
+      unique_id: user?.uniqueId || user?.unique_id,
+      nickname: user?.nickname,
+      avatar_url: user?.avatarLarger || user?.avatarMedium || user?.avatarThumb || user?.avatar_url,
+      signature: user?.signature,
+      follower_count: followerCount,
+      following_count: followingCount,
+      total_favorited: totalFavorited,
+      aweme_count: videoCount,
+      engagement_rate: engagementRate, // 新增互动率字段
+      sec_uid: user?.secUid || user?.sec_uid,
+      bio_link: user?.bioLink?.link || user?.bio_url || null,
+      is_verified: user?.verified || user?.verification_type > 0 || user?.custom_verify !== "",
+      region: user?.region || user?.language,
+      // 保留原始信息
+      raw_data: rawData
+    };
+  }
+
+  /**
+   * 获取用户发布的视频
+   * API: /tiktok/web/fetch_user_posts
+   */
+  async getUserPosts(params: GetUserPostsParams) {
+    const response = await this.client.get('/tiktok/web/fetch_user_posts', {
+      sec_user_id: params.secUid, // 确保这里是 sec_user_id
+      count: params.count || 20,
+      cursor: params.cursor || 0
+    });
+
+    const rawData = response.data;
+    
+    // 清洗 Posts 数据
+    let posts: any[] = [];
+    if (rawData?.aweme_list && Array.isArray(rawData.aweme_list)) {
+      posts = rawData.aweme_list;
+    }
+
+    const formattedPosts = posts.map(post => ({
+      aweme_id: post.aweme_id,
+      desc: post.desc,
+      create_time: post.create_time,
+      video_url: post.video?.play_addr?.url_list?.[0],
+      cover_url: post.video?.cover?.url_list?.[0],
+      duration: post.video?.duration,
+      statistics: {
+        digg_count: post.statistics?.digg_count || 0, // 点赞
+        comment_count: post.statistics?.comment_count || 0, // 评论
+        share_count: post.statistics?.share_count || 0, // 分享
+        play_count: post.statistics?.play_count || 0, // 播放
+        collect_count: post.statistics?.collect_count || 0 // 收藏
+      },
+      music: {
+        id: post.music?.id,
+        title: post.music?.title,
+        author: post.music?.author,
+        cover_url: post.music?.cover_thumb?.url_list?.[0]
+      },
+      // 分页游标
+      cursor: rawData?.cursor || rawData?.max_cursor || 0,
+      has_more: rawData?.has_more === 1 || rawData?.has_more === true,
+      // 保留原始信息
+      raw_data: post
+    }));
+
+    return {
+      list: formattedPosts,
+      cursor: rawData?.cursor || rawData?.max_cursor || 0,
+      has_more: rawData?.has_more === 1 || rawData?.has_more === true
+    };
+  }
+
+  /**
+   * 获取视频评论
+   * API: /tiktok/web/fetch_post_comment
+   */
+  async getPostComments(params: GetPostCommentsParams) {
+    console.log(`[getPostComments] Fetching comments for videoId: ${params.videoId}`);
+    
+    const response = await this.client.get('/tiktok/web/fetch_post_comment', {
+      aweme_id: params.videoId,
+      count: params.count || 50,
+      cursor: params.cursor || 0
+    });
+
+    const rawData = response.data;
+    
+    // 清洗 Comments 数据
+    let comments: any[] = [];
+    if (rawData?.comments && Array.isArray(rawData.comments)) {
+      comments = rawData.comments;
+    }
+
+    const formattedComments = comments.map(comment => ({
+      cid: String(comment.cid), // 强制转为字符串
+      text: comment.text,
+      create_time: comment.create_time,
+      digg_count: comment.digg_count,
+      reply_comment_total: comment.reply_comment_total || comment.reply_count || 0, // 兼容 reply_count
+      user: {
+        unique_id: comment.user?.unique_id,
+        nickname: comment.user?.nickname,
+        avatar_url: comment.user?.avatar_thumb?.url_list?.[0],
+        sec_uid: comment.user?.sec_uid
+      },
+      // 保留原始信息
+      raw_data: comment
+    }));
+
+    return {
+      list: formattedComments,
+      comments: formattedComments, // 兼容前端可能使用的字段名
+      cursor: rawData?.cursor || 0,
+      has_more: rawData?.has_more === 1 || rawData?.has_more === true
+    };
+  }
+
+  /**
+   * 获取评论回复
+   * API: /tiktok/app/v3/fetch_video_comment_replies
+   */
+  async getPostCommentReplies(params: GetPostCommentRepliesParams) {
+    console.log(`[getPostCommentReplies] Fetching replies for commentId: ${params.commentId}, itemId: ${params.itemId}`);
+
+    try {
+      // 尝试使用 APP V3 接口
+      const response = await this.client.get('/tiktok/app/v3/fetch_video_comment_replies', {
+        item_id: params.itemId,
+        comment_id: params.commentId,
+        count: params.count || 20,
+        cursor: params.cursor || 0
+      });
+
+      const rawData = response.data;
+      
+      // 清洗 Reply 数据
+      let comments: any[] = [];
+      if (rawData?.comments && Array.isArray(rawData.comments)) {
+        comments = rawData.comments;
+      } else if (rawData?.data?.comments && Array.isArray(rawData.data.comments)) {
+        comments = rawData.data.comments;
+      }
+
+      const formattedComments = comments.map(comment => ({
+        cid: String(comment.cid), // 强制转为字符串
+        text: comment.text,
+        create_time: comment.create_time,
+        digg_count: comment.digg_count,
+        reply_comment_total: comment.reply_comment_total || 0,
+        user: {
+          unique_id: comment.user?.unique_id,
+          nickname: comment.user?.nickname,
+          avatar_url: comment.user?.avatar_thumb?.url_list?.[0],
+          sec_uid: comment.user?.sec_uid
+        },
+        // 保留原始信息
+        raw_data: comment
+      }));
+
+      return {
+        list: formattedComments,
+        comments: formattedComments,
+        cursor: rawData?.cursor || 0,
+        has_more: rawData?.has_more === 1 || rawData?.has_more === true
+      };
+    } catch (error: any) {
+      console.warn(`[getPostCommentReplies] Failed to fetch replies (code: ${error.response?.status}):`, error.response?.data || error.message);
+      // 降级处理:返回空列表,避免前端报错
+      return {
+        list: [],
+        comments: [],
+        cursor: 0,
+        has_more: false
+      };
+    }
+  }
+}

+ 68 - 0
modules/fmode-tikhub-server/src/client.ts

@@ -0,0 +1,68 @@
+
+import axios, { AxiosInstance } from 'axios';
+import { TikHubConfig, TikHubResponse } from './types.ts';
+
+/**
+ * TikHub 客户端
+ * 直接调用 api.tikhub.io 的 API
+ */
+export class TikHubClient {
+  private axiosInstance: AxiosInstance;
+  private readonly BASE_URL = 'https://api.tikhub.io/api/v1';
+  private readonly API_KEY = 'tKIbAsEM8X+GmE2vHqGW7D/ICwK1Q5V4viKFrWiPB6HholGdLFqZJmmyNw==';
+
+  constructor(config?: Partial<TikHubConfig>) {
+    // 允许通过配置覆盖默认 Key 和 URL
+    const apiKey = config?.apiKey || this.API_KEY;
+    const baseURL = config?.serverURL || this.BASE_URL;
+
+    this.axiosInstance = axios.create({
+      baseURL,
+      headers: {
+        // TikHub 要求使用 Authorization: Bearer <token>
+        'Authorization': `Bearer ${apiKey}`,
+        'Content-Type': 'application/json'
+      }
+    });
+
+    // 添加请求拦截器用于调试
+    this.axiosInstance.interceptors.request.use(request => {
+      console.log(`[TikHub] ${request.method?.toUpperCase()} ${request.baseURL}${request.url}`, request.params || request.data);
+      return request;
+    });
+  }
+
+  /**
+   * 通用 GET 请求
+   * @param url 请求路径
+   * @param params 查询参数
+   */
+  async get<T = any>(url: string, params: any = {}): Promise<T> {
+    try {
+      const response = await this.axiosInstance.get<T>(url, { params });
+      return response.data;
+    } catch (error: any) {
+      console.error(`TikHub API Error [GET ${url}]:`, error.response?.data || error.message);
+      // 打印完整的错误响应,帮助排查 400 原因
+      if (error.response?.data) {
+        console.error('Error Details:', JSON.stringify(error.response.data, null, 2));
+      }
+      throw error;
+    }
+  }
+
+  /**
+   * 通用 POST 请求
+   * @param url 请求路径
+   * @param data 请求体
+   */
+  async post<T = any>(url: string, data: any = {}): Promise<T> {
+    try {
+      const response = await this.axiosInstance.post<T>(url, data);
+      return response.data;
+    } catch (error: any) {
+      console.error(`TikHub API Error [POST ${url}]:`, error.response?.data || error.message);
+      throw error;
+    }
+  }
+}

+ 5 - 0
modules/fmode-tikhub-server/src/mod.ts

@@ -0,0 +1,5 @@
+
+export * from './types.ts';
+export * from './client.ts';
+export * from './api/users.ts';
+export * from './routes.ts';

+ 130 - 0
modules/fmode-tikhub-server/src/routes.ts

@@ -0,0 +1,130 @@
+
+import { Router, Request, Response, NextFunction } from 'express';
+import { TikHubClient } from './client.ts';
+import { TikHubUsersApi } from './api/users.ts';
+
+// 统一响应格式
+const sendResponse = (res: Response, data: any) => {
+  res.json({
+    success: true,
+    data,
+    timestamp: new Date().toISOString()
+  });
+};
+
+const asyncHandler = (fn: (req: Request, res: Response, next: NextFunction) => Promise<any>) => 
+  (req: Request, res: Response, next: NextFunction) => {
+    Promise.resolve(fn(req, res, next)).catch(next);
+  };
+
+/**
+ * 创建 TikHub 路由
+ * @param config 可选配置,用于初始化 TikHubClient
+ */
+export const createTikHubRouter = (config?: any) => {
+  const router = Router();
+  
+  // 单例 Client (或者根据请求创建,如果需要动态配置)
+  const client = new TikHubClient(config);
+  const usersApi = new TikHubUsersApi(client);
+
+  // 1. 搜索用户
+  // GET /search?keyword=abc&cursor=0
+  router.get('/search', asyncHandler(async (req, res) => {
+    const { keyword, cursor } = req.query;
+    if (!keyword) {
+      throw new Error('Keyword is required');
+    }
+    const result = await usersApi.searchUsers({
+      keyword: String(keyword),
+      cursor: cursor ? Number(cursor) : 0
+    });
+    sendResponse(res, result);
+  }));
+
+  // 1.1 搜索视频 (替代方案)
+  // GET /search/videos?keyword=abc&cursor=0
+  router.get('/search/videos', asyncHandler(async (req, res) => {
+    const { keyword, cursor } = req.query;
+    if (!keyword) {
+      throw new Error('Keyword is required');
+    }
+    const result = await usersApi.searchVideos({
+      keyword: String(keyword),
+      cursor: cursor ? Number(cursor) : 0
+    });
+    sendResponse(res, result);
+  }));
+
+  // 2. 获取用户详情
+  // GET /users/:uniqueId
+  router.get('/users/:uniqueId', asyncHandler(async (req, res) => {
+    const { uniqueId } = req.params;
+    const result = await usersApi.getUserProfile({ uniqueId });
+    sendResponse(res, result);
+  }));
+
+  // 3. 获取用户帖子 (通过 secUid)
+  // GET /users/:secUid/posts?count=20&cursor=0
+  router.get('/users/:secUid/posts', asyncHandler(async (req, res) => {
+    const { secUid } = req.params;
+    const { count, cursor } = req.query;
+    const result = await usersApi.getUserPosts({
+      secUid,
+      count: count ? Number(count) : 20,
+      cursor: cursor ? Number(cursor) : 0
+    });
+    sendResponse(res, result);
+  }));
+
+  // 3.1 获取用户帖子 (通过 uniqueId - 复合操作)
+  // GET /users/:uniqueId/posts_by_id?count=20&cursor=0
+  // 修正:由于 fetch_user_posts 接口不稳定 (404),此处改为直接调用 searchVideos 接口
+  // 通过搜索 uniqueId 对应的视频来作为“用户作品列表”的替代方案
+  router.get('/users/by-id/:uniqueId/posts', asyncHandler(async (req, res) => {
+    const { uniqueId } = req.params;
+    const { count, cursor } = req.query;
+
+    // 直接调用搜索视频接口
+    const result = await usersApi.searchVideos({
+      keyword: uniqueId,
+      cursor: cursor ? Number(cursor) : 0
+    });
+    
+    sendResponse(res, result);
+  }));
+
+  // 4. 获取视频评论
+  // GET /posts/:videoId/comments?count=50&cursor=0
+  router.get('/posts/:videoId/comments', asyncHandler(async (req, res) => {
+    const { videoId } = req.params;
+    const { count, cursor } = req.query;
+    const result = await usersApi.getPostComments({
+      videoId,
+      count: count ? Number(count) : 50,
+      cursor: cursor ? Number(cursor) : 0
+    });
+    sendResponse(res, result);
+  }));
+
+  // 5. 获取评论回复
+  // GET /comments/:commentId/replies?itemId=xxx&count=20&cursor=0
+  router.get('/comments/:commentId/replies', asyncHandler(async (req, res) => {
+    const { commentId } = req.params;
+    const { itemId, count, cursor } = req.query;
+    
+    if (!itemId) {
+      throw new Error('itemId is required');
+    }
+
+    const result = await usersApi.getPostCommentReplies({
+      commentId,
+      itemId: String(itemId),
+      count: count ? Number(count) : 20,
+      cursor: cursor ? Number(cursor) : 0
+    });
+    sendResponse(res, result);
+  }));
+
+  return router;
+};

+ 38 - 0
modules/fmode-tikhub-server/src/types.ts

@@ -0,0 +1,38 @@
+
+export interface TikHubConfig {
+  apiKey?: string;
+  serverURL?: string;
+}
+
+export interface PaginationParams {
+  cursor?: number;
+  count?: number;
+}
+
+export interface SearchUsersParams extends PaginationParams {
+  keyword: string;
+}
+
+export interface GetUserProfileParams {
+  uniqueId: string;
+}
+
+export interface GetUserPostsParams extends PaginationParams {
+  secUid: string;
+}
+
+export interface GetPostCommentsParams extends PaginationParams {
+  videoId: string;
+}
+
+export interface GetPostCommentRepliesParams extends PaginationParams {
+  itemId: string;
+  commentId: string;
+}
+
+export interface TikHubResponse<T = any> {
+  success: boolean;
+  code: number;
+  data: T;
+  error?: string;
+}

+ 1820 - 0
package-lock.json

@@ -0,0 +1,1820 @@
+{
+  "name": "moshengqi-server",
+  "version": "1.0.0",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "moshengqi-server",
+      "version": "1.0.0",
+      "license": "ISC",
+      "dependencies": {
+        "@fmode/studio": "^0.0.12",
+        "@types/cors": "^2.8.19",
+        "@types/express": "^5.0.6",
+        "@types/morgan": "^1.9.10",
+        "@types/multer": "^2.0.0",
+        "axios": "^1.13.5",
+        "cors": "^2.8.6",
+        "express": "^5.2.1",
+        "morgan": "^1.10.1",
+        "multer": "^2.0.2",
+        "parse": "^8.0.3",
+        "xlsx": "^0.18.5",
+        "xmlhttprequest": "^1.8.0"
+      },
+      "devDependencies": {
+        "@types/node": "^25.2.0",
+        "ts-node": "^10.9.2",
+        "typescript": "^5.9.3"
+      }
+    },
+    "node_modules/@babel/runtime": {
+      "version": "7.28.6",
+      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz",
+      "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/runtime-corejs3": {
+      "version": "7.29.0",
+      "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.29.0.tgz",
+      "integrity": "sha512-TgUkdp71C9pIbBcHudc+gXZnihEDOjUAmXO1VO4HHGES7QLZcShR0stfKIxLSNIYx2fqhmJChOjm/wkF8wv4gA==",
+      "license": "MIT",
+      "dependencies": {
+        "core-js-pure": "^3.48.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@cspotcode/source-map-support": {
+      "version": "0.8.1",
+      "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
+      "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/trace-mapping": "0.3.9"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@fmode/studio": {
+      "version": "0.0.12",
+      "resolved": "https://registry.npmjs.org/@fmode/studio/-/studio-0.0.12.tgz",
+      "integrity": "sha512-CTthYDLTJttF4eEakru4dI4Bdk3s/VLiIvaWBHdrznRAzHkabZcGZD0Ebig13/IS3U+zIYsVGyi8FeDJI5l5TQ==",
+      "cpu": [
+        "x64",
+        "arm64"
+      ],
+      "license": "ISC",
+      "os": [
+        "darwin",
+        "linux",
+        "win32"
+      ],
+      "dependencies": {
+        "commander": "^14.0.0"
+      },
+      "bin": {
+        "fmode": "bin/fmode.js",
+        "studio": "bin/fmode.js"
+      },
+      "engines": {
+        "bun": ">=1.0.0",
+        "node": ">=18.0.0"
+      }
+    },
+    "node_modules/@jridgewell/resolve-uri": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+      "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@jridgewell/sourcemap-codec": {
+      "version": "1.5.5",
+      "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+      "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@jridgewell/trace-mapping": {
+      "version": "0.3.9",
+      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
+      "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/resolve-uri": "^3.0.3",
+        "@jridgewell/sourcemap-codec": "^1.4.10"
+      }
+    },
+    "node_modules/@tsconfig/node10": {
+      "version": "1.0.12",
+      "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz",
+      "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@tsconfig/node12": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
+      "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@tsconfig/node14": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
+      "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@tsconfig/node16": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
+      "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/body-parser": {
+      "version": "1.19.6",
+      "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz",
+      "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/connect": "*",
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/connect": {
+      "version": "3.4.38",
+      "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
+      "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/cors": {
+      "version": "2.8.19",
+      "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz",
+      "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/express": {
+      "version": "5.0.6",
+      "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz",
+      "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/body-parser": "*",
+        "@types/express-serve-static-core": "^5.0.0",
+        "@types/serve-static": "^2"
+      }
+    },
+    "node_modules/@types/express-serve-static-core": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz",
+      "integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*",
+        "@types/qs": "*",
+        "@types/range-parser": "*",
+        "@types/send": "*"
+      }
+    },
+    "node_modules/@types/http-errors": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz",
+      "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==",
+      "license": "MIT"
+    },
+    "node_modules/@types/morgan": {
+      "version": "1.9.10",
+      "resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.10.tgz",
+      "integrity": "sha512-sS4A1zheMvsADRVfT0lYbJ4S9lmsey8Zo2F7cnbYjWHP67Q0AwMYuuzLlkIM2N8gAbb9cubhIVFwcIN2XyYCkA==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/multer": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/@types/multer/-/multer-2.0.0.tgz",
+      "integrity": "sha512-C3Z9v9Evij2yST3RSBktxP9STm6OdMc5uR1xF1SGr98uv8dUlAL2hqwrZ3GVB3uyMyiegnscEK6PGtYvNrjTjw==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/express": "*"
+      }
+    },
+    "node_modules/@types/node": {
+      "version": "25.2.3",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.3.tgz",
+      "integrity": "sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ==",
+      "license": "MIT",
+      "dependencies": {
+        "undici-types": "~7.16.0"
+      }
+    },
+    "node_modules/@types/qs": {
+      "version": "6.14.0",
+      "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz",
+      "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==",
+      "license": "MIT"
+    },
+    "node_modules/@types/range-parser": {
+      "version": "1.2.7",
+      "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz",
+      "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==",
+      "license": "MIT"
+    },
+    "node_modules/@types/send": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz",
+      "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/serve-static": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz",
+      "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/http-errors": "*",
+        "@types/node": "*"
+      }
+    },
+    "node_modules/accepts": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
+      "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==",
+      "license": "MIT",
+      "dependencies": {
+        "mime-types": "^3.0.0",
+        "negotiator": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/acorn": {
+      "version": "8.15.0",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
+      "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "acorn": "bin/acorn"
+      },
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/acorn-walk": {
+      "version": "8.3.4",
+      "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz",
+      "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "acorn": "^8.11.0"
+      },
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/adler-32": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz",
+      "integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==",
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/append-field": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
+      "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==",
+      "license": "MIT"
+    },
+    "node_modules/arg": {
+      "version": "4.1.3",
+      "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
+      "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+      "license": "MIT"
+    },
+    "node_modules/axios": {
+      "version": "1.13.5",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz",
+      "integrity": "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==",
+      "license": "MIT",
+      "dependencies": {
+        "follow-redirects": "^1.15.11",
+        "form-data": "^4.0.5",
+        "proxy-from-env": "^1.1.0"
+      }
+    },
+    "node_modules/basic-auth": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
+      "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
+      "license": "MIT",
+      "dependencies": {
+        "safe-buffer": "5.1.2"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/body-parser": {
+      "version": "2.2.2",
+      "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz",
+      "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==",
+      "license": "MIT",
+      "dependencies": {
+        "bytes": "^3.1.2",
+        "content-type": "^1.0.5",
+        "debug": "^4.4.3",
+        "http-errors": "^2.0.0",
+        "iconv-lite": "^0.7.0",
+        "on-finished": "^2.4.1",
+        "qs": "^6.14.1",
+        "raw-body": "^3.0.1",
+        "type-is": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/buffer-from": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+      "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+      "license": "MIT"
+    },
+    "node_modules/busboy": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
+      "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
+      "dependencies": {
+        "streamsearch": "^1.1.0"
+      },
+      "engines": {
+        "node": ">=10.16.0"
+      }
+    },
+    "node_modules/bytes": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+      "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/call-bind-apply-helpers": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+      "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "function-bind": "^1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/call-bound": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+      "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.2",
+        "get-intrinsic": "^1.3.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/cfb": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz",
+      "integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "adler-32": "~1.3.0",
+        "crc-32": "~1.2.0"
+      },
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/codepage": {
+      "version": "1.15.0",
+      "resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz",
+      "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==",
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/combined-stream": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+      "license": "MIT",
+      "dependencies": {
+        "delayed-stream": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/commander": {
+      "version": "14.0.3",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz",
+      "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=20"
+      }
+    },
+    "node_modules/concat-stream": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz",
+      "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==",
+      "engines": [
+        "node >= 6.0"
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "buffer-from": "^1.0.0",
+        "inherits": "^2.0.3",
+        "readable-stream": "^3.0.2",
+        "typedarray": "^0.0.6"
+      }
+    },
+    "node_modules/content-disposition": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz",
+      "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/content-type": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
+      "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/cookie": {
+      "version": "0.7.2",
+      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
+      "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/cookie-signature": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
+      "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.6.0"
+      }
+    },
+    "node_modules/core-js-pure": {
+      "version": "3.48.0",
+      "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.48.0.tgz",
+      "integrity": "sha512-1slJgk89tWC51HQ1AEqG+s2VuwpTRr8ocu4n20QUcH1v9lAN0RXen0Q0AABa/DK1I7RrNWLucplOHMx8hfTGTw==",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/core-js"
+      }
+    },
+    "node_modules/cors": {
+      "version": "2.8.6",
+      "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz",
+      "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==",
+      "license": "MIT",
+      "dependencies": {
+        "object-assign": "^4",
+        "vary": "^1"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/crc-32": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
+      "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
+      "license": "Apache-2.0",
+      "bin": {
+        "crc32": "bin/crc32.njs"
+      },
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/create-require": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
+      "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/crypto-js": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
+      "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
+      "license": "MIT"
+    },
+    "node_modules/debug": {
+      "version": "4.4.3",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+      "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+      "license": "MIT",
+      "dependencies": {
+        "ms": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/delayed-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/depd": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+      "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/diff": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz",
+      "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">=0.3.1"
+      }
+    },
+    "node_modules/dunder-proto": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+      "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.1",
+        "es-errors": "^1.3.0",
+        "gopd": "^1.2.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/ee-first": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+      "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
+      "license": "MIT"
+    },
+    "node_modules/encodeurl": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
+      "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/es-define-property": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+      "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-errors": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+      "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-object-atoms": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+      "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-set-tostringtag": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+      "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.6",
+        "has-tostringtag": "^1.0.2",
+        "hasown": "^2.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/escape-html": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+      "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
+      "license": "MIT"
+    },
+    "node_modules/etag": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+      "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/express": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
+      "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
+      "license": "MIT",
+      "dependencies": {
+        "accepts": "^2.0.0",
+        "body-parser": "^2.2.1",
+        "content-disposition": "^1.0.0",
+        "content-type": "^1.0.5",
+        "cookie": "^0.7.1",
+        "cookie-signature": "^1.2.1",
+        "debug": "^4.4.0",
+        "depd": "^2.0.0",
+        "encodeurl": "^2.0.0",
+        "escape-html": "^1.0.3",
+        "etag": "^1.8.1",
+        "finalhandler": "^2.1.0",
+        "fresh": "^2.0.0",
+        "http-errors": "^2.0.0",
+        "merge-descriptors": "^2.0.0",
+        "mime-types": "^3.0.0",
+        "on-finished": "^2.4.1",
+        "once": "^1.4.0",
+        "parseurl": "^1.3.3",
+        "proxy-addr": "^2.0.7",
+        "qs": "^6.14.0",
+        "range-parser": "^1.2.1",
+        "router": "^2.2.0",
+        "send": "^1.1.0",
+        "serve-static": "^2.2.0",
+        "statuses": "^2.0.1",
+        "type-is": "^2.0.1",
+        "vary": "^1.1.2"
+      },
+      "engines": {
+        "node": ">= 18"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/finalhandler": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz",
+      "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==",
+      "license": "MIT",
+      "dependencies": {
+        "debug": "^4.4.0",
+        "encodeurl": "^2.0.0",
+        "escape-html": "^1.0.3",
+        "on-finished": "^2.4.1",
+        "parseurl": "^1.3.3",
+        "statuses": "^2.0.1"
+      },
+      "engines": {
+        "node": ">= 18.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/follow-redirects": {
+      "version": "1.15.11",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
+      "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/RubenVerborgh"
+        }
+      ],
+      "license": "MIT",
+      "engines": {
+        "node": ">=4.0"
+      },
+      "peerDependenciesMeta": {
+        "debug": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/form-data": {
+      "version": "4.0.5",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
+      "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
+      "license": "MIT",
+      "dependencies": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.8",
+        "es-set-tostringtag": "^2.1.0",
+        "hasown": "^2.0.2",
+        "mime-types": "^2.1.12"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/form-data/node_modules/mime-db": {
+      "version": "1.52.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/form-data/node_modules/mime-types": {
+      "version": "2.1.35",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+      "license": "MIT",
+      "dependencies": {
+        "mime-db": "1.52.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/forwarded": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+      "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/frac": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz",
+      "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==",
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/fresh": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
+      "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/function-bind": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+      "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/get-intrinsic": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+      "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.2",
+        "es-define-property": "^1.0.1",
+        "es-errors": "^1.3.0",
+        "es-object-atoms": "^1.1.1",
+        "function-bind": "^1.1.2",
+        "get-proto": "^1.0.1",
+        "gopd": "^1.2.0",
+        "has-symbols": "^1.1.0",
+        "hasown": "^2.0.2",
+        "math-intrinsics": "^1.1.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/get-proto": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+      "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+      "license": "MIT",
+      "dependencies": {
+        "dunder-proto": "^1.0.1",
+        "es-object-atoms": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/gopd": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+      "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/has-symbols": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+      "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/has-tostringtag": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+      "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+      "license": "MIT",
+      "dependencies": {
+        "has-symbols": "^1.0.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/hasown": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+      "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+      "license": "MIT",
+      "dependencies": {
+        "function-bind": "^1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/http-errors": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
+      "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
+      "license": "MIT",
+      "dependencies": {
+        "depd": "~2.0.0",
+        "inherits": "~2.0.4",
+        "setprototypeof": "~1.2.0",
+        "statuses": "~2.0.2",
+        "toidentifier": "~1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/iconv-lite": {
+      "version": "0.7.2",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz",
+      "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==",
+      "license": "MIT",
+      "dependencies": {
+        "safer-buffer": ">= 2.1.2 < 3.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/idb-keyval": {
+      "version": "6.2.2",
+      "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.2.tgz",
+      "integrity": "sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg==",
+      "license": "Apache-2.0"
+    },
+    "node_modules/inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+      "license": "ISC"
+    },
+    "node_modules/ipaddr.js": {
+      "version": "1.9.1",
+      "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+      "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/is-promise": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz",
+      "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
+      "license": "MIT"
+    },
+    "node_modules/make-error": {
+      "version": "1.3.6",
+      "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
+      "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/math-intrinsics": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+      "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/media-typer": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
+      "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/merge-descriptors": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz",
+      "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/mime-db": {
+      "version": "1.54.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
+      "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mime-types": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz",
+      "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==",
+      "license": "MIT",
+      "dependencies": {
+        "mime-db": "^1.54.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/minimist": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+      "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/mkdirp": {
+      "version": "0.5.6",
+      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+      "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
+      "license": "MIT",
+      "dependencies": {
+        "minimist": "^1.2.6"
+      },
+      "bin": {
+        "mkdirp": "bin/cmd.js"
+      }
+    },
+    "node_modules/morgan": {
+      "version": "1.10.1",
+      "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.1.tgz",
+      "integrity": "sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A==",
+      "license": "MIT",
+      "dependencies": {
+        "basic-auth": "~2.0.1",
+        "debug": "2.6.9",
+        "depd": "~2.0.0",
+        "on-finished": "~2.3.0",
+        "on-headers": "~1.1.0"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/morgan/node_modules/debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "license": "MIT",
+      "dependencies": {
+        "ms": "2.0.0"
+      }
+    },
+    "node_modules/morgan/node_modules/ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+      "license": "MIT"
+    },
+    "node_modules/morgan/node_modules/on-finished": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+      "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==",
+      "license": "MIT",
+      "dependencies": {
+        "ee-first": "1.1.1"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/ms": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+      "license": "MIT"
+    },
+    "node_modules/multer": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.2.tgz",
+      "integrity": "sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw==",
+      "license": "MIT",
+      "dependencies": {
+        "append-field": "^1.0.0",
+        "busboy": "^1.6.0",
+        "concat-stream": "^2.0.0",
+        "mkdirp": "^0.5.6",
+        "object-assign": "^4.1.1",
+        "type-is": "^1.6.18",
+        "xtend": "^4.0.2"
+      },
+      "engines": {
+        "node": ">= 10.16.0"
+      }
+    },
+    "node_modules/multer/node_modules/media-typer": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+      "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/multer/node_modules/mime-db": {
+      "version": "1.52.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/multer/node_modules/mime-types": {
+      "version": "2.1.35",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+      "license": "MIT",
+      "dependencies": {
+        "mime-db": "1.52.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/multer/node_modules/type-is": {
+      "version": "1.6.18",
+      "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+      "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+      "license": "MIT",
+      "dependencies": {
+        "media-typer": "0.3.0",
+        "mime-types": "~2.1.24"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/negotiator": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
+      "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/object-assign": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+      "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/object-inspect": {
+      "version": "1.13.4",
+      "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
+      "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/on-finished": {
+      "version": "2.4.1",
+      "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+      "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+      "license": "MIT",
+      "dependencies": {
+        "ee-first": "1.1.1"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/on-headers": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz",
+      "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+      "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+      "license": "ISC",
+      "dependencies": {
+        "wrappy": "1"
+      }
+    },
+    "node_modules/parse": {
+      "version": "8.1.0",
+      "resolved": "https://registry.npmjs.org/parse/-/parse-8.1.0.tgz",
+      "integrity": "sha512-QJvYHrNXXKfo8F9wUPB5eeT8VOTc9tObWuf07dAKgQi3375gC3bejyxIezOh3c6SOmCVpvBL2Ro0jspQbeEgkA==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@babel/runtime": "7.28.6",
+        "@babel/runtime-corejs3": "7.29.0",
+        "crypto-js": "4.2.0",
+        "idb-keyval": "6.2.2",
+        "react-native-crypto-js": "1.0.0",
+        "ws": "8.19.0"
+      },
+      "engines": {
+        "node": ">=20.19.0 <21 || >=22.12.0 <23 || >=24.1.0 <25"
+      }
+    },
+    "node_modules/parseurl": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+      "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/path-to-regexp": {
+      "version": "8.3.0",
+      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz",
+      "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==",
+      "license": "MIT",
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/proxy-addr": {
+      "version": "2.0.7",
+      "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+      "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+      "license": "MIT",
+      "dependencies": {
+        "forwarded": "0.2.0",
+        "ipaddr.js": "1.9.1"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/proxy-from-env": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+      "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
+      "license": "MIT"
+    },
+    "node_modules/qs": {
+      "version": "6.14.1",
+      "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz",
+      "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==",
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "side-channel": "^1.1.0"
+      },
+      "engines": {
+        "node": ">=0.6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/range-parser": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+      "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/raw-body": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz",
+      "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==",
+      "license": "MIT",
+      "dependencies": {
+        "bytes": "~3.1.2",
+        "http-errors": "~2.0.1",
+        "iconv-lite": "~0.7.0",
+        "unpipe": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/react-native-crypto-js": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/react-native-crypto-js/-/react-native-crypto-js-1.0.0.tgz",
+      "integrity": "sha512-FNbLuG/HAdapQoybeZSoes1PWdOj0w242gb+e1R0hicf3Gyj/Mf8M9NaED2AnXVOX01b2FXomwUiw1xP1K+8sA==",
+      "license": "MIT"
+    },
+    "node_modules/readable-stream": {
+      "version": "3.6.2",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+      "license": "MIT",
+      "dependencies": {
+        "inherits": "^2.0.3",
+        "string_decoder": "^1.1.1",
+        "util-deprecate": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/router": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
+      "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==",
+      "license": "MIT",
+      "dependencies": {
+        "debug": "^4.4.0",
+        "depd": "^2.0.0",
+        "is-promise": "^4.0.0",
+        "parseurl": "^1.3.3",
+        "path-to-regexp": "^8.0.0"
+      },
+      "engines": {
+        "node": ">= 18"
+      }
+    },
+    "node_modules/safe-buffer": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+      "license": "MIT"
+    },
+    "node_modules/safer-buffer": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+      "license": "MIT"
+    },
+    "node_modules/send": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz",
+      "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==",
+      "license": "MIT",
+      "dependencies": {
+        "debug": "^4.4.3",
+        "encodeurl": "^2.0.0",
+        "escape-html": "^1.0.3",
+        "etag": "^1.8.1",
+        "fresh": "^2.0.0",
+        "http-errors": "^2.0.1",
+        "mime-types": "^3.0.2",
+        "ms": "^2.1.3",
+        "on-finished": "^2.4.1",
+        "range-parser": "^1.2.1",
+        "statuses": "^2.0.2"
+      },
+      "engines": {
+        "node": ">= 18"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/serve-static": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz",
+      "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==",
+      "license": "MIT",
+      "dependencies": {
+        "encodeurl": "^2.0.0",
+        "escape-html": "^1.0.3",
+        "parseurl": "^1.3.3",
+        "send": "^1.2.0"
+      },
+      "engines": {
+        "node": ">= 18"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/setprototypeof": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+      "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+      "license": "ISC"
+    },
+    "node_modules/side-channel": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
+      "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "object-inspect": "^1.13.3",
+        "side-channel-list": "^1.0.0",
+        "side-channel-map": "^1.0.1",
+        "side-channel-weakmap": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/side-channel-list": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
+      "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "object-inspect": "^1.13.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/side-channel-map": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
+      "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.2",
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.5",
+        "object-inspect": "^1.13.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/side-channel-weakmap": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+      "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.2",
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.5",
+        "object-inspect": "^1.13.3",
+        "side-channel-map": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/ssf": {
+      "version": "0.11.2",
+      "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz",
+      "integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "frac": "~1.1.2"
+      },
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/statuses": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
+      "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/streamsearch": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
+      "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
+      "engines": {
+        "node": ">=10.0.0"
+      }
+    },
+    "node_modules/string_decoder": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+      "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+      "license": "MIT",
+      "dependencies": {
+        "safe-buffer": "~5.2.0"
+      }
+    },
+    "node_modules/string_decoder/node_modules/safe-buffer": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+      "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/toidentifier": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+      "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.6"
+      }
+    },
+    "node_modules/ts-node": {
+      "version": "10.9.2",
+      "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
+      "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@cspotcode/source-map-support": "^0.8.0",
+        "@tsconfig/node10": "^1.0.7",
+        "@tsconfig/node12": "^1.0.7",
+        "@tsconfig/node14": "^1.0.0",
+        "@tsconfig/node16": "^1.0.2",
+        "acorn": "^8.4.1",
+        "acorn-walk": "^8.1.1",
+        "arg": "^4.1.0",
+        "create-require": "^1.1.0",
+        "diff": "^4.0.1",
+        "make-error": "^1.1.1",
+        "v8-compile-cache-lib": "^3.0.1",
+        "yn": "3.1.1"
+      },
+      "bin": {
+        "ts-node": "dist/bin.js",
+        "ts-node-cwd": "dist/bin-cwd.js",
+        "ts-node-esm": "dist/bin-esm.js",
+        "ts-node-script": "dist/bin-script.js",
+        "ts-node-transpile-only": "dist/bin-transpile.js",
+        "ts-script": "dist/bin-script-deprecated.js"
+      },
+      "peerDependencies": {
+        "@swc/core": ">=1.2.50",
+        "@swc/wasm": ">=1.2.50",
+        "@types/node": "*",
+        "typescript": ">=2.7"
+      },
+      "peerDependenciesMeta": {
+        "@swc/core": {
+          "optional": true
+        },
+        "@swc/wasm": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/type-is": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
+      "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==",
+      "license": "MIT",
+      "dependencies": {
+        "content-type": "^1.0.5",
+        "media-typer": "^1.1.0",
+        "mime-types": "^3.0.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/typedarray": {
+      "version": "0.0.6",
+      "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+      "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==",
+      "license": "MIT"
+    },
+    "node_modules/typescript": {
+      "version": "5.9.3",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+      "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "bin": {
+        "tsc": "bin/tsc",
+        "tsserver": "bin/tsserver"
+      },
+      "engines": {
+        "node": ">=14.17"
+      }
+    },
+    "node_modules/undici-types": {
+      "version": "7.16.0",
+      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
+      "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
+      "license": "MIT"
+    },
+    "node_modules/unpipe": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+      "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/util-deprecate": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+      "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+      "license": "MIT"
+    },
+    "node_modules/v8-compile-cache-lib": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
+      "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/vary": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+      "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/wmf": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz",
+      "integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==",
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/word": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz",
+      "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==",
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/wrappy": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+      "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+      "license": "ISC"
+    },
+    "node_modules/ws": {
+      "version": "8.19.0",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
+      "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10.0.0"
+      },
+      "peerDependencies": {
+        "bufferutil": "^4.0.1",
+        "utf-8-validate": ">=5.0.2"
+      },
+      "peerDependenciesMeta": {
+        "bufferutil": {
+          "optional": true
+        },
+        "utf-8-validate": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/xlsx": {
+      "version": "0.18.5",
+      "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz",
+      "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "adler-32": "~1.3.0",
+        "cfb": "~1.2.1",
+        "codepage": "~1.15.0",
+        "crc-32": "~1.2.1",
+        "ssf": "~0.11.2",
+        "wmf": "~1.0.1",
+        "word": "~0.3.0"
+      },
+      "bin": {
+        "xlsx": "bin/xlsx.njs"
+      },
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/xmlhttprequest": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz",
+      "integrity": "sha512-58Im/U0mlVBLM38NdZjHyhuMtCqa61469k2YP/AaPbvCoV9aQGUpbJBj1QRm2ytRiVQBD/fsw7L2bJGDVQswBA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/xtend": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+      "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.4"
+      }
+    },
+    "node_modules/yn": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
+      "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    }
+  }
+}

+ 1 - 0
package.json

@@ -16,6 +16,7 @@
     "@types/express": "^5.0.6",
     "@types/morgan": "^1.9.10",
     "@types/multer": "^2.0.0",
+    "axios": "^1.13.5",
     "cors": "^2.8.6",
     "express": "^5.2.1",
     "morgan": "^1.10.1",

+ 11 - 4
server.ts

@@ -1,6 +1,7 @@
 
 import express from 'express';
 import { readFileSync } from 'node:fs';
+import { createTikHubRouter } from './modules/fmode-tikhub-server/src/mod';
 
 const Parse = require('parse');
 
@@ -26,7 +27,7 @@ function getConfig() {
     console.warn('Config file not found, using defaults:', errorMessage);
     return {
       parse: {
-        port: 10003,
+        port: 3000,
         appId: 'hb-voc',
         serverURL: 'http://hb-server.fmode.cn/parse'
       }
@@ -61,10 +62,13 @@ app.get('/', (req: any, res: any) => {
     message: 'Fmode Server - Gongzuo API',
     version: '1.0.0',
     timestamp: new Date().toISOString(),
-    endpoints: ['/api', '/health']
+    endpoints: ['/api', '/health', '/api/tikhub']
   });
 });
 
+// 挂载 TikHub 模块路由
+app.use('/api/tikhub', createTikHubRouter());
+
 // 404 处理
 app.use((req: any, res: any) => {
   res.status(404).json({
@@ -89,7 +93,7 @@ async function startServer() {
   const PORT = process.env.PORT || config.parse?.port || 3000;
   app.listen(PORT, () => {
     console.log('\n' + '='.repeat(60));
-    console.log('🚀 Fmode Server - MSQ Started');
+    console.log('🚀 Fmode Server - HB Started');
     console.log('='.repeat(60));
     console.log(`Timestamp: ${new Date().toISOString()}`);
     console.log('='.repeat(60) + '\n');
@@ -108,4 +112,7 @@ process.on('SIGINT', () => {
 });
 
 // 启动服务器
-await startServer();
+startServer().catch(err => {
+  console.error('Failed to start server:', err);
+  process.exit(1);
+});

Vissa filer visades inte eftersom för många filer har ändrats