feat: refresh content workflow and verification settings
All checks were successful
docker-images / build-and-push (admin, admin, termi-astro-admin, admin/Dockerfile) (push) Successful in 43s
docker-images / build-and-push (backend, backend, termi-astro-backend, backend/Dockerfile) (push) Successful in 25m9s
docker-images / build-and-push (frontend, frontend, termi-astro-frontend, frontend/Dockerfile) (push) Successful in 51s
All checks were successful
docker-images / build-and-push (admin, admin, termi-astro-admin, admin/Dockerfile) (push) Successful in 43s
docker-images / build-and-push (backend, backend, termi-astro-backend, backend/Dockerfile) (push) Successful in 25m9s
docker-images / build-and-push (frontend, frontend, termi-astro-frontend, frontend/Dockerfile) (push) Successful in 51s
This commit is contained in:
@@ -14,8 +14,10 @@ interface Props {
|
||||
const { requestUrl, siteSettings } = Astro.props as Props;
|
||||
const subscribeApiUrl = `${resolvePublicApiBaseUrl(requestUrl)}/subscriptions`;
|
||||
const browserPushApiUrl = `${resolvePublicApiBaseUrl(requestUrl)}/subscriptions/browser-push`;
|
||||
const captchaApiUrl = `${resolvePublicApiBaseUrl(requestUrl)}/comments/captcha`;
|
||||
const popupSettings = siteSettings.subscriptions;
|
||||
const turnstileSiteKey = popupSettings.turnstileEnabled
|
||||
const verificationMode = popupSettings.verificationMode;
|
||||
const turnstileSiteKey = verificationMode === 'turnstile'
|
||||
? popupSettings.turnstileSiteKey || resolvePublicCommentTurnstileSiteKey()
|
||||
: '';
|
||||
const webPushPublicKey = popupSettings.webPushEnabled
|
||||
@@ -29,7 +31,9 @@ const webPushPublicKey = popupSettings.webPushEnabled
|
||||
data-subscription-popup-root
|
||||
data-api-url={subscribeApiUrl}
|
||||
data-browser-push-api-url={browserPushApiUrl}
|
||||
data-captcha-url={captchaApiUrl}
|
||||
data-delay-ms={String(Math.max(popupSettings.popupDelaySeconds, 3) * 1000)}
|
||||
data-verification-mode={verificationMode}
|
||||
data-turnstile-site-key={turnstileSiteKey || undefined}
|
||||
data-web-push-public-key={webPushPublicKey || undefined}
|
||||
hidden
|
||||
@@ -137,16 +141,48 @@ const webPushPublicKey = popupSettings.webPushEnabled
|
||||
/>
|
||||
</label>
|
||||
|
||||
{turnstileSiteKey && (
|
||||
{verificationMode !== 'off' && (
|
||||
<div class="mt-4 rounded-2xl border border-[var(--border-color)] bg-[var(--header-bg)]/60 px-4 py-3">
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<p class="text-xs font-semibold uppercase tracking-[0.18em] text-[var(--text-tertiary)]">
|
||||
人机验证
|
||||
</p>
|
||||
<span class="text-xs text-[var(--text-tertiary)]">Cloudflare Turnstile</span>
|
||||
{verificationMode === 'turnstile' ? (
|
||||
<span class="text-xs text-[var(--text-tertiary)]">Cloudflare Turnstile</span>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
class="terminal-action-button px-3 py-2 text-xs"
|
||||
data-subscription-popup-refresh-captcha
|
||||
>
|
||||
<i class="fas fa-rotate-right"></i>
|
||||
<span>刷新</span>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<div class="mt-3" data-subscription-popup-turnstile></div>
|
||||
<input type="hidden" name="turnstileToken" />
|
||||
{verificationMode === 'turnstile' ? (
|
||||
<>
|
||||
<div class="mt-3" data-subscription-popup-turnstile></div>
|
||||
<input type="hidden" name="turnstileToken" />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<p
|
||||
class="mt-3 text-sm text-[var(--text-secondary)]"
|
||||
data-subscription-popup-captcha-question
|
||||
>
|
||||
加载中...
|
||||
</p>
|
||||
<input type="hidden" name="captchaToken" />
|
||||
<input
|
||||
type="text"
|
||||
name="captchaAnswer"
|
||||
inputmode="numeric"
|
||||
placeholder="请输入上方答案"
|
||||
class="mt-3 terminal-form-input"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -199,8 +235,12 @@ const webPushPublicKey = popupSettings.webPushEnabled
|
||||
const dismissButton = root.querySelector('[data-subscription-popup-dismiss]');
|
||||
const apiUrl = root.getAttribute('data-api-url');
|
||||
const browserPushApiUrl = root.getAttribute('data-browser-push-api-url');
|
||||
const captchaApiUrl = root.getAttribute('data-captcha-url') || '/api/comments/captcha';
|
||||
const browserPushPublicKey = root.getAttribute('data-web-push-public-key') || '';
|
||||
const browserPushButton = root.querySelector('[data-subscription-popup-browser-push]');
|
||||
const verificationMode = root.getAttribute('data-verification-mode') || 'off';
|
||||
const useTurnstile = verificationMode === 'turnstile';
|
||||
const useCaptcha = verificationMode === 'captcha';
|
||||
const turnstileSiteKey = root.getAttribute('data-turnstile-site-key') || '';
|
||||
const turnstileContainer = root.querySelector(
|
||||
'[data-subscription-popup-turnstile]',
|
||||
@@ -208,6 +248,18 @@ const webPushPublicKey = popupSettings.webPushEnabled
|
||||
const turnstileTokenInput = form?.querySelector(
|
||||
'input[name="turnstileToken"]',
|
||||
) as HTMLInputElement | null;
|
||||
const captchaQuestion = root.querySelector(
|
||||
'[data-subscription-popup-captcha-question]',
|
||||
) as HTMLElement | null;
|
||||
const refreshCaptchaButton = root.querySelector(
|
||||
'[data-subscription-popup-refresh-captcha]',
|
||||
) as HTMLButtonElement | null;
|
||||
const captchaTokenInput = form?.querySelector(
|
||||
'input[name="captchaToken"]',
|
||||
) as HTMLInputElement | null;
|
||||
const captchaAnswerInput = form?.querySelector(
|
||||
'input[name="captchaAnswer"]',
|
||||
) as HTMLInputElement | null;
|
||||
const pathname = window.location.pathname || '/';
|
||||
const delayMs = Math.max(3000, Number(root.getAttribute('data-delay-ms') || '18000'));
|
||||
const defaultStatus = status instanceof HTMLElement ? status.textContent?.trim() || '' : '';
|
||||
@@ -322,8 +374,10 @@ const webPushPublicKey = popupSettings.webPushEnabled
|
||||
if (focusEmail && shouldFocusEmail()) {
|
||||
emailInput.focus({ preventScroll: true });
|
||||
}
|
||||
if (turnstileSiteKey) {
|
||||
if (useTurnstile) {
|
||||
void ensureTurnstile(false);
|
||||
} else if (useCaptcha) {
|
||||
void loadCaptcha(false);
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -408,13 +462,42 @@ const webPushPublicKey = popupSettings.webPushEnabled
|
||||
}
|
||||
};
|
||||
|
||||
const resetHumanCheck = () => {
|
||||
if (!turnstileSiteKey || !turnstileTokenInput) {
|
||||
const loadCaptcha = async (showError = true) => {
|
||||
if (!captchaQuestion || !captchaTokenInput || !captchaAnswerInput) {
|
||||
return;
|
||||
}
|
||||
|
||||
turnstileTokenInput.value = '';
|
||||
turnstileWidget?.reset();
|
||||
captchaQuestion.textContent = '加载中...';
|
||||
captchaTokenInput.value = '';
|
||||
captchaAnswerInput.value = '';
|
||||
|
||||
try {
|
||||
const response = await fetch(captchaApiUrl);
|
||||
if (!response.ok) {
|
||||
throw new Error(await response.text());
|
||||
}
|
||||
|
||||
const payload = (await response.json()) as { token?: string; question?: string };
|
||||
captchaTokenInput.value = payload.token || '';
|
||||
captchaQuestion.textContent = payload.question || '请刷新验证码';
|
||||
} catch (error) {
|
||||
captchaQuestion.textContent = '验证码加载失败,请刷新重试';
|
||||
if (showError) {
|
||||
setError(error instanceof Error ? error.message : '验证码加载失败,请刷新后重试。');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const resetHumanCheck = () => {
|
||||
if (useTurnstile && turnstileTokenInput) {
|
||||
turnstileTokenInput.value = '';
|
||||
turnstileWidget?.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
if (useCaptcha) {
|
||||
void loadCaptcha(false);
|
||||
}
|
||||
};
|
||||
|
||||
const syncBrowserPushState = async () => {
|
||||
@@ -481,6 +564,9 @@ const webPushPublicKey = popupSettings.webPushEnabled
|
||||
});
|
||||
|
||||
dismissButton.addEventListener('click', () => closePopup(true));
|
||||
refreshCaptchaButton?.addEventListener('click', () => {
|
||||
void loadCaptcha(false);
|
||||
});
|
||||
|
||||
window.addEventListener('keydown', (event) => {
|
||||
if (event.key === 'Escape' && opened) {
|
||||
@@ -495,12 +581,24 @@ const webPushPublicKey = popupSettings.webPushEnabled
|
||||
return;
|
||||
}
|
||||
|
||||
if (turnstileSiteKey) {
|
||||
if (useTurnstile) {
|
||||
const token = turnstileTokenInput?.value.trim() || '';
|
||||
if (!token) {
|
||||
setError('请先完成人机验证。');
|
||||
return;
|
||||
}
|
||||
} else if (useCaptcha) {
|
||||
const captchaToken = captchaTokenInput?.value.trim() || '';
|
||||
const captchaAnswer = captchaAnswerInput?.value.trim() || '';
|
||||
if (!captchaToken) {
|
||||
setError('验证码加载失败,请刷新后重试。');
|
||||
return;
|
||||
}
|
||||
if (!captchaAnswer) {
|
||||
setError('请先填写验证码答案。');
|
||||
captchaAnswerInput?.focus();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
setPending('正在申请浏览器通知权限...');
|
||||
@@ -517,6 +615,8 @@ const webPushPublicKey = popupSettings.webPushEnabled
|
||||
subscription,
|
||||
source: 'frontend-popup',
|
||||
turnstileToken: turnstileTokenInput?.value || undefined,
|
||||
captchaToken: captchaTokenInput?.value || undefined,
|
||||
captchaAnswer: captchaAnswerInput?.value || undefined,
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -554,12 +654,24 @@ const webPushPublicKey = popupSettings.webPushEnabled
|
||||
return;
|
||||
}
|
||||
|
||||
if (turnstileSiteKey) {
|
||||
if (useTurnstile) {
|
||||
const token = String(formData.get('turnstileToken') || '').trim();
|
||||
if (!token) {
|
||||
setError('请先完成人机验证。');
|
||||
return;
|
||||
}
|
||||
} else if (useCaptcha) {
|
||||
const captchaToken = String(formData.get('captchaToken') || '').trim();
|
||||
const captchaAnswer = String(formData.get('captchaAnswer') || '').trim();
|
||||
if (!captchaToken) {
|
||||
setError('验证码加载失败,请刷新后重试。');
|
||||
return;
|
||||
}
|
||||
if (!captchaAnswer) {
|
||||
setError('请先填写验证码答案。');
|
||||
captchaAnswerInput?.focus();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
setPending('正在提交订阅申请...');
|
||||
@@ -575,6 +687,8 @@ const webPushPublicKey = popupSettings.webPushEnabled
|
||||
displayName,
|
||||
source: 'frontend-popup',
|
||||
turnstileToken: formData.get('turnstileToken'),
|
||||
captchaToken: formData.get('captchaToken'),
|
||||
captchaAnswer: formData.get('captchaAnswer'),
|
||||
}),
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user