- ' . esc_html($provider_name) . ''); ?> -
- - - - - - - - - - - - -diff --git a/.gitignore b/.gitignore index d7c25d50..6a76763f 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,7 @@ !/tests/widget-system/icon-card-contract.test.cjs !/tests/widget-system/page-display-save-harness.php !/tests/widget-system/admin-ui-contract.test.cjs +!/tests/widget-system/auth-source-contract.test.cjs !/tests/visual/docker-compose.zibll.yml !/tests/visual/zibll-Caddyfile !/tests/visual/README.md diff --git a/README.md b/README.md index 49f4e57d..bf3197d2 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ AKSRT 是一款现代化的 WordPress 主题,采用**原生 WordPress 主题 - **自定义管理面板 (SPA)** — Vue3 + Vite + Naive UI 的 12 个一级业务域后台 - **统一设置契约** — Schema 驱动表单、域控制器、导入导出、按域重置、迁移状态与健康检查 - **支付/下载主线** — 下载/付费内容中心、充值/VIP/分成/返佣/网关结构、10 分钟下载 token -- **用户与增长** — OAuth 登录、邮箱验证门禁、签到奖励、邀请奖励、等级/徽章/认证/举报结构 +- **用户与增长** — 统一登录注册、OAuth 登录、签到奖励、邀请奖励、等级/徽章/认证/举报结构 - **内容与交互** — 首页模块化、归档/搜索/404、单篇资源页、评论审核与通知中心 - **运维工具** — 配置导出导入、dry-run 校验、邮件回滚、数据库与环境诊断 - **自定义页面模板**:About / Friends / Login / Profile / Register @@ -60,9 +60,8 @@ AKSRT-theme/ │ ├── pages/ # 自定义页面模板 │ │ ├── page-about.php # 关于页面 │ │ ├── page-friends.php # 友链墙 -│ │ ├── page-login.php # 登录页面 +│ │ ├── page-sign.php # 唯一登录注册页面 │ │ ├── page-profile.php # 用户中心 -│ │ └── page-register.php # 注册页面 │ │ │ └── parts/ # 可复用模板片段 │ ├── content-post-card.php # 文章卡片组件 diff --git a/assets/admin/src/api/modules/tools.js b/assets/admin/src/api/modules/tools.js index 7da837bf..e793e95e 100644 --- a/assets/admin/src/api/modules/tools.js +++ b/assets/admin/src/api/modules/tools.js @@ -3,7 +3,6 @@ export const toolsApiMethods = { * 扩展&增强 / 备份&导入 (Extensions & Tools) * ================================================================ */ - getHealthEmailVerification() { return this.request('/health/email-verification') }, getUpdateInfo() { return this.request('/tools/update-info') }, detectUpdate() { return this.request('/tools/update-check', { method: 'POST', body: JSON.stringify({}) }) }, dismissUpdate(version) { return this.request('/tools/update-dismiss', { method: 'POST', body: JSON.stringify({ version }) }) }, diff --git a/assets/admin/src/constants/sections/tools.js b/assets/admin/src/constants/sections/tools.js index ceae319e..bd6c6ef5 100644 --- a/assets/admin/src/constants/sections/tools.js +++ b/assets/admin/src/constants/sections/tools.js @@ -41,7 +41,7 @@ export const toolsSection = { label: '健康检查', route: '/tools/health', title: '健康检查', - desc: '查看主题环境、数据表、邮箱验证迁移和系统诊断状态。', + desc: '查看主题环境、数据表和系统诊断状态。', component: 'ToolsHealth', }), customPage({ diff --git a/assets/admin/src/pages/tools/ToolsHealth.vue b/assets/admin/src/pages/tools/ToolsHealth.vue index 7662bf9c..0093a97f 100644 --- a/assets/admin/src/pages/tools/ToolsHealth.vue +++ b/assets/admin/src/pages/tools/ToolsHealth.vue @@ -2,7 +2,7 @@
扫码登录暂不可用
'; - } - }) - .catch(function () { - container.innerHTML = '网络错误
'; - }); - } - - function startQrPolling(checkUrl, token) { - stopQrPolling(); - qrLoginTimer = setInterval(function () { - request(checkUrl, { token: token }) - .then(function (result) { - var data = (result && result.data) || result || {}; - var body = data.data || data; - if ((result && result.ok) || data.success || body.logged_in) { - if (body.logged_in || (data.data && data.data.logged_in)) { - stopQrPolling(); - onAuthSuccess(body.data || body); - } - } - }) - .catch(function () { /* keep polling */ }); - }, 2000); - } - - function stopQrPolling() { - if (qrLoginTimer) { - clearInterval(qrLoginTimer); - qrLoginTimer = null; - } - } - - // ── Social Login Priority (§10.7) ── - - function initSocialPriority() { - var wxPriority = window.aksrtData && aksrtData.signinWxPriority; - if (wxPriority && modal) { - var wxBtn = modal.querySelector('.social-login-item.weixingzh, .social-login-item[data-provider="wechat"]'); - if (wxBtn) { - setTimeout(function () { wxBtn.click(); }, 100); - } - } - } - - // ── Agreement Check: shake instead of disable (§10.7) ── - - function initAgreementShake() { - if (!modal) return; - modal.querySelectorAll('.aksrt-modal-auth__submit').forEach(function (btn) { - btn.addEventListener('click', function (e) { - var form = btn.closest('form'); - if (!form) return; - var agree = form.querySelector('[name="agree_terms"], [name="user_agreement"]'); - if (agree && !agree.checked) { - e.preventDefault(); - e.stopImmediatePropagation(); - agree.closest('label, .aksrt-modal-auth__agreement').classList.add('ani', 'shake'); - notyf('请先同意用户协议', 'warning'); - setTimeout(function () { - agree.closest('label, .aksrt-modal-auth__agreement').classList.remove('ani', 'shake'); - }, 400); - } - }); - }); - } - - // ── Password Visibility: unified (§10.7) ── - // Already handled above; added unification note. - - window.AksrtAuth = { - open: open, - close: close, - }; - - if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', init); - } else { - init(); - } -})(); diff --git a/assets/js/routes/user-center.js b/assets/js/routes/user-center.js index 3d5c3f66..024eeddc 100644 --- a/assets/js/routes/user-center.js +++ b/assets/js/routes/user-center.js @@ -901,6 +901,26 @@ if (!form) return; if (form.dataset.aksrtSettingsInitialized === 'true') return; form.dataset.aksrtSettingsInitialized = 'true'; + var sendButton = form.querySelector('[data-aksrt-sms-trigger]'); + if (sendButton) sendButton.addEventListener('click', function () { + var phone = String((form.querySelector('[name="phone"]') || {}).value || '').trim(); + if (!/^1[3-9]\d{9}$/.test(phone)) { notyf('请输入正确的手机号', 'warning'); return; } + var toastId = 'aksrt-settings-phone-code-toast'; + var restRoot = (window.aksrtData && aksrtData.apiUrl) || '/wp-json/aksrt/v1'; + var nonce = (window.aksrtData && aksrtData.restNonce) || ''; + sendButton.disabled = true; + notyf('正在发送验证码…', 'load', '', toastId); + fetch(String(restRoot).replace(/\/$/, '') + '/user/phone-code', { + method: 'POST', credentials: 'same-origin', + headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': nonce }, + body: JSON.stringify({ phone: phone }) + }).then(function (r) { return r.json().then(function (j) { return { ok: r.ok, json: j }; }); }).then(function (res) { + if (!res.ok || !res.json.success) throw new Error(res.json.message || '发送失败'); + notyf(res.json.message || '验证码已发送', 'success', 5, toastId); + var left = Number((res.json.data && res.json.data.countdown) || 60); + var timer = setInterval(function () { left -= 1; sendButton.textContent = left + 's'; if (left <= 0) { clearInterval(timer); sendButton.disabled = false; sendButton.textContent = '获取验证码'; } }, 1000); + }).catch(function (error) { sendButton.disabled = false; notyf(error.message || '发送失败', 'danger', 5, toastId); }); + }); form.addEventListener('submit', function (e) { e.preventDefault(); var phoneEl = form.querySelector('[name="phone"]'); @@ -946,7 +966,7 @@ var captcha = collectCaptcha(form); var restRoot = (window.aksrtData && aksrtData.apiUrl) || '/wp-json/aksrt/v1'; var nonce = (window.aksrtData && aksrtData.restNonce) || ''; - fetch(String(restRoot).replace(/\/$/, '') + '/auth/bind-phone', { + fetch(String(restRoot).replace(/\/$/, '') + '/user/phone', { method: 'POST', credentials: 'same-origin', headers: { diff --git a/assets/js/runtime/loader.js b/assets/js/runtime/loader.js index 27ba0a4e..59bfd052 100644 --- a/assets/js/runtime/loader.js +++ b/assets/js/runtime/loader.js @@ -64,7 +64,6 @@ tbquire.config({ 'main' : 'main.min', 'poster-share' : 'poster-share.min', 'slidercaptcha' : 'slidercaptcha.min', - 'sign-register' : 'sign-register', 'comment' : 'comment.min', 'page-template' : 'page-template.min', 'page-edit' : 'page-edit.min', diff --git a/assets/js/runtime/main.js b/assets/js/runtime/main.js index 8afba85f..379d60fc 100644 --- a/assets/js/runtime/main.js +++ b/assets/js/runtime/main.js @@ -2460,9 +2460,6 @@ function z_auto_fun() { container: 'body', }); - //登录注册 - $('.signsubmit-loader').length && tbquire(['sign-register']); - //复制内容 $('[data-clipboard-text]').length && tbquire(['clipboard']); @@ -3025,114 +3022,6 @@ _win.bd.on('click', '[ajax-action]', function () { }); }); -//登录注册 -_win.bd.on('click', '.signin-loader', function () { - if (_win.sign_type == 'page') { - window.location.href = _win.signin_url; - window.location.reload; - } else { - $('.modal:not(#u_sign)').modal('hide'); - $('#u_sign').modal('show'); - - if (_win.signin_wx_priority) { - $('a[href="#tab-qrcode-signin"]').tab('show'); - $('.social-login-item.weixingzh:first').click(); - } else { - $('a[href="#tab-sign-in"]').tab('show'); - } - } -}); - -_win.bd.on('click', '.signup-loader', function () { - if (_win.sign_type == 'page') { - window.location.href = _win.signup_url; - window.location.reload; - } else { - $('.modal:not(#u_sign)').modal('hide'); - $('#u_sign').modal('show'); - $('a[href="#tab-sign-up"]').tab('show'); - } -}); - -//扫码登录、绑定 -_win.bd.on('click', '.qrcode-signin', function () { - $('.modal:not(#u_sign)').modal('hide'); - $('#u_sign').modal('show'); - $('a[href="#tab-qrcode-signin"]').tab('show'); - - var _this = $(this); - var url = _this.attr('href'); - var container = $('.qrcode-signin-container'); - if (container.find('[get-only-one]').length) { - container.find('[name="code"]').val('').trigger('change'); - return !1; - } - - container.html(''); - - $.post( - url, - null, - function (n) { - n || notyf(aksrt__('qrcode_failed'), 'danger'); - var ys = n.ys ? n.ys : n.error ? 'danger' : ''; - n.msg && notyf(n.msg, ys); - if (n && n.html) { - container.html(n.html); - _win.qrcode_signin = { - url: n.url, - state: n.state, - }; - checkLogin(); - } - }, - 'json' - ); - return !1; -}); - -function checkLogin() { - if (_win.checkLoginTimer) return; - - //循环执行调整到ajax外部,避免移动端截图扫码,停止查询登录 - // 2秒后再次查询 - _win.checkLoginTimer = setInterval(checkLoginAjax, 2000); -} - -function checkLoginAjax() { - var url = _win.qrcode_signin.url; - var state = _win.qrcode_signin.state; - if (!url || !state) { - clearInterval(_win.checkLoginTimer); - _win.checkLoginTimer = false; - return; - } - $.post( - url, - { - state: state, - oauth_rurl: window.location.href, - action: 'check_callback', - }, - function (n) { - //做逻辑判断,登录跳转 - if (n && n.goto) { - window.location.href = n.goto; - window.location.reload; - } - }, - 'json' - ); -} - -//模态框关闭停止查询登录 -_win.bd.on('hide.bs.modal', '.modal#u_sign', function () { - _win.qrcode_signin = { - url: false, - state: false, - }; -}); - //防抖函数 function throttle(fn, delay) { let valid = true; diff --git a/assets/js/runtime/main.min.js b/assets/js/runtime/main.min.js index 70851495..0b2ad9d8 100644 --- a/assets/js/runtime/main.min.js +++ b/assets/js/runtime/main.min.js @@ -1,3 +1 @@ -function aksrt__(t){var e=window._win&&window._win.i18n;return e&&Object.prototype.hasOwnProperty.call(e,t)?e[t]:t}function aksrt_is_fatal_error_response(t){return t&&(t.indexOf("Fatal error")>-1||t.indexOf("致命错误")>-1)}function aksrt_sprintf(t){var e=Array.prototype.slice.call(arguments,1),a=aksrt__(t),i=0;return a.replace(/%(\d+\$)?s/g,function(t,a){var n=a?parseInt(a,10)-1:i++;return void 0!==e[n]&&null!==e[n]?e[n]:t})}function only_drop(t){var e=$(t.target).closest(".dropup,.dropdown");$(".dropup.open,.dropdown.open").not(e).removeClass("open")}function new_dplayer(){var t=".new-dplayer:not(.dplayer-initialized),[data-dplayer]:not(.dplayer-initialized)";$(t).length&&$(t).each(function(){var t=$(this);get_new_dplayer(t,function(e){t.addClass("dplayer-initialized").siblings(".dplayer-featured").find(".switch-video").each(function(){var t=$(this),a=t.attr("video-url"),i=t.attr("video-pic");t.data({"video-url":a,"video-pic":i}),t.attr({"dplayer-id":e.container.id}),setTimeout(function(){t.removeAttr("video-url").removeAttr("video-pic")},50)})})})}function get_new_dplayer(t,e){var a={},i=t.attr("video-url")||t.data("video-url"),n=t.attr("data-volume"),o=parseInt((Math.random()+1)*Math.pow(10,4));if(!t.find(".dplayer-video-wrap").length&&i){t.attr("id","dplayer-"+o),a.container=document.getElementById("dplayer-"+o),a.theme="#f04494",a.video={url:i,pic:t.attr("video-pic")||"",type:t.attr("video-type")||"auto"},t.attr("data-loop")&&(a.loop=!0),t.attr("data-autoplay")&&(a.autoplay=!0),n&&n<1&&(a.volume=n),"none"===n&&(a.volume=0);var r=t.attr("video-option");if(r){try{r=JSON.parse(r)}catch(t){console.log(t)}$.extend(a,r)}var s=i.toLowerCase();return-1!=s.indexOf(".m3u")&&_win.dplayer.tbquire.push("hls"),-1!=s.indexOf(".mpd")&&_win.dplayer.tbquire.push("dash"),-1!=s.indexOf(".flv")&&_win.dplayer.tbquire.push("flv"),$("link#dplayer").length||$("dplayer").append(''),_win.dplayer.tbquire.push("dplayer"),tbquire(_win.dplayer.tbquire,function(){try{var i=new DPlayer(a);i.on("play",function(){_win.dplayer.play=i}),t.data("dplayer",i),_win.dplayer.obj[o]=i,t.attr({"video-url":"","video-pic":""}),$.isFunction(e)&&e(i)}catch(t){console.error(t)}}),o}}function thumb_dplayer(t,e){function a(t,e,a){e.find(".dplayer-thumb").removeClass(c),_win.dplayer.thumb_play=a,e.find(".progress").show(),t.addClass(d)}function i(t,e,a){a.pause(),e.find(".dplayer-thumb").addClass(c),_win.dplayer.thumb_play=!1,e.find(".progress").hide(),t.removeClass(d)}function n(){if(_win.dplayer.thumb_play){var a=$(_win.dplayer.thumb_play.container).closest(e),n=a.closest(t);i(n,a,_win.dplayer.thumb_play)}}function o(){function t(t){t.on("loadeddata",function(){setTimeout(function(){n.find(".progress-bar").css({width:"100%",opacity:"0",transition:"width .2s, opacity .3s .3s"})},100),setTimeout(function(){n.find(".progress").remove()},800)}),setTimeout(function(){a(i,n,t),t.play()},200)}var i=$(this),n=i.find(e);if(n.length){var o=n.find(".dplayer-thumb").data("dplayer");if(o)return a(i,n,o),o.play();if(!n.find(".dplayer-thumb").length){var r=n.attr("video-url"),s=n.attr("data-volume")||"none",l="",d=~~n.attr("scale-height"),c='- ' . esc_html($provider_name) . ''); ?> -
- - - - - - - - - - - - -