177 lines
5.1 KiB
Go
177 lines
5.1 KiB
Go
package auth
|
||
|
||
import (
|
||
"encoding/json"
|
||
"fmt"
|
||
"io"
|
||
"net/http"
|
||
"time"
|
||
|
||
"code.gitea.io/gitea/services/context"
|
||
|
||
wechat_model "code.gitea.io/gitea/models/wechat"
|
||
"code.gitea.io/gitea/modules/log"
|
||
"code.gitea.io/gitea/modules/setting"
|
||
wechat_service "code.gitea.io/gitea/services/wechat"
|
||
)
|
||
|
||
// Define a Wechat Error type message
|
||
type WechatError struct {
|
||
message string
|
||
}
|
||
|
||
// Implement the Error() method for the `WechatError` type
|
||
func (e *WechatError) Error() string {
|
||
return e.message
|
||
}
|
||
|
||
type ResponseData struct {
|
||
Code int `json:"code"`
|
||
Msg string `json:"msg"`
|
||
Data wechat_service.WechatTempQRData `json:"data"`
|
||
}
|
||
|
||
/**
|
||
* GetWechatQRTicket 生成微信官方账户临时二维码
|
||
*
|
||
* @return string 生成的微信二维码的 ticket
|
||
* @return string 生成的微信二维码图片URL
|
||
* @return error 如果生成二维码过程中出现错误,则返回相应的错误信息
|
||
*/
|
||
func GetWechatQRTicket(ctx *context.Context) (wechatQrTicket string, QRImageURL string, errorGenerateQr error) {
|
||
|
||
sceneStr := setting.Domain
|
||
qrExpireSeconds := setting.Wechat.TempQrExpireSeconds
|
||
|
||
// 构建请求的 URL
|
||
url := fmt.Sprintf("https://%s/api/wechat/login/qr/generate?qrExpireSeconds=%d&sceneStr=%s", setting.Wechat.DefaultDomainName, qrExpireSeconds, sceneStr)
|
||
|
||
// 发送 GET 请求
|
||
resp, err := http.Get(url)
|
||
if err != nil {
|
||
return "", "", fmt.Errorf("failed to send GET request: %v", err)
|
||
}
|
||
defer resp.Body.Close()
|
||
|
||
bodyBytes, err := io.ReadAll(resp.Body)
|
||
if err != nil {
|
||
return "", "", fmt.Errorf("failed to read response body: %v", err)
|
||
}
|
||
// 检查响应状态码
|
||
if resp.StatusCode != http.StatusOK {
|
||
return "", "", fmt.Errorf("received non-200 response status: %s", resp.Status)
|
||
}
|
||
|
||
// 解析 JSON 响应
|
||
var data ResponseData
|
||
if err := json.Unmarshal(bodyBytes, &data); err != nil {
|
||
return "", "", fmt.Errorf("failed to unmarshal JSON response: %v", err)
|
||
}
|
||
quit := make(chan bool) // 用来通知goroutine停止
|
||
// 启动一个新的goroutine来进行轮询
|
||
go func() {
|
||
// 创建一个超时定时器通道
|
||
timeout := time.After(time.Duration(qrExpireSeconds) * time.Second)
|
||
ticker := time.NewTicker(1 * time.Second)
|
||
defer ticker.Stop()
|
||
|
||
for {
|
||
select {
|
||
case <-ticker.C:
|
||
checkWechatQrTicketStatus(ctx, wechatQrTicket, quit)
|
||
|
||
case <-quit:
|
||
log.Info("Stopping polling...")
|
||
return
|
||
|
||
case <-timeout:
|
||
log.Info("Polling timed out after", qrExpireSeconds, "seconds.")
|
||
quit <- true // 发送停止信号给轮询goroutine
|
||
}
|
||
}
|
||
}()
|
||
return data.Data.Ticket, data.Data.QrImageSrcUrl, nil
|
||
}
|
||
|
||
// Response represents the top-level JSON structure
|
||
type Response struct {
|
||
Code int `json:"code"`
|
||
Msg string `json:"msg"`
|
||
Data wechat_service.WechatTempQRStatus `json:"data"`
|
||
}
|
||
|
||
// 假设这是用于检查二维码状态的函数
|
||
func checkWechatQrTicketStatus(ctx *context.Context, qrTicket string, quit chan bool) {
|
||
|
||
url := fmt.Sprintf("https://%s/api/wechat/login/qr/check-status?ticket=%s&_=%d",
|
||
setting.Wechat.DefaultDomainName, qrTicket, time.Now().UnixMilli())
|
||
|
||
resp, err := http.Get(url)
|
||
if err != nil {
|
||
log.Error("There was a problem with the fetch operation:", err)
|
||
return
|
||
}
|
||
defer resp.Body.Close()
|
||
|
||
if resp.StatusCode != http.StatusOK {
|
||
log.Error("Network response was not ok")
|
||
return
|
||
}
|
||
|
||
bodyBytes, err := io.ReadAll(resp.Body)
|
||
if err != nil {
|
||
log.Error("Error reading response body:", err)
|
||
return
|
||
}
|
||
// 解析 JSON 响应
|
||
var data Response
|
||
if err := json.Unmarshal(bodyBytes, &data); err != nil {
|
||
log.Error("failed to unmarshal JSON response: %v", err)
|
||
return
|
||
}
|
||
if data.Code == 0 && data.Data.IsScanned {
|
||
log.Info("Caching WeChat QR Scanned Info: %s\n", bodyBytes)
|
||
// {"code":0,"msg":"操作成功","data":{
|
||
// "is_scanned":true,
|
||
// "scene_str":"2d521f80047c42aba27ee9beade35985@p2Z6hfheDxg=",
|
||
// "openid":"oQowJ6cD9WSuoxYaCc7mryfn-lVo",
|
||
// "is_binded":true}}
|
||
// 准备扫码状态VO对象
|
||
qrStatus := wechat_service.WechatTempQRStatus{
|
||
SceneStr: data.Data.SceneStr,
|
||
IsScanned: data.Data.IsScanned,
|
||
OpenId: data.Data.OpenId,
|
||
}
|
||
// 从微信服务器消息推送中解析扫码人的 OpenId
|
||
user, err := wechat_model.QueryUserByOpenid(ctx, qrStatus.OpenId)
|
||
if user == nil {
|
||
// 未找到 OpenId 对应的 DevStar 用户信息,提示前端导向注册页
|
||
qrStatus.IsBinded = false
|
||
qrStatusString, err := qrStatus.Marshal2JSONString()
|
||
if err == nil {
|
||
// 将扫码人的微信公众号 OpenId 标记为等待注册,等待时间用户注册完成,默认24小时
|
||
// key: qrScanResponseDigest.Ticket
|
||
// value: JSON 字符串
|
||
// TTL: setting.Wechat.TempQrExpireSeconds
|
||
wechat_service.SetWechatQrTicketWithTTL(
|
||
qrTicket,
|
||
qrStatusString,
|
||
setting.Wechat.RegisterationExpireSeconds)
|
||
}
|
||
quit <- true
|
||
return
|
||
}
|
||
|
||
qrStatus.IsBinded = true
|
||
qrVOJsonString, err := qrStatus.Marshal2JSONString()
|
||
if err == nil {
|
||
wechat_service.SetWechatQrTicketWithTTL(
|
||
qrTicket,
|
||
qrVOJsonString,
|
||
setting.Wechat.TempQrExpireSeconds)
|
||
}
|
||
quit <- true
|
||
return
|
||
}
|
||
}
|