Files
copilot-app/internal/controller/auth/github.go
2025-08-13 19:03:20 +08:00

154 lines
4.2 KiB
Go

package auth
import (
_ "embed"
"fmt"
"github.com/gin-gonic/gin"
"net/http"
"os"
"ripper/internal/app/github_auth"
"ripper/internal/middleware"
"ripper/internal/response"
jwtpkg "ripper/pkg/jwt"
"time"
)
type postLoginDeviceCodeRequest struct {
ClientId string `json:"client_id" form:"client_id"`
}
type postLoginDeviceCodeResponse struct {
DeviceCode string `json:"device_code"` // 设备代码
UserCode string `json:"user_code"` // 用户代码
VerificationUrl string `json:"verification_uri"` // 验证地址
ExpiresIn int `json:"expires_in"` // 过期时间
Interval int `json:"interval"` // 间隔时间
}
type loginDeviceRequestInfo struct {
Code string `json:"code"`
Authorization string `json:"authorization"`
DisplayUserName string `json:"displayUserName,omitempty"`
Password string `json:"password"`
}
func postLoginDeviceCode(ctx *gin.Context) {
cli := postLoginDeviceCodeRequest{}
if err := ctx.ShouldBind(&cli); err != nil {
response.FailJson(ctx, response.FailStruct{
Code: -1,
Msg: "Invalid client id.",
}, false)
return
}
if cli.ClientId == "" {
response.FailJson(ctx, response.FailStruct{
Code: -1,
Msg: "Client id is required.",
}, false)
return
}
uid, devid, err := github_auth.BindClientToCode(cli.ClientId, 1800)
if err != nil {
response.FailJson(ctx, response.FailStruct{
Code: -1,
Msg: err.Error(),
}, false)
return
}
ctx.JSON(http.StatusOK, postLoginDeviceCodeResponse{
DeviceCode: devid,
UserCode: uid,
VerificationUrl: fmt.Sprintf("%s/login/device?user_code=%s", os.Getenv("DEFAULT_BASE_URL"), uid),
ExpiresIn: 1800,
Interval: 5,
})
}
func postLoginOauthAccessToken(ctx *gin.Context) {
v, exists := ctx.Get("client_auth_info")
if !exists {
ctx.JSON(http.StatusOK, gin.H{
"error": "authorization_pending",
"error_description": "The authorization request is still pending.",
"error_uri": "https://docs.github.com/developers/apps/authorizing-oauth-apps#error-codes-for-the-device-flow",
})
return
}
cliAuthInfo := v.(*github_auth.ClientAuthInfo)
t := time.Now()
t.Add(24 * 3 * time.Hour)
u, err := github_auth.GetClientAuthInfo(cliAuthInfo.UserCode)
if err != nil {
ctx.JSON(http.StatusOK, gin.H{
"error": "access_denied",
"error_description": "You must make a new request for a device code.",
"error_uri": "https://docs.github.com/developers/apps/authorizing-oauth-apps#error-codes-for-the-device-flow",
})
return
}
tk, _ := jwtpkg.CreateToken(&middleware.UserLoad{
UserDisplayName: cliAuthInfo.DisplayUserName,
CardCode: u.CardCode,
Client: cliAuthInfo.ClientId,
RegisteredClaims: jwtpkg.CreateStandardClaims(t.Unix(), "user"),
})
_ = github_auth.RemoveClientAuthInfoByDeviceCode(cliAuthInfo.ClientId)
ctx.JSON(http.StatusOK, gin.H{
"access_token": tk,
"scope": "",
"token_type": "bearer",
})
}
func postLoginDevice(ctx *gin.Context) {
var info loginDeviceRequestInfo
if err := response.BindStruct(ctx, &info); err != nil {
response.FailJson(ctx, response.FailStruct{
Code: 422,
Msg: "请求参数错误",
}, false)
return
}
// 验证密码
loginPassword := os.Getenv("LOGIN_PASSWORD")
if loginPassword != "" && info.Password != loginPassword {
response.FailJson(ctx, response.FailStruct{
Code: 422,
Msg: "访问密码错误",
}, false)
return
}
// 检查code是否存在
authInfo, err := github_auth.GetClientAuthInfo(info.Code)
if err != nil {
response.FailJson(ctx, response.FailStruct{
Code: 422,
Msg: "授权码填写错误",
}, false)
return
}
err = github_auth.UpdateClientAuthStatusByDeviceCode(authInfo.DeviceCode, info.Authorization, info.DisplayUserName)
if err != nil {
response.FailJson(ctx, response.FailStruct{
Code: 500,
Msg: "系统异常, 请稍后再试",
}, false)
return
}
response.SuccessJson(ctx, "ok")
}
func getLoginDevice(ctx *gin.Context) {
ctx.Header("Content-Type", "text/html; charset=utf-8")
ctx.HTML(http.StatusOK, "code.html", gin.H{})
}
func getHelpPage(ctx *gin.Context) {
ctx.Header("Content-Type", "text/html; charset=utf-8")
ctx.HTML(http.StatusOK, "help.html", gin.H{})
}