diff --git a/modules/setting/wechat.go b/modules/setting/wechat.go index 6e4c71cfd3..c6d3b88b78 100644 --- a/modules/setting/wechat.go +++ b/modules/setting/wechat.go @@ -24,6 +24,7 @@ type PowerWechatOfficialAccountUserConfigType struct { type OfficialAccountType struct { Enabled bool + DefaultDomainName string UserConfig PowerWechatOfficialAccountUserConfigType TempQrExpireSeconds int PowerWechat *PowerWechatOfficialAccountType.OfficialAccount @@ -40,12 +41,20 @@ type OfficialAccountType struct { */ func loadWechatSettingsFrom(rootCfg ConfigProvider) { sec := rootCfg.Section("wechat") + Wechat.OfficialAccount.Enabled = sec.Key("ENABLED_WECHAT_QR_SIGNIN").MustBool(true) + log.Info("ENABLED_WECHAT_QR_SIGNIN == '%b'", Wechat.OfficialAccount.Enabled) + Wechat.OfficialAccount.DefaultDomainName = sec.Key("WECHAT_QR_SERVICE_DOMAIN_NAME").MustString("devstar.cn") + Wechat.OfficialAccount.UserConfig.AppID = sec.Key("WECHAT_OFFICIAL_ACCOUNT_APP_ID").MustString("") Wechat.OfficialAccount.UserConfig.AppSecret = sec.Key("WECHAT_OFFICIAL_ACCOUNT_APP_SECRET").MustString("") Wechat.OfficialAccount.UserConfig.RedisAddr = sec.Key("WECHAT_OFFICIAL_ACCOUNT_REDIS_ADDR").MustString("") Wechat.OfficialAccount.UserConfig.MessageToken = sec.Key("WECHAT_OFFICIAL_ACCOUNT_MESSAGE_TOKEN").MustString("") Wechat.OfficialAccount.UserConfig.MessageAesKey = sec.Key("WECHAT_OFFICIAL_ACCOUNT_MESSAGE_AES_KEY").MustString("") - createPowerWechatApp(Wechat.OfficialAccount.UserConfig) + if Wechat.OfficialAccount.UserConfig.AppID != "" && Wechat.OfficialAccount.UserConfig.AppSecret != "" { + log.Info("createPowerWechatApp AppID:%s ", Wechat.OfficialAccount.UserConfig.AppID) + createPowerWechatApp(Wechat.OfficialAccount.UserConfig) + } + Wechat.OfficialAccount.TempQrExpireSeconds = sec.Key("WECHAT_OFFICIAL_ACCOUNT_TEMP_QR_EXPIRE_SECONDS").MustInt(60) // 扫码后,最长注册时间:默认24小时 Wechat.OfficialAccount.RegisterationExpireSeconds = sec.Key("WECHAT_OFFICIAL_ACCOUNT_REGISTERATION_EXPIRE_SECONDS").MustInt(86400) @@ -87,8 +96,5 @@ func createPowerWechatApp(userConfig PowerWechatOfficialAccountUserConfigType) { }) if err != nil { log.Warn("创建微信工具类 PowerWechat 失败,请检查 modules/setting/wechat.go ") - } else { - Wechat.OfficialAccount.Enabled = true } - } diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 27dac6f1b6..fcc6d277d8 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -307,6 +307,8 @@ federated_avatar_lookup_popup = Enable federated avatar lookup using Libravatar. disable_registration = Disable Self-Registration disable_registration_popup = Disable user self-registration. Only administrators will be able to create new user accounts. allow_only_external_registration_popup = Allow Registration Only Through External Services +wechat_qr_signin = Enable Wechat QR Code Sign-In +wechat_qr_signin_popup = Enable user sign-in via Wechat QR Code. openid_signin = Enable OpenID Sign-In openid_signin_popup = Enable user sign-in via OpenID. openid_signup = Enable OpenID Self-Registration diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index 9d2bf486ff..eb24245d4c 100644 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -306,6 +306,8 @@ federated_avatar_lookup_popup=启用 Federated Avatars 查找以使用开源的 disable_registration=禁止用户自助注册 disable_registration_popup=禁用用户自助注册。只有管理员才能创建新的用户帐户。 allow_only_external_registration_popup=仅允许通过外部服务注册 +wechat_qr_signin=启用 微信二维码 登录 +wechat_qr_signin_popup=启用通过 微信二维码 登录 openid_signin=启用 OpenID 登录 openid_signin_popup=启用通过 OpenID 登录 openid_signup=启用 OpenID 自助注册 diff --git a/routers/api/wechat/official_account/login-qr-handlers.go b/routers/api/wechat/official_account/login-qr-handlers.go index 11ae73c402..b0c9991765 100644 --- a/routers/api/wechat/official_account/login-qr-handlers.go +++ b/routers/api/wechat/official_account/login-qr-handlers.go @@ -1,9 +1,10 @@ package official_account import ( + "net/http" + Result "code.gitea.io/gitea/routers/entity" wechat_official_account_service "code.gitea.io/gitea/services/wechat/official_account" - "net/http" ) // QrCheckCodeStatus 检查二维码扫描状态 @@ -31,7 +32,6 @@ func QrCheckCodeStatus(responseWriter http.ResponseWriter, request *http.Request Result.RespFailedIllegalWechatQrTicket.RespondJson2HttpResponseWriter(responseWriter) return } - // 调用 WeChat Official Account Service 层,获取二维码扫码状态 qrStatusVO, err := wechat_official_account_service.GetTempQRStatus(ticket) if err != nil { diff --git a/routers/install/install.go b/routers/install/install.go index fde8b37ed5..eb9f585b73 100644 --- a/routers/install/install.go +++ b/routers/install/install.go @@ -153,6 +153,7 @@ func Install(ctx *context.Context) { form.EnableOpenIDSignIn = setting.Service.EnableOpenIDSignIn form.EnableOpenIDSignUp = setting.Service.EnableOpenIDSignUp + form.EnableWechatQRSignIn = true form.DisableRegistration = setting.Service.DisableRegistration form.AllowOnlyExternalRegistration = setting.Service.AllowOnlyExternalRegistration form.EnableCaptcha = setting.Service.EnableCaptcha @@ -445,7 +446,17 @@ func SubmitInstall(ctx *context.Context) { ctx.RenderWithErr(ctx.Tr("install.save_config_failed", err), tplInstall, &form) return } - + if form.EnableWechatQRSignIn { + cfg.Section("wechat").Key("ENABLED_WECHAT_QR_SIGNIN").SetValue("true") + // cfg.Section("wechat").Key("WECHAT_QR_SERVICE_DOMAIN_NAME").SetValue("devstar.cn") + // cfg.Section("wechat").Key("WECHAT_OFFICIAL_ACCOUNT_TEMP_QR_EXPIRE_SECONDS").SetValue("180") + // cfg.Section("wechat").Key("WECHAT_OFFICIAL_ACCOUNT_REGISTERATION_EXPIRE_SECONDS").SetValue("86400") + // cfg.Section("wechat").Key("WECHAT_OFFICIAL_ACCOUNT_APP_ID").SetValue("") + // cfg.Section("wechat").Key("WECHAT_OFFICIAL_ACCOUNT_APP_SECRET").SetValue("") + // cfg.Section("wechat").Key("WECHAT_OFFICIAL_ACCOUNT_REDIS_ADDR").SetValue("") + // cfg.Section("wechat").Key("WECHAT_OFFICIAL_ACCOUNT_MESSAGE_TOKEN").SetValue("") + // cfg.Section("wechat").Key("WECHAT_OFFICIAL_ACCOUNT_MESSAGE_AES_KEY").SetValue("") + } cfg.Section("openid").Key("ENABLE_OPENID_SIGNIN").SetValue(fmt.Sprint(form.EnableOpenIDSignIn)) cfg.Section("openid").Key("ENABLE_OPENID_SIGNUP").SetValue(fmt.Sprint(form.EnableOpenIDSignUp)) cfg.Section("service").Key("DISABLE_REGISTRATION").SetValue(fmt.Sprint(form.DisableRegistration)) diff --git a/routers/web/auth/auth.go b/routers/web/auth/auth.go index 2f028fe0e5..54aaaad306 100644 --- a/routers/web/auth/auth.go +++ b/routers/web/auth/auth.go @@ -5,9 +5,6 @@ package auth import ( - wechat_official_account_openid_model "code.gitea.io/gitea/models/wechat" - wechat_official_account_cache_service "code.gitea.io/gitea/services/wechat/cache" - wechat_official_account_service "code.gitea.io/gitea/services/wechat/official_account" "errors" "fmt" "html/template" @@ -15,6 +12,10 @@ import ( "net/url" "strings" + wechat_official_account_openid_model "code.gitea.io/gitea/models/wechat" + wechat_official_account_cache_service "code.gitea.io/gitea/services/wechat/cache" + wechat_official_account_service "code.gitea.io/gitea/services/wechat/official_account" + "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" @@ -167,6 +168,15 @@ func CheckAutoLogin(ctx *context.Context) bool { return false } +func SignInNavbarSetting(ctx *context.Context) { + ctx.Data["EnableOpenIDSignIn"] = setting.Service.EnableOpenIDSignIn + ctx.Data["EnableWechatQrLogin"] = setting.Wechat.OfficialAccount.Enabled + ctx.Data["EnableSSPI"] = auth.IsSSPIEnabled(ctx) + ctx.Data["PageIsLogin"] = false + ctx.Data["PageIsSignUp"] = false + ctx.Data["PageIsWechatQrLogin"] = false +} + // SignInWechatQr 渲染微信扫码登录页面 func SignInWechatQr(ctx *context.Context) { if CheckAutoLogin(ctx) { @@ -177,14 +187,15 @@ func SignInWechatQr(ctx *context.Context) { RedirectAfterLogin(ctx) return } + + SignInNavbarSetting(ctx) + ctx.Data["Title"] = ctx.Tr("sign_in") ctx.Data["SignInWechatQrLink"] = setting.AppSubURL + "/user/login/wechat/official-account" ctx.Data["PageIsSignIn"] = true - ctx.Data["PageIsLogin"] = true ctx.Data["PageIsWechatQrLogin"] = true - ctx.Data["EnableSSPI"] = auth.IsSSPIEnabled(ctx) - _, err := GenerateWechatQr(ctx) + wechatQrTicket, wechatQrCodeUrl, err := auth_service.GetWechatQRTicket(ctx) if err != nil { wechatQrFallbackLoginURL := "/user/login" redirectTo := ctx.FormString("redirect_to") @@ -196,6 +207,10 @@ func SignInWechatQr(ctx *context.Context) { return } + ctx.Data["wechatQrTicket"] = wechatQrTicket + ctx.Data["wechatQrCodeUrl"] = wechatQrCodeUrl + ctx.Data["wechatQrExpireSeconds"] = setting.Wechat.OfficialAccount.TempQrExpireSeconds + ctx.HTML(http.StatusOK, tplSignInWechatQr) } @@ -212,6 +227,8 @@ func SignIn(ctx *context.Context) { return } + SignInNavbarSetting(ctx) + oauth2Providers, err := oauth2.GetOAuth2Providers(ctx, optional.Some(true)) if err != nil { ctx.ServerError("UserSignIn", err) @@ -222,8 +239,6 @@ func SignIn(ctx *context.Context) { ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login" ctx.Data["PageIsSignIn"] = true ctx.Data["PageIsLogin"] = true - ctx.Data["PageIsPasswordLogin"] = true - ctx.Data["EnableSSPI"] = auth.IsSSPIEnabled(ctx) if setting.Service.EnableCaptcha && setting.Service.RequireCaptchaForLogin { context.SetCaptchaData(ctx) @@ -478,9 +493,10 @@ func SignOut(ctx *context.Context) { // SignUp render the register page func SignUp(ctx *context.Context) { - ctx.Data["Title"] = ctx.Tr("sign_up") + ctx.Data["Title"] = ctx.Tr("sign_up") ctx.Data["SignUpLink"] = setting.AppSubURL + "/user/sign_up" + SignInNavbarSetting(ctx) oauth2Providers, err := oauth2.GetOAuth2Providers(ctx, optional.Some(true)) if err != nil { diff --git a/routers/web/auth/openid.go b/routers/web/auth/openid.go index 2143b8096a..5d57f1c9ec 100644 --- a/routers/web/auth/openid.go +++ b/routers/web/auth/openid.go @@ -39,6 +39,8 @@ func SignInOpenID(ctx *context.Context) { return } + SignInNavbarSetting(ctx) + ctx.Data["PageIsSignIn"] = true ctx.Data["PageIsLoginOpenID"] = true ctx.HTML(http.StatusOK, tplSignInOpenID) diff --git a/routers/web/auth/wechat_utils.go b/routers/web/auth/wechat_utils.go deleted file mode 100644 index d5aa0d78bd..0000000000 --- a/routers/web/auth/wechat_utils.go +++ /dev/null @@ -1,58 +0,0 @@ -package auth - -import ( - "encoding/base64" - binaryUtils "encoding/binary" - "strings" - "time" - - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/services/context" - "github.com/google/uuid" -) - -// 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 -} - -/** - * 生成微信临时二维码 - * - * @param ctx 页面会话上下文环境 - * @return string 生成的微信二维码的 ticket - * @return error 如果生成二维码过程中出现错误,则返回相应的错误信息 - */ -func GenerateWechatQr(ctx *context.Context) (wechatQrTicket string, errorGenerateQr error) { - - if setting.Wechat.OfficialAccount.PowerWechat == nil { - log.Warn("PowerWechat工具类配置错误, 不会生成公众号带参数二维码") - return "", &WechatError{message: "ERROR: PowerWechat 配置错误 (PowerWechat app instance has not yet been initialized!)"} - } - - // 生成随机 sceneStr 场景值 - // sceneStr生成规则:UUIDv4后边拼接 当前UnixNano时间戳转为byte数组后的Base64 - // e.g, sceneStr == "1c78e8d914fb4307a3588ac0f6bc092a@yPXAm+ve5hc=" - bytesArrayUnit64 := make([]byte, 8) - binaryUtils.LittleEndian.PutUint64(bytesArrayUnit64, uint64(time.Now().UnixNano())) - currentTimestampNanoBase64 := base64.StdEncoding.EncodeToString(bytesArrayUnit64) - sceneStr := strings.ReplaceAll(uuid.New().String(), "-", "") + "@" + currentTimestampNanoBase64 - - qrExpireSeconds := setting.Wechat.OfficialAccount.TempQrExpireSeconds - qrData, err := setting.Wechat.OfficialAccount.PowerWechat.QRCode.Temporary(ctx, sceneStr, qrExpireSeconds) - if err == nil { - wechatQrTicket = qrData.Ticket - ctx.Data["wechatQrTicket"] = wechatQrTicket - ctx.Data["wechatQrCodeUrl"] = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=" + wechatQrTicket - ctx.Data["wechatQrExpireSeconds"] = qrData.ExpireSeconds - } else { - log.Warn(" [!] 无法生成微信公众号带参数临时二维码: %s", err.Error()) - } - return wechatQrTicket, err -} diff --git a/routers/web/user/setting/account.go b/routers/web/user/setting/account.go index b70b32e682..4967be74d2 100644 --- a/routers/web/user/setting/account.go +++ b/routers/web/user/setting/account.go @@ -18,8 +18,8 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/web" - web_auth_utils "code.gitea.io/gitea/routers/web/auth" "code.gitea.io/gitea/services/auth" + auth_service "code.gitea.io/gitea/services/auth" "code.gitea.io/gitea/services/auth/source/db" "code.gitea.io/gitea/services/auth/source/smtp" "code.gitea.io/gitea/services/context" @@ -42,11 +42,15 @@ func Account(ctx *context.Context) { loadAccountData(ctx) // 界面原型:更新微信,展示公众号带参数二维码 - _, err := web_auth_utils.GenerateWechatQr(ctx) + wechatQrTicket, wechatQrCodeUrl, err := auth_service.GetWechatQRTicket(ctx) if err != nil { log.Warn("微信创建二维码失败,跳过") } + ctx.Data["wechatQrTicket"] = wechatQrTicket + ctx.Data["wechatQrCodeUrl"] = wechatQrCodeUrl + ctx.Data["wechatQrExpireSeconds"] = setting.Wechat.OfficialAccount.TempQrExpireSeconds + ctx.HTML(http.StatusOK, tplSettingsAccount) } diff --git a/services/auth/wechat_qr.go b/services/auth/wechat_qr.go new file mode 100644 index 0000000000..d064b8d4a9 --- /dev/null +++ b/services/auth/wechat_qr.go @@ -0,0 +1,200 @@ +package auth + +import ( + "encoding/base64" + binaryUtils "encoding/binary" + "encoding/json" + "fmt" + "io" + "net/http" + "strings" + "time" + + "code.gitea.io/gitea/services/context" + + wechat_official_account_user_model "code.gitea.io/gitea/models/wechat" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" + wechat_official_account_cache_service "code.gitea.io/gitea/services/wechat/cache" + wechat_official_account_vo "code.gitea.io/gitea/services/wechat/vo" + "github.com/google/uuid" +) + +// 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 struct { + Ticket string `json:"ticket"` + ExpireSeconds int `json:"expire_seconds"` + URL string `json:"url"` + QRImageURL string `json:"qr_image_url"` + } `json:"data"` +} + +/** + * GetWechatQRTicket 生成微信官方账户临时二维码 + * + * @return string 生成的微信二维码的 ticket + * @return string 生成的微信二维码图片URL + * @return error 如果生成二维码过程中出现错误,则返回相应的错误信息 + */ +func GetWechatQRTicket(ctx *context.Context) (wechatQrTicket string, QRImageURL string, errorGenerateQr error) { + // 生成随机 sceneStr 场景值 + // sceneStr生成规则:UUIDv4后边拼接 当前UnixNano时间戳转为byte数组后的Base64 + // e.g, sceneStr == "1c78e8d914fb4307a3588ac0f6bc092a@yPXAm+ve5hc=" + bytesArrayUnit64 := make([]byte, 8) + binaryUtils.LittleEndian.PutUint64(bytesArrayUnit64, uint64(time.Now().UnixNano())) + currentTimestampNanoBase64 := base64.StdEncoding.EncodeToString(bytesArrayUnit64) + sceneStr := strings.ReplaceAll(uuid.New().String(), "-", "") + "@" + currentTimestampNanoBase64 + + qrExpireSeconds := setting.Wechat.OfficialAccount.TempQrExpireSeconds + + // 构建请求的 URL + url := fmt.Sprintf("https://%s/api/wechat/official-account/login/qr/generate?qrExpireSeconds=%d&sceneStr=%s", setting.Wechat.OfficialAccount.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.QRImageURL, nil +} + +// Response represents the top-level JSON structure +type Response struct { + Code int `json:"code"` + Msg string `json:"msg"` + Data Data `json:"data"` +} + +// Data represents the "data" object in the JSON +type Data struct { + IsScanned bool `json:"is_scanned"` + SceneStr string `json:"scene_str"` + OpenID string `json:"openid"` + IsBinded bool `json:"is_binded"` +} + +// 假设这是用于检查二维码状态的函数 +func checkWechatQrTicketStatus(ctx *context.Context, qrTicket string, quit chan bool) { + + url := fmt.Sprintf("https://%s/api/wechat/official-account/login/qr/check-status?ticket=%s&_=%d", + setting.Wechat.OfficialAccount.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对象 + qrStatusVO := wechat_official_account_vo.GetWechatOfficialAccountTempQRStatusVO{ + SceneStr: data.Data.SceneStr, + IsScanned: data.Data.IsScanned, + OpenId: data.Data.OpenID, + } + // 从微信服务器消息推送中解析扫码人的 OpenID + _, err := wechat_official_account_user_model.QueryUserByOpenid(ctx, qrStatusVO.OpenId) + if err != nil { + // 未找到 OpenID 对应的 DevStar 用户信息,提示前端导向注册页 + qrStatusVO.IsBinded = false + qrStatusVOString, err := qrStatusVO.Marshal2JSONString() + if err == nil { + // 将扫码人的微信公众号 OpenID 标记为等待注册,等待时间用户注册完成,默认24小时 + // key: qrScanResponseDigest.Ticket + // value: JSON 字符串 + // TTL: setting.Wechat.OfficialAccount.TempQrExpireSeconds + wechat_official_account_cache_service.SetWechatOfficialAccountQrTicketWithTTL( + qrTicket, + qrStatusVOString, + setting.Wechat.OfficialAccount.RegisterationExpireSeconds) + } + quit <- true + return + } + + qrStatusVO.IsBinded = true + qrVOJsonString, err := qrStatusVO.Marshal2JSONString() + if err == nil { + wechat_official_account_cache_service.SetWechatOfficialAccountQrTicketWithTTL( + qrTicket, + qrVOJsonString, + setting.Wechat.OfficialAccount.TempQrExpireSeconds) + } + quit <- true + return + } +} diff --git a/services/context/context.go b/services/context/context.go index 3750ff7c32..6c0ec7ad42 100644 --- a/services/context/context.go +++ b/services/context/context.go @@ -158,6 +158,7 @@ func Contexter() func(next http.Handler) http.Handler { ctx.Data.MergeFrom(middleware.CommonTemplateContextData()) ctx.Data["Context"] = ctx // TODO: use "ctx" in template and remove this ctx.Data["CurrentURL"] = setting.AppSubURL + req.URL.RequestURI() + ctx.Data["EnableWechatQrLogin"] = setting.Wechat.OfficialAccount.Enabled ctx.Data["Link"] = ctx.Link // PageData is passed by reference, and it will be rendered to `window.config.pageData` in `head.tmpl` for JavaScript modules diff --git a/services/forms/user_form.go b/services/forms/user_form.go index b63666a80e..b7800de733 100644 --- a/services/forms/user_form.go +++ b/services/forms/user_form.go @@ -52,6 +52,7 @@ type InstallForm struct { EnableFederatedAvatar bool EnableOpenIDSignIn bool EnableOpenIDSignUp bool + EnableWechatQRSignIn bool DisableRegistration bool AllowOnlyExternalRegistration bool EnableCaptcha bool diff --git a/services/repository/generate.go b/services/repository/generate.go index dfb0200d3e..5cc3ef86c0 100644 --- a/services/repository/generate.go +++ b/services/repository/generate.go @@ -362,7 +362,7 @@ func GenerateGitContentFromGitURL(ctx context.Context, gitURL, commitID string, log.Error("Unable to add %v as remote origin to temporary repo to %s: stdout %s\nError: %v", repo, tmpDir, stdout, err) return fmt.Errorf("git remote add: %w", err) } - commitMessage := "Initial commit from " + gitURL + "(" + sha1 + ")" + commitMessage := "Initial commit from " + gitURL + " ( " + sha1 + " ) " initRepoCommit(ctx, tmpDir, repo, repo.Owner, repo.DefaultBranch, commitMessage) if err := repo_module.UpdateRepoSize(ctx, repo); err != nil { diff --git a/templates/base/head_navbar.tmpl b/templates/base/head_navbar.tmpl index 1ccea3bd24..cdf86cd805 100644 --- a/templates/base/head_navbar.tmpl +++ b/templates/base/head_navbar.tmpl @@ -177,7 +177,7 @@ {{svg "octicon-person"}} {{ctx.Locale.Tr "register"}} {{end}} - + {{svg "octicon-sign-in"}} {{ctx.Locale.Tr "sign_in"}} {{end}} diff --git a/templates/install.tmpl b/templates/install.tmpl index 965e57f213..c452fd1fe3 100644 --- a/templates/install.tmpl +++ b/templates/install.tmpl @@ -221,12 +221,6 @@ -
-
- - -
-
@@ -245,6 +239,18 @@
+
+
+ + +
+
+
+
+ + +
+
diff --git a/templates/user/auth/signin_navbar.tmpl b/templates/user/auth/signin_navbar.tmpl index 4f0348ffb2..88631c02c9 100644 --- a/templates/user/auth/signin_navbar.tmpl +++ b/templates/user/auth/signin_navbar.tmpl @@ -1,44 +1,29 @@ +{{if or .EnableOpenIDSignIn .EnableSSPI .EnableWechatQrLogin}}
- - {{/* 微信扫码登录 */}} - {{if .PageIsLogin}} - + + {{ctx.Locale.Tr "auth.login_userpass"}} + + + {{ctx.Locale.Tr "auth.create_new_account"}} + + {{if .EnableWechatQrLogin}} + {{ctx.Locale.Tr "settings.wechat_qr_login"}} + + {{end}} + {{if .EnableOpenIDSignIn}} + + {{svg "fontawesome-openid"}} +  OpenID {{end}} - - {{/* 2. 密码登录 */}} - {{if .PageIsLogin}} - - {{ctx.Locale.Tr "password"}} + {{if .EnableSSPI}} + + {{svg "fontawesome-windows"}} +  SSPI - {{end}} - - {{/* 3. 手机短信登录 */}} - {{/* */}} - {{/* {{ctx.Locale.Tr "settings.phone_sms_code"}} */}} - {{/* */}} - - {{if or .EnableOpenIDSignIn .EnableSSPI}} - - {{ctx.Locale.Tr "auth.login_userpass"}} - - - {{ctx.Locale.Tr "auth.create_new_account"}} - - {{if .EnableOpenIDSignIn}} - - {{svg "fontawesome-openid"}} -  OpenID - - {{end}} - {{if .EnableSSPI}} - - {{svg "fontawesome-windows"}} -  SSPI - - {{end}} {{end}}
+{{end}}