125 lines
3.1 KiB
Go
125 lines
3.1 KiB
Go
package api
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"litepan/internal/domain"
|
|
"litepan/internal/driver"
|
|
"litepan/internal/settings"
|
|
)
|
|
|
|
type qrStartReq struct {
|
|
DriverType string `json:"driver_type"`
|
|
}
|
|
|
|
type qrStartResp struct {
|
|
Token string `json:"token"`
|
|
QRImageBase64 string `json:"qr_image_base64"`
|
|
QRURL string `json:"qr_url"`
|
|
ExpiresIn int `json:"expires_in"`
|
|
Title string `json:"title,omitempty"`
|
|
Hint string `json:"hint,omitempty"`
|
|
}
|
|
|
|
type qrPollReq struct {
|
|
DriverType string `json:"driver_type"`
|
|
Token string `json:"token"`
|
|
}
|
|
|
|
type qrPollResp struct {
|
|
Status string `json:"status"`
|
|
Cookie string `json:"cookie,omitempty"`
|
|
AccessToken string `json:"access_token,omitempty"`
|
|
RefreshToken string `json:"refresh_token,omitempty"`
|
|
Message string `json:"message,omitempty"`
|
|
}
|
|
|
|
func (h *Handler) qrEphemeralConfig() driver.EphemeralConfig {
|
|
return driver.EphemeralConfig{
|
|
OAuthServerURL: func(ctx context.Context) string {
|
|
if h.settings == nil {
|
|
return domain.NormalizeOAuthServerURL("")
|
|
}
|
|
return domain.NormalizeOAuthServerURL(h.settings.String(settings.KeyOAuthServerURL))
|
|
},
|
|
}
|
|
}
|
|
|
|
func qrProvider(ctx context.Context, driverType string, cfg driver.EphemeralConfig) (driver.QRLoginProvider, func(context.Context), error) {
|
|
dt := strings.TrimSpace(driverType)
|
|
if dt == "" {
|
|
return nil, nil, domain.Errorf(domain.CodeValidation, "缺少 driver_type")
|
|
}
|
|
drv, release, err := driver.OpenEphemeral(ctx, dt, "", cfg)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
p, ok := drv.(driver.QRLoginProvider)
|
|
if !ok {
|
|
release(ctx)
|
|
return nil, nil, domain.Errorf(domain.CodeValidation, "该驱动不支持扫码登录")
|
|
}
|
|
return p, release, nil
|
|
}
|
|
|
|
func (h *Handler) startQRLogin(w http.ResponseWriter, r *http.Request) {
|
|
var in qrStartReq
|
|
if err := decodeJSON(r, &in); err != nil {
|
|
writeErr(w, err)
|
|
return
|
|
}
|
|
p, release, err := qrProvider(r.Context(), in.DriverType, h.qrEphemeralConfig())
|
|
if err != nil {
|
|
writeErr(w, err)
|
|
return
|
|
}
|
|
defer release(r.Context())
|
|
|
|
res, err := p.StartQRLogin(r.Context())
|
|
if err != nil {
|
|
writeErr(w, err)
|
|
return
|
|
}
|
|
writeOK(w, qrStartResp{
|
|
Token: res.Token,
|
|
QRImageBase64: res.QRImageBase64,
|
|
QRURL: res.QRURL,
|
|
ExpiresIn: res.ExpiresIn,
|
|
Title: res.Title,
|
|
Hint: res.Hint,
|
|
})
|
|
}
|
|
|
|
func (h *Handler) pollQRLogin(w http.ResponseWriter, r *http.Request) {
|
|
var in qrPollReq
|
|
if err := decodeJSON(r, &in); err != nil {
|
|
writeErr(w, err)
|
|
return
|
|
}
|
|
if strings.TrimSpace(in.Token) == "" {
|
|
writeErr(w, domain.Errorf(domain.CodeValidation, "缺少扫码会话 token"))
|
|
return
|
|
}
|
|
p, release, err := qrProvider(r.Context(), in.DriverType, h.qrEphemeralConfig())
|
|
if err != nil {
|
|
writeErr(w, err)
|
|
return
|
|
}
|
|
defer release(r.Context())
|
|
|
|
res, err := p.PollQRLogin(r.Context(), in.Token)
|
|
if err != nil {
|
|
writeErr(w, err)
|
|
return
|
|
}
|
|
writeOK(w, qrPollResp{
|
|
Status: string(res.Status),
|
|
Cookie: res.Credentials.Cookie,
|
|
AccessToken: res.Credentials.AccessToken,
|
|
RefreshToken: res.Credentials.RefreshToken,
|
|
Message: res.Message,
|
|
})
|
|
}
|