Geheimer Vorschau-Link: /vorschau/<subdomain>/<token> setzt ein Cookie nur fuer diese Preview
- websiteProjects.shareToken (scripts/vorschau-link-setup.mjs) - POST/DELETE /api/admin/website-projects/:id/vorschau-link fuer Mitarbeiter (neu erzeugen / abschalten) - GET /api/vorschau-link/pruefen fuer die Login-Pruefung der Previews (preview-portal /auth/verify) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
594aa9196a
commit
2873638b1c
6 files changed
+368
No files matched your search
@@ -7,6 +7,7 @@ import {
|
||||
import { assignProject, reconcileProject } from '../../services/projectAssignment.js'
|
||||
import { completeAssignment } from '../../services/projectKindAssign.js'
|
||||
import { requireAppwriteTeam } from '../../middleware/appwriteStaff.js'
|
||||
import { renewShareLink, removeShareLink } from '../../services/vorschauLink.js'
|
||||
|
||||
const router = Router()
|
||||
|
||||
@@ -127,4 +128,21 @@ router.post('/:id/pushes', async (req, res) => {
|
||||
}
|
||||
})
|
||||
|
||||
// Geheimer Vorschau-Link: neu erzeugen (alter Link wird ungueltig) oder abschalten
|
||||
router.post('/:id/vorschau-link', async (req, res) => {
|
||||
try {
|
||||
return res.json({ ok: true, ...(await renewShareLink(req.params.id)) })
|
||||
} catch (err) {
|
||||
return res.status(err.status || 500).json({ error: err.message || 'Link konnte nicht erzeugt werden' })
|
||||
}
|
||||
})
|
||||
|
||||
router.delete('/:id/vorschau-link', async (req, res) => {
|
||||
try {
|
||||
return res.json({ ok: true, ...(await removeShareLink(req.params.id)) })
|
||||
} catch (err) {
|
||||
return res.status(err.status || 500).json({ error: err.message || 'Link konnte nicht abgeschaltet werden' })
|
||||
}
|
||||
})
|
||||
|
||||
export default router
|
||||
@@ -0,0 +1,59 @@
|
||||
import { Router } from 'express'
|
||||
import { checkShare, cookieName, previewUrl, shareCookieOptions } from '../services/vorschauLink.js'
|
||||
|
||||
// Geheimer Vorschau-Link, siehe services/vorschauLink.js
|
||||
|
||||
function seite(res, status, titel, text) {
|
||||
res
|
||||
.status(status)
|
||||
.set('Cache-Control', 'no-store')
|
||||
.set('X-Robots-Tag', 'noindex, nofollow')
|
||||
.type('html')
|
||||
.send(`<!doctype html>
|
||||
<html lang="de"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="robots" content="noindex, nofollow"><title>${titel}</title>
|
||||
<style>body{font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;background:#f6f7fb;color:#1a1f36;margin:0;display:grid;place-items:center;min-height:100vh;padding:16px;box-sizing:border-box}
|
||||
main{background:#fff;border-radius:12px;padding:32px;max-width:420px;box-shadow:0 1px 3px rgba(0,0,0,.08)}h1{font-size:20px;margin:0 0 12px}p{margin:0;line-height:1.5;color:#4f566b}</style>
|
||||
</head><body><main><h1>${titel}</h1><p>${text}</p></main></body></html>`)
|
||||
}
|
||||
|
||||
/** Seite: GET /vorschau/:subdomain/:token -> Cookie setzen und auf die Preview weiterleiten */
|
||||
export function vorschauLinkSeite() {
|
||||
const router = Router()
|
||||
router.get('/:subdomain/:token', async (req, res) => {
|
||||
let result
|
||||
try {
|
||||
result = await checkShare(req.params.subdomain, req.params.token)
|
||||
} catch (err) {
|
||||
console.error('[vorschau-link] Pruefung fehlgeschlagen:', err.message)
|
||||
return seite(res, 500, 'Vorschau nicht erreichbar', 'Bitte in ein paar Minuten noch einmal versuchen.')
|
||||
}
|
||||
if (result.reason === 'not_ready') {
|
||||
return seite(res, 503, 'Vorschau wird vorbereitet', 'Die Seite wird gerade gebaut. Bitte in ein paar Minuten noch einmal versuchen.')
|
||||
}
|
||||
if (!result.allowed) {
|
||||
return seite(res, 404, 'Link nicht gültig', 'Dieser Vorschau-Link ist nicht (mehr) gültig. Bitte frag bei WEBklar nach einem neuen Link.')
|
||||
}
|
||||
res.cookie(cookieName(result.subdomain), req.params.token, shareCookieOptions())
|
||||
res.set('Cache-Control', 'no-store')
|
||||
res.set('Referrer-Policy', 'no-referrer')
|
||||
return res.redirect(302, previewUrl(result.subdomain))
|
||||
})
|
||||
return router
|
||||
}
|
||||
|
||||
/** Fuer die Login-Pruefung der Previews: GET /api/vorschau-link/pruefen?subdomain=..., Header x-vorschau-token */
|
||||
export function vorschauLinkPruefung() {
|
||||
const router = Router()
|
||||
router.get('/pruefen', async (req, res) => {
|
||||
res.set('Cache-Control', 'no-store')
|
||||
try {
|
||||
const result = await checkShare(req.query.subdomain, req.get('x-vorschau-token'))
|
||||
return res.status(result.allowed ? 200 : 403).json({ allowed: result.allowed, reason: result.reason })
|
||||
} catch (err) {
|
||||
console.error('[vorschau-link] Pruefung fehlgeschlagen:', err.message)
|
||||
return res.status(500).json({ allowed: false })
|
||||
}
|
||||
})
|
||||
return router
|
||||
}
|
||||
Reference in new issue
Block a user