import { PASSWORD_PAGE_ROUTE, SIGN_IN_ROUTE, WALL_PAGE_HELPER_NAME } from './constants' /** The TeleportHQ wordmark, inline so the page needs no asset behind the wall. */ const TELEPORT_LOGO_SVG = '' const escapeForJsString = (value: string): string => value.replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/\n/g, ' ') /** * `utils/site-wall-page.js`: the password page as HTML, rendered by the * sign-in route — a route can answer with any status, which a Next.js page * cannot promise, and the wall needs its 401 so nothing behind it is indexed. * Rendered on every request, never cached. The branding is TeleportHQ's; a * workspace that pays for hosting may show its own logo instead (Project * Settings). */ export const buildPasswordPageModule = ( options: { logoUrl?: string; siteTitle?: string } = {} ): string => { const customLogo = options.logoUrl && /^https?:\/\//.test(options.logoUrl) ? options.logoUrl : '' const title = escapeForJsString(options.siteTitle || '') return `// GENERATED by TeleportHQ — the password page of the site's wall (see utils/site-wall.js, ${WALL_PAGE_HELPER_NAME}). export var SITE_TITLE = '${title}'; export var CUSTOM_LOGO = '${customLogo}'; var SIGN_IN_ROUTE = '${SIGN_IN_ROUTE}'; var PASSWORD_PAGE = '${PASSWORD_PAGE_ROUTE}'; var TELEPORT_LOGO = ${JSON.stringify(TELEPORT_LOGO_SVG)}; function escapeHtml(value) { return String(value) .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } var STYLES = [ "html, body { margin: 0; min-height: 100%; background: #f4f4f6; }", "body { display: flex; align-items: center; justify-content: center; min-height: 100vh; padding: 24px; box-sizing: border-box; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; color: #121212; }", ".card { width: 100%; max-width: 420px; background: #fff; border-radius: 16px; box-shadow: 0 12px 40px rgba(18, 18, 18, 0.08); padding: 40px 32px 32px; box-sizing: border-box; text-align: center; }", ".logo { width: 100%; max-width: 260px; margin: 0 auto 28px; }", ".logo svg { width: 100%; height: auto; display: block; }", ".logo-custom { max-width: 220px; max-height: 120px; margin: 0 auto 28px; display: block; object-fit: contain; }", "h1 { font-size: 20px; font-weight: 600; margin: 0 0 8px; }", "p { font-size: 15px; line-height: 1.5; color: #55555f; margin: 0 0 24px; }", "input { width: 100%; box-sizing: border-box; font-size: 16px; padding: 12px 14px; border: 1px solid #d6d6de; border-radius: 10px; outline: none; margin-bottom: 12px; }", "input:focus { border-color: #121212; }", "button { width: 100%; font-size: 16px; font-weight: 600; padding: 12px 14px; border: none; border-radius: 10px; background: #121212; color: #fff; cursor: pointer; }", "button[disabled] { opacity: 0.7; cursor: default; }", ".error { font-size: 14px; color: #c62828; margin: 12px 0 0; min-height: 20px; }", ].join('\\n'); var SCRIPT = [ "(function () {", " var form = document.getElementById('tq-wall-form');", " var input = document.getElementById('tq-wall-password');", " var button = document.getElementById('tq-wall-submit');", " var error = document.getElementById('tq-wall-error');", " form.addEventListener('submit', function (event) {", " event.preventDefault();", " var password = input.value.trim();", " if (!password || button.disabled) return;", " button.disabled = true;", " button.textContent = 'Checking\\u2026';", " error.textContent = '';", " fetch('" + SIGN_IN_ROUTE + "', {", " method: 'POST',", " headers: { 'content-type': 'application/json', accept: 'application/json' },", " body: JSON.stringify({ password: password }),", " })", " .then(function (response) { return response.status; })", " .catch(function () { return 0; })", " .then(function (status) {", " if (status === 200) {", " if (window.location.pathname === '" + PASSWORD_PAGE + "') { window.location.replace('/'); } else { window.location.reload(); }", " return;", " }", " error.textContent = status === 401 ? 'That password is not right.' : status === 429 ? 'Too many tries. Wait a minute and try again.' : 'The password could not be checked right now. Try again in a minute.';", " button.disabled = false;", " button.textContent = 'Continue';", " input.focus();", " });", " });", "})();", ].join('\\n'); // The page, as HTML. Nothing in it is fetched from the site itself: the site's // files sit behind the very wall the page stands in front of. export function renderPasswordPage() { var title = SITE_TITLE ? escapeHtml(SITE_TITLE) + ' \\u2014 password required' : 'Password required'; var logo = CUSTOM_LOGO ? "" + escapeHtml(SITE_TITLE || " : ""; return [ '', "", '', "", "", "", '' + title + '', '', '', '', "
", logo, '

This site is password protected

', '

Enter the password to continue.

', "
", "", "", '
', "", '
', '', '', '', ].join('\\n'); } ` }