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>
92 lines
2.6 KiB
JavaScript
92 lines
2.6 KiB
JavaScript
import { Router } from 'express'
|
|
import { config } from '../../config.js'
|
|
import { requireAppwriteStaff } from '../../middleware/appwriteStaff.js'
|
|
import { listDocuments, Query } from '../../services/appwriteAdmin.js'
|
|
import { createStaffPreviewToken } from '../../services/staffPreviewTokens.js'
|
|
|
|
const router = Router()
|
|
|
|
function isStaffAdmin(user) {
|
|
return (user?.labels || []).includes('admin')
|
|
}
|
|
|
|
function isPreviewProjectReady(project) {
|
|
if (!project) return false
|
|
if (project.hasPreview === false) return false
|
|
const status = (project.provisioningStatus || project.status || '').toLowerCase()
|
|
return ['ready', 'deployed'].includes(status)
|
|
}
|
|
|
|
function safePreviewUrl(url) {
|
|
try {
|
|
const parsed = new URL(String(url || ''))
|
|
if (
|
|
parsed.hostname.endsWith('.project.webklar.com') ||
|
|
parsed.hostname === 'project.webklar.com'
|
|
) {
|
|
return parsed.toString()
|
|
}
|
|
} catch {
|
|
/* ignore */
|
|
}
|
|
return null
|
|
}
|
|
|
|
function previewUrlForSubdomain(subdomain) {
|
|
const safe = String(subdomain || '')
|
|
.trim()
|
|
.toLowerCase()
|
|
.replace(/[^a-z0-9-]/g, '')
|
|
if (!safe) return null
|
|
return `https://${safe}.${config.preview.baseHost}`
|
|
}
|
|
|
|
router.use(requireAppwriteStaff)
|
|
|
|
router.post('/', async (req, res) => {
|
|
const user = req.appwriteUser
|
|
if (!isStaffAdmin(user)) {
|
|
return res.status(403).json({ error: 'Admin-Berechtigung erforderlich' })
|
|
}
|
|
|
|
const subdomain = String(req.body?.subdomain || '')
|
|
.trim()
|
|
.toLowerCase()
|
|
const previewUrl = safePreviewUrl(req.body?.previewUrl) || previewUrlForSubdomain(subdomain)
|
|
|
|
if (!subdomain || !previewUrl) {
|
|
return res.status(400).json({ error: 'subdomain oder gueltige previewUrl erforderlich' })
|
|
}
|
|
|
|
try {
|
|
const projects = await listDocuments(config.collections.websiteProjects, [
|
|
Query.equal('subdomain', subdomain),
|
|
Query.limit(1),
|
|
])
|
|
const project = projects[0]
|
|
if (!project) {
|
|
return res.status(404).json({ error: 'Projekt nicht gefunden' })
|
|
}
|
|
if (!isPreviewProjectReady(project)) {
|
|
return res.status(409).json({ error: 'Projekt-Vorschau ist noch nicht bereit' })
|
|
}
|
|
|
|
const token = createStaffPreviewToken({
|
|
userId: user.$id,
|
|
email: user.email || '',
|
|
name: user.name || user.email || 'Admin',
|
|
previewUrl,
|
|
customerId: project.customerId || '',
|
|
})
|
|
|
|
const portalBase = `https://${config.preview.baseHost}`
|
|
return res.json({
|
|
openUrl: `${portalBase}/api/auth/staff-preview?token=${encodeURIComponent(token)}`,
|
|
})
|
|
} catch (err) {
|
|
return res.status(err.status || 500).json({ error: err.message || 'Preview-Login fehlgeschlagen' })
|
|
}
|
|
})
|
|
|
|
export default router
|