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 |
+
}
|