Spaces:
Sleeping
Sleeping
Upload 15 files
Browse files- .env +22 -0
- Dockerfile +22 -31
- package-lock.json +80 -1
- package.json +34 -33
- proxy_server.log +30 -0
- src/lightweight-client-express.js +2 -6
- src/lightweight-client.js +119 -235
.env
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Notion的cookie,请把浏览器Cookie部分完整复制下来,多个cookie用|分割
|
2 |
+
NOTION_COOKIE="notion_browser_id=fdf38010-21d4-433e-a2fd-65cab87f49f6; device_id=202d872b-594c-81d6-9a95-003bb32158fc; NEXT_LOCALE=zh-CN; _gcl_au=1.1.132916819.1748542408; _ga=GA1.1.1824875636.1748542409; _fbp=fb.1.1748542411909.266391455622580792; notion_user_id=200d872b-594c-81d3-bb1b-00024f30311d; p_sync_session=%7B%22tokenId%22%3A%22v02%3Async_session%3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlax4r-9CwW1tAnM4hm7SsLu9h-MytbYGKknbSPCj2F07ufgzyShCKc0MIvr1LYGI2ihhhjiqn8aHlsgy2otKQnuChsnq0LwuaQVt%22%2C%22userIds%22%3A%5B%22200d872b-594c-81d3-bb1b-00024f30311d%22%5D%7D; _cioid=200d872b594c81d3bb1b00024f30311d; notion_cookie_sync_completed=%7B%22completed%22%3Atrue%2C%22version%22%3A4%2C%22attempts%22%3A1%7D; notion_users=[%22200d872b-594c-81d3-bb1b-00024f30311d%22]; _hjSessionUser_3664679=eyJpZCI6IjgwYjAzZGViLWUxZmMtNTA3MC1iNDA2LWMyNzhlODdiZDdlNyIsImNyZWF0ZWQiOjE3NDg1NDI0MTE4ODEsImV4aXN0aW5nIjp0cnVlfQ==; notion_check_cookie_consent=false; _cfuvid=Pd_QxMVa8SMbHI8r0zNaAYlgczHrP_plvfapRgoHamE-1749308468696-0.0.1.1-604800000; _rdt_uuid=1748542408747.963fa76b-30e7-4e18-ab01-aba3f7a1457e; _hjSession_3664679=eyJpZCI6IjJmOTk2OTQzLTNjMzktNDE3ZS05YmFjLWVkZGJiNDlmNzBlNSIsImMiOjE3NDkzMDg0Nzc5MzcsInMiOjAsInIiOjAsInNiIjowLCJzciI6MCwic2UiOjAsImZzIjowLCJzcCI6MX0=; __cf_bm=9qEQ2V9hIxBqYj.ajFLUvkgaSFQRRvQbSweUzjulXLQ-1749308501-1.0.1.1-LsQtYoKBUqSSz23TcFlKQ_HKBokINZNca_y_5K4DflDfjU8LrpAylSqnX4tAn97QJ51zBJ9uMZneTnU_tPoBCokRsFffnV2Kt69cp_U5KcvakaUpeNtVModlsviSJKmQ; notion_locale=zh-CN/autodetect; token_v2=v03%3AeyJhbGciOiJkaXIiLCJraWQiOiJwcm9kdWN0aW9uOnRva2VuLXYzOjIwMjQtMTEtMDciLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIn0..udCqaHPmeajJluGEoRCwMQ.KwYcnDQeUSch7kuEiW2rD7T7Pwwg6CaU9UM2b-xfydsnnFdHNnxLFSGBeyVNbFaMfy6sWw0P9rSujhF9iov_tAvnhZ2IFEG4ND9Jwu8VNQwsOLQUAj3WfHGaHnTAGEbgNN2vRXtsb_CHYi4PJtd5bweDEEBtMhZIDBcwUTCIJpXIk9pS8aPfY5N6_weYJSJ6a959TerGpfRMZDasH_C0MDxLgiJZft6qGmarx0XOhU_QkUONs6pFfe1no029nLwy1clKJLAshicHTKx7mqmXIjfMWLaHXbKKTC08Pekxhxwx35kwadnS1JgYI85-I0SQZ9VanCs82_CAfDUBIbw5VWn6xQEmCOTpo1mf68vSX_k.lCEqDypSmYd-LZkB3MvzOpu8YXw6E2Slhy0F1Kh9xPk; _ga_9ZJ8CB186L=GS2.1.s1749308471$o3$g1$t1749308568$j60$l0$h0|notion_browser_id=a8182934-adff-461b-8c79-1506999adea4; device_id=200d872b-594c-812e-b369-003bcda8cdcd; _gcl_au=1.1.1170453355.1748542388; _ga=GA1.1.701605925.1748542389; _hjSessionUser_3664679=eyJpZCI6ImVlMmY4ZDIxLTMxY2QtNWMzNS1hZmQ1LThmYThjMjBiMTdhMCIsImNyZWF0ZWQiOjE3NDg1NDIzOTExMzcsImV4aXN0aW5nIjp0cnVlfQ==; notion_check_cookie_consent=false; _fbp=fb.1.1749309115969.575678538958199822; _hjSession_3664679=eyJpZCI6ImYyMGQ5MThkLTE5MWMtNDJlZi04ZTJlLTQwZTk3ZmQ4MzM2YiIsImMiOjE3NDkzMDkxMTcxODYsInMiOjAsInIiOjAsInNiIjowLCJzciI6MCwic2UiOjAsImZzIjowLCJzcCI6MH0=; NEXT_LOCALE=en-US; _cfuvid=AxS6qsfXNaj6l97fAzOZU19l.Tq0B7u1SDbVVm._.J4-1749309401915-0.0.1.1-604800000; notion_locale=en-US/user_choice; _rdt_uuid=1748542388717.64a3c6ac-6961-4401-9aec-9dfbb27dd932; csrf=3c4feb4b-6326-40c0-9475-4e1884f6cbbd; notion_user_id=20bd872b-594c-81a1-b249-0002d1feacbe; notion_users=%5B%2220bd872b-594c-81a1-b249-0002d1feacbe%22%5D; p_sync_session=%7B%22tokenId%22%3A%22v02%3Async_session%3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGn5K6DxWWr51s8yjzd8Tv7u9h-MytbYGKknbSPCj2F07ufgzySUZeMoMevz1LdHU0yNhhj-uycaHls1l2thMEH6B0pW81u17PwVt%22%2C%22userIds%22%3A%5B%2220bd872b-594c-81a1-b249-0002d1feacbe%22%5D%7D; _cioid=20bd872b594c81a1b2490002d1feacbe; token_v2=v03%3AeyJhbGciOiJkaXIiLCJraWQiOiJwcm9kdWN0aW9uOnRva2VuLXYzOjIwMjQtMTEtMDciLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIn0..g6nQTmUif39kpQiFt5x-1g.lwqScXxX6fzfW4oAw6_bJh1CrPh5N0vjBFNyo0_g3uv424aZFa_1a3pgTfDbJzydbbLnYDaNnLBZcYjfeZkcgz8dMpuZ2WtlHscPQoEPaXVjzD3PidXmUPmRyA2sNVMZJtaLd39_zJra8j8pKLYsCVEdfrgj_QYdedTAvAVjbA1twjdEv0Bl3ur8o63kQUOkzIWro5hDgqAr4AMkkCzmNR2X2AtjvOZFdt9YlnYtLn-tdCYEtOZGV-zhNL34QjXZVWmdnnaQg8p0lBm9ZP-y3hztbjwLTVYPNBDKnhZeu-Qra96KZZitMZDsZt944GbKUTOPL0PF8vULVBrYyoHPQw.y4DOnuNzKTwrPk0WVKUv1RERNMqSt1MuBM9MGuM5uMM; _ga_9ZJ8CB186L=GS2.1.s1749309117$o3$g1$t1749311301$j60$l0$h0; __cf_bm=a09Uutq_rs.5z8Bf6fJvEply_vQblokHM1U6Yd9y4Yo-1749311305-1.0.1.1-H3RXZorJC7x1BR8jgC2fSwewxFUMy2P06KP2_FHT99tHLoZ5EKDfj_Olhohp2qTDbAJXDEM_ofP2_Qywfs4bA7jJgSCAGX_PKpHCoBTOQ_KqFb8DZ40X9cA7KDo3CSSW"
|
3 |
+
# 反代密码,没有的话默认密码是 default_token
|
4 |
+
PROXY_AUTH_TOKEN="sk-123456"
|
5 |
+
# (可选)代理地址,如果填了所有请求将经过代理,针对notion限制ip对策,可使用http或socks5代理(notion支持ipv6哦,有ipv6池子的可以拿出来了)
|
6 |
+
PROXY_URL=""
|
7 |
+
# 新版本正常用户不需要,如果不是有特殊需求请一定留空(只有使用notion teams白嫖别人space的用户需填写)
|
8 |
+
NOTION_SPACE_ID=""
|
9 |
+
# 新版本正常用户不需要,如果不是有特殊需求请一定留空(只有使用notion teams白嫖别人space的用户需填写)
|
10 |
+
NOTION_ACTIVE_USER_HEADER=""
|
11 |
+
# 是否使用原生代理池,如果为true,则使用原生代理池,如果为false,则使用代理池
|
12 |
+
USE_NATIVE_PROXY_POOL=true
|
13 |
+
|
14 |
+
# 代理服务器配置
|
15 |
+
# 平台选择:auto(自动检测), windows, linux, android
|
16 |
+
PROXY_SERVER_PLATFORM="auto"
|
17 |
+
# ���理服务器端口
|
18 |
+
PROXY_SERVER_PORT=10655
|
19 |
+
# 代理服务器日志文件路径
|
20 |
+
PROXY_SERVER_LOG_PATH="./proxy_server.log"
|
21 |
+
# 是否启用代理服务器
|
22 |
+
ENABLE_PROXY_SERVER=true
|
Dockerfile
CHANGED
@@ -1,32 +1,23 @@
|
|
1 |
-
# Use an official Node.js runtime as a parent image
|
2 |
-
FROM node:18-slim
|
3 |
-
|
4 |
-
# Set the working directory in the container
|
5 |
-
WORKDIR /app
|
6 |
-
|
7 |
-
# Copy package.json and package-lock.json to the working directory
|
8 |
-
COPY package*.json ./
|
9 |
-
|
10 |
-
# Install app dependencies
|
11 |
-
# Using --production flag to install only production dependencies
|
12 |
-
# Playwright needs special handling to download browsers
|
13 |
-
# We need to install dependencies first to make sure Playwright downloads its browsers.
|
14 |
-
RUN npm install --production
|
15 |
-
|
16 |
-
# Copy the rest of the application source code to the working directory
|
17 |
-
COPY . .
|
18 |
-
|
19 |
-
#
|
20 |
-
|
21 |
-
|
22 |
-
#
|
23 |
-
RUN chown -R node:node /app
|
24 |
-
|
25 |
-
# Switch to a non-root user
|
26 |
-
USER node
|
27 |
-
|
28 |
-
# Make port 7860 available to the world outside this container
|
29 |
-
EXPOSE 7860
|
30 |
-
|
31 |
-
# Define the command to run the app
|
32 |
CMD ["npm", "start"]
|
|
|
1 |
+
# Use an official Node.js runtime as a parent image
|
2 |
+
FROM node:18-slim
|
3 |
+
|
4 |
+
# Set the working directory in the container
|
5 |
+
WORKDIR /app
|
6 |
+
|
7 |
+
# Copy package.json and package-lock.json to the working directory
|
8 |
+
COPY package*.json ./
|
9 |
+
|
10 |
+
# Install app dependencies
|
11 |
+
# Using --production flag to install only production dependencies
|
12 |
+
# Playwright needs special handling to download browsers
|
13 |
+
# We need to install dependencies first to make sure Playwright downloads its browsers.
|
14 |
+
RUN npm install --production
|
15 |
+
|
16 |
+
# Copy the rest of the application source code to the working directory
|
17 |
+
COPY . .
|
18 |
+
|
19 |
+
# Make port 7860 available to the world outside this container
|
20 |
+
EXPOSE 7860
|
21 |
+
|
22 |
+
# Define the command to run the app
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
23 |
CMD ["npm", "start"]
|
package-lock.json
CHANGED
@@ -16,7 +16,8 @@
|
|
16 |
"https-proxy-agent": "^7.0.2",
|
17 |
"jsdom": "^22.1.0",
|
18 |
"node-fetch": "^3.3.2",
|
19 |
-
"playwright": "^1.40.1"
|
|
|
20 |
},
|
21 |
"devDependencies": {
|
22 |
"nodemon": "^3.0.2"
|
@@ -995,6 +996,18 @@
|
|
995 |
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
996 |
"license": "ISC"
|
997 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
998 |
"node_modules/ipaddr.js": {
|
999 |
"version": "1.9.1",
|
1000 |
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
@@ -1056,6 +1069,11 @@
|
|
1056 |
"integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==",
|
1057 |
"license": "MIT"
|
1058 |
},
|
|
|
|
|
|
|
|
|
|
|
1059 |
"node_modules/jsdom": {
|
1060 |
"version": "22.1.0",
|
1061 |
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-22.1.0.tgz",
|
@@ -1772,6 +1790,67 @@
|
|
1772 |
"node": ">=10"
|
1773 |
}
|
1774 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1775 |
"node_modules/statuses": {
|
1776 |
"version": "2.0.1",
|
1777 |
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
|
|
16 |
"https-proxy-agent": "^7.0.2",
|
17 |
"jsdom": "^22.1.0",
|
18 |
"node-fetch": "^3.3.2",
|
19 |
+
"playwright": "^1.40.1",
|
20 |
+
"socks-proxy-agent": "^8.0.5"
|
21 |
},
|
22 |
"devDependencies": {
|
23 |
"nodemon": "^3.0.2"
|
|
|
996 |
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
997 |
"license": "ISC"
|
998 |
},
|
999 |
+
"node_modules/ip-address": {
|
1000 |
+
"version": "9.0.5",
|
1001 |
+
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz",
|
1002 |
+
"integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==",
|
1003 |
+
"dependencies": {
|
1004 |
+
"jsbn": "1.1.0",
|
1005 |
+
"sprintf-js": "^1.1.3"
|
1006 |
+
},
|
1007 |
+
"engines": {
|
1008 |
+
"node": ">= 12"
|
1009 |
+
}
|
1010 |
+
},
|
1011 |
"node_modules/ipaddr.js": {
|
1012 |
"version": "1.9.1",
|
1013 |
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
|
|
1069 |
"integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==",
|
1070 |
"license": "MIT"
|
1071 |
},
|
1072 |
+
"node_modules/jsbn": {
|
1073 |
+
"version": "1.1.0",
|
1074 |
+
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz",
|
1075 |
+
"integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A=="
|
1076 |
+
},
|
1077 |
"node_modules/jsdom": {
|
1078 |
"version": "22.1.0",
|
1079 |
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-22.1.0.tgz",
|
|
|
1790 |
"node": ">=10"
|
1791 |
}
|
1792 |
},
|
1793 |
+
"node_modules/smart-buffer": {
|
1794 |
+
"version": "4.2.0",
|
1795 |
+
"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
|
1796 |
+
"integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
|
1797 |
+
"engines": {
|
1798 |
+
"node": ">= 6.0.0",
|
1799 |
+
"npm": ">= 3.0.0"
|
1800 |
+
}
|
1801 |
+
},
|
1802 |
+
"node_modules/socks": {
|
1803 |
+
"version": "2.8.4",
|
1804 |
+
"resolved": "https://registry.npmjs.org/socks/-/socks-2.8.4.tgz",
|
1805 |
+
"integrity": "sha512-D3YaD0aRxR3mEcqnidIs7ReYJFVzWdd6fXJYUM8ixcQcJRGTka/b3saV0KflYhyVJXKhb947GndU35SxYNResQ==",
|
1806 |
+
"dependencies": {
|
1807 |
+
"ip-address": "^9.0.5",
|
1808 |
+
"smart-buffer": "^4.2.0"
|
1809 |
+
},
|
1810 |
+
"engines": {
|
1811 |
+
"node": ">= 10.0.0",
|
1812 |
+
"npm": ">= 3.0.0"
|
1813 |
+
}
|
1814 |
+
},
|
1815 |
+
"node_modules/socks-proxy-agent": {
|
1816 |
+
"version": "8.0.5",
|
1817 |
+
"resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz",
|
1818 |
+
"integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==",
|
1819 |
+
"dependencies": {
|
1820 |
+
"agent-base": "^7.1.2",
|
1821 |
+
"debug": "^4.3.4",
|
1822 |
+
"socks": "^2.8.3"
|
1823 |
+
},
|
1824 |
+
"engines": {
|
1825 |
+
"node": ">= 14"
|
1826 |
+
}
|
1827 |
+
},
|
1828 |
+
"node_modules/socks-proxy-agent/node_modules/debug": {
|
1829 |
+
"version": "4.4.1",
|
1830 |
+
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
|
1831 |
+
"integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
|
1832 |
+
"dependencies": {
|
1833 |
+
"ms": "^2.1.3"
|
1834 |
+
},
|
1835 |
+
"engines": {
|
1836 |
+
"node": ">=6.0"
|
1837 |
+
},
|
1838 |
+
"peerDependenciesMeta": {
|
1839 |
+
"supports-color": {
|
1840 |
+
"optional": true
|
1841 |
+
}
|
1842 |
+
}
|
1843 |
+
},
|
1844 |
+
"node_modules/socks-proxy-agent/node_modules/ms": {
|
1845 |
+
"version": "2.1.3",
|
1846 |
+
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
1847 |
+
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
|
1848 |
+
},
|
1849 |
+
"node_modules/sprintf-js": {
|
1850 |
+
"version": "1.1.3",
|
1851 |
+
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
|
1852 |
+
"integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA=="
|
1853 |
+
},
|
1854 |
"node_modules/statuses": {
|
1855 |
"version": "2.0.1",
|
1856 |
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
package.json
CHANGED
@@ -1,33 +1,34 @@
|
|
1 |
-
{
|
2 |
-
"name": "notion2api-nodejs",
|
3 |
-
"version": "1.0.0",
|
4 |
-
"description": "Notion API client with lightweight browser-free option",
|
5 |
-
"main": "src/lightweight-client-express.js",
|
6 |
-
"type": "module",
|
7 |
-
"scripts": {
|
8 |
-
"start": "node src/lightweight-client-express.js",
|
9 |
-
"dev": "nodemon src/lightweight-client-express.js",
|
10 |
-
"original": "node src/index.js"
|
11 |
-
},
|
12 |
-
"keywords": [
|
13 |
-
"notion",
|
14 |
-
"openai",
|
15 |
-
"api",
|
16 |
-
"bridge"
|
17 |
-
],
|
18 |
-
"author": "",
|
19 |
-
"license": "MIT",
|
20 |
-
"dependencies": {
|
21 |
-
"axios": "^1.9.0",
|
22 |
-
"chalk": "^4.1.2",
|
23 |
-
"dotenv": "^16.3.1",
|
24 |
-
"express": "^4.18.2",
|
25 |
-
"https-proxy-agent": "^7.0.2",
|
26 |
-
"jsdom": "^22.1.0",
|
27 |
-
"node-fetch": "^3.3.2",
|
28 |
-
"playwright": "^1.40.1"
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
}
|
|
|
|
1 |
+
{
|
2 |
+
"name": "notion2api-nodejs",
|
3 |
+
"version": "1.0.0",
|
4 |
+
"description": "Notion API client with lightweight browser-free option",
|
5 |
+
"main": "src/lightweight-client-express.js",
|
6 |
+
"type": "module",
|
7 |
+
"scripts": {
|
8 |
+
"start": "node src/lightweight-client-express.js",
|
9 |
+
"dev": "nodemon src/lightweight-client-express.js",
|
10 |
+
"original": "node src/index.js"
|
11 |
+
},
|
12 |
+
"keywords": [
|
13 |
+
"notion",
|
14 |
+
"openai",
|
15 |
+
"api",
|
16 |
+
"bridge"
|
17 |
+
],
|
18 |
+
"author": "",
|
19 |
+
"license": "MIT",
|
20 |
+
"dependencies": {
|
21 |
+
"axios": "^1.9.0",
|
22 |
+
"chalk": "^4.1.2",
|
23 |
+
"dotenv": "^16.3.1",
|
24 |
+
"express": "^4.18.2",
|
25 |
+
"https-proxy-agent": "^7.0.2",
|
26 |
+
"jsdom": "^22.1.0",
|
27 |
+
"node-fetch": "^3.3.2",
|
28 |
+
"playwright": "^1.40.1",
|
29 |
+
"socks-proxy-agent": "^8.0.5"
|
30 |
+
},
|
31 |
+
"devDependencies": {
|
32 |
+
"nodemon": "^3.0.2"
|
33 |
+
}
|
34 |
+
}
|
proxy_server.log
CHANGED
@@ -102,3 +102,33 @@
|
|
102 |
2025/06/07 23:50:04.640668 chrome_tls_proxy.go:710: 已禁用并发限制:允许同一IP发起多个并发请求
|
103 |
2025/06/07 23:50:04.640668 chrome_tls_proxy.go:711: 已强制使用IPv4连接,并使用8.8.8.8作为DNS服务器
|
104 |
2025/06/07 23:50:04.640668 chrome_tls_proxy.go:712: 使用Chrome浏览器的TLS指纹进行连接
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
102 |
2025/06/07 23:50:04.640668 chrome_tls_proxy.go:710: 已禁用并发限制:允许同一IP发起多个并发请求
|
103 |
2025/06/07 23:50:04.640668 chrome_tls_proxy.go:711: 已强制使用IPv4连接,并使用8.8.8.8作为DNS服务器
|
104 |
2025/06/07 23:50:04.640668 chrome_tls_proxy.go:712: 使用Chrome浏览器的TLS指纹进行连接
|
105 |
+
2025/06/08 00:44:51.400537 chrome_tls_proxy.go:706: Chrome TLS指纹代理服务器运行在 http://localhost:10655/proxy
|
106 |
+
2025/06/08 00:44:51.408056 chrome_tls_proxy.go:707: 使用方法:向/proxy发送POST请求,请求体包含目标URL、方法、请求头和请求体
|
107 |
+
2025/06/08 00:44:51.408056 chrome_tls_proxy.go:708: 支持流式响应,需要在请求体中添加 'stream': true
|
108 |
+
2025/06/08 00:44:51.408056 chrome_tls_proxy.go:709: 已禁用速率限制:允许无限制请求
|
109 |
+
2025/06/08 00:44:51.408056 chrome_tls_proxy.go:710: 已禁用并发限制:允许同一IP发起多个并发请求
|
110 |
+
2025/06/08 00:44:51.408056 chrome_tls_proxy.go:711: 已强制使用IPv4连接,并使用8.8.8.8作为DNS服务器
|
111 |
+
2025/06/08 00:44:51.408056 chrome_tls_proxy.go:712: 使用Chrome浏览器的TLS指纹进行连接
|
112 |
+
2025/06/08 00:45:24.831086 chrome_tls_proxy.go:706: Chrome TLS指纹代理服务器运行在 http://localhost:10655/proxy
|
113 |
+
2025/06/08 00:45:24.838018 chrome_tls_proxy.go:707: 使用方法:向/proxy发送POST请求,请求体包含目标URL、方法、请求头和请求体
|
114 |
+
2025/06/08 00:45:24.838018 chrome_tls_proxy.go:708: 支持流式响应,需要在请求体中添加 'stream': true
|
115 |
+
2025/06/08 00:45:24.838018 chrome_tls_proxy.go:709: 已禁用速率限制:允许无限制请求
|
116 |
+
2025/06/08 00:45:24.838018 chrome_tls_proxy.go:710: 已禁用并发限制:允许同一IP发起多个并发请求
|
117 |
+
2025/06/08 00:45:24.838018 chrome_tls_proxy.go:711: 已强制使用IPv4连接,并使用8.8.8.8作为DNS服务器
|
118 |
+
2025/06/08 00:45:24.838018 chrome_tls_proxy.go:712: 使用Chrome浏览器的TLS指纹进行连接
|
119 |
+
2025/06/08 00:47:10.787309 chrome_tls_proxy.go:706: Chrome TLS指纹代理服务器运行在 http://localhost:10655/proxy
|
120 |
+
2025/06/08 00:47:10.791530 chrome_tls_proxy.go:707: 使用方法:向/proxy发送POST请求,请求体包含目标URL、方法、请求头和请求体
|
121 |
+
2025/06/08 00:47:10.791530 chrome_tls_proxy.go:708: 支持流式响应,需要在请求体中添加 'stream': true
|
122 |
+
2025/06/08 00:47:10.791530 chrome_tls_proxy.go:709: 已禁用速率限制:允许无限制请求
|
123 |
+
2025/06/08 00:47:10.791530 chrome_tls_proxy.go:710: 已禁用并发限制:允许同一IP发起多个并发请求
|
124 |
+
2025/06/08 00:47:10.791530 chrome_tls_proxy.go:711: 已强制使用IPv4连接,并使用8.8.8.8作为DNS服务器
|
125 |
+
2025/06/08 00:47:10.791530 chrome_tls_proxy.go:712: 使用Chrome浏览器的TLS指纹进行连接
|
126 |
+
2025/06/08 00:47:10.792061 chrome_tls_proxy.go:716: 服务器启动失败: listen tcp :10655: bind: Only one usage of each socket address (protocol/network address/port) is normally permitted.
|
127 |
+
2025/06/08 00:54:49.318617 chrome_tls_proxy.go:706: Chrome TLS指纹代理服务器运行在 http://localhost:10655/proxy
|
128 |
+
2025/06/08 00:54:49.325299 chrome_tls_proxy.go:707: 使用方法:向/proxy发送POST请求,请求体包含目标URL、方法、请求头和请求体
|
129 |
+
2025/06/08 00:54:49.325299 chrome_tls_proxy.go:708: 支持流式响应,需要在请求体中添加 'stream': true
|
130 |
+
2025/06/08 00:54:49.325299 chrome_tls_proxy.go:709: 已禁用速率限制:允许无限制请求
|
131 |
+
2025/06/08 00:54:49.325299 chrome_tls_proxy.go:710: 已禁用并发限制:允许同一IP发起多个并发请求
|
132 |
+
2025/06/08 00:54:49.325299 chrome_tls_proxy.go:711: 已强制使用IPv4连接,并使用8.8.8.8作为DNS服务器
|
133 |
+
2025/06/08 00:54:49.325299 chrome_tls_proxy.go:712: 使用Chrome浏览器的TLS指纹进行连接
|
134 |
+
2025/06/08 00:54:49.325813 chrome_tls_proxy.go:716: 服务器启动失败: listen tcp :10655: bind: Only one usage of each socket address (protocol/network address/port) is normally permitted.
|
src/lightweight-client-express.js
CHANGED
@@ -10,7 +10,6 @@ import {
|
|
10 |
import {
|
11 |
initialize,
|
12 |
streamNotionResponse,
|
13 |
-
buildNotionRequest,
|
14 |
FETCHED_IDS_SUCCESSFULLY
|
15 |
} from './lightweight-client.js';
|
16 |
import { proxyPool } from './ProxyPool.js';
|
@@ -131,9 +130,6 @@ app.post('/v1/chat/completions', authenticate, async (req, res) => {
|
|
131 |
});
|
132 |
}
|
133 |
|
134 |
-
// 构建Notion请求
|
135 |
-
const notionRequestBody = buildNotionRequest(requestData);
|
136 |
-
|
137 |
// 处理流式响应
|
138 |
if (requestData.stream) {
|
139 |
res.setHeader('Content-Type', 'text/event-stream');
|
@@ -141,7 +137,7 @@ app.post('/v1/chat/completions', authenticate, async (req, res) => {
|
|
141 |
res.setHeader('Connection', 'keep-alive');
|
142 |
|
143 |
logger.info(`开始流式响应`);
|
144 |
-
const stream = await streamNotionResponse(
|
145 |
stream.pipe(res);
|
146 |
|
147 |
// 处理客户端断开连接
|
@@ -153,7 +149,7 @@ app.post('/v1/chat/completions', authenticate, async (req, res) => {
|
|
153 |
// 创建一个内部流来收集完整响应
|
154 |
logger.info(`开始非流式响应`);
|
155 |
const chunks = [];
|
156 |
-
const stream = await streamNotionResponse(
|
157 |
|
158 |
return new Promise((resolve, reject) => {
|
159 |
stream.on('data', (chunk) => {
|
|
|
10 |
import {
|
11 |
initialize,
|
12 |
streamNotionResponse,
|
|
|
13 |
FETCHED_IDS_SUCCESSFULLY
|
14 |
} from './lightweight-client.js';
|
15 |
import { proxyPool } from './ProxyPool.js';
|
|
|
130 |
});
|
131 |
}
|
132 |
|
|
|
|
|
|
|
133 |
// 处理流式响应
|
134 |
if (requestData.stream) {
|
135 |
res.setHeader('Content-Type', 'text/event-stream');
|
|
|
137 |
res.setHeader('Connection', 'keep-alive');
|
138 |
|
139 |
logger.info(`开始流式响应`);
|
140 |
+
const stream = await streamNotionResponse(requestData);
|
141 |
stream.pipe(res);
|
142 |
|
143 |
// 处理客户端断开连接
|
|
|
149 |
// 创建一个内部流来收集完整响应
|
150 |
logger.info(`开始非流式响应`);
|
151 |
const chunks = [];
|
152 |
+
const stream = await streamNotionResponse(requestData);
|
153 |
|
154 |
return new Promise((resolve, reject) => {
|
155 |
stream.on('data', (chunk) => {
|
src/lightweight-client.js
CHANGED
@@ -18,7 +18,7 @@ const __filename = fileURLToPath(import.meta.url);
|
|
18 |
const __dirname = dirname(__filename);
|
19 |
|
20 |
// 加载环境变量
|
21 |
-
dotenv.config({ path: join(dirname(
|
22 |
|
23 |
// 日志配置
|
24 |
const logger = {
|
@@ -30,15 +30,10 @@ const logger = {
|
|
30 |
|
31 |
// 配置
|
32 |
const NOTION_API_URL = "https://www.notion.so/api/v3/runInferenceTranscript";
|
33 |
-
let
|
34 |
-
let NOTION_SPACE_ID = process.env.NOTION_SPACE_ID;
|
35 |
-
let FETCHED_NOTION_USER_ID = process.env.NOTION_ACTIVE_USER_HEADER;
|
36 |
const USE_NATIVE_PROXY_POOL = process.env.USE_NATIVE_PROXY_POOL === 'true';
|
37 |
const ENABLE_PROXY_SERVER = process.env.ENABLE_PROXY_SERVER === 'true';
|
38 |
-
let
|
39 |
-
let isMoreThanOne = false;
|
40 |
-
let index = 0;
|
41 |
-
|
42 |
|
43 |
// 代理配置
|
44 |
const PROXY_URL = process.env.PROXY_URL || "";
|
@@ -72,21 +67,14 @@ process.on('exit', () => {
|
|
72 |
});
|
73 |
});
|
74 |
|
75 |
-
function
|
76 |
-
|
77 |
-
|
78 |
-
let cookiePairs = cookies.split('|');
|
79 |
-
if (cookiePairs.length > 1){
|
80 |
-
isMoreThanOne=true;
|
81 |
-
}
|
82 |
-
let currentIndex = index;
|
83 |
-
index++;
|
84 |
-
if (index >= cookiePairs.length) {
|
85 |
-
index = 0;
|
86 |
-
}
|
87 |
-
return cookiePairs[currentIndex];
|
88 |
}
|
89 |
-
|
|
|
|
|
|
|
90 |
}
|
91 |
|
92 |
// 添加一个通用的cookie解析函数
|
@@ -136,184 +124,78 @@ function parseCookiesFromString(cookieString, logPrefix = "") {
|
|
136 |
}
|
137 |
|
138 |
// 获取Notion空间ID和用户ID
|
139 |
-
async function
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
return;
|
144 |
-
}
|
145 |
-
|
146 |
try {
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
referrer: "https://www.notion.so/",
|
153 |
-
contentType: "text/html",
|
154 |
-
includeNodeLocations: true,
|
155 |
-
storageQuota: 10000000,
|
156 |
-
pretendToBeVisual: true,
|
157 |
-
userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36"
|
158 |
-
});
|
159 |
-
|
160 |
-
// 设置全局对象
|
161 |
-
const { window } = dom;
|
162 |
-
|
163 |
-
// 使用更安全的方式设置全局对象
|
164 |
-
try {
|
165 |
-
if (!global.window) {
|
166 |
-
global.window = window;
|
167 |
-
}
|
168 |
-
|
169 |
-
if (!global.document) {
|
170 |
-
global.document = window.document;
|
171 |
-
}
|
172 |
-
|
173 |
-
// 安全地设置navigator
|
174 |
-
if (!global.navigator) {
|
175 |
-
try {
|
176 |
-
Object.defineProperty(global, 'navigator', {
|
177 |
-
value: window.navigator,
|
178 |
-
writable: true,
|
179 |
-
configurable: true
|
180 |
-
});
|
181 |
-
} catch (navError) {
|
182 |
-
logger.warning(`无法设置navigator: ${navError.message},继续执行`);
|
183 |
-
// 继续执行,不会中断流程
|
184 |
-
}
|
185 |
-
}
|
186 |
-
} catch (globalError) {
|
187 |
-
logger.warning(`设置全局对象时出错: ${globalError.message}`);
|
188 |
-
}
|
189 |
-
|
190 |
-
// 设置cookie
|
191 |
-
document.cookie = NOTION_COOKIE;
|
192 |
|
193 |
-
|
194 |
-
const fetchOptions = {
|
195 |
method: 'POST',
|
196 |
headers: {
|
197 |
'Content-Type': 'application/json',
|
198 |
'accept': '*/*',
|
199 |
-
'
|
200 |
-
'
|
201 |
-
'notion-client-version': '23.13.0.3686',
|
202 |
-
'origin': 'https://www.notion.so',
|
203 |
-
'referer': 'https://www.notion.so/',
|
204 |
-
'user-agent': window.navigator.userAgent,
|
205 |
-
'Cookie': NOTION_COOKIE
|
206 |
},
|
207 |
body: JSON.stringify({}),
|
208 |
};
|
|
|
|
|
|
|
|
|
209 |
|
210 |
-
// 添加代理配置(如果有)
|
211 |
-
if (PROXY_URL) {
|
212 |
-
const { HttpsProxyAgent } = await import('https-proxy-agent');
|
213 |
-
fetchOptions.agent = new HttpsProxyAgent(PROXY_URL);
|
214 |
-
logger.info(`使用代理: ${PROXY_URL}`);
|
215 |
-
}
|
216 |
-
logger.info(`正在获取Notion用户/空间ID...`);
|
217 |
-
|
218 |
-
// 发送请求
|
219 |
-
const response = await fetch("https://www.notion.so/api/v3/getSpaces", fetchOptions);
|
220 |
-
|
221 |
-
if (!response.ok) {
|
222 |
-
throw new Error(`HTTP error! status: ${response.status}`);
|
223 |
-
}
|
224 |
-
|
225 |
-
const data = await response.json();
|
226 |
-
|
227 |
-
// 提取用户ID
|
228 |
const userIdKey = Object.keys(data)[0];
|
229 |
-
if (!userIdKey)
|
230 |
-
|
231 |
-
|
232 |
-
return;
|
233 |
-
}
|
234 |
-
|
235 |
-
FETCHED_NOTION_USER_ID = userIdKey;
|
236 |
-
logger.success(`已获取Notion用户ID`);
|
237 |
-
|
238 |
-
// 提取空间ID
|
239 |
const userRoot = data[userIdKey]?.user_root?.[userIdKey];
|
240 |
const spaceViewPointers = userRoot?.value?.value?.space_view_pointers;
|
241 |
-
|
242 |
-
|
243 |
-
NOTION_SPACE_ID = spaceViewPointers[0].spaceId;
|
244 |
-
|
245 |
-
if (NOTION_SPACE_ID) {
|
246 |
-
logger.success(`已获取Notion空间ID`);
|
247 |
-
FETCHED_IDS_SUCCESSFULLY = true;
|
248 |
-
} else {
|
249 |
-
logger.error(`无法从space_view_pointers中提取spaceId`);
|
250 |
-
FETCHED_IDS_SUCCESSFULLY = false;
|
251 |
-
}
|
252 |
-
} else {
|
253 |
-
logger.error(`在响应中找不到space_view_pointers或spaceId`);
|
254 |
-
FETCHED_IDS_SUCCESSFULLY = false;
|
255 |
-
}
|
256 |
-
|
257 |
-
// 清理全局对象
|
258 |
-
try {
|
259 |
-
if (global.window) delete global.window;
|
260 |
-
if (global.document) delete global.document;
|
261 |
-
|
262 |
-
// 安全地删除navigator
|
263 |
-
if (global.navigator) {
|
264 |
-
try {
|
265 |
-
delete global.navigator;
|
266 |
-
} catch (navError) {
|
267 |
-
// 如果无法删除,尝试将其设置为undefined
|
268 |
-
try {
|
269 |
-
Object.defineProperty(global, 'navigator', {
|
270 |
-
value: undefined,
|
271 |
-
writable: true,
|
272 |
-
configurable: true
|
273 |
-
});
|
274 |
-
} catch (defineError) {
|
275 |
-
logger.warning(`无法清理navigator: ${defineError.message}`);
|
276 |
-
}
|
277 |
-
}
|
278 |
-
}
|
279 |
-
} catch (cleanupError) {
|
280 |
-
logger.warning(`清理全局对象时出错: ${cleanupError.message}`);
|
281 |
}
|
282 |
-
|
|
|
283 |
} catch (error) {
|
284 |
-
logger.error(
|
285 |
-
|
286 |
-
|
287 |
-
|
288 |
-
|
289 |
-
|
290 |
-
if (global.document) delete global.document;
|
291 |
-
|
292 |
-
// 安全地删除navigator
|
293 |
-
if (global.navigator) {
|
294 |
-
try {
|
295 |
-
delete global.navigator;
|
296 |
-
} catch (navError) {
|
297 |
-
// 如果无法删除,尝试将其设置为undefined
|
298 |
-
try {
|
299 |
-
Object.defineProperty(global, 'navigator', {
|
300 |
-
value: undefined,
|
301 |
-
writable: true,
|
302 |
-
configurable: true
|
303 |
-
});
|
304 |
-
} catch (defineError) {
|
305 |
-
logger.warning(`无法清理navigator: ${defineError.message}`);
|
306 |
-
}
|
307 |
-
}
|
308 |
-
}
|
309 |
-
} catch (cleanupError) {
|
310 |
-
logger.warning(`清理全局对象时出错: ${cleanupError.message}`);
|
311 |
-
}
|
312 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
313 |
}
|
314 |
|
315 |
// 构建Notion请求
|
316 |
-
function buildNotionRequest(requestData) {
|
317 |
// 当前时间
|
318 |
const now = new Date();
|
319 |
// 格式化为ISO字符串,确保包含毫秒和时区
|
@@ -348,8 +230,8 @@ function buildNotionRequest(requestData) {
|
|
348 |
transcript.push(new NotionTranscriptItem({
|
349 |
type: "context",
|
350 |
value: new NotionTranscriptContextValue({
|
351 |
-
userId:
|
352 |
-
spaceId:
|
353 |
surface: "home_module",
|
354 |
timezone: "America/Los_Angeles",
|
355 |
userName: userName,
|
@@ -389,7 +271,7 @@ function buildNotionRequest(requestData) {
|
|
389 |
transcript.push(new NotionTranscriptItemByuser({
|
390 |
type: "user",
|
391 |
value: [[content]],
|
392 |
-
userId:
|
393 |
createdAt: message.createdAt || isoString
|
394 |
}));
|
395 |
} else if (message.role === "user") {
|
@@ -397,7 +279,7 @@ function buildNotionRequest(requestData) {
|
|
397 |
transcript.push(new NotionTranscriptItemByuser({
|
398 |
type: "user",
|
399 |
value: [[content]],
|
400 |
-
userId:
|
401 |
createdAt: message.createdAt || isoString
|
402 |
}));
|
403 |
} else if (message.role === "assistant") {
|
@@ -413,7 +295,7 @@ function buildNotionRequest(requestData) {
|
|
413 |
|
414 |
// 创建请求体
|
415 |
return new NotionRequestBody({
|
416 |
-
spaceId:
|
417 |
transcript: transcript,
|
418 |
createThread: true,
|
419 |
traceId: randomUUID(),
|
@@ -428,13 +310,25 @@ function buildNotionRequest(requestData) {
|
|
428 |
}
|
429 |
|
430 |
// 流式处理Notion响应
|
431 |
-
async function streamNotionResponse(
|
432 |
-
// 创建流
|
433 |
const stream = new PassThrough();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
434 |
|
435 |
-
|
436 |
-
stream.write(':\n\n'); // 发送一个空注释行,保持连接活跃
|
437 |
|
|
|
|
|
|
|
|
|
|
|
438 |
// 设置HTTP头模板
|
439 |
const headers = {
|
440 |
'Content-Type': 'application/json',
|
@@ -443,10 +337,10 @@ async function streamNotionResponse(notionRequestBody) {
|
|
443 |
'notion-audit-log-platform': 'web',
|
444 |
'notion-client-version': '23.13.0.3686',
|
445 |
'origin': 'https://www.notion.so',
|
446 |
-
'referer': 'https://www.notion.so/
|
447 |
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36',
|
448 |
-
'x-notion-active-user-header':
|
449 |
-
'x-notion-space-id':
|
450 |
};
|
451 |
|
452 |
// 设置超时处理,确保流不会无限等待
|
@@ -477,7 +371,7 @@ async function streamNotionResponse(notionRequestBody) {
|
|
477 |
notionRequestBody,
|
478 |
headers,
|
479 |
NOTION_API_URL,
|
480 |
-
|
481 |
timeoutId
|
482 |
).catch((error) => {
|
483 |
logger.error(`流处理出错: ${error}`);
|
@@ -677,11 +571,6 @@ async function fetchNotionResponse(chunkQueue, notionRequestBody, headers, notio
|
|
677 |
reader.on('end', () => {
|
678 |
try {
|
679 |
logger.info(`响应完成`);
|
680 |
-
if (isMoreThanOne){
|
681 |
-
NOTION_COOKIE = getCookie(); // 获取下一个cookie
|
682 |
-
logger.info('开始切换cookie和ID');
|
683 |
-
fetchAndSetNotionIds(); // 切换到下一个ID
|
684 |
-
}
|
685 |
|
686 |
// 如果没有收到任何响应,发送一个提示消息
|
687 |
if (!responseReceived) {
|
@@ -884,48 +773,43 @@ async function fetchNotionResponse(chunkQueue, notionRequestBody, headers, notio
|
|
884 |
async function initialize() {
|
885 |
logger.info(`初始化Notion配置...`);
|
886 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
887 |
// 启动代理服务器
|
888 |
try {
|
889 |
-
|
|
|
|
|
890 |
} catch (error) {
|
891 |
logger.error(`启动代理服务器失败: ${error.message}`);
|
892 |
}
|
893 |
|
894 |
-
// 检查是否已从环境变量中获取ID
|
895 |
-
if (NOTION_SPACE_ID && FETCHED_NOTION_USER_ID) {
|
896 |
-
logger.info(`使用环境变量��的Notion ID配置
|
897 |
-
- NOTION_SPACE_ID: ${NOTION_SPACE_ID}
|
898 |
-
- NOTION_ACTIVE_USER_HEADER: ${FETCHED_NOTION_USER_ID}`);
|
899 |
-
// logger.info(`- NOTION_SPACE_ID: ${NOTION_SPACE_ID}`);
|
900 |
-
// logger.info(`- NOTION_ACTIVE_USER_HEADER: ${FETCHED_NOTION_USER_ID}`);
|
901 |
-
FETCHED_IDS_SUCCESSFULLY = true;
|
902 |
-
} else {
|
903 |
-
// 尝试从Notion API获取ID
|
904 |
-
NOTION_COOKIE = getCookie(); // 获取第一个cookie
|
905 |
-
if (!NOTION_COOKIE) {
|
906 |
-
logger.error(`错误: 未设置NOTION_COOKIE环境变量,应用无法正常工作`);
|
907 |
-
logger.error(`请在.env文件中设置有效的NOTION_COOKIE值`);
|
908 |
-
return;
|
909 |
-
}
|
910 |
-
|
911 |
-
logger.info(`未检测到ID配置,正在通过API自动获取Notion ID...`);
|
912 |
-
await fetchAndSetNotionIds();
|
913 |
-
|
914 |
-
if (!FETCHED_IDS_SUCCESSFULLY) {
|
915 |
-
logger.error(`获取Notion ID失败,应用无法正常工作`);
|
916 |
-
logger.error(`请检查以下可能的问题:`);
|
917 |
-
logger.error(`1. NOTION_COOKIE是否有效`);
|
918 |
-
logger.error(`2. 网络连接是否正常`);
|
919 |
-
logger.error(`3. Notion服务是否可访问`);
|
920 |
-
logger.error(`或者手动在.env文件中设置NOTION_SPACE_ID和NOTION_ACTIVE_USER_HEADER`);
|
921 |
-
} else {
|
922 |
-
logger.info(`成功通过API获取Notion ID
|
923 |
-
- 用户ID: ${FETCHED_NOTION_USER_ID}
|
924 |
-
- 空间ID: ${NOTION_SPACE_ID}`);
|
925 |
-
// logger.info(`- 用户ID: ${FETCHED_NOTION_USER_ID}`);
|
926 |
-
// logger.info(`- 空间ID: ${NOTION_SPACE_ID}`);
|
927 |
-
}
|
928 |
-
}
|
929 |
if (process.env.USE_NATIVE_PROXY_POOL === 'true') {
|
930 |
logger.info(`正在初始化本地代理池...`);
|
931 |
await proxyPool.initialize();
|
|
|
18 |
const __dirname = dirname(__filename);
|
19 |
|
20 |
// 加载环境变量
|
21 |
+
dotenv.config({ path: join(dirname(dirname(__filename)), '.env') });
|
22 |
|
23 |
// 日志配置
|
24 |
const logger = {
|
|
|
30 |
|
31 |
// 配置
|
32 |
const NOTION_API_URL = "https://www.notion.so/api/v3/runInferenceTranscript";
|
33 |
+
let ACCOUNTS = [];
|
|
|
|
|
34 |
const USE_NATIVE_PROXY_POOL = process.env.USE_NATIVE_PROXY_POOL === 'true';
|
35 |
const ENABLE_PROXY_SERVER = process.env.ENABLE_PROXY_SERVER === 'true';
|
36 |
+
let currentAccountIndex = 0;
|
|
|
|
|
|
|
37 |
|
38 |
// 代理配置
|
39 |
const PROXY_URL = process.env.PROXY_URL || "";
|
|
|
67 |
});
|
68 |
});
|
69 |
|
70 |
+
function getNextAccount() {
|
71 |
+
if (ACCOUNTS.length === 0) {
|
72 |
+
return null;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
73 |
}
|
74 |
+
const account = ACCOUNTS[currentAccountIndex];
|
75 |
+
const index = currentAccountIndex;
|
76 |
+
currentAccountIndex = (currentAccountIndex + 1) % ACCOUNTS.length;
|
77 |
+
return { ...account, index };
|
78 |
}
|
79 |
|
80 |
// 添加一个通用的cookie解析函数
|
|
|
124 |
}
|
125 |
|
126 |
// 获取Notion空间ID和用户ID
|
127 |
+
async function fetchAccountDetails(notionCookie) {
|
128 |
+
let userId, spaceId, email;
|
129 |
+
|
130 |
+
// Part 1: Get User and Space ID from getSpaces
|
|
|
|
|
|
|
131 |
try {
|
132 |
+
const dom = new JSDOM("", { url: "https://www.notion.so" });
|
133 |
+
global.window = dom.window;
|
134 |
+
global.document = dom.window.document;
|
135 |
+
global.navigator = dom.window.navigator;
|
136 |
+
document.cookie = notionCookie;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
137 |
|
138 |
+
const getSpacesOptions = {
|
|
|
139 |
method: 'POST',
|
140 |
headers: {
|
141 |
'Content-Type': 'application/json',
|
142 |
'accept': '*/*',
|
143 |
+
'user-agent': global.navigator.userAgent,
|
144 |
+
'Cookie': notionCookie
|
|
|
|
|
|
|
|
|
|
|
145 |
},
|
146 |
body: JSON.stringify({}),
|
147 |
};
|
148 |
+
|
149 |
+
const spacesResponse = await fetch("https://www.notion.so/api/v3/getSpaces", getSpacesOptions);
|
150 |
+
if (!spacesResponse.ok) throw new Error(`getSpaces failed with status ${spacesResponse.status}`);
|
151 |
+
const data = await spacesResponse.json();
|
152 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
153 |
const userIdKey = Object.keys(data)[0];
|
154 |
+
if (!userIdKey) throw new Error("Could not extract user ID from getSpaces response.");
|
155 |
+
userId = userIdKey;
|
156 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
157 |
const userRoot = data[userIdKey]?.user_root?.[userIdKey];
|
158 |
const spaceViewPointers = userRoot?.value?.value?.space_view_pointers;
|
159 |
+
if (spaceViewPointers && spaceViewPointers.length > 0) {
|
160 |
+
spaceId = spaceViewPointers[0].spaceId;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
161 |
}
|
162 |
+
if (!spaceId) throw new Error("Could not extract space ID from getSpaces response.");
|
163 |
+
|
164 |
} catch (error) {
|
165 |
+
logger.error(`获取用户/空间ID失败: ${error.message}`);
|
166 |
+
return null;
|
167 |
+
} finally {
|
168 |
+
if (global.window) delete global.window;
|
169 |
+
if (global.document) delete global.document;
|
170 |
+
if (global.navigator) delete global.navigator;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
171 |
}
|
172 |
+
|
173 |
+
// Part 2: Get User Email from getUserAnalyticsSettings
|
174 |
+
try {
|
175 |
+
const analyticsOptions = {
|
176 |
+
method: 'POST',
|
177 |
+
headers: {
|
178 |
+
'Content-Type': 'application/json',
|
179 |
+
'accept': '*/*',
|
180 |
+
'Cookie': notionCookie,
|
181 |
+
'x-notion-active-user-header': userId,
|
182 |
+
},
|
183 |
+
body: JSON.stringify({}),
|
184 |
+
};
|
185 |
+
const analyticsResponse = await fetch("https://www.notion.so/api/v3/getUserAnalyticsSettings", analyticsOptions);
|
186 |
+
if (!analyticsResponse.ok) throw new Error(`getUserAnalyticsSettings failed with status ${analyticsResponse.status}`);
|
187 |
+
const analyticsData = await analyticsResponse.json();
|
188 |
+
email = analyticsData?.user_email || '邮箱获取失败';
|
189 |
+
} catch (error) {
|
190 |
+
logger.warning(`获取用户邮箱失败: ${error.message}. 将继续操作。`);
|
191 |
+
email = '邮箱获取失败';
|
192 |
+
}
|
193 |
+
|
194 |
+
return { cookie: notionCookie, userId, spaceId, email };
|
195 |
}
|
196 |
|
197 |
// 构建Notion请求
|
198 |
+
function buildNotionRequest(requestData, account) {
|
199 |
// 当前时间
|
200 |
const now = new Date();
|
201 |
// 格式化为ISO字符串,确保包含毫秒和时区
|
|
|
230 |
transcript.push(new NotionTranscriptItem({
|
231 |
type: "context",
|
232 |
value: new NotionTranscriptContextValue({
|
233 |
+
userId: account.userId,
|
234 |
+
spaceId: account.spaceId,
|
235 |
surface: "home_module",
|
236 |
timezone: "America/Los_Angeles",
|
237 |
userName: userName,
|
|
|
271 |
transcript.push(new NotionTranscriptItemByuser({
|
272 |
type: "user",
|
273 |
value: [[content]],
|
274 |
+
userId: account.userId,
|
275 |
createdAt: message.createdAt || isoString
|
276 |
}));
|
277 |
} else if (message.role === "user") {
|
|
|
279 |
transcript.push(new NotionTranscriptItemByuser({
|
280 |
type: "user",
|
281 |
value: [[content]],
|
282 |
+
userId: account.userId,
|
283 |
createdAt: message.createdAt || isoString
|
284 |
}));
|
285 |
} else if (message.role === "assistant") {
|
|
|
295 |
|
296 |
// 创建请求体
|
297 |
return new NotionRequestBody({
|
298 |
+
spaceId: account.spaceId,
|
299 |
transcript: transcript,
|
300 |
createThread: true,
|
301 |
traceId: randomUUID(),
|
|
|
310 |
}
|
311 |
|
312 |
// 流式处理Notion响应
|
313 |
+
async function streamNotionResponse(requestData) {
|
|
|
314 |
const stream = new PassThrough();
|
315 |
+
const account = getNextAccount();
|
316 |
+
|
317 |
+
if (!account) {
|
318 |
+
const errorMessage = "无法处理请求: 未配置或加载任何有效的Notion账号。";
|
319 |
+
logger.error(errorMessage);
|
320 |
+
stream.emit('error', new Error(errorMessage));
|
321 |
+
stream.end();
|
322 |
+
return stream;
|
323 |
+
}
|
324 |
|
325 |
+
const notionRequestBody = buildNotionRequest(requestData, account);
|
|
|
326 |
|
327 |
+
logger.info(`正在使用账号 #${account.index + 1} (${account.email}) 进行本次请求...`);
|
328 |
+
|
329 |
+
// 从代理池获取代理
|
330 |
+
const proxyToUse = await proxyPool.getProxy();
|
331 |
+
|
332 |
// 设置HTTP头模板
|
333 |
const headers = {
|
334 |
'Content-Type': 'application/json',
|
|
|
337 |
'notion-audit-log-platform': 'web',
|
338 |
'notion-client-version': '23.13.0.3686',
|
339 |
'origin': 'https://www.notion.so',
|
340 |
+
'referer': 'https://www.notion.so/',
|
341 |
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36',
|
342 |
+
'x-notion-active-user-header': account.userId,
|
343 |
+
'x-notion-space-id': account.spaceId
|
344 |
};
|
345 |
|
346 |
// 设置超时处理,确保流不会无限等待
|
|
|
371 |
notionRequestBody,
|
372 |
headers,
|
373 |
NOTION_API_URL,
|
374 |
+
account.cookie,
|
375 |
timeoutId
|
376 |
).catch((error) => {
|
377 |
logger.error(`流处理出错: ${error}`);
|
|
|
571 |
reader.on('end', () => {
|
572 |
try {
|
573 |
logger.info(`响应完成`);
|
|
|
|
|
|
|
|
|
|
|
574 |
|
575 |
// 如果没有收到任何响应,发送一个提示消息
|
576 |
if (!responseReceived) {
|
|
|
773 |
async function initialize() {
|
774 |
logger.info(`初始化Notion配置...`);
|
775 |
|
776 |
+
const cookieString = process.env.NOTION_COOKIE || '';
|
777 |
+
if (!cookieString) {
|
778 |
+
logger.error("关键错误: 环境变量 'NOTION_COOKIE' 未设置。请在.env文件中提供至少一个有效的Notion Cookie。");
|
779 |
+
FETCHED_IDS_SUCCESSFULLY = false;
|
780 |
+
return;
|
781 |
+
}
|
782 |
+
|
783 |
+
const cookies = cookieString.split('|').filter(c => c.trim() !== '');
|
784 |
+
logger.info(`发现 ${cookies.length} 个Cookie,正在逐一获取账号信息...`);
|
785 |
+
|
786 |
+
for (const cookie of cookies) {
|
787 |
+
const accountDetails = await fetchAccountDetails(cookie.trim());
|
788 |
+
if (accountDetails) {
|
789 |
+
ACCOUNTS.push(accountDetails);
|
790 |
+
logger.success(`成功加载账号: ${accountDetails.email}`);
|
791 |
+
} else {
|
792 |
+
logger.warning(`加载其中一个Cookie失败,已跳过。`);
|
793 |
+
}
|
794 |
+
}
|
795 |
+
|
796 |
+
if (ACCOUNTS.length === 0) {
|
797 |
+
logger.error("未能成功加载任何Notion账号。请检查您的COOKIE是否有效或已过期。");
|
798 |
+
FETCHED_IDS_SUCCESSFULLY = false;
|
799 |
+
} else {
|
800 |
+
logger.success(`共成功加载 ${ACCOUNTS.length} 个账号。服务已准备就绪。`);
|
801 |
+
FETCHED_IDS_SUCCESSFULLY = true;
|
802 |
+
}
|
803 |
+
|
804 |
// 启动代理服务器
|
805 |
try {
|
806 |
+
if (ENABLE_PROXY_SERVER) {
|
807 |
+
await proxyServer.start();
|
808 |
+
}
|
809 |
} catch (error) {
|
810 |
logger.error(`启动代理服务器失败: ${error.message}`);
|
811 |
}
|
812 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
813 |
if (process.env.USE_NATIVE_PROXY_POOL === 'true') {
|
814 |
logger.info(`正在初始化本地代理池...`);
|
815 |
await proxyPool.initialize();
|