chore: aktuellen Live-Stand sichern (Admin-API, Services, Auth-Erweiterungen)
Sichert den bisher uncommitteten Produktionsstand des Kundenbereich-Servers, damit kuenftige Deploys (git reset --hard) nichts mehr verwerfen. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -44,7 +44,7 @@ function renderProjectCard(project, features) {
|
||||
<h2>${escapeHtml(project.projectName || project.subdomain || 'Projekt')}</h2>
|
||||
<dl>
|
||||
<dt>Subdomain</dt><dd>${escapeHtml(project.subdomain || '–')}</dd>
|
||||
<dt>Vorschau</dt><dd>${project.previewUrl ? `<a href="${escapeAttr(project.previewUrl)}" target="_blank" rel="noopener">${escapeHtml(project.previewUrl)}</a>` : '–'}</dd>
|
||||
<dt>Vorschau</dt><dd>${project.previewUrl ? `<a class="btn btn-green" href="${escapeAttr(project.previewUrl)}">Projekt-Webseite öffnen</a>` : '–'}</dd>
|
||||
<dt>Live-Domain</dt><dd>${project.liveDomain ? escapeHtml(project.liveDomain) : '–'}</dd>
|
||||
<dt>Status</dt><dd>${escapeHtml(project.status || '–')}</dd>
|
||||
<dt>Bereitstellung</dt><dd>${escapeHtml(project.provisioningStatus || '–')}</dd>
|
||||
@@ -66,11 +66,84 @@ function escapeAttr(str) {
|
||||
return escapeHtml(str).replace(/'/g, ''')
|
||||
}
|
||||
|
||||
function getQueryParam(name) {
|
||||
return new URLSearchParams(window.location.search).get(name) || ''
|
||||
}
|
||||
|
||||
function safeRedirectUrl(url) {
|
||||
try {
|
||||
const u = new URL(url)
|
||||
if (u.hostname.endsWith('.project.webklar.com') || u.hostname === 'project.webklar.com') {
|
||||
return u.toString()
|
||||
}
|
||||
} catch {
|
||||
/* ignore invalid URL */
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function isProjectPreviewReady(project) {
|
||||
const status = (project?.provisioningStatus || project?.status || '').toLowerCase()
|
||||
return ['ready', 'deployed'].includes(status) && Boolean(project?.previewUrl)
|
||||
}
|
||||
|
||||
function previewSubdomainFromUrl(url) {
|
||||
try {
|
||||
const host = new URL(url).hostname.toLowerCase()
|
||||
const match = host.match(/^([a-z0-9-]+)\.project\.webklar\.com$/)
|
||||
return match ? match[1] : ''
|
||||
} catch {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
async function canAccessPreviewUrl(url) {
|
||||
const subdomain = previewSubdomainFromUrl(url)
|
||||
if (!subdomain) return false
|
||||
try {
|
||||
const access = await fetch(
|
||||
`/api/auth/preview-access?subdomain=${encodeURIComponent(subdomain)}`,
|
||||
{ credentials: 'same-origin' }
|
||||
).then((r) => r.json())
|
||||
return Boolean(access.allowed)
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
async function resolvePostLoginTarget() {
|
||||
const next = safeRedirectUrl(getQueryParam('next'))
|
||||
if (next && (await canAccessPreviewUrl(next))) {
|
||||
return next
|
||||
}
|
||||
|
||||
try {
|
||||
const { projects } = await api('/api/projects')
|
||||
const readyProjects = (projects || []).filter(isProjectPreviewReady)
|
||||
if (readyProjects.length === 1) {
|
||||
return readyProjects[0].previewUrl
|
||||
}
|
||||
} catch {
|
||||
/* dashboard fallback */
|
||||
}
|
||||
|
||||
return '/dashboard.html'
|
||||
}
|
||||
|
||||
async function postLoginRedirect() {
|
||||
window.location.href = await resolvePostLoginTarget()
|
||||
}
|
||||
|
||||
async function initLoginPage() {
|
||||
const form = document.getElementById('login-form')
|
||||
const errorEl = document.getElementById('login-error')
|
||||
const btn = document.getElementById('login-btn')
|
||||
|
||||
const errorFromQuery = getQueryParam('error')
|
||||
if (errorFromQuery) {
|
||||
showError(errorEl, errorFromQuery)
|
||||
}
|
||||
|
||||
async function applyCooldown(seconds) {
|
||||
if (seconds <= 0) return
|
||||
showError(
|
||||
@@ -106,7 +179,7 @@ async function initLoginPage() {
|
||||
try {
|
||||
const me = await api('/api/auth/me')
|
||||
if (me.authenticated && me.customer) {
|
||||
window.location.href = '/dashboard.html'
|
||||
await postLoginRedirect()
|
||||
return
|
||||
}
|
||||
} catch {
|
||||
@@ -129,7 +202,7 @@ async function initLoginPage() {
|
||||
password: document.getElementById('password').value,
|
||||
}),
|
||||
})
|
||||
window.location.href = '/dashboard.html'
|
||||
await postLoginRedirect()
|
||||
return
|
||||
} catch (err) {
|
||||
showError(errorEl, err.message)
|
||||
|
||||
Reference in New Issue
Block a user