mirror of
https://github.com/adams549659584/go-proxy-bingai.git
synced 2025-06-03 15:02:30 +08:00
feat: ⚡ 可选聊天服务器、cloudflare 聊天服务器部署
This commit is contained in:
parent
842e02b9d7
commit
bfd58c5a52
1
.vscode/launch.json
vendored
1
.vscode/launch.json
vendored
@ -18,6 +18,7 @@
|
|||||||
// "Go_Proxy_BingAI_SOCKS_PWD": "xxx",
|
// "Go_Proxy_BingAI_SOCKS_PWD": "xxx",
|
||||||
// "Go_Proxy_BingAI_USER_TOKEN_1": "xxx",
|
// "Go_Proxy_BingAI_USER_TOKEN_1": "xxx",
|
||||||
// "Go_Proxy_BingAI_USER_TOKEN_2": "xxx"
|
// "Go_Proxy_BingAI_USER_TOKEN_2": "xxx"
|
||||||
|
// "Go_Proxy_BingAI_AUTH_KEY": "xxx",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
29
README.md
29
README.md
@ -2,6 +2,12 @@
|
|||||||
|
|
||||||
基于微软 New Bing 简单定制,拥有一致的 UI 体验,支持 ChatGPT 提示词,国内可用,基本兼容微软 Bing AI 所有功能,无需登录即可畅聊。
|
基于微软 New Bing 简单定制,拥有一致的 UI 体验,支持 ChatGPT 提示词,国内可用,基本兼容微软 Bing AI 所有功能,无需登录即可畅聊。
|
||||||
|
|
||||||
|
⭐ Bing 官方聊天服务器(相对较快和稳定,推荐)不可用时, 可用 ModHeader 添加 X-Forwarded-For 请求头,对应 URL 是 wss://sydney.bing.com/sydney/ChatHub,具体可参考 https://zhuanlan.zhihu.com/p/606655303
|
||||||
|
|
||||||
|
⭐ 聊天服务器 (暂时默认 Cloudflare ) 可在右上角 设置 => 服务选择 中切换
|
||||||
|
|
||||||
|
⭐ 自定义聊天服务器参考下面 [部署聊天服务器](#部署聊天服务器) 章节
|
||||||
|
|
||||||
⭐ 国内可用 (部署服务器需要直连 www.bing.com 不重定向 CN ,可配置 socks 连接)
|
⭐ 国内可用 (部署服务器需要直连 www.bing.com 不重定向 CN ,可配置 socks 连接)
|
||||||
|
|
||||||
⭐ 支持现有开源提示词库
|
⭐ 支持现有开源提示词库
|
||||||
@ -22,6 +28,7 @@
|
|||||||
- [Railway](#Railway)
|
- [Railway](#Railway)
|
||||||
- [Vercel](#Vercel)
|
- [Vercel](#Vercel)
|
||||||
- [Render](#Render)
|
- [Render](#Render)
|
||||||
|
- [部署聊天服务器](#部署聊天服务器)
|
||||||
- [TODO](#TODO)
|
- [TODO](#TODO)
|
||||||
|
|
||||||
## 网页展示
|
## 网页展示
|
||||||
@ -104,6 +111,8 @@ Go_Proxy_BingAI_SOCKS_PWD=xxx
|
|||||||
Go_Proxy_BingAI_USER_TOKEN_1=xxx
|
Go_Proxy_BingAI_USER_TOKEN_1=xxx
|
||||||
Go_Proxy_BingAI_USER_TOKEN_2=xxx
|
Go_Proxy_BingAI_USER_TOKEN_2=xxx
|
||||||
Go_Proxy_BingAI_USER_TOKEN_3=xxx ...
|
Go_Proxy_BingAI_USER_TOKEN_3=xxx ...
|
||||||
|
# 简单授权认证密码,可选
|
||||||
|
Go_Proxy_BingAI_AUTH_KEY=xxx
|
||||||
```
|
```
|
||||||
|
|
||||||
## 部署
|
## 部署
|
||||||
@ -153,7 +162,7 @@ services:
|
|||||||
|
|
||||||
### Release
|
### Release
|
||||||
|
|
||||||
在 [Github Releases](https://github.com/adams549659584/go-proxy-bingai/releases) 下载适用于对应平台的压缩包,解压后可得到可执行文件 go-proxy-bingai,直接运行即可。
|
在 [GitHub Releases](https://github.com/adams549659584/go-proxy-bingai/releases) 下载适用于对应平台的压缩包,解压后可得到可执行文件 go-proxy-bingai,直接运行即可。
|
||||||
|
|
||||||
### Railway
|
### Railway
|
||||||
|
|
||||||
@ -176,6 +185,8 @@ RAILWAY_DOCKERFILE_PATH=docker/Dockerfile
|
|||||||
|
|
||||||
### Vercel
|
### Vercel
|
||||||
|
|
||||||
|
> ⭐ Vercel 部署不支持 Websocket ,需选择 官方聊天服务器 或 Cloudflare
|
||||||
|
|
||||||
一键部署,点这里 => [](https://vercel.com/new/clone?repository-url=https://github.com/adams549659584/go-proxy-bingai&project-name=go-proxy-bingai&repository-name=go-proxy-bingai-vercel)
|
一键部署,点这里 => [](https://vercel.com/new/clone?repository-url=https://github.com/adams549659584/go-proxy-bingai&project-name=go-proxy-bingai&repository-name=go-proxy-bingai-vercel)
|
||||||
|
|
||||||

|

|
||||||
@ -190,11 +201,23 @@ RAILWAY_DOCKERFILE_PATH=docker/Dockerfile
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
## 部署聊天服务器
|
||||||
|
|
||||||
|
> 核心代码 [worker.js](./cloudflare/worker.js)
|
||||||
|
|
||||||
|
> 具体部署 Cloudflare Workers 教程自行查询,大概如下
|
||||||
|
|
||||||
|
- [注册 Cloudflare 账号](https://dash.cloudflare.com/sign-up)
|
||||||
|
|
||||||
|
- 创建 Worker 服务,复制 [worker.js](./cloudflare/worker.js) 全部代码,粘贴至创建的服务中,保存并部署。
|
||||||
|
|
||||||
|
- 触发器 中自定义访问域名。
|
||||||
|
|
||||||
## TODO
|
## TODO
|
||||||
|
|
||||||
- [x] 撰写
|
- [x] 撰写
|
||||||
- [x] Vue3 重构
|
- [x] Vue3 重构
|
||||||
- [x] 提示词
|
- [x] 提示词
|
||||||
- [x] 历史聊天
|
- [x] 历史聊天
|
||||||
- [ ] 导出消息到本地(Markdown、图片、PDF)
|
- [x] 导出消息到本地(Markdown、图片、PDF)
|
||||||
- [ ] 简单访问权限控制
|
- [x] 简单访问权限控制
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"adams549659584/go-proxy-bingai/common"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ChatHub(w http.ResponseWriter, r *http.Request) {
|
|
||||||
common.NewSingleHostReverseProxy(common.BING_CHAT_URL).ServeHTTP(w, r)
|
|
||||||
}
|
|
48
api/helper/helper.go
Normal file
48
api/helper/helper.go
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
package helper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"adams549659584/go-proxy-bingai/common"
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Response struct {
|
||||||
|
Code int `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Data interface{} `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func CommonResult(w http.ResponseWriter, code int, msg string, data interface{}) error {
|
||||||
|
res := Response{
|
||||||
|
Code: code,
|
||||||
|
Message: msg,
|
||||||
|
Data: data,
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
err := json.NewEncoder(w).Encode(res)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SuccessResult(w http.ResponseWriter, data interface{}) error {
|
||||||
|
return CommonResult(w, http.StatusOK, "success", data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ErrorResult(w http.ResponseWriter, code int, msg string) error {
|
||||||
|
return CommonResult(w, code, msg, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func UnauthorizedResult(w http.ResponseWriter) error {
|
||||||
|
return ErrorResult(w, http.StatusUnauthorized, "unauthorized")
|
||||||
|
}
|
||||||
|
|
||||||
|
func CheckAuth(r *http.Request) bool {
|
||||||
|
isAuth := true
|
||||||
|
if len(common.AUTH_KEY) > 0 {
|
||||||
|
ckAuthKey, _ := r.Cookie(common.AUTH_KEY_COOKIE_NAME)
|
||||||
|
isAuth = ckAuthKey != nil && len(ckAuthKey.Value) > 0 && common.AUTH_KEY == ckAuthKey.Value
|
||||||
|
}
|
||||||
|
return isAuth
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"adams549659584/go-proxy-bingai/api/helper"
|
||||||
"adams549659584/go-proxy-bingai/common"
|
"adams549659584/go-proxy-bingai/common"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
@ -9,6 +10,10 @@ func Index(w http.ResponseWriter, r *http.Request) {
|
|||||||
if r.URL.Path == "/" {
|
if r.URL.Path == "/" {
|
||||||
http.Redirect(w, r, common.PROXY_WEB_PAGE_PATH, http.StatusFound)
|
http.Redirect(w, r, common.PROXY_WEB_PAGE_PATH, http.StatusFound)
|
||||||
} else {
|
} else {
|
||||||
|
if !helper.CheckAuth(r) {
|
||||||
|
helper.UnauthorizedResult(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
common.NewSingleHostReverseProxy(common.BING_URL).ServeHTTP(w, r)
|
common.NewSingleHostReverseProxy(common.BING_URL).ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
15
api/sydney.go
Normal file
15
api/sydney.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"adams549659584/go-proxy-bingai/api/helper"
|
||||||
|
"adams549659584/go-proxy-bingai/common"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Sydney(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if !helper.CheckAuth(r) {
|
||||||
|
helper.UnauthorizedResult(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.NewSingleHostReverseProxy(common.BING_SYDNEY_URL).ServeHTTP(w, r)
|
||||||
|
}
|
24
api/sysConfig.go
Normal file
24
api/sysConfig.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"adams549659584/go-proxy-bingai/api/helper"
|
||||||
|
"adams549659584/go-proxy-bingai/common"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SysConfig struct {
|
||||||
|
// 是否系统配置 cookie
|
||||||
|
IsSysCK bool `json:"isSysCK"`
|
||||||
|
// 是否已授权
|
||||||
|
IsAuth bool `json:"isAuth"`
|
||||||
|
SydneyBaseUrl string `json:"sydneyBaseUrl"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func SysConf(w http.ResponseWriter, r *http.Request) {
|
||||||
|
isAuth := helper.CheckAuth(r)
|
||||||
|
conf := SysConfig{
|
||||||
|
IsSysCK: len(common.USER_TOKEN_LIST) > 0,
|
||||||
|
IsAuth: isAuth,
|
||||||
|
}
|
||||||
|
helper.SuccessResult(w, conf)
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"adams549659584/go-proxy-bingai/api/helper"
|
||||||
"adams549659584/go-proxy-bingai/common"
|
"adams549659584/go-proxy-bingai/common"
|
||||||
"adams549659584/go-proxy-bingai/web"
|
"adams549659584/go-proxy-bingai/web"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -10,6 +11,10 @@ func WebStatic(w http.ResponseWriter, r *http.Request) {
|
|||||||
if _, ok := web.WEB_PATH_MAP[r.URL.Path]; ok || r.URL.Path == common.PROXY_WEB_PREFIX_PATH {
|
if _, ok := web.WEB_PATH_MAP[r.URL.Path]; ok || r.URL.Path == common.PROXY_WEB_PREFIX_PATH {
|
||||||
http.StripPrefix(common.PROXY_WEB_PREFIX_PATH, http.FileServer(web.GetWebFS())).ServeHTTP(w, r)
|
http.StripPrefix(common.PROXY_WEB_PREFIX_PATH, http.FileServer(web.GetWebFS())).ServeHTTP(w, r)
|
||||||
} else {
|
} else {
|
||||||
|
if !helper.CheckAuth(r) {
|
||||||
|
helper.UnauthorizedResult(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
common.NewSingleHostReverseProxy(common.BING_URL).ServeHTTP(w, r)
|
common.NewSingleHostReverseProxy(common.BING_URL).ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
124
cloudflare/worker.js
Normal file
124
cloudflare/worker.js
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
const SYDNEY_ORIGIN = 'https://sydney.bing.com';
|
||||||
|
const KEEP_REQ_HEADERS = [
|
||||||
|
'accept',
|
||||||
|
'accept-encoding',
|
||||||
|
'accept-language',
|
||||||
|
'connection',
|
||||||
|
'cookie',
|
||||||
|
'upgrade',
|
||||||
|
'user-agent',
|
||||||
|
'sec-websocket-extensions',
|
||||||
|
'sec-websocket-key',
|
||||||
|
'sec-websocket-version',
|
||||||
|
'x-request-id',
|
||||||
|
'content-length',
|
||||||
|
'content-type',
|
||||||
|
'access-control-request-headers',
|
||||||
|
'access-control-request-method',
|
||||||
|
];
|
||||||
|
const IP_RANGE = [
|
||||||
|
['3.2.50.0', '3.5.31.255'], //192,000
|
||||||
|
['3.12.0.0', '3.23.255.255'], //786,432
|
||||||
|
['3.30.0.0', '3.33.34.255'], //205,568
|
||||||
|
['3.40.0.0', '3.63.255.255'], //1,572,864
|
||||||
|
['3.80.0.0', '3.95.255.255'], //1,048,576
|
||||||
|
['3.100.0.0', '3.103.255.255'], //262,144
|
||||||
|
['3.116.0.0', '3.119.255.255'], //262,144
|
||||||
|
['3.128.0.0', '3.247.255.255'], //7,864,320
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 随机整数 [min,max)
|
||||||
|
* @param {number} min
|
||||||
|
* @param {number} max
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const getRandomInt = (min, max) => Math.floor(Math.random() * (max - min)) + min;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ip 转 int
|
||||||
|
* @param {string} ip
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const ipToInt = (ip) => {
|
||||||
|
const ipArr = ip.split('.');
|
||||||
|
let result = 0;
|
||||||
|
result += +ipArr[0] << 24;
|
||||||
|
result += +ipArr[1] << 16;
|
||||||
|
result += +ipArr[2] << 8;
|
||||||
|
result += +ipArr[3];
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* int 转 ip
|
||||||
|
* @param {number} intIP
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const intToIp = (intIP) => {
|
||||||
|
return `${(intIP >> 24) & 255}.${(intIP >> 16) & 255}.${(intIP >> 8) & 255}.${intIP & 255}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getRandomIP = () => {
|
||||||
|
const randIndex = getRandomInt(0, IP_RANGE.length);
|
||||||
|
const startIp = IP_RANGE[randIndex][0];
|
||||||
|
const endIp = IP_RANGE[randIndex][1];
|
||||||
|
const startIPInt = ipToInt(startIp);
|
||||||
|
const endIPInt = ipToInt(endIp);
|
||||||
|
const randomInt = getRandomInt(startIPInt, endIPInt);
|
||||||
|
const randomIP = intToIp(randomInt);
|
||||||
|
return randomIP;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
/**
|
||||||
|
* fetch
|
||||||
|
* @param {Request} request
|
||||||
|
* @param {*} env
|
||||||
|
* @param {*} ctx
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async fetch(request, env, ctx) {
|
||||||
|
const currentUrl = new URL(request.url);
|
||||||
|
const targetUrl = new URL(SYDNEY_ORIGIN + currentUrl.pathname + currentUrl.search);
|
||||||
|
|
||||||
|
const newHeaders = new Headers();
|
||||||
|
request.headers.forEach((value, key) => {
|
||||||
|
// console.log(`old : ${key} : ${value}`);
|
||||||
|
if (KEEP_REQ_HEADERS.includes(key)) {
|
||||||
|
newHeaders.set(key, value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
newHeaders.set('host', targetUrl.host);
|
||||||
|
newHeaders.set('origin', targetUrl.origin);
|
||||||
|
newHeaders.set('referer', 'https://www.bing.com/search?q=Bing+AI');
|
||||||
|
const randIP = getRandomIP();
|
||||||
|
// console.log('randIP : ', randIP);
|
||||||
|
newHeaders.set('X-Forwarded-For', randIP);
|
||||||
|
const oldUA = request.headers.get('user-agent');
|
||||||
|
const isMobile = oldUA.includes('Mobile') || oldUA.includes('Android');
|
||||||
|
if (isMobile) {
|
||||||
|
newHeaders.set(
|
||||||
|
'user-agent',
|
||||||
|
'Mozilla/5.0 (iPhone; CPU iPhone OS 15_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.7 Mobile/15E148 Safari/605.1.15 BingSapphire/1.0.410427012'
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
newHeaders.set('user-agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 Edg/113.0.1774.35');
|
||||||
|
}
|
||||||
|
|
||||||
|
// newHeaders.forEach((value, key) => console.log(`${key} : ${value}`));
|
||||||
|
const newReq = new Request(targetUrl, {
|
||||||
|
method: request.method,
|
||||||
|
headers: newHeaders,
|
||||||
|
body: request.body,
|
||||||
|
});
|
||||||
|
// console.log('request url : ', newReq.url);
|
||||||
|
const res = await fetch(newReq);
|
||||||
|
return res;
|
||||||
|
// const newRes = new Response(res.body, res);
|
||||||
|
// newRes.headers.set('access-control-allow-origin', '*');
|
||||||
|
// newRes.headers.set('access-control-allow-methods', '*');
|
||||||
|
// newRes.headers.set('access-control-allow-headers', '*');
|
||||||
|
// return newRes;
|
||||||
|
},
|
||||||
|
};
|
@ -2322,9 +2322,6 @@ var IP_RANGE = [][]string{
|
|||||||
{"100.43.0.0", "100.43.95.255"}, //24,576
|
{"100.43.0.0", "100.43.95.255"}, //24,576
|
||||||
{"100.43.128.0", "100.63.255.255"}, //1,343,488
|
{"100.43.128.0", "100.63.255.255"}, //1,343,488
|
||||||
{"100.128.0.0", "100.255.255.255"}, //8,388,608
|
{"100.128.0.0", "100.255.255.255"}, //8,388,608
|
||||||
{"101.45.0.0", "101.45.255.255"}, //65,536
|
|
||||||
{"101.49.0.0", "101.49.127.255"}, //32,768
|
|
||||||
{"101.49.192.0", "101.49.255.255"}, //16,384
|
|
||||||
{"104.0.0.0", "104.28.7.255"}, //1,837,056
|
{"104.0.0.0", "104.28.7.255"}, //1,837,056
|
||||||
{"104.28.157.0", "104.28.255.255"}, //25,344
|
{"104.28.157.0", "104.28.255.255"}, //25,344
|
||||||
{"104.29.105.0", "104.30.1.255"}, //39,168
|
{"104.29.105.0", "104.30.1.255"}, //39,168
|
||||||
@ -2926,7 +2923,6 @@ var IP_RANGE = [][]string{
|
|||||||
{"137.167.0.0", "137.169.40.255"}, //141,568
|
{"137.167.0.0", "137.169.40.255"}, //141,568
|
||||||
{"137.169.43.0", "137.170.255.255"}, //120,064
|
{"137.169.43.0", "137.170.255.255"}, //120,064
|
||||||
{"137.173.0.0", "137.173.255.255"}, //65,536
|
{"137.173.0.0", "137.173.255.255"}, //65,536
|
||||||
{"137.175.0.0", "137.175.127.255"}, //32,768
|
|
||||||
{"137.176.0.0", "137.177.34.255"}, //74,496
|
{"137.176.0.0", "137.177.34.255"}, //74,496
|
||||||
{"137.177.36.0", "137.184.159.255"}, //490,496
|
{"137.177.36.0", "137.184.159.255"}, //490,496
|
||||||
{"137.184.176.0", "137.184.247.255"}, //18,432
|
{"137.184.176.0", "137.184.247.255"}, //18,432
|
||||||
|
@ -20,8 +20,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
BING_CHAT_DOMAIN = "https://sydney.bing.com"
|
BING_SYDNEY_DOMAIN = "https://sydney.bing.com"
|
||||||
BING_CHAT_URL, _ = url.Parse(BING_CHAT_DOMAIN + "/sydney/ChatHub")
|
// BING_CHAT_URL, _ = url.Parse(BING_CHAT_DOMAIN + "/sydney/ChatHub")
|
||||||
|
BING_SYDNEY_URL, _ = url.Parse(BING_SYDNEY_DOMAIN)
|
||||||
BING_URL, _ = url.Parse("https://www.bing.com")
|
BING_URL, _ = url.Parse("https://www.bing.com")
|
||||||
// EDGE_SVC_URL, _ = url.Parse("https://edgeservices.bing.com")
|
// EDGE_SVC_URL, _ = url.Parse("https://edgeservices.bing.com")
|
||||||
KEEP_REQ_HEADER_MAP = map[string]bool{
|
KEEP_REQ_HEADER_MAP = map[string]bool{
|
||||||
@ -38,6 +39,10 @@ var (
|
|||||||
"Sec-Websocket-Version": true,
|
"Sec-Websocket-Version": true,
|
||||||
"X-Request-Id": true,
|
"X-Request-Id": true,
|
||||||
"X-Forwarded-For": true,
|
"X-Forwarded-For": true,
|
||||||
|
"Content-Length": true,
|
||||||
|
"Content-Type": true,
|
||||||
|
"Access-Control-Request-Headers": true,
|
||||||
|
"Access-Control-Request-Method": true,
|
||||||
}
|
}
|
||||||
DEL_LOCATION_DOMAINS = []string{
|
DEL_LOCATION_DOMAINS = []string{
|
||||||
"https://cn.bing.com",
|
"https://cn.bing.com",
|
||||||
@ -50,12 +55,29 @@ var (
|
|||||||
USER_TOKEN_ENV_NAME_PREFIX = "Go_Proxy_BingAI_USER_TOKEN"
|
USER_TOKEN_ENV_NAME_PREFIX = "Go_Proxy_BingAI_USER_TOKEN"
|
||||||
USER_TOKEN_LIST []string
|
USER_TOKEN_LIST []string
|
||||||
RAND_COOKIE_INDEX_NAME = "BingAI_Rand_CK"
|
RAND_COOKIE_INDEX_NAME = "BingAI_Rand_CK"
|
||||||
|
// socks
|
||||||
|
SOCKS_URL string
|
||||||
|
SOCKS_USER string
|
||||||
|
SOCKS_PWD string
|
||||||
|
// 访问权限密钥,可选
|
||||||
|
AUTH_KEY string
|
||||||
|
AUTH_KEY_COOKIE_NAME = "BingAI_Auth_Key"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
initEnv()
|
||||||
initUserToken()
|
initUserToken()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func initEnv() {
|
||||||
|
// socks
|
||||||
|
SOCKS_URL = os.Getenv("Go_Proxy_BingAI_SOCKS_URL")
|
||||||
|
SOCKS_USER = os.Getenv("Go_Proxy_BingAI_SOCKS_USER")
|
||||||
|
SOCKS_PWD = os.Getenv("Go_Proxy_BingAI_SOCKS_PWD")
|
||||||
|
// auth
|
||||||
|
AUTH_KEY = os.Getenv("Go_Proxy_BingAI_AUTH_KEY")
|
||||||
|
}
|
||||||
|
|
||||||
func initUserToken() {
|
func initUserToken() {
|
||||||
for _, env := range os.Environ() {
|
for _, env := range os.Environ() {
|
||||||
if strings.HasPrefix(env, USER_TOKEN_ENV_NAME_PREFIX) {
|
if strings.HasPrefix(env, USER_TOKEN_ENV_NAME_PREFIX) {
|
||||||
@ -70,7 +92,7 @@ func NewSingleHostReverseProxy(target *url.URL) *httputil.ReverseProxy {
|
|||||||
httpsSchemeName := "https"
|
httpsSchemeName := "https"
|
||||||
var originalHost string
|
var originalHost string
|
||||||
var originalPath string
|
var originalPath string
|
||||||
var originalDomain string
|
// var originalDomain string
|
||||||
var randIP string
|
var randIP string
|
||||||
var resCKRandIndex string
|
var resCKRandIndex string
|
||||||
director := func(req *http.Request) {
|
director := func(req *http.Request) {
|
||||||
@ -79,18 +101,18 @@ func NewSingleHostReverseProxy(target *url.URL) *httputil.ReverseProxy {
|
|||||||
}
|
}
|
||||||
originalHost = req.Host
|
originalHost = req.Host
|
||||||
originalPath = req.URL.Path
|
originalPath = req.URL.Path
|
||||||
originalDomain = fmt.Sprintf("%s:%s", originalScheme, originalHost)
|
// originalDomain = fmt.Sprintf("%s:%s", originalScheme, originalHost)
|
||||||
|
|
||||||
req.URL.Scheme = target.Scheme
|
req.URL.Scheme = target.Scheme
|
||||||
req.URL.Host = target.Host
|
req.URL.Host = target.Host
|
||||||
req.Host = target.Host
|
req.Host = target.Host
|
||||||
|
|
||||||
originalRefer := req.Referer()
|
// originalRefer := req.Referer()
|
||||||
if originalRefer != "" && !strings.Contains(originalRefer, PROXY_WEB_PAGE_PATH) {
|
// if originalRefer != "" && !strings.Contains(originalRefer, originalDomain) {
|
||||||
req.Header.Set("Referer", strings.ReplaceAll(originalRefer, originalDomain, BING_URL.String()))
|
// req.Header.Set("Referer", strings.ReplaceAll(originalRefer, originalDomain, BING_URL.String()))
|
||||||
} else {
|
// } else {
|
||||||
req.Header.Set("Referer", fmt.Sprintf("%s/search?q=Bing+AI", BING_URL.String()))
|
req.Header.Set("Referer", fmt.Sprintf("%s/search?q=Bing+AI", BING_URL.String()))
|
||||||
}
|
// }
|
||||||
|
|
||||||
// 同一会话尽量保持相同的随机IP
|
// 同一会话尽量保持相同的随机IP
|
||||||
ckRandIP, _ := req.Cookie(RAND_IP_COOKIE_NAME)
|
ckRandIP, _ := req.Cookie(RAND_IP_COOKIE_NAME)
|
||||||
@ -199,9 +221,9 @@ func NewSingleHostReverseProxy(target *url.URL) *httputil.ReverseProxy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 跨域
|
// 跨域
|
||||||
res.Header.Set("Access-Control-Allow-Origin", "*")
|
// res.Header.Set("Access-Control-Allow-Origin", "*")
|
||||||
res.Header.Set("Access-Control-Allow-Methods", "*")
|
// res.Header.Set("Access-Control-Allow-Methods", "*")
|
||||||
res.Header.Set("Access-Control-Allow-Headers", "*")
|
// res.Header.Set("Access-Control-Allow-Headers", "*")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -226,18 +248,15 @@ func NewSingleHostReverseProxy(target *url.URL) *httputil.ReverseProxy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// socks
|
// socks
|
||||||
socksUrl := os.Getenv("Go_Proxy_BingAI_SOCKS_URL")
|
if SOCKS_URL != "" {
|
||||||
if socksUrl != "" {
|
|
||||||
socksUser := os.Getenv("Go_Proxy_BingAI_SOCKS_USER")
|
|
||||||
socksPwd := os.Getenv("Go_Proxy_BingAI_SOCKS_PWD")
|
|
||||||
var socksAuth *proxy.Auth
|
var socksAuth *proxy.Auth
|
||||||
if socksUser != "" && socksPwd != "" {
|
if SOCKS_USER != "" && SOCKS_PWD != "" {
|
||||||
socksAuth = &proxy.Auth{
|
socksAuth = &proxy.Auth{
|
||||||
User: socksUser,
|
User: SOCKS_USER,
|
||||||
Password: socksPwd,
|
Password: SOCKS_PWD,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s5Proxy, err := proxy.SOCKS5("tcp", socksUrl, socksAuth, proxy.Direct)
|
s5Proxy, err := proxy.SOCKS5("tcp", SOCKS_URL, socksAuth, proxy.Direct)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "go-proxy-bingai",
|
"name": "go-proxy-bingai",
|
||||||
"version": "1.7.0",
|
"version": "1.8.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
@ -24,7 +24,7 @@ _w['_sydConvConfig'] = {
|
|||||||
Syd2TFlights: '',
|
Syd2TFlights: '',
|
||||||
sydIDs:
|
sydIDs:
|
||||||
'winmuid3tf,osbsdusgreccf,ttstmout,crchatrev,winlongmsgtf,ctrlworkpay,norespwtf,tempcacheread,temptacache,505scss0,508jbcars0,515enbotdets0,5082tsports,515vaoprvs,424dagslnv1s0,kcimgattcf,427startpms0',
|
'winmuid3tf,osbsdusgreccf,ttstmout,crchatrev,winlongmsgtf,ctrlworkpay,norespwtf,tempcacheread,temptacache,505scss0,508jbcars0,515enbotdets0,5082tsports,515vaoprvs,424dagslnv1s0,kcimgattcf,427startpms0',
|
||||||
sydBaseUrl: 'https://sydney.bing.com',
|
sydBaseUrl: 'https://sydney.vcanbb.chat',
|
||||||
compSydBaseUrl: '',
|
compSydBaseUrl: '',
|
||||||
isCompliantSydneyEndpointEnabled: false,
|
isCompliantSydneyEndpointEnabled: false,
|
||||||
useAccountLinkingForConversationLimitUpsell: false,
|
useAccountLinkingForConversationLimitUpsell: false,
|
||||||
|
17
frontend/src/api/model/ApiResult.ts
Normal file
17
frontend/src/api/model/ApiResult.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
export interface ApiResult<T> {
|
||||||
|
code: ApiResultCode;
|
||||||
|
message: string;
|
||||||
|
data: T;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ApiResultCode {
|
||||||
|
/**
|
||||||
|
* 成功
|
||||||
|
*/
|
||||||
|
OK = 200,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 未授权
|
||||||
|
*/
|
||||||
|
Unauthorized = 401,
|
||||||
|
}
|
4
frontend/src/api/model/sysconf/SysConfig.ts
Normal file
4
frontend/src/api/model/sysconf/SysConfig.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export interface SysConfig {
|
||||||
|
isSysCK: boolean;
|
||||||
|
isAuth: boolean;
|
||||||
|
}
|
15
frontend/src/api/sysconf.ts
Normal file
15
frontend/src/api/sysconf.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import type { ApiResult } from './model/ApiResult';
|
||||||
|
import type { SysConfig } from './model/sysconf/SysConfig';
|
||||||
|
|
||||||
|
export async function getSysConfig() {
|
||||||
|
const url = '/sysconf';
|
||||||
|
return fetch(url, {
|
||||||
|
credentials: 'include',
|
||||||
|
}).then((res) => res.json() as unknown as ApiResult<SysConfig>);
|
||||||
|
}
|
||||||
|
|
||||||
|
const sysConfApi = {
|
||||||
|
getSysConfig,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default sysConfApi;
|
@ -2,33 +2,36 @@
|
|||||||
import { h, ref } from 'vue';
|
import { h, ref } from 'vue';
|
||||||
import { NDropdown, type DropdownOption, NModal, NInput, NButton, useMessage, NImage } from 'naive-ui';
|
import { NDropdown, type DropdownOption, NModal, NInput, NButton, useMessage, NImage } from 'naive-ui';
|
||||||
import settingSvgUrl from '@/assets/img/setting.svg?url';
|
import settingSvgUrl from '@/assets/img/setting.svg?url';
|
||||||
import cookies from '@/utils/cookies';
|
|
||||||
import { usePromptStore } from '@/stores/modules/prompt';
|
import { usePromptStore } from '@/stores/modules/prompt';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import ChatNavItem from './ChatNavItem.vue';
|
import ChatNavItem from './ChatNavItem.vue';
|
||||||
import type { Component } from 'vue';
|
import type { Component } from 'vue';
|
||||||
import { isMobile } from '@/utils/utils';
|
import { isMobile } from '@/utils/utils';
|
||||||
import CreateImage from '@/components/CreateImage/CreateImage.vue';
|
import CreateImage from '@/components/CreateImage/CreateImage.vue';
|
||||||
|
import { useChatStore } from '@/stores/modules/chat';
|
||||||
|
import { useUserStore } from '@/stores/modules/user';
|
||||||
|
|
||||||
const isShowMore = ref(false);
|
const isShowMore = ref(false);
|
||||||
const isShowSetTokenModal = ref(false);
|
const isShowSetTokenModal = ref(false);
|
||||||
const userToken = ref('');
|
const userToken = ref('');
|
||||||
const userTokenCookieName = '_U';
|
|
||||||
const randIpCookieName = 'BingAI_Rand_IP';
|
|
||||||
const message = useMessage();
|
const message = useMessage();
|
||||||
const promptStore = usePromptStore();
|
const promptStore = usePromptStore();
|
||||||
const { isShowPromptSotre } = storeToRefs(promptStore);
|
const { isShowPromptSotre } = storeToRefs(promptStore);
|
||||||
const isShowClearCacheModal = ref(false);
|
const isShowClearCacheModal = ref(false);
|
||||||
const isShowCreateImageModal = ref(false);
|
const isShowCreateImageModal = ref(false);
|
||||||
|
const chatStore = useChatStore();
|
||||||
|
const { isShowChatServiceSelectModal } = storeToRefs(chatStore);
|
||||||
|
const userStore = useUserStore();
|
||||||
|
|
||||||
const navType = {
|
const navType = {
|
||||||
github: 'github',
|
github: 'github',
|
||||||
|
version: 'version',
|
||||||
|
chatService: 'chatService',
|
||||||
|
promptStore: 'promptStore',
|
||||||
setToken: 'setToken',
|
setToken: 'setToken',
|
||||||
compose: 'compose',
|
compose: 'compose',
|
||||||
createImage: 'createImage',
|
createImage: 'createImage',
|
||||||
promptStore: 'promptStore',
|
|
||||||
reset: 'reset',
|
reset: 'reset',
|
||||||
version: 'version',
|
|
||||||
};
|
};
|
||||||
const navConfigs = [
|
const navConfigs = [
|
||||||
{
|
{
|
||||||
@ -40,6 +43,10 @@ const navConfigs = [
|
|||||||
key: navType.version,
|
key: navType.version,
|
||||||
label: '版本信息',
|
label: '版本信息',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: navType.chatService,
|
||||||
|
label: '服务选择',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: navType.promptStore,
|
key: navType.promptStore,
|
||||||
label: '提示词库',
|
label: '提示词库',
|
||||||
@ -74,23 +81,35 @@ const handleSelect = (key: string) => {
|
|||||||
message.success(`当前版本号为:${__APP_INFO__.version}`);
|
message.success(`当前版本号为:${__APP_INFO__.version}`);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case navType.chatService:
|
||||||
|
{
|
||||||
|
isShowChatServiceSelectModal.value = true;
|
||||||
|
chatStore.checkAllSydneyConfig();
|
||||||
|
}
|
||||||
|
break;
|
||||||
case navType.promptStore:
|
case navType.promptStore:
|
||||||
|
{
|
||||||
isShowPromptSotre.value = true;
|
isShowPromptSotre.value = true;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case navType.setToken:
|
case navType.setToken:
|
||||||
{
|
{
|
||||||
userToken.value = cookies.get(userTokenCookieName) || '';
|
userToken.value = userStore.getUserToken();
|
||||||
isShowSetTokenModal.value = true;
|
isShowSetTokenModal.value = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case navType.reset:
|
|
||||||
isShowClearCacheModal.value = true;
|
|
||||||
break;
|
|
||||||
case navType.createImage:
|
case navType.createImage:
|
||||||
if (!cookies.get(userTokenCookieName)) {
|
{
|
||||||
|
if (!userStore.sysConfig?.isSysCK && !userStore.getUserToken()) {
|
||||||
message.warning('体验画图功能需先登录');
|
message.warning('体验画图功能需先登录');
|
||||||
}
|
}
|
||||||
isShowCreateImageModal.value = true;
|
isShowCreateImageModal.value = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case navType.reset:
|
||||||
|
{
|
||||||
|
isShowClearCacheModal.value = true;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@ -98,9 +117,7 @@ const handleSelect = (key: string) => {
|
|||||||
};
|
};
|
||||||
const resetCache = async () => {
|
const resetCache = async () => {
|
||||||
isShowClearCacheModal.value = false;
|
isShowClearCacheModal.value = false;
|
||||||
cookies.set(userTokenCookieName, '', -1);
|
await userStore.resetCache();
|
||||||
cookies.set(randIpCookieName, '', -1);
|
|
||||||
await clearCache();
|
|
||||||
message.success('清理完成');
|
message.success('清理完成');
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
};
|
};
|
||||||
@ -110,28 +127,9 @@ const saveUserToken = () => {
|
|||||||
message.warning('请先填入用户 Cookie');
|
message.warning('请先填入用户 Cookie');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
cookies.set(userTokenCookieName, userToken.value, 7 * 24 * 60, '/');
|
userStore.saveUserToken(userToken.value);
|
||||||
isShowSetTokenModal.value = false;
|
isShowSetTokenModal.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const clearCache = async () => {
|
|
||||||
// del storage
|
|
||||||
localStorage.clear();
|
|
||||||
sessionStorage.clear();
|
|
||||||
// del sw cache
|
|
||||||
const cacheKeys = await caches.keys();
|
|
||||||
for (const cacheKey of cacheKeys) {
|
|
||||||
await caches.open(cacheKey).then(async (cache) => {
|
|
||||||
const requests = await cache.keys();
|
|
||||||
return await Promise.all(
|
|
||||||
requests.map((request) => {
|
|
||||||
console.log(`del cache : `, request.url);
|
|
||||||
return cache.delete(request);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {} from 'vue';
|
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
navConfig: {
|
navConfig: {
|
||||||
|
@ -0,0 +1,63 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { NModal, NTable, NTag, NButton, NInput, useMessage } from 'naive-ui';
|
||||||
|
import { useChatStore, type SydneyConfig } from '@/stores/modules/chat';
|
||||||
|
import { storeToRefs } from 'pinia';
|
||||||
|
|
||||||
|
const chatStore = useChatStore();
|
||||||
|
const { isShowChatServiceSelectModal, sydneyConfigs, selectedSydneyBaseUrl } = storeToRefs(chatStore);
|
||||||
|
|
||||||
|
const message = useMessage();
|
||||||
|
|
||||||
|
const checkSydneyConfig = async (config: SydneyConfig) => {
|
||||||
|
config.isUsable = undefined;
|
||||||
|
config.delay = undefined;
|
||||||
|
const checkResult = await chatStore.checkSydneyConfig(config);
|
||||||
|
if (checkResult.errorMsg) {
|
||||||
|
message.error(checkResult.errorMsg);
|
||||||
|
}
|
||||||
|
config.isUsable = checkResult.isUsable;
|
||||||
|
config.delay = checkResult.delay;
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectSydneyConfig = (config: SydneyConfig) => {
|
||||||
|
selectedSydneyBaseUrl.value = config.baseUrl;
|
||||||
|
CIB.config.sydney.baseUrl = config.baseUrl;
|
||||||
|
isShowChatServiceSelectModal.value = false;
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NModal class="w-11/12 xl:w-[900px]" v-model:show="isShowChatServiceSelectModal" preset="card" title="聊天服务器设置">
|
||||||
|
<NTable striped>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="(config, index) in sydneyConfigs" :key="index">
|
||||||
|
<td>
|
||||||
|
<span v-if="config.isCus" class="hidden xl:block">{{ config.label }}</span>
|
||||||
|
<span v-else>{{ config.label }}</span>
|
||||||
|
<NInput class="xl:hidden" v-if="config.isCus" v-model:value="config.baseUrl" placeholder="自定义聊天服务器链接" @change="checkSydneyConfig(config)"></NInput>
|
||||||
|
</td>
|
||||||
|
<td class="hidden xl:table-cell">
|
||||||
|
<NInput v-if="config.isCus" v-model:value="config.baseUrl" placeholder="自定义聊天服务器链接" @change="checkSydneyConfig(config)"></NInput>
|
||||||
|
<span v-else>{{ config.baseUrl }}</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div v-if="config.baseUrl && config.isUsable === undefined && config.delay === undefined" class="flex justify-center items-center flex-wrap gap-2">
|
||||||
|
<NButton tertiary :loading="true" size="small" type="info"></NButton>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="config.baseUrl" class="flex justify-center items-center flex-wrap gap-2" @click="checkSydneyConfig(config)">
|
||||||
|
<NTag v-if="config.isUsable === false" type="error" class="cursor-pointer">不可用</NTag>
|
||||||
|
<NTag v-if="config.delay" type="success" class="cursor-pointer">{{ config.delay }} ms</NTag>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div class="flex justify-center items-center flex-wrap gap-2">
|
||||||
|
<NButton class="hidden xl:table-cell" secondary @click="checkSydneyConfig(config)">检测</NButton>
|
||||||
|
<NButton v-if="config.baseUrl === selectedSydneyBaseUrl" secondary type="success">当前</NButton>
|
||||||
|
<NButton v-else secondary type="info" @click="selectSydneyConfig(config)">选择</NButton>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</NTable>
|
||||||
|
</NModal>
|
||||||
|
</template>
|
108
frontend/src/stores/modules/chat/index.ts
Normal file
108
frontend/src/stores/modules/chat/index.ts
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
import { ref } from 'vue';
|
||||||
|
import { defineStore } from 'pinia';
|
||||||
|
|
||||||
|
export interface SydneyConfig {
|
||||||
|
baseUrl: string;
|
||||||
|
label: string;
|
||||||
|
isUsable?: boolean;
|
||||||
|
delay?: number;
|
||||||
|
isCus?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CheckSydneyConfigResult {
|
||||||
|
isUsable: boolean;
|
||||||
|
errorMsg?: string;
|
||||||
|
delay?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useChatStore = defineStore(
|
||||||
|
'chat-store',
|
||||||
|
() => {
|
||||||
|
const chatHubPath = '/sydney/ChatHub';
|
||||||
|
|
||||||
|
const isShowChatServiceSelectModal = ref(false);
|
||||||
|
const selectedSydneyBaseUrl = ref('');
|
||||||
|
const sydneyConfigs = ref<SydneyConfig[]>([
|
||||||
|
{
|
||||||
|
baseUrl: 'https://sydney.bing.com',
|
||||||
|
label: 'Bing 官方',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
baseUrl: 'https://sydney.vcanbb.chat',
|
||||||
|
label: 'Cloudflare',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
baseUrl: location.origin,
|
||||||
|
label: '本站',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
baseUrl: '',
|
||||||
|
label: '自定义',
|
||||||
|
isCus: true,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const sydneyCheckTimeoutMS = 3000;
|
||||||
|
|
||||||
|
const checkSydneyConfig = async (config: SydneyConfig): Promise<CheckSydneyConfigResult> => {
|
||||||
|
if (!config.baseUrl) {
|
||||||
|
return {
|
||||||
|
isUsable: false,
|
||||||
|
errorMsg: '链接不可为空',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const startTime = Date.now();
|
||||||
|
const ws = new WebSocket(config.baseUrl.replace('http', 'ws') + chatHubPath);
|
||||||
|
const wsTimer = setTimeout(() => {
|
||||||
|
ws.close();
|
||||||
|
}, sydneyCheckTimeoutMS);
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
ws.onopen = () => {
|
||||||
|
clearTimeout(wsTimer);
|
||||||
|
resolve(ws.close());
|
||||||
|
};
|
||||||
|
ws.onerror = () => {
|
||||||
|
clearTimeout(wsTimer);
|
||||||
|
reject(new Error(`聊天服务器 ${config.baseUrl} 连接失败`));
|
||||||
|
};
|
||||||
|
ws.onclose = () => reject(new Error(`聊天服务器 ${config.baseUrl} 连接超时`));
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
isUsable: true,
|
||||||
|
delay: Date.now() - startTime,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
isUsable: false,
|
||||||
|
errorMsg: error instanceof Error ? error.message : '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkAllSydneyConfig = async () => {
|
||||||
|
const checkAllConfigPromises = sydneyConfigs.value
|
||||||
|
.filter((x) => x.baseUrl)
|
||||||
|
.map(async (config) => {
|
||||||
|
const checkResult = await checkSydneyConfig(config);
|
||||||
|
config.isUsable = checkResult.isUsable;
|
||||||
|
config.delay = checkResult.delay;
|
||||||
|
});
|
||||||
|
await Promise.all(checkAllConfigPromises);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
isShowChatServiceSelectModal,
|
||||||
|
sydneyConfigs,
|
||||||
|
selectedSydneyBaseUrl,
|
||||||
|
checkSydneyConfig,
|
||||||
|
checkAllSydneyConfig,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{
|
||||||
|
persist: {
|
||||||
|
key: 'chat-store',
|
||||||
|
storage: localStorage,
|
||||||
|
paths: ['selectedSydneyBaseUrl', 'sydneyConfigs'],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
125
frontend/src/stores/modules/user/index.ts
Normal file
125
frontend/src/stores/modules/user/index.ts
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
import { ref } from 'vue';
|
||||||
|
import { defineStore } from 'pinia';
|
||||||
|
import cookies from '@/utils/cookies';
|
||||||
|
import { sleep } from '@/utils/utils';
|
||||||
|
import sysconfApi from '@/api/sysconf';
|
||||||
|
import { ApiResultCode } from '@/api/model/ApiResult';
|
||||||
|
import type { SysConfig } from '@/api/model/sysconf/SysConfig';
|
||||||
|
|
||||||
|
export const useUserStore = defineStore(
|
||||||
|
'user-store',
|
||||||
|
() => {
|
||||||
|
const maxTryCreateConversationIdCount = 10;
|
||||||
|
const userTokenCookieName = '_U';
|
||||||
|
const randIpCookieName = 'BingAI_Rand_IP';
|
||||||
|
const authKeyCookieName = 'BingAI_Auth_Key';
|
||||||
|
|
||||||
|
const sysConfig = ref<SysConfig>();
|
||||||
|
|
||||||
|
const getSysConfig = async () => {
|
||||||
|
const res = await sysconfApi.getSysConfig();
|
||||||
|
if (res.code === ApiResultCode.OK) {
|
||||||
|
sysConfig.value = {
|
||||||
|
...sysConfig.value,
|
||||||
|
...res.data,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getConversationExpiry = () => {
|
||||||
|
const B = new Date();
|
||||||
|
return B.setMinutes(B.getMinutes() + CIB.config.sydney.expiryInMinutes), B;
|
||||||
|
};
|
||||||
|
|
||||||
|
const tryCreateConversationId = async (tryCount = 0) => {
|
||||||
|
if (tryCount >= maxTryCreateConversationIdCount) {
|
||||||
|
console.log(`已重试 ${tryCount} 次,自动创建停止`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const conversationRes = await fetch('/turing/conversation/create', {
|
||||||
|
credentials: 'include',
|
||||||
|
})
|
||||||
|
.then((res) => res.json())
|
||||||
|
.catch((err) => `error`);
|
||||||
|
if (conversationRes?.result?.value === 'Success') {
|
||||||
|
console.log('成功创建会话ID : ', conversationRes.conversationId);
|
||||||
|
CIB.manager.conversation.updateId(conversationRes.conversationId, getConversationExpiry(), conversationRes.clientId, conversationRes.conversationSignature);
|
||||||
|
} else {
|
||||||
|
await sleep(300);
|
||||||
|
tryCount += 1;
|
||||||
|
console.log(`开始第 ${tryCount} 次重试创建会话ID`);
|
||||||
|
cookies.set(randIpCookieName, '', -1);
|
||||||
|
tryCreateConversationId(tryCount);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getUserToken = () => {
|
||||||
|
const userCookieVal = cookies.get(userTokenCookieName) || '';
|
||||||
|
return userCookieVal;
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkUserToken = () => {
|
||||||
|
const token = getUserToken();
|
||||||
|
if (!token) {
|
||||||
|
// 未登录不显示历史记录
|
||||||
|
CIB.config.features.enableGetChats = false;
|
||||||
|
CIB.vm.sidePanel.isVisibleMobile = false;
|
||||||
|
CIB.vm.sidePanel.isVisibleDesktop = false;
|
||||||
|
// 创建会话id
|
||||||
|
tryCreateConversationId();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const saveUserToken = (token: string) => {
|
||||||
|
cookies.set(userTokenCookieName, token, 7 * 24 * 60, '/');
|
||||||
|
};
|
||||||
|
|
||||||
|
const setAuthKey = (authKey: string) => {
|
||||||
|
cookies.set(authKeyCookieName, authKey);
|
||||||
|
};
|
||||||
|
|
||||||
|
const clearCache = async () => {
|
||||||
|
// del storage
|
||||||
|
localStorage.clear();
|
||||||
|
sessionStorage.clear();
|
||||||
|
// del sw cache
|
||||||
|
const cacheKeys = await caches.keys();
|
||||||
|
for (const cacheKey of cacheKeys) {
|
||||||
|
await caches.open(cacheKey).then(async (cache) => {
|
||||||
|
const requests = await cache.keys();
|
||||||
|
return await Promise.all(
|
||||||
|
requests.map((request) => {
|
||||||
|
console.log(`del cache : `, request.url);
|
||||||
|
return cache.delete(request);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const resetCache = async () => {
|
||||||
|
cookies.set(userTokenCookieName, '', -1);
|
||||||
|
cookies.set(randIpCookieName, '', -1);
|
||||||
|
cookies.set(authKeyCookieName, '', -1);
|
||||||
|
await clearCache();
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
sysConfig,
|
||||||
|
getSysConfig,
|
||||||
|
getUserToken,
|
||||||
|
checkUserToken,
|
||||||
|
saveUserToken,
|
||||||
|
resetCache,
|
||||||
|
setAuthKey,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{
|
||||||
|
persist: {
|
||||||
|
key: 'user-store',
|
||||||
|
storage: localStorage,
|
||||||
|
paths: [],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
@ -1,20 +1,30 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, ref, computed } from 'vue';
|
import { onMounted, ref, computed } from 'vue';
|
||||||
import { NEmpty, NButton } from 'naive-ui';
|
import { NEmpty, NButton, useMessage, NResult, NInput } from 'naive-ui';
|
||||||
import conversationCssText from '@/assets/css/conversation.css?raw';
|
import conversationCssText from '@/assets/css/conversation.css?raw';
|
||||||
import { usePromptStore, type IPrompt } from '@/stores/modules/prompt';
|
import { usePromptStore, type IPrompt } from '@/stores/modules/prompt';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import VirtualList from 'vue3-virtual-scroll-list';
|
import VirtualList from 'vue3-virtual-scroll-list';
|
||||||
import ChatPromptItem from './ChatPromptItem.vue';
|
import ChatPromptItem from './ChatPromptItem.vue';
|
||||||
import { isMobile, sleep } from '@/utils/utils';
|
import { isMobile } from '@/utils/utils';
|
||||||
import cookies from '@/utils/cookies';
|
|
||||||
import LoadingSpinner from '@/components/LoadingSpinner/LoadingSpinner.vue';
|
import LoadingSpinner from '@/components/LoadingSpinner/LoadingSpinner.vue';
|
||||||
|
import { ApiResultCode } from '@/api/model/ApiResult';
|
||||||
|
import type { SysConfig } from '@/api/model/sysconf/SysConfig';
|
||||||
|
import { useChatStore } from '@/stores/modules/chat';
|
||||||
|
import ChatServiceSelect from '@/components/ChatServiceSelect/ChatServiceSelect.vue';
|
||||||
|
import { useUserStore } from '@/stores/modules/user';
|
||||||
|
|
||||||
|
const message = useMessage();
|
||||||
const isShowLoading = ref(true);
|
const isShowLoading = ref(true);
|
||||||
|
|
||||||
const promptStore = usePromptStore();
|
const promptStore = usePromptStore();
|
||||||
const { isShowPromptSotre, isShowChatPrompt, keyword, promptList, searchPromptList, selectedPromptIndex } = storeToRefs(promptStore);
|
const { isShowPromptSotre, isShowChatPrompt, keyword, promptList, searchPromptList, selectedPromptIndex } = storeToRefs(promptStore);
|
||||||
|
|
||||||
|
const chatStore = useChatStore();
|
||||||
|
const { isShowChatServiceSelectModal, sydneyConfigs, selectedSydneyBaseUrl } = storeToRefs(chatStore);
|
||||||
|
|
||||||
|
const userStore = useUserStore();
|
||||||
|
|
||||||
const scrollbarRef = ref<{
|
const scrollbarRef = ref<{
|
||||||
scrollToIndex: (index: number) => {};
|
scrollToIndex: (index: number) => {};
|
||||||
getOffset: () => number;
|
getOffset: () => number;
|
||||||
@ -22,12 +32,13 @@ const scrollbarRef = ref<{
|
|||||||
getScrollSize: () => number;
|
getScrollSize: () => number;
|
||||||
}>();
|
}>();
|
||||||
const isInput = ref(false);
|
const isInput = ref(false);
|
||||||
const maxTryCreateConversationIdCount = 10;
|
|
||||||
const userTokenCookieName = '_U';
|
|
||||||
const randIpCookieName = 'BingAI_Rand_IP';
|
|
||||||
const isPromptScrolling = ref(false);
|
const isPromptScrolling = ref(false);
|
||||||
const promptItemHeight = 130;
|
const promptItemHeight = 130;
|
||||||
|
|
||||||
|
const isShowUnauthorizedModal = ref(false);
|
||||||
|
const authKey = ref('');
|
||||||
|
const isAuthBtnLoading = ref(false);
|
||||||
|
|
||||||
const isShowHistory = computed(() => {
|
const isShowHistory = computed(() => {
|
||||||
return (CIB.vm.isMobile && CIB.vm.sidePanel.isVisibleMobile) || (!CIB.vm.isMobile && CIB.vm.sidePanel.isVisibleDesktop);
|
return (CIB.vm.isMobile && CIB.vm.sidePanel.isVisibleMobile) || (!CIB.vm.isMobile && CIB.vm.sidePanel.isVisibleDesktop);
|
||||||
});
|
});
|
||||||
@ -35,15 +46,59 @@ const isShowHistory = computed(() => {
|
|||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await initChat();
|
await initChat();
|
||||||
// CIB.vm.isMobile = isMobile();
|
// CIB.vm.isMobile = isMobile();
|
||||||
checkUserToken();
|
initSysConfig();
|
||||||
// show
|
// show conversion
|
||||||
SydneyFullScreenConv.initWithWaitlistUpdate({ cookLoc: {} }, 10);
|
SydneyFullScreenConv.initWithWaitlistUpdate({ cookLoc: {} }, 10);
|
||||||
|
|
||||||
|
initChatService();
|
||||||
|
|
||||||
isShowLoading.value = false;
|
isShowLoading.value = false;
|
||||||
hackStyle();
|
hackStyle();
|
||||||
initChatPrompt();
|
initChatPrompt();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const initChatService = () => {
|
||||||
|
if (selectedSydneyBaseUrl.value) {
|
||||||
|
CIB.config.sydney.baseUrl = selectedSydneyBaseUrl.value;
|
||||||
|
isShowChatServiceSelectModal.value = false;
|
||||||
|
} else {
|
||||||
|
isShowChatServiceSelectModal.value = true;
|
||||||
|
selectedSydneyBaseUrl.value = CIB.config.sydney.baseUrl;
|
||||||
|
const isCus = sydneyConfigs.value.filter((x) => !x.isCus).every((x) => x.baseUrl !== selectedSydneyBaseUrl.value);
|
||||||
|
if (isCus) {
|
||||||
|
const cusSydneyConfig = sydneyConfigs.value.find((x) => x.isCus);
|
||||||
|
if (cusSydneyConfig) {
|
||||||
|
cusSydneyConfig.baseUrl = selectedSydneyBaseUrl.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chatStore.checkAllSydneyConfig();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const initSysConfig = async () => {
|
||||||
|
const res = await userStore.getSysConfig();
|
||||||
|
switch (res.code) {
|
||||||
|
case ApiResultCode.OK:
|
||||||
|
{
|
||||||
|
if (!res.data.isAuth) {
|
||||||
|
isShowUnauthorizedModal.value = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
afterAuth(res.data);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
message.error(`[${res.code}] ${res.message}`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const afterAuth = (data: SysConfig) => {
|
||||||
|
if (!data.isSysCK) {
|
||||||
|
userStore.checkUserToken();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const initChat = async () => {
|
const initChat = async () => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
sj_evt.bind('sydFSC.init', resolve, true);
|
sj_evt.bind('sydFSC.init', resolve, true);
|
||||||
@ -51,45 +106,6 @@ const initChat = async () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const checkUserToken = () => {
|
|
||||||
const userCookieVal = cookies.get(userTokenCookieName);
|
|
||||||
if (!userCookieVal) {
|
|
||||||
// 未登录不显示历史记录
|
|
||||||
CIB.config.features.enableGetChats = false;
|
|
||||||
CIB.vm.sidePanel.isVisibleMobile = false;
|
|
||||||
CIB.vm.sidePanel.isVisibleDesktop = false;
|
|
||||||
// 创建会话id
|
|
||||||
tryCreateConversationId();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getConversationExpiry = () => {
|
|
||||||
const B = new Date();
|
|
||||||
return B.setMinutes(B.getMinutes() + CIB.config.sydney.expiryInMinutes), B;
|
|
||||||
};
|
|
||||||
|
|
||||||
const tryCreateConversationId = async (tryCount = 0) => {
|
|
||||||
if (tryCount >= maxTryCreateConversationIdCount) {
|
|
||||||
console.log(`已重试 ${tryCount} 次,自动创建停止`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const conversationRes = await fetch('/turing/conversation/create', {
|
|
||||||
credentials: 'include',
|
|
||||||
})
|
|
||||||
.then((res) => res.json())
|
|
||||||
.catch((err) => `error`);
|
|
||||||
if (conversationRes?.result?.value === 'Success') {
|
|
||||||
console.log('成功创建会话ID : ', conversationRes.conversationId);
|
|
||||||
CIB.manager.conversation.updateId(conversationRes.conversationId, getConversationExpiry(), conversationRes.clientId, conversationRes.conversationSignature);
|
|
||||||
} else {
|
|
||||||
await sleep(300);
|
|
||||||
tryCount += 1;
|
|
||||||
console.log(`开始第 ${tryCount} 次重试创建会话ID`);
|
|
||||||
cookies.set(randIpCookieName, '', -1);
|
|
||||||
tryCreateConversationId(tryCount);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const hackStyle = () => {
|
const hackStyle = () => {
|
||||||
if (location.hostname === 'localhost') {
|
if (location.hostname === 'localhost') {
|
||||||
CIB.config.sydney.hostnamesToBypassSecureConnection = CIB.config.sydney.hostnamesToBypassSecureConnection.filter((x) => x !== location.hostname);
|
CIB.config.sydney.hostnamesToBypassSecureConnection = CIB.config.sydney.hostnamesToBypassSecureConnection.filter((x) => x !== location.hostname);
|
||||||
@ -233,6 +249,24 @@ const handlePromptListScroll = () => {
|
|||||||
}
|
}
|
||||||
}, 100);
|
}, 100);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const auth = async () => {
|
||||||
|
if (!authKey.value) {
|
||||||
|
message.error('请先输入授权码');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
isAuthBtnLoading.value = true;
|
||||||
|
userStore.setAuthKey(authKey.value);
|
||||||
|
const res = await userStore.getSysConfig();
|
||||||
|
if (res.data.isAuth) {
|
||||||
|
message.success('授权成功');
|
||||||
|
isShowUnauthorizedModal.value = false;
|
||||||
|
afterAuth(res.data);
|
||||||
|
} else {
|
||||||
|
message.error('授权码有误');
|
||||||
|
}
|
||||||
|
isAuthBtnLoading.value = false;
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -264,4 +298,17 @@ const handlePromptListScroll = () => {
|
|||||||
</NEmpty>
|
</NEmpty>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
<footer>
|
||||||
|
<!-- 服务器选择 -->
|
||||||
|
<ChatServiceSelect />
|
||||||
|
<!-- 授权 -->
|
||||||
|
<div v-if="isShowUnauthorizedModal" class="fixed top-0 left-0 w-screen h-screen flex justify-center items-center bg-black/40 z-50">
|
||||||
|
<NResult class="bg-white px-28 py-4 rounded-md" status="403" title="401 未授权">
|
||||||
|
<template #footer>
|
||||||
|
<NInput v-model:value="authKey" type="password" placeholder="请输入授权码" maxlength="60"></NInput>
|
||||||
|
<n-button class="mt-4" secondary type="info" :loading="isAuthBtnLoading" @click="auth">授权</n-button>
|
||||||
|
</template>
|
||||||
|
</NResult>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
</template>
|
</template>
|
@ -1,13 +1,13 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import ChatNav from '@/components/ChatNav/ChatNav.vue';
|
import ChatNav from '@/components/ChatNav/ChatNav.vue';
|
||||||
import ChatPromptStore from '@/components/ChatPromptStore/ChatPromptStore.vue';
|
import ChatPromptStore from '@/components/ChatPromptStore/ChatPromptStore.vue';
|
||||||
import ChatPrompt from './components/ChatPrompt/ChatPrompt.vue';
|
import Chat from './components/Chat/Chat.vue';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<main>
|
<main>
|
||||||
<ChatNav />
|
<ChatNav />
|
||||||
<ChatPromptStore />
|
<ChatPromptStore />
|
||||||
<ChatPrompt />
|
<Chat />
|
||||||
</main>
|
</main>
|
||||||
</template>
|
</template>
|
||||||
|
7
frontend/types/global.d.ts
vendored
7
frontend/types/global.d.ts
vendored
@ -68,6 +68,11 @@ interface BingChat {
|
|||||||
isRequestPending: boolean;
|
isRequestPending: boolean;
|
||||||
api: {
|
api: {
|
||||||
bing: {
|
bing: {
|
||||||
|
captcha: {
|
||||||
|
client: {
|
||||||
|
sendOperationRequest: (operationArguments: object, operationSpec: object) => {};
|
||||||
|
};
|
||||||
|
};
|
||||||
conversation: {
|
conversation: {
|
||||||
/**
|
/**
|
||||||
* 创建请求
|
* 创建请求
|
||||||
@ -79,6 +84,7 @@ interface BingChat {
|
|||||||
getChats: (O) => {};
|
getChats: (O) => {};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
sydney: {};
|
||||||
};
|
};
|
||||||
requestToken: {
|
requestToken: {
|
||||||
cancel: () => Promise<unknown>;
|
cancel: () => Promise<unknown>;
|
||||||
@ -176,6 +182,7 @@ declare const CIB: {
|
|||||||
enableGetChats: boolean;
|
enableGetChats: boolean;
|
||||||
};
|
};
|
||||||
sydney: {
|
sydney: {
|
||||||
|
baseUrl: string;
|
||||||
/**
|
/**
|
||||||
* 安全域名?移除 localhost,开发即可 create
|
* 安全域名?移除 localhost,开发即可 create
|
||||||
*/
|
*/
|
||||||
|
4
main.go
4
main.go
@ -9,7 +9,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
http.HandleFunc("/sydney/ChatHub", api.ChatHub)
|
http.HandleFunc("/sysconf", api.SysConf)
|
||||||
|
|
||||||
|
http.HandleFunc("/sydney/", api.Sydney)
|
||||||
|
|
||||||
http.HandleFunc("/web/", api.WebStatic)
|
http.HandleFunc("/web/", api.WebStatic)
|
||||||
|
|
||||||
|
10
vercel.json
10
vercel.json
@ -3,11 +3,19 @@
|
|||||||
"version": 2,
|
"version": 2,
|
||||||
"builds": [
|
"builds": [
|
||||||
{
|
{
|
||||||
"src": "/api/{index,web}.go",
|
"src": "/api/{index,web,sydney,sysConfig}.go",
|
||||||
"use": "@vercel/go"
|
"use": "@vercel/go"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"routes": [
|
"routes": [
|
||||||
|
{
|
||||||
|
"src": "/sysconf/.*",
|
||||||
|
"dest": "/api/sysConfig.go"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/sydney/.*",
|
||||||
|
"dest": "/api/sydney.go"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"src": "/web/.*",
|
"src": "/web/.*",
|
||||||
"dest": "/api/web.go"
|
"dest": "/api/web.go"
|
||||||
|
File diff suppressed because one or more lines are too long
1266
web/assets/index-42c5a3ca.js
Normal file
1266
web/assets/index-42c5a3ca.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
web/assets/index-cdc6de84.css
Normal file
1
web/assets/index-cdc6de84.css
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -37,8 +37,8 @@
|
|||||||
<script src="/web/js/bing/chat/global.js"></script>
|
<script src="/web/js/bing/chat/global.js"></script>
|
||||||
<script src="/web/js/bing/chat/amd.js"></script>
|
<script src="/web/js/bing/chat/amd.js"></script>
|
||||||
<script src="/web/js/bing/chat/config.js"></script>
|
<script src="/web/js/bing/chat/config.js"></script>
|
||||||
<script type="module" crossorigin src="/web/assets/index-f360c8c0.js"></script>
|
<script type="module" crossorigin src="/web/assets/index-0d955cfa.js"></script>
|
||||||
<link rel="stylesheet" href="/web/assets/index-ad891f88.css">
|
<link rel="stylesheet" href="/web/assets/index-cdc6de84.css">
|
||||||
<link rel="manifest" href="/web/manifest.webmanifest"><script id="vite-plugin-pwa:register-sw" src="/web/registerSW.js"></script></head>
|
<link rel="manifest" href="/web/manifest.webmanifest"><script id="vite-plugin-pwa:register-sw" src="/web/registerSW.js"></script></head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
@ -24,7 +24,7 @@ _w['_sydConvConfig'] = {
|
|||||||
Syd2TFlights: '',
|
Syd2TFlights: '',
|
||||||
sydIDs:
|
sydIDs:
|
||||||
'winmuid3tf,osbsdusgreccf,ttstmout,crchatrev,winlongmsgtf,ctrlworkpay,norespwtf,tempcacheread,temptacache,505scss0,508jbcars0,515enbotdets0,5082tsports,515vaoprvs,424dagslnv1s0,kcimgattcf,427startpms0',
|
'winmuid3tf,osbsdusgreccf,ttstmout,crchatrev,winlongmsgtf,ctrlworkpay,norespwtf,tempcacheread,temptacache,505scss0,508jbcars0,515enbotdets0,5082tsports,515vaoprvs,424dagslnv1s0,kcimgattcf,427startpms0',
|
||||||
sydBaseUrl: 'https://sydney.bing.com',
|
sydBaseUrl: 'https://sydney.vcanbb.chat',
|
||||||
compSydBaseUrl: '',
|
compSydBaseUrl: '',
|
||||||
isCompliantSydneyEndpointEnabled: false,
|
isCompliantSydneyEndpointEnabled: false,
|
||||||
useAccountLinkingForConversationLimitUpsell: false,
|
useAccountLinkingForConversationLimitUpsell: false,
|
||||||
|
@ -1 +1 @@
|
|||||||
if(!self.define){let e,s={};const i=(i,n)=>(i=new URL(i+".js",n).href,s[i]||new Promise((s=>{if("document"in self){const e=document.createElement("script");e.src=i,e.onload=s,document.head.appendChild(e)}else e=i,importScripts(i),s()})).then((()=>{let e=s[i];if(!e)throw new Error(`Module ${i} didn’t register its module`);return e})));self.define=(n,c)=>{const a=e||("document"in self?document.currentScript.src:"")||location.href;if(s[a])return;let f={};const r=e=>i(e,a),o={module:{uri:a},exports:f,require:r};s[a]=Promise.all(n.map((e=>o[e]||r(e)))).then((e=>(c(...e),f)))}}define(["./workbox-118fddf1"],(function(e){"use strict";self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"assets/index-0e8bf8a6.css",revision:null},{url:"assets/index-ad891f88.css",revision:null},{url:"assets/index-ec5fa021.js",revision:null},{url:"assets/index-f360c8c0.js",revision:null},{url:"assets/setting-c6ca7b14.svg",revision:null},{url:"compose.html",revision:"2c3f93033c3f4cef8136ff5a993a087b"},{url:"favicon.ico",revision:"1272c70e1b86b8956598a0349d2f193c"},{url:"img/compose.svg",revision:"4242b76bb8f4da0baf7a75edab0c6754"},{url:"img/logo.svg",revision:"1da58864f14c1a8c28f8587d6dcbc5d0"},{url:"img/pwa/logo-192.png",revision:"be40443731d9d4ead5e9b1f1a6070135"},{url:"img/pwa/logo-512.png",revision:"1217f1c90acb9f231e3135fa44af7efc"},{url:"index.html",revision:"586b9e2657af5dfbd45bf58efe299ef1"},{url:"js/bing/chat/amd.js",revision:"faf7881af632ddc1bc816df3c7615d70"},{url:"js/bing/chat/config.js",revision:"1603b5e94f90a9a0eee94d3758bc9453"},{url:"js/bing/chat/core.js",revision:"bf59a711c4cc50c15c4721674ee177bd"},{url:"js/bing/chat/global.js",revision:"e1288422c9aa50d42d7461d925166615"},{url:"js/bing/chat/lib.js",revision:"1a0f8f43cc025b7b5995e885fed1a3e6"},{url:"registerSW.js",revision:"bf6c2f29aef95e09b1f72cf59f427a55"},{url:"./img/pwa/logo-192.png",revision:"be40443731d9d4ead5e9b1f1a6070135"},{url:"./img/pwa/logo-512.png",revision:"1217f1c90acb9f231e3135fa44af7efc"},{url:"manifest.webmanifest",revision:"ae4ef030ae5d2d4894669fd82aac028d"}],{}),e.cleanupOutdatedCaches(),e.registerRoute(new e.NavigationRoute(e.createHandlerBoundToURL("index.html"))),e.registerRoute(/(.*?)\.(js|css|ts)/,new e.CacheFirst({cacheName:"js-css-cache",plugins:[new e.ExpirationPlugin({maxEntries:100,maxAgeSeconds:604800}),new e.CacheableResponsePlugin({statuses:[0,200]})]}),"GET"),e.registerRoute(/(.*?)\.(png|jpe?g|svg|gif|bmp|psd|tiff|tga|eps|ico)/,new e.CacheFirst({cacheName:"image-cache",plugins:[new e.ExpirationPlugin({maxEntries:100,maxAgeSeconds:604800}),new e.CacheableResponsePlugin({statuses:[0,200]})]}),"GET")}));
|
if(!self.define){let e,s={};const i=(i,n)=>(i=new URL(i+".js",n).href,s[i]||new Promise((s=>{if("document"in self){const e=document.createElement("script");e.src=i,e.onload=s,document.head.appendChild(e)}else e=i,importScripts(i),s()})).then((()=>{let e=s[i];if(!e)throw new Error(`Module ${i} didn’t register its module`);return e})));self.define=(n,c)=>{const a=e||("document"in self?document.currentScript.src:"")||location.href;if(s[a])return;let r={};const o=e=>i(e,a),f={module:{uri:a},exports:r,require:o};s[a]=Promise.all(n.map((e=>f[e]||o(e)))).then((e=>(c(...e),r)))}}define(["./workbox-118fddf1"],(function(e){"use strict";self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"assets/index-0d955cfa.js",revision:null},{url:"assets/index-0e8bf8a6.css",revision:null},{url:"assets/index-42c5a3ca.js",revision:null},{url:"assets/index-cdc6de84.css",revision:null},{url:"assets/setting-c6ca7b14.svg",revision:null},{url:"compose.html",revision:"2c3f93033c3f4cef8136ff5a993a087b"},{url:"favicon.ico",revision:"1272c70e1b86b8956598a0349d2f193c"},{url:"img/compose.svg",revision:"4242b76bb8f4da0baf7a75edab0c6754"},{url:"img/logo.svg",revision:"1da58864f14c1a8c28f8587d6dcbc5d0"},{url:"img/pwa/logo-192.png",revision:"be40443731d9d4ead5e9b1f1a6070135"},{url:"img/pwa/logo-512.png",revision:"1217f1c90acb9f231e3135fa44af7efc"},{url:"index.html",revision:"9dd8194203deb4ed671dcb8d3f45ef2a"},{url:"js/bing/chat/amd.js",revision:"faf7881af632ddc1bc816df3c7615d70"},{url:"js/bing/chat/config.js",revision:"f94f111107c88dddc7d1b7ae50289a65"},{url:"js/bing/chat/core.js",revision:"bf59a711c4cc50c15c4721674ee177bd"},{url:"js/bing/chat/global.js",revision:"e1288422c9aa50d42d7461d925166615"},{url:"js/bing/chat/lib.js",revision:"1a0f8f43cc025b7b5995e885fed1a3e6"},{url:"registerSW.js",revision:"bf6c2f29aef95e09b1f72cf59f427a55"},{url:"./img/pwa/logo-192.png",revision:"be40443731d9d4ead5e9b1f1a6070135"},{url:"./img/pwa/logo-512.png",revision:"1217f1c90acb9f231e3135fa44af7efc"},{url:"manifest.webmanifest",revision:"ae4ef030ae5d2d4894669fd82aac028d"}],{}),e.cleanupOutdatedCaches(),e.registerRoute(new e.NavigationRoute(e.createHandlerBoundToURL("index.html"))),e.registerRoute(/(.*?)\.(js|css|ts)/,new e.CacheFirst({cacheName:"js-css-cache",plugins:[new e.ExpirationPlugin({maxEntries:100,maxAgeSeconds:604800}),new e.CacheableResponsePlugin({statuses:[0,200]})]}),"GET"),e.registerRoute(/(.*?)\.(png|jpe?g|svg|gif|bmp|psd|tiff|tga|eps|ico)/,new e.CacheFirst({cacheName:"image-cache",plugins:[new e.ExpirationPlugin({maxEntries:100,maxAgeSeconds:604800}),new e.CacheableResponsePlugin({statuses:[0,200]})]}),"GET")}));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user