Spaces:
Sleeping
Sleeping
Upload 10 files
Browse files- .env.example +1 -0
- .gitignore +2 -0
- Dockerfile +9 -0
- Makefile +45 -0
- assets.go +28 -0
- build.sh +28 -0
- go.mod +39 -0
- go.sum +99 -0
- html/auth.tmpl +110 -0
- main.go +587 -0
.env.example
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
PORT=8081
|
.gitignore
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
1 |
+
copilot2gpt
|
2 |
+
.env
|
Dockerfile
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FROM alpine:latest
|
2 |
+
|
3 |
+
WORKDIR /app
|
4 |
+
|
5 |
+
COPY ./dist/copilot2gpt-linux-386-v0.6.1.tar.gz .
|
6 |
+
|
7 |
+
RUN tar -xf copilot2gpt-linux-386-v0.6.1.tar.gz
|
8 |
+
|
9 |
+
CMD ["./copilot2gpt"]
|
Makefile
ADDED
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Binary name
|
2 |
+
BINARY=copilot2gpt
|
3 |
+
# 构建目录
|
4 |
+
DIST_DIR := ./dist
|
5 |
+
# Builds the project
|
6 |
+
build:
|
7 |
+
GO111MODULE=on go build -o ${BINARY} -ldflags "-X main.Version=${VERSION}"
|
8 |
+
# GO111MODULE=on go test -v
|
9 |
+
# Installs our project: copies binaries
|
10 |
+
install:
|
11 |
+
GO111MODULE=on go install
|
12 |
+
release:
|
13 |
+
# Clean
|
14 |
+
go clean
|
15 |
+
rm -rf ${DIST_DIR}/*.gz
|
16 |
+
# Build for mac
|
17 |
+
GO111MODULE=on go build -ldflags "-s -w -X main.Version=${VERSION}"
|
18 |
+
tar czvf ${DIST_DIR}/${BINARY}-mac64-${VERSION}.tar.gz ./${BINARY} .env.example
|
19 |
+
# Build for arm
|
20 |
+
go clean
|
21 |
+
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 GO111MODULE=on go build -ldflags "-s -w -X main.Version=${VERSION}"
|
22 |
+
tar czvf ${DIST_DIR}/${BINARY}-linux-arm64-${VERSION}.tar.gz ./${BINARY} .env.example
|
23 |
+
# Build for linux386
|
24 |
+
go clean
|
25 |
+
CGO_ENABLED=0 GOOS=linux GOARCH=386 GO111MODULE=on go build -ldflags "-s -w -X main.Version=${VERSION}"
|
26 |
+
tar czvf ${DIST_DIR}/${BINARY}-linux-386-${VERSION}.tar.gz ./${BINARY} .env.example
|
27 |
+
# Build for linux
|
28 |
+
go clean
|
29 |
+
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -ldflags "-s -w -X main.Version=${VERSION}"
|
30 |
+
tar czvf ${DIST_DIR}/${BINARY}-linux-64-${VERSION}.tar.gz ./${BINARY} .env.example
|
31 |
+
# Build for win386
|
32 |
+
go clean
|
33 |
+
CGO_ENABLED=0 GOOS=windows GOARCH=386 GO111MODULE=on go build -ldflags "-s -w -X main.Version=${VERSION}"
|
34 |
+
tar czvf ${DIST_DIR}/${BINARY}-win-386-${VERSION}.tar.gz ./${BINARY}.exe .env.example
|
35 |
+
# Build for win
|
36 |
+
go clean
|
37 |
+
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 GO111MODULE=on go build -ldflags "-s -w -X main.Version=${VERSION}"
|
38 |
+
tar czvf ${DIST_DIR}/${BINARY}-win-64-${VERSION}.tar.gz ./${BINARY}.exe .env.example
|
39 |
+
go clean
|
40 |
+
# Cleans our projects: deletes binaries
|
41 |
+
clean:
|
42 |
+
go clean
|
43 |
+
rm -rf ${DIST_DIR}/*.gz
|
44 |
+
|
45 |
+
.PHONY: clean build
|
assets.go
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package main
|
2 |
+
|
3 |
+
import (
|
4 |
+
"time"
|
5 |
+
|
6 |
+
"github.com/jessevdk/go-assets"
|
7 |
+
)
|
8 |
+
|
9 |
+
var _Assetsf86c9bbb3d0fc4401249716089ed36f50263323a = "<!doctype html>\n<html>\n<h1>\n {{ .title }}\n</h1>\n打开链接 <a href=\"https://github.com/login/device\" target=\"__blank\">https://github.com/login/device</a>\n<!-- <button onclick=\"copyToClipboard()\">复制</button> -->\n<p>输入:<input type=\"text\" value=\"{{ .userCode }}\" disabled=\"disabled\" id=\"code\" size=\"10\"></p>\n<p>获取有延迟,填写完毕请勿刷新页面</p>\n<p>ghu: <input type=\"text\" value=\"获取中\" id=\"ghu\" size=\"50\">   <button onclick=\"checkGhu(this)\">检测</button></p>\n<script>\n var deviceCode = \"{{ .deviceCode }}\"\n\n let intervalId = null;\n let count = 0;\n\n // 每5秒执行一次的函数\n function polling() {\n count++;\n console.log('Polling: ' + count);\n var xhr = new XMLHttpRequest();\n xhr.open(\"POST\", \"/auth/check\", true);\n var formData = new FormData();\n formData.append(\"deviceCode\", deviceCode);\n\n xhr.onreadystatechange = function () {\n if (xhr.status >= 200 && xhr.status < 300) {\n if (xhr.responseText.length > 0) {\n try {\n var data = JSON.parse(xhr.responseText);\n // 现在你可以使用解析后的数据了\n if (data.code == \"0\") {\n stopPolling();\n var inputElement = document.getElementById('ghu');\n inputElement.value = data.data; // 将值更改为你想要的任何字符串\n }\n } catch (e) {\n console.error(\"解析JSON数据时出错: \", e);\n }\n }\n } else {\n console.error(\"请求失败,HTTP状态码: \", xhr.status);\n }\n };\n xhr.send(formData);\n }\n\n // 15分钟后停止轮询的函数\n function stopPolling() {\n clearInterval(intervalId);\n console.log('Polling stopped');\n }\n\n // 开始轮询\n intervalId = setInterval(polling, 6 * 1000); // 5秒\n\n // 15分钟后停止轮询\n setTimeout(stopPolling, 15 * 60 * 1000); // 15分钟\n\n function copyToClipboard() {\n let text = document.getElementById('code').innerHTML;\n const copyContent = async () => {\n try {\n await navigator.clipboard.writeText(text);\n console.log('Content copied to clipboard');\n } catch (err) {\n console.error('Failed to copy: ', err);\n }\n }\n }\n\n function checkGhu(btn) {\n btn.disabled = true;\n\n var ghu = document.getElementById('ghu').value;\n var xhr = new XMLHttpRequest();\n xhr.open(\"POST\", \"/auth/checkGhu\", true);\n var formData = new FormData();\n formData.append(\"ghu\", ghu);\n\n xhr.onreadystatechange = function () {\n // 请求结束,无论成功还是失败,都重新启用按钮\n if (xhr.readyState == 4) {\n btn.disabled = false;\n if (xhr.status >= 200 && xhr.status < 300) {\n if (xhr.responseText.length > 0) {\n try {\n var data = JSON.parse(xhr.responseText);\n // 现在你可以使用解析后的数据了\n if (data.code == \"0\") {\n alert(data.data);\n } else {\n alert(data.msg);\n }\n } catch (e) {\n console.error(\"解析JSON数据时出错: \", e);\n }\n }\n } else {\n console.error(\"请求失败,HTTP状态码: \", xhr.status);\n }\n }\n\n };\n xhr.send(formData);\n }\n\n</script>\n\n</html>"
|
10 |
+
|
11 |
+
// Assets returns go-assets FileSystem
|
12 |
+
var Assets = assets.NewFileSystem(map[string][]string{"/": []string{"html"}, "/html": []string{"auth.tmpl"}}, map[string]*assets.File{
|
13 |
+
"/": &assets.File{
|
14 |
+
Path: "/",
|
15 |
+
FileMode: 0x800001ed,
|
16 |
+
Mtime: time.Unix(1706450942, 1706450942218803764),
|
17 |
+
Data: nil,
|
18 |
+
}, "/html": &assets.File{
|
19 |
+
Path: "/html",
|
20 |
+
FileMode: 0x800001ed,
|
21 |
+
Mtime: time.Unix(1706329545, 1706329545851955289),
|
22 |
+
Data: nil,
|
23 |
+
}, "/html/auth.tmpl": &assets.File{
|
24 |
+
Path: "/html/auth.tmpl",
|
25 |
+
FileMode: 0x1a4,
|
26 |
+
Mtime: time.Unix(1706451252, 1706451252248792005),
|
27 |
+
Data: []byte(_Assetsf86c9bbb3d0fc4401249716089ed36f50263323a),
|
28 |
+
}}, "")
|
build.sh
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/bin/bash
|
2 |
+
source .env
|
3 |
+
if [ -z "$VERSION" ]
|
4 |
+
then
|
5 |
+
echo "VERSION is empty"
|
6 |
+
exit 1
|
7 |
+
else
|
8 |
+
echo "VERSION is $VERSION"
|
9 |
+
fi
|
10 |
+
|
11 |
+
|
12 |
+
# Run make release with the specified version
|
13 |
+
make release VERSION="$VERSION"
|
14 |
+
|
15 |
+
# Build the docker image
|
16 |
+
sudo docker build -t copilot2gpt .
|
17 |
+
|
18 |
+
# Tag the docker image
|
19 |
+
sudo docker tag copilot2gpt ersichub/copilot2gpt:"$VERSION"
|
20 |
+
|
21 |
+
# Push the docker image to Docker Hub
|
22 |
+
sudo docker push ersichub/copilot2gpt:"$VERSION"
|
23 |
+
|
24 |
+
# Tag the docker image
|
25 |
+
sudo docker tag copilot2gpt ersichub/copilot2gpt:latest
|
26 |
+
|
27 |
+
# Push the docker image to Docker Hub
|
28 |
+
sudo docker push ersichub/copilot2gpt:latest
|
go.mod
ADDED
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
module copilot2gpt
|
2 |
+
|
3 |
+
go 1.21.0
|
4 |
+
|
5 |
+
require (
|
6 |
+
github.com/bytedance/sonic v1.10.2 // indirect
|
7 |
+
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
|
8 |
+
github.com/chenzhuoyu/iasm v0.9.1 // indirect
|
9 |
+
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
10 |
+
github.com/gin-contrib/sse v0.1.0 // indirect
|
11 |
+
github.com/gin-gonic/gin v1.9.1 // indirect
|
12 |
+
github.com/go-playground/locales v0.14.1 // indirect
|
13 |
+
github.com/go-playground/universal-translator v0.18.1 // indirect
|
14 |
+
github.com/go-playground/validator/v10 v10.16.0 // indirect
|
15 |
+
github.com/goccy/go-json v0.10.2 // indirect
|
16 |
+
github.com/google/uuid v1.5.0 // indirect
|
17 |
+
github.com/jessevdk/go-assets v0.0.0-20160921144138-4f4301a06e15 // indirect
|
18 |
+
github.com/joho/godotenv v1.5.1 // indirect
|
19 |
+
github.com/json-iterator/go v1.1.12 // indirect
|
20 |
+
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
|
21 |
+
github.com/leodido/go-urn v1.2.4 // indirect
|
22 |
+
github.com/mattn/go-isatty v0.0.20 // indirect
|
23 |
+
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
24 |
+
github.com/modern-go/reflect2 v1.0.2 // indirect
|
25 |
+
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
|
26 |
+
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
|
27 |
+
github.com/tidwall/gjson v1.17.0 // indirect
|
28 |
+
github.com/tidwall/match v1.1.1 // indirect
|
29 |
+
github.com/tidwall/pretty v1.2.0 // indirect
|
30 |
+
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
31 |
+
github.com/ugorji/go/codec v1.2.12 // indirect
|
32 |
+
golang.org/x/arch v0.6.0 // indirect
|
33 |
+
golang.org/x/crypto v0.16.0 // indirect
|
34 |
+
golang.org/x/net v0.19.0 // indirect
|
35 |
+
golang.org/x/sys v0.15.0 // indirect
|
36 |
+
golang.org/x/text v0.14.0 // indirect
|
37 |
+
google.golang.org/protobuf v1.31.0 // indirect
|
38 |
+
gopkg.in/yaml.v3 v3.0.1 // indirect
|
39 |
+
)
|
go.sum
ADDED
@@ -0,0 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
2 |
+
github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
|
3 |
+
github.com/bytedance/sonic v1.10.2 h1:GQebETVBxYB7JGWJtLBi07OVzWwt+8dWA00gEVW2ZFE=
|
4 |
+
github.com/bytedance/sonic v1.10.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
|
5 |
+
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
6 |
+
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
7 |
+
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0=
|
8 |
+
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA=
|
9 |
+
github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
|
10 |
+
github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0=
|
11 |
+
github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
|
12 |
+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
13 |
+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
14 |
+
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
15 |
+
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
16 |
+
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
17 |
+
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
18 |
+
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
|
19 |
+
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
|
20 |
+
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
21 |
+
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
22 |
+
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
23 |
+
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
24 |
+
github.com/go-playground/validator/v10 v10.16.0 h1:x+plE831WK4vaKHO/jpgUGsvLKIqRRkz6M78GuJAfGE=
|
25 |
+
github.com/go-playground/validator/v10 v10.16.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
26 |
+
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
27 |
+
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
28 |
+
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
29 |
+
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
30 |
+
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
31 |
+
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
|
32 |
+
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
33 |
+
github.com/jessevdk/go-assets v0.0.0-20160921144138-4f4301a06e15 h1:cW/amwGEJK5MSKntPXRjX4dxs/nGxGT8gXKIsKFmHGc=
|
34 |
+
github.com/jessevdk/go-assets v0.0.0-20160921144138-4f4301a06e15/go.mod h1:Fdm/oWRW+CH8PRbLntksCNtmcCBximKPkVQYvmMl80k=
|
35 |
+
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
36 |
+
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
37 |
+
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
38 |
+
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
39 |
+
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
40 |
+
github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
|
41 |
+
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
42 |
+
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
43 |
+
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
|
44 |
+
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
45 |
+
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
46 |
+
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
47 |
+
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
48 |
+
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
49 |
+
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
50 |
+
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
51 |
+
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
52 |
+
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
53 |
+
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
54 |
+
github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
|
55 |
+
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
56 |
+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
57 |
+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
58 |
+
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
59 |
+
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
60 |
+
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
61 |
+
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
62 |
+
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
63 |
+
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
64 |
+
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
65 |
+
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
66 |
+
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
67 |
+
github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM=
|
68 |
+
github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
69 |
+
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
70 |
+
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
71 |
+
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
72 |
+
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
73 |
+
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
74 |
+
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
75 |
+
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
76 |
+
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
77 |
+
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
78 |
+
golang.org/x/arch v0.6.0 h1:S0JTfE48HbRj80+4tbvZDYsJ3tGv6BUU3XxyZ7CirAc=
|
79 |
+
golang.org/x/arch v0.6.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
80 |
+
golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
|
81 |
+
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
82 |
+
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
|
83 |
+
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
|
84 |
+
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
85 |
+
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
86 |
+
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
87 |
+
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
88 |
+
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
89 |
+
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
90 |
+
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
91 |
+
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
92 |
+
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
93 |
+
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
94 |
+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
95 |
+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
96 |
+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
97 |
+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
98 |
+
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
99 |
+
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
html/auth.tmpl
ADDED
@@ -0,0 +1,110 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!doctype html>
|
2 |
+
<html>
|
3 |
+
<h1>
|
4 |
+
{{ .title }}
|
5 |
+
</h1>
|
6 |
+
打开链接 <a href="https://github.com/login/device" target="__blank">https://github.com/login/device</a>
|
7 |
+
<!-- <button onclick="copyToClipboard()">复制</button> -->
|
8 |
+
<p>输入:<input type="text" value="{{ .userCode }}" disabled="disabled" id="code" size="10"></p>
|
9 |
+
<p>获取有延迟,填写完毕请勿刷新页面</p>
|
10 |
+
<p>ghu: <input type="text" value="获取中" id="ghu" size="50">   <button onclick="checkGhu(this)">检测</button></p>
|
11 |
+
<script>
|
12 |
+
var deviceCode = "{{ .deviceCode }}"
|
13 |
+
|
14 |
+
let intervalId = null;
|
15 |
+
let count = 0;
|
16 |
+
|
17 |
+
// 每5秒执行一次的函数
|
18 |
+
function polling() {
|
19 |
+
count++;
|
20 |
+
console.log('Polling: ' + count);
|
21 |
+
var xhr = new XMLHttpRequest();
|
22 |
+
xhr.open("POST", "/auth/check", true);
|
23 |
+
var formData = new FormData();
|
24 |
+
formData.append("deviceCode", deviceCode);
|
25 |
+
|
26 |
+
xhr.onreadystatechange = function () {
|
27 |
+
if (xhr.status >= 200 && xhr.status < 300) {
|
28 |
+
if (xhr.responseText.length > 0) {
|
29 |
+
try {
|
30 |
+
var data = JSON.parse(xhr.responseText);
|
31 |
+
// 现在你可以使用解析后的数据了
|
32 |
+
if (data.code == "0") {
|
33 |
+
stopPolling();
|
34 |
+
var inputElement = document.getElementById('ghu');
|
35 |
+
inputElement.value = data.data; // 将值更改为你想要的任何字符串
|
36 |
+
}
|
37 |
+
} catch (e) {
|
38 |
+
console.error("解析JSON数据时出错: ", e);
|
39 |
+
}
|
40 |
+
}
|
41 |
+
} else {
|
42 |
+
console.error("请求失败,HTTP状态码: ", xhr.status);
|
43 |
+
}
|
44 |
+
};
|
45 |
+
xhr.send(formData);
|
46 |
+
}
|
47 |
+
|
48 |
+
// 15分钟后停止轮询的函数
|
49 |
+
function stopPolling() {
|
50 |
+
clearInterval(intervalId);
|
51 |
+
console.log('Polling stopped');
|
52 |
+
}
|
53 |
+
|
54 |
+
// 开始轮询
|
55 |
+
intervalId = setInterval(polling, 6 * 1000); // 5秒
|
56 |
+
|
57 |
+
// 15分钟后停止轮询
|
58 |
+
setTimeout(stopPolling, 15 * 60 * 1000); // 15分钟
|
59 |
+
|
60 |
+
function copyToClipboard() {
|
61 |
+
let text = document.getElementById('code').innerHTML;
|
62 |
+
const copyContent = async () => {
|
63 |
+
try {
|
64 |
+
await navigator.clipboard.writeText(text);
|
65 |
+
console.log('Content copied to clipboard');
|
66 |
+
} catch (err) {
|
67 |
+
console.error('Failed to copy: ', err);
|
68 |
+
}
|
69 |
+
}
|
70 |
+
}
|
71 |
+
|
72 |
+
function checkGhu(btn) {
|
73 |
+
btn.disabled = true;
|
74 |
+
|
75 |
+
var ghu = document.getElementById('ghu').value;
|
76 |
+
var xhr = new XMLHttpRequest();
|
77 |
+
xhr.open("POST", "/auth/checkGhu", true);
|
78 |
+
var formData = new FormData();
|
79 |
+
formData.append("ghu", ghu);
|
80 |
+
|
81 |
+
xhr.onreadystatechange = function () {
|
82 |
+
// 请求结束,无论成功还是失败,都重新启用按钮
|
83 |
+
if (xhr.readyState == 4) {
|
84 |
+
btn.disabled = false;
|
85 |
+
if (xhr.status >= 200 && xhr.status < 300) {
|
86 |
+
if (xhr.responseText.length > 0) {
|
87 |
+
try {
|
88 |
+
var data = JSON.parse(xhr.responseText);
|
89 |
+
// 现在你可以使用解析后的数据了
|
90 |
+
if (data.code == "0") {
|
91 |
+
alert(data.data);
|
92 |
+
} else {
|
93 |
+
alert(data.msg);
|
94 |
+
}
|
95 |
+
} catch (e) {
|
96 |
+
console.error("解析JSON数据时出错: ", e);
|
97 |
+
}
|
98 |
+
}
|
99 |
+
} else {
|
100 |
+
console.error("请求失败,HTTP状态码: ", xhr.status);
|
101 |
+
}
|
102 |
+
}
|
103 |
+
|
104 |
+
};
|
105 |
+
xhr.send(formData);
|
106 |
+
}
|
107 |
+
|
108 |
+
</script>
|
109 |
+
|
110 |
+
</html>
|
main.go
ADDED
@@ -0,0 +1,587 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
package main
|
2 |
+
|
3 |
+
import (
|
4 |
+
"bufio"
|
5 |
+
"bytes"
|
6 |
+
"compress/gzip"
|
7 |
+
"crypto/sha256"
|
8 |
+
"encoding/hex"
|
9 |
+
"encoding/json"
|
10 |
+
"fmt"
|
11 |
+
"html/template"
|
12 |
+
"io"
|
13 |
+
"log"
|
14 |
+
"net/http"
|
15 |
+
"net/url"
|
16 |
+
"os"
|
17 |
+
"strings"
|
18 |
+
"time"
|
19 |
+
|
20 |
+
"github.com/gin-gonic/gin"
|
21 |
+
"github.com/google/uuid"
|
22 |
+
"github.com/joho/godotenv"
|
23 |
+
"github.com/patrickmn/go-cache"
|
24 |
+
"github.com/tidwall/gjson"
|
25 |
+
)
|
26 |
+
|
27 |
+
const tokenUrl = "https://api.github.com/copilot_internal/v2/token"
|
28 |
+
const completionsUrl = "https://api.githubcopilot.com/chat/completions"
|
29 |
+
const embeddingsUrl = "https://api.githubcopilot.com/embeddings"
|
30 |
+
|
31 |
+
var requestUrl = ""
|
32 |
+
|
33 |
+
type Model struct {
|
34 |
+
ID string `json:"id"`
|
35 |
+
Object string `json:"object"`
|
36 |
+
Created int `json:"created"`
|
37 |
+
OwnedBy string `json:"owned_by"`
|
38 |
+
Root string `json:"root"`
|
39 |
+
Parent *string `json:"parent"`
|
40 |
+
}
|
41 |
+
|
42 |
+
type ModelList struct {
|
43 |
+
Object string `json:"object"`
|
44 |
+
Data []Model `json:"data"`
|
45 |
+
}
|
46 |
+
|
47 |
+
var version = "v0.6.1"
|
48 |
+
var port = "8081"
|
49 |
+
var client_id = "Iv1.b507a08c87ecfe98"
|
50 |
+
|
51 |
+
func main() {
|
52 |
+
err := godotenv.Load()
|
53 |
+
if err == nil {
|
54 |
+
// 从环境变量中获取配置值
|
55 |
+
portEnv := os.Getenv("PORT")
|
56 |
+
if portEnv != "" {
|
57 |
+
port = portEnv
|
58 |
+
version = os.Getenv("VERSION")
|
59 |
+
}
|
60 |
+
}
|
61 |
+
|
62 |
+
log.Printf("Server is running on port %s, version: %s\n", port, version)
|
63 |
+
log.Printf("client_id: %s\n", client_id)
|
64 |
+
|
65 |
+
gin.SetMode(gin.ReleaseMode)
|
66 |
+
|
67 |
+
r := gin.Default()
|
68 |
+
|
69 |
+
// CORS 中间件
|
70 |
+
r.Use(func(c *gin.Context) {
|
71 |
+
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
72 |
+
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
|
73 |
+
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")
|
74 |
+
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT")
|
75 |
+
|
76 |
+
if c.Request.Method == "OPTIONS" {
|
77 |
+
c.AbortWithStatus(204)
|
78 |
+
return
|
79 |
+
}
|
80 |
+
|
81 |
+
c.Next()
|
82 |
+
})
|
83 |
+
|
84 |
+
r.GET("/", func(c *gin.Context) {
|
85 |
+
c.String(http.StatusOK, `
|
86 |
+
curl --location 'http://127.0.0.1:8081/v1/chat/completions' \
|
87 |
+
--header 'Content-Type: application/json' \
|
88 |
+
--header 'Authorization: Bearer ghu_xxx' \
|
89 |
+
--data '{
|
90 |
+
"model": "gpt-4",
|
91 |
+
"messages": [{"role": "user", "content": "hi"}]
|
92 |
+
}'`)
|
93 |
+
})
|
94 |
+
|
95 |
+
r.GET("/v1/models", func(c *gin.Context) {
|
96 |
+
c.JSON(http.StatusOK, models())
|
97 |
+
})
|
98 |
+
|
99 |
+
r.POST("/v1/chat/completions", func(c *gin.Context) {
|
100 |
+
c.Header("Cache-Control", "no-cache, must-revalidate")
|
101 |
+
c.Header("Connection", "keep-alive")
|
102 |
+
|
103 |
+
requestUrl = completionsUrl
|
104 |
+
forwardRequest(c)
|
105 |
+
})
|
106 |
+
|
107 |
+
r.POST("/v1/embeddings", func(c *gin.Context) {
|
108 |
+
c.Header("Cache-Control", "no-cache, must-revalidate")
|
109 |
+
c.Header("Connection", "keep-alive")
|
110 |
+
|
111 |
+
requestUrl = embeddingsUrl
|
112 |
+
forwardRequest(c)
|
113 |
+
})
|
114 |
+
|
115 |
+
// 获取ghu
|
116 |
+
|
117 |
+
t, err := loadTemplate()
|
118 |
+
if err != nil {
|
119 |
+
panic(err)
|
120 |
+
}
|
121 |
+
r.SetHTMLTemplate(t)
|
122 |
+
|
123 |
+
r.GET("/auth", func(c *gin.Context) {
|
124 |
+
// 获取设备授权码
|
125 |
+
deviceCode, userCode, err := getDeviceCode()
|
126 |
+
if err != nil {
|
127 |
+
c.String(http.StatusOK, "获取设备码失败:"+err.Error())
|
128 |
+
return
|
129 |
+
}
|
130 |
+
|
131 |
+
// 使用 deviceCode 和 userCode
|
132 |
+
fmt.Println("Device Code: ", deviceCode)
|
133 |
+
fmt.Println("User Code: ", userCode)
|
134 |
+
|
135 |
+
c.HTML(http.StatusOK, "/html/auth.tmpl", gin.H{
|
136 |
+
"title": "Get Copilot Token",
|
137 |
+
"deviceCode": deviceCode,
|
138 |
+
"userCode": userCode,
|
139 |
+
})
|
140 |
+
})
|
141 |
+
|
142 |
+
r.POST("/auth/check", func(c *gin.Context) {
|
143 |
+
returnData := map[string]string{
|
144 |
+
"code": "1",
|
145 |
+
"msg": "",
|
146 |
+
"data": "",
|
147 |
+
}
|
148 |
+
|
149 |
+
deviceCode := c.PostForm("deviceCode")
|
150 |
+
if deviceCode == "" {
|
151 |
+
returnData["msg"] = "device code null"
|
152 |
+
c.JSON(http.StatusOK, returnData)
|
153 |
+
return
|
154 |
+
}
|
155 |
+
token, err := checkUserCode(deviceCode)
|
156 |
+
if err != nil {
|
157 |
+
returnData["msg"] = err.Error()
|
158 |
+
c.JSON(http.StatusOK, returnData)
|
159 |
+
return
|
160 |
+
}
|
161 |
+
if token == "" {
|
162 |
+
returnData["msg"] = "token null"
|
163 |
+
c.JSON(http.StatusOK, returnData)
|
164 |
+
return
|
165 |
+
}
|
166 |
+
returnData["code"] = "0"
|
167 |
+
returnData["msg"] = "success"
|
168 |
+
returnData["data"] = token
|
169 |
+
c.JSON(http.StatusOK, returnData)
|
170 |
+
return
|
171 |
+
})
|
172 |
+
|
173 |
+
r.POST("/auth/checkGhu", func(c *gin.Context) {
|
174 |
+
returnData := map[string]string{
|
175 |
+
"code": "1",
|
176 |
+
"msg": "",
|
177 |
+
"data": "",
|
178 |
+
}
|
179 |
+
|
180 |
+
ghu := c.PostForm("ghu")
|
181 |
+
if ghu == "" {
|
182 |
+
returnData["msg"] = "ghu null"
|
183 |
+
c.JSON(http.StatusOK, returnData)
|
184 |
+
return
|
185 |
+
}
|
186 |
+
if !strings.HasPrefix(ghu, "gh") {
|
187 |
+
returnData["msg"] = "ghu 格式错误"
|
188 |
+
c.JSON(http.StatusOK, returnData)
|
189 |
+
return
|
190 |
+
}
|
191 |
+
|
192 |
+
info := checkGhuToken(ghu)
|
193 |
+
|
194 |
+
returnData["code"] = "0"
|
195 |
+
returnData["msg"] = "success"
|
196 |
+
returnData["data"] = info
|
197 |
+
c.JSON(http.StatusOK, returnData)
|
198 |
+
return
|
199 |
+
})
|
200 |
+
|
201 |
+
r.Run(":" + port)
|
202 |
+
}
|
203 |
+
|
204 |
+
func forwardRequest(c *gin.Context) {
|
205 |
+
var jsonBody map[string]interface{}
|
206 |
+
if err := c.ShouldBindJSON(&jsonBody); err != nil {
|
207 |
+
c.JSON(http.StatusBadRequest, gin.H{"error": "Request body is missing or not in JSON format"})
|
208 |
+
return
|
209 |
+
}
|
210 |
+
|
211 |
+
ghuToken := strings.Split(c.GetHeader("Authorization"), " ")[1]
|
212 |
+
|
213 |
+
if !strings.HasPrefix(ghuToken, "gh") {
|
214 |
+
c.JSON(http.StatusBadRequest, gin.H{"error": "auth token not found"})
|
215 |
+
log.Printf("token 格式错误:%s\n", ghuToken)
|
216 |
+
return
|
217 |
+
}
|
218 |
+
|
219 |
+
// 检查 token 是否有效
|
220 |
+
if !checkToken(ghuToken) {
|
221 |
+
c.JSON(http.StatusBadRequest, gin.H{"error": "auth token is invalid"})
|
222 |
+
log.Printf("token 无效:%s\n", ghuToken)
|
223 |
+
return
|
224 |
+
}
|
225 |
+
accToken, err := getAccToken(ghuToken)
|
226 |
+
if accToken == "" {
|
227 |
+
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
228 |
+
return
|
229 |
+
}
|
230 |
+
|
231 |
+
sessionId := fmt.Sprintf("%s%d", uuid.New().String(), time.Now().UnixNano()/int64(time.Millisecond))
|
232 |
+
machineID := sha256.Sum256([]byte(uuid.New().String()))
|
233 |
+
machineIDStr := hex.EncodeToString(machineID[:])
|
234 |
+
accHeaders := getAccHeaders(accToken, uuid.New().String(), sessionId, machineIDStr)
|
235 |
+
client := &http.Client{}
|
236 |
+
|
237 |
+
jsonData, err := json.Marshal(jsonBody)
|
238 |
+
if err != nil {
|
239 |
+
c.AbortWithError(http.StatusInternalServerError, err)
|
240 |
+
return
|
241 |
+
}
|
242 |
+
|
243 |
+
isStream := gjson.GetBytes(jsonData, "stream").String() == "true"
|
244 |
+
|
245 |
+
req, err := http.NewRequest("POST", requestUrl, bytes.NewBuffer(jsonData))
|
246 |
+
if err != nil {
|
247 |
+
c.AbortWithError(http.StatusInternalServerError, err)
|
248 |
+
}
|
249 |
+
|
250 |
+
for key, value := range accHeaders {
|
251 |
+
req.Header.Add(key, value)
|
252 |
+
}
|
253 |
+
|
254 |
+
resp, err := client.Do(req)
|
255 |
+
if err != nil {
|
256 |
+
return
|
257 |
+
}
|
258 |
+
defer resp.Body.Close()
|
259 |
+
|
260 |
+
if resp.StatusCode != http.StatusOK {
|
261 |
+
bodyBytes, err := io.ReadAll(resp.Body)
|
262 |
+
if err != nil {
|
263 |
+
log.Fatal(err)
|
264 |
+
}
|
265 |
+
bodyString := string(bodyBytes)
|
266 |
+
log.Printf("对话失败:%d, %s ", resp.StatusCode, bodyString)
|
267 |
+
cache := cache.New(5*time.Minute, 10*time.Minute)
|
268 |
+
cache.Delete(ghuToken)
|
269 |
+
c.AbortWithError(resp.StatusCode, fmt.Errorf(bodyString))
|
270 |
+
return
|
271 |
+
}
|
272 |
+
|
273 |
+
c.Header("Content-Type", "application/json; charset=utf-8")
|
274 |
+
|
275 |
+
if isStream {
|
276 |
+
returnStream(c, resp)
|
277 |
+
} else {
|
278 |
+
returnJson(c, resp)
|
279 |
+
}
|
280 |
+
return
|
281 |
+
}
|
282 |
+
|
283 |
+
func returnJson(c *gin.Context, resp *http.Response) {
|
284 |
+
c.Header("Content-Type", "application/json; charset=utf-8")
|
285 |
+
|
286 |
+
body, err := io.ReadAll(resp.Body.(io.Reader))
|
287 |
+
if err != nil {
|
288 |
+
c.AbortWithError(http.StatusInternalServerError, err)
|
289 |
+
return
|
290 |
+
}
|
291 |
+
|
292 |
+
c.Writer.Write(body)
|
293 |
+
return
|
294 |
+
}
|
295 |
+
|
296 |
+
func returnStream(c *gin.Context, resp *http.Response) {
|
297 |
+
c.Header("Content-Type", "text/event-stream; charset=utf-8")
|
298 |
+
|
299 |
+
// 创建一个新的扫描器
|
300 |
+
scanner := bufio.NewScanner(resp.Body)
|
301 |
+
|
302 |
+
// 使用Scan方法来读取流
|
303 |
+
for scanner.Scan() {
|
304 |
+
line := scanner.Bytes()
|
305 |
+
|
306 |
+
// 替换 "content":null 为 "content":""
|
307 |
+
modifiedLine := bytes.Replace(line, []byte(`"content":null`), []byte(`"content":""`), -1)
|
308 |
+
|
309 |
+
// 将修改后的数据写入响应体
|
310 |
+
if _, err := c.Writer.Write(modifiedLine); err != nil {
|
311 |
+
c.AbortWithError(http.StatusInternalServerError, err)
|
312 |
+
return
|
313 |
+
}
|
314 |
+
|
315 |
+
// 添加一个换行符
|
316 |
+
if _, err := c.Writer.Write([]byte("\n")); err != nil {
|
317 |
+
c.AbortWithError(http.StatusInternalServerError, err)
|
318 |
+
return
|
319 |
+
}
|
320 |
+
}
|
321 |
+
|
322 |
+
if scanner.Err() != nil {
|
323 |
+
// 处理来自扫描器的任何错误
|
324 |
+
c.AbortWithError(http.StatusInternalServerError, scanner.Err())
|
325 |
+
return
|
326 |
+
}
|
327 |
+
return
|
328 |
+
}
|
329 |
+
|
330 |
+
func models() ModelList {
|
331 |
+
jsonStr := `{
|
332 |
+
"object": "list",
|
333 |
+
"data": [
|
334 |
+
{"id": "text-search-babbage-doc-001","object": "model","created": 1651172509,"owned_by": "openai-dev"},
|
335 |
+
{"id": "gpt-4-0613","object": "model","created": 1686588896,"owned_by": "openai"},
|
336 |
+
{"id": "gpt-4", "object": "model", "created": 1687882411, "owned_by": "openai"},
|
337 |
+
{"id": "babbage", "object": "model", "created": 1649358449, "owned_by": "openai"},
|
338 |
+
{"id": "gpt-3.5-turbo-0613", "object": "model", "created": 1686587434, "owned_by": "openai"},
|
339 |
+
{"id": "text-babbage-001", "object": "model", "created": 1649364043, "owned_by": "openai"},
|
340 |
+
{"id": "gpt-3.5-turbo", "object": "model", "created": 1677610602, "owned_by": "openai"},
|
341 |
+
{"id": "gpt-3.5-turbo-1106", "object": "model", "created": 1698959748, "owned_by": "system"},
|
342 |
+
{"id": "curie-instruct-beta", "object": "model", "created": 1649364042, "owned_by": "openai"},
|
343 |
+
{"id": "gpt-3.5-turbo-0301", "object": "model", "created": 1677649963, "owned_by": "openai"},
|
344 |
+
{"id": "gpt-3.5-turbo-16k-0613", "object": "model", "created": 1685474247, "owned_by": "openai"},
|
345 |
+
{"id": "text-embedding-ada-002", "object": "model", "created": 1671217299, "owned_by": "openai-internal"},
|
346 |
+
{"id": "davinci-similarity", "object": "model", "created": 1651172509, "owned_by": "openai-dev"},
|
347 |
+
{"id": "curie-similarity", "object": "model", "created": 1651172510, "owned_by": "openai-dev"},
|
348 |
+
{"id": "babbage-search-document", "object": "model", "created": 1651172510, "owned_by": "openai-dev"},
|
349 |
+
{"id": "curie-search-document", "object": "model", "created": 1651172508, "owned_by": "openai-dev"},
|
350 |
+
{"id": "babbage-code-search-code", "object": "model", "created": 1651172509, "owned_by": "openai-dev"},
|
351 |
+
{"id": "ada-code-search-text", "object": "model", "created": 1651172510, "owned_by": "openai-dev"},
|
352 |
+
{"id": "text-search-curie-query-001", "object": "model", "created": 1651172509, "owned_by": "openai-dev"},
|
353 |
+
{"id": "text-davinci-002", "object": "model", "created": 1649880484, "owned_by": "openai"},
|
354 |
+
{"id": "ada", "object": "model", "created": 1649357491, "owned_by": "openai"},
|
355 |
+
{"id": "text-ada-001", "object": "model", "created": 1649364042, "owned_by": "openai"},
|
356 |
+
{"id": "ada-similarity", "object": "model", "created": 1651172507, "owned_by": "openai-dev"},
|
357 |
+
{"id": "code-search-ada-code-001", "object": "model", "created": 1651172507, "owned_by": "openai-dev"},
|
358 |
+
{"id": "text-similarity-ada-001", "object": "model", "created": 1651172505, "owned_by": "openai-dev"},
|
359 |
+
{"id": "text-davinci-edit-001", "object": "model", "created": 1649809179, "owned_by": "openai"},
|
360 |
+
{"id": "code-davinci-edit-001", "object": "model", "created": 1649880484, "owned_by": "openai"},
|
361 |
+
{"id": "text-search-curie-doc-001", "object": "model", "created": 1651172509, "owned_by": "openai-dev"},
|
362 |
+
{"id": "text-curie-001", "object": "model", "created": 1649364043, "owned_by": "openai"},
|
363 |
+
{"id": "curie", "object": "model", "created": 1649359874, "owned_by": "openai"},
|
364 |
+
{"id": "davinci", "object": "model", "created": 1649359874, "owned_by": "openai"},
|
365 |
+
{"id": "gpt-4-0314", "object": "model", "created": 1687882410, "owned_by": "openai"}
|
366 |
+
]
|
367 |
+
}`
|
368 |
+
|
369 |
+
var modelList ModelList
|
370 |
+
json.Unmarshal([]byte(jsonStr), &modelList)
|
371 |
+
return modelList
|
372 |
+
}
|
373 |
+
|
374 |
+
func getAccToken(ghuToken string) (string, error) {
|
375 |
+
var accToken = ""
|
376 |
+
|
377 |
+
cache := cache.New(15*time.Minute, 60*time.Minute)
|
378 |
+
cacheToken, found := cache.Get(ghuToken)
|
379 |
+
if found {
|
380 |
+
accToken = cacheToken.(string)
|
381 |
+
} else {
|
382 |
+
client := &http.Client{}
|
383 |
+
req, err := http.NewRequest("GET", tokenUrl, nil)
|
384 |
+
if err != nil {
|
385 |
+
return accToken, err
|
386 |
+
}
|
387 |
+
|
388 |
+
headers := getHeaders(ghuToken)
|
389 |
+
|
390 |
+
for key, value := range headers {
|
391 |
+
req.Header.Add(key, value)
|
392 |
+
}
|
393 |
+
|
394 |
+
resp, err := client.Do(req)
|
395 |
+
if err != nil {
|
396 |
+
return accToken, err
|
397 |
+
}
|
398 |
+
defer resp.Body.Close()
|
399 |
+
|
400 |
+
var reader interface{}
|
401 |
+
switch resp.Header.Get("Content-Encoding") {
|
402 |
+
case "gzip":
|
403 |
+
reader, err = gzip.NewReader(resp.Body)
|
404 |
+
if err != nil {
|
405 |
+
return accToken, fmt.Errorf("数据解压失败")
|
406 |
+
}
|
407 |
+
default:
|
408 |
+
reader = resp.Body
|
409 |
+
}
|
410 |
+
|
411 |
+
body, err := io.ReadAll(reader.(io.Reader))
|
412 |
+
if err != nil {
|
413 |
+
return accToken, fmt.Errorf("数据读取失败")
|
414 |
+
}
|
415 |
+
if resp.StatusCode == http.StatusOK {
|
416 |
+
accToken = gjson.GetBytes(body, "token").String()
|
417 |
+
if accToken == "" {
|
418 |
+
return accToken, fmt.Errorf("acc_token 未返回")
|
419 |
+
}
|
420 |
+
cache.Set(ghuToken, accToken, 14*time.Minute)
|
421 |
+
} else {
|
422 |
+
log.Printf("获取 acc_token 请求失败:%d, %s ", resp.StatusCode, string(body))
|
423 |
+
return accToken, fmt.Errorf("获取 acc_token 请求失败: %d", resp.StatusCode)
|
424 |
+
}
|
425 |
+
}
|
426 |
+
return accToken, nil
|
427 |
+
}
|
428 |
+
|
429 |
+
func checkToken(ghuToken string) bool {
|
430 |
+
client := &http.Client{}
|
431 |
+
|
432 |
+
url := "https://api.github.com/user"
|
433 |
+
req, err := http.NewRequest("GET", url, nil)
|
434 |
+
if err != nil {
|
435 |
+
fmt.Println(err)
|
436 |
+
return false
|
437 |
+
}
|
438 |
+
req.Header.Add("Accept", "application/vnd.github+json")
|
439 |
+
req.Header.Add("Authorization", "Bearer "+ghuToken)
|
440 |
+
req.Header.Add("X-GitHub-Api-Version", "2022-11-28")
|
441 |
+
|
442 |
+
resp, err := client.Do(req)
|
443 |
+
if err != nil {
|
444 |
+
fmt.Println(err)
|
445 |
+
return false
|
446 |
+
}
|
447 |
+
defer resp.Body.Close()
|
448 |
+
return resp.StatusCode == http.StatusOK
|
449 |
+
}
|
450 |
+
|
451 |
+
func getHeaders(ghoToken string) map[string]string {
|
452 |
+
return map[string]string{
|
453 |
+
"Host": "api.github.com",
|
454 |
+
"Authorization": "token " + ghoToken,
|
455 |
+
|
456 |
+
"Editor-Version": "vscode/1.85.1",
|
457 |
+
"Editor-Plugin-Version": "copilot-chat/0.11.1",
|
458 |
+
"User-Agent": "GitHubCopilotChat/0.11.1",
|
459 |
+
"Accept": "*/*",
|
460 |
+
"Accept-Encoding": "gzip, deflate, br",
|
461 |
+
}
|
462 |
+
}
|
463 |
+
|
464 |
+
func getAccHeaders(accessToken, uuid string, sessionId string, machineId string) map[string]string {
|
465 |
+
return map[string]string{
|
466 |
+
"Host": "api.githubcopilot.com",
|
467 |
+
"Authorization": "Bearer " + accessToken,
|
468 |
+
"X-Request-Id": uuid,
|
469 |
+
"X-Github-Api-Version": "2023-07-07",
|
470 |
+
"Vscode-Sessionid": sessionId,
|
471 |
+
"Vscode-machineid": machineId,
|
472 |
+
"Editor-Version": "vscode/1.85.1",
|
473 |
+
"Editor-Plugin-Version": "copilot-chat/0.11.1",
|
474 |
+
"Openai-Organization": "github-copilot",
|
475 |
+
"Openai-Intent": "conversation-panel",
|
476 |
+
"Content-Type": "application/json",
|
477 |
+
"User-Agent": "GitHubCopilotChat/0.11.1",
|
478 |
+
"Copilot-Integration-Id": "vscode-chat",
|
479 |
+
"Accept": "*/*",
|
480 |
+
"Accept-Encoding": "gzip, deflate, br",
|
481 |
+
}
|
482 |
+
}
|
483 |
+
|
484 |
+
func getDeviceCode() (string, string, error) {
|
485 |
+
requestUrl := "https://github.com/login/device/code"
|
486 |
+
|
487 |
+
body := url.Values{}
|
488 |
+
headers := map[string]string{
|
489 |
+
"Accept": "application/json",
|
490 |
+
}
|
491 |
+
|
492 |
+
body.Set("client_id", client_id)
|
493 |
+
res, err := handleRequest("POST", body, requestUrl, headers)
|
494 |
+
deviceCode := gjson.Get(res, "device_code").String()
|
495 |
+
userCode := gjson.Get(res, "user_code").String()
|
496 |
+
|
497 |
+
if deviceCode == "" {
|
498 |
+
return "", "", fmt.Errorf("device code null")
|
499 |
+
}
|
500 |
+
if userCode == "" {
|
501 |
+
return "", "", fmt.Errorf("user code null")
|
502 |
+
}
|
503 |
+
return deviceCode, userCode, err
|
504 |
+
}
|
505 |
+
|
506 |
+
func checkUserCode(deviceCode string) (string, error) {
|
507 |
+
requestUrl := "https://github.com/login/oauth/access_token"
|
508 |
+
body := url.Values{}
|
509 |
+
headers := map[string]string{
|
510 |
+
"Accept": "application/json",
|
511 |
+
}
|
512 |
+
|
513 |
+
body.Set("client_id", client_id)
|
514 |
+
body.Set("device_code", deviceCode)
|
515 |
+
body.Set("grant_type", "urn:ietf:params:oauth:grant-type:device_code")
|
516 |
+
res, err := handleRequest("POST", body, requestUrl, headers)
|
517 |
+
if err != nil {
|
518 |
+
return "", err
|
519 |
+
}
|
520 |
+
token := gjson.Get(res, "access_token").String()
|
521 |
+
return token, nil
|
522 |
+
}
|
523 |
+
|
524 |
+
func checkGhuToken(ghuToken string) string {
|
525 |
+
requestUrl := "https://api.github.com/copilot_internal/v2/token"
|
526 |
+
body := url.Values{}
|
527 |
+
headers := map[string]string{
|
528 |
+
"Authorization": "Bearer " + ghuToken,
|
529 |
+
"editor-version": "JetBrains-IU/232.10203.10",
|
530 |
+
"editor-plugin-version": "copilot-intellij/1.3.3.3572",
|
531 |
+
"User-Agent": "GithubCopilot/1.129.0",
|
532 |
+
"Host": "api.github.com",
|
533 |
+
}
|
534 |
+
|
535 |
+
res, err := handleRequest("GET", body, requestUrl, headers)
|
536 |
+
if err != nil {
|
537 |
+
return "查询失败"
|
538 |
+
}
|
539 |
+
info := gjson.Get(res, "sku").String()
|
540 |
+
if info != "" {
|
541 |
+
return info
|
542 |
+
} else {
|
543 |
+
return "未订阅"
|
544 |
+
}
|
545 |
+
}
|
546 |
+
|
547 |
+
func handleRequest(method string, body url.Values, requestUrl string, headers map[string]string) (string, error) {
|
548 |
+
client := &http.Client{}
|
549 |
+
|
550 |
+
req, err := http.NewRequest(method, requestUrl, bytes.NewBuffer([]byte(body.Encode())))
|
551 |
+
if err != nil {
|
552 |
+
return "", err
|
553 |
+
}
|
554 |
+
for key, value := range headers {
|
555 |
+
req.Header.Add(key, value)
|
556 |
+
}
|
557 |
+
resp, err := client.Do(req)
|
558 |
+
if err != nil {
|
559 |
+
return "", err
|
560 |
+
}
|
561 |
+
defer resp.Body.Close()
|
562 |
+
|
563 |
+
respBody, err := io.ReadAll(resp.Body)
|
564 |
+
if err != nil {
|
565 |
+
return "", fmt.Errorf("status code: %d, read body error", resp.StatusCode)
|
566 |
+
}
|
567 |
+
|
568 |
+
return string(respBody), nil
|
569 |
+
}
|
570 |
+
|
571 |
+
func loadTemplate() (*template.Template, error) {
|
572 |
+
t := template.New("")
|
573 |
+
for name, file := range Assets.Files {
|
574 |
+
if file.IsDir() || !strings.HasSuffix(name, ".tmpl") {
|
575 |
+
continue
|
576 |
+
}
|
577 |
+
h, err := io.ReadAll(file)
|
578 |
+
if err != nil {
|
579 |
+
return nil, err
|
580 |
+
}
|
581 |
+
t, err = t.New(name).Parse(string(h))
|
582 |
+
if err != nil {
|
583 |
+
return nil, err
|
584 |
+
}
|
585 |
+
}
|
586 |
+
return t, nil
|
587 |
+
}
|