Preview: Bereitschaft aus echtem Zustand statt hasPreview (Standardwert false sperrte Previews)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
04b45e4207
commit
c37dd49b05
4 files changed
+87
-14
No files matched your search
@@ -2,6 +2,7 @@ import { Router } from 'express'
|
||||
import { config } from '../../config.js'
|
||||
import { requireAppwriteStaff } from '../../middleware/appwriteStaff.js'
|
||||
import { listDocuments, Query } from '../../services/appwriteAdmin.js'
|
||||
import { isPreviewProjectReady } from '../../services/previewReadiness.js'
|
||||
import { createStaffPreviewToken } from '../../services/staffPreviewTokens.js'
|
||||
|
||||
const router = Router()
|
||||
@@ -10,13 +11,6 @@ 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 || ''))
|
||||
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
previewSessionCookieOptions,
|
||||
verifyPreviewSessionToken,
|
||||
} from '../services/previewSession.js'
|
||||
import { isPreviewProjectReady } from '../services/previewReadiness.js'
|
||||
import { consumeStaffPreviewToken } from '../services/staffPreviewTokens.js'
|
||||
import { resolveStaffAccount } from '../services/staffAccount.js'
|
||||
import { MFA_COOKIE, MFA_TTL_MS, resendStaffMfa, startStaffMfa, verifyStaffMfa } from '../services/staffMfa.js'
|
||||
@@ -120,13 +121,6 @@ async function resolveSessionCustomer(session) {
|
||||
return customer
|
||||
}
|
||||
|
||||
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 isAdminSession(session) {
|
||||
return session?.role === 'admin' || (session?.labels || []).includes('admin')
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Ist die Vorschau eines Projekts freigabebereit? Wird von /api/auth/preview-access und
|
||||
* dem Staff-Preview-Open genutzt.
|
||||
*
|
||||
* `hasPreview` wird bewusst NICHT mehr ausgewertet: Das Appwrite-Attribut wurde am 22.07.2026
|
||||
* mit Standardwert false angelegt und wird seit Commit 617d463 nicht mehr gepflegt, daher
|
||||
* waere jedes neuere Projekt faelschlich gesperrt. Massgeblich ist der echte Zustand.
|
||||
*/
|
||||
export function isPreviewProjectReady(project) {
|
||||
if (!project) return false
|
||||
if (!String(project.subdomain || '').trim()) return false
|
||||
if (project.previewMode === 'off') return false
|
||||
if (project.projectType === 'repo' || project.projectType === 'project') return false
|
||||
const status = String(project.provisioningStatus || project.status || '').toLowerCase()
|
||||
return ['ready', 'deployed'].includes(status)
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// Preview-Bereitschaft: echter Zustand statt hasPreview (Standardwert false sperrte Previews)
|
||||
import { test, before, after } from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import express from 'express'
|
||||
import cookieParser from 'cookie-parser'
|
||||
import { startFakeAppwrite } from './helpers/fakeAppwrite.js'
|
||||
import { isPreviewProjectReady } from '../server/services/previewReadiness.js'
|
||||
|
||||
const base = { subdomain: 'kunde-a', projectType: 'website', provisioningStatus: 'deployed', hasPreview: false }
|
||||
|
||||
test('hasPreview false + deployed/ready + Subdomain => bereit', () => {
|
||||
assert.equal(isPreviewProjectReady(base), true)
|
||||
assert.equal(isPreviewProjectReady({ ...base, provisioningStatus: undefined, status: 'Ready' }), true)
|
||||
})
|
||||
test('kein Projekt / keine Subdomain => nicht bereit', () => {
|
||||
assert.equal(isPreviewProjectReady(null), false)
|
||||
assert.equal(isPreviewProjectReady({ ...base, subdomain: '' }), false)
|
||||
assert.equal(isPreviewProjectReady({ ...base, subdomain: ' ' }), false)
|
||||
})
|
||||
test('previewMode off => nicht bereit', () => {
|
||||
assert.equal(isPreviewProjectReady({ ...base, previewMode: 'off' }), false)
|
||||
assert.equal(isPreviewProjectReady({ ...base, previewMode: 'dev' }), true)
|
||||
})
|
||||
test('projectType repo/project => nicht bereit', () => {
|
||||
assert.equal(isPreviewProjectReady({ ...base, projectType: 'repo' }), false)
|
||||
assert.equal(isPreviewProjectReady({ ...base, projectType: 'project' }), false)
|
||||
})
|
||||
test('Status pending/leer => nicht bereit', () => {
|
||||
assert.equal(isPreviewProjectReady({ ...base, provisioningStatus: 'pending' }), false)
|
||||
assert.equal(isPreviewProjectReady({ ...base, provisioningStatus: '', status: '' }), false)
|
||||
})
|
||||
|
||||
let aw, server, baseUrl, token
|
||||
before(async () => {
|
||||
aw = await startFakeAppwrite({
|
||||
seed: { websiteProjects: [{ $id: 'p1', customerId: 'c1', ...base }] },
|
||||
})
|
||||
Object.assign(process.env, { APPWRITE_ENDPOINT: aw.endpoint, APPWRITE_API_KEY: 'test-key', PREVIEW_SESSION_SECRET: 'x'.repeat(40) })
|
||||
const { createPreviewSessionToken } = await import('../server/services/previewSession.js')
|
||||
token = {
|
||||
c1: await createPreviewSessionToken({ userId: 'u1', customerId: 'c1', email: 'a@b.de' }),
|
||||
c2: await createPreviewSessionToken({ userId: 'u2', customerId: 'c2', email: 'c@d.de' }),
|
||||
}
|
||||
const { default: auth } = await import('../server/routes/auth.js')
|
||||
const app = express()
|
||||
app.use(cookieParser())
|
||||
app.use('/api/auth', auth)
|
||||
server = await new Promise((r) => { const s = app.listen(0, '127.0.0.1', () => r(s)) })
|
||||
baseUrl = `http://127.0.0.1:${server.address().port}`
|
||||
})
|
||||
after(async () => {
|
||||
server.closeAllConnections()
|
||||
await new Promise((r) => server.close(r))
|
||||
await aw.close()
|
||||
})
|
||||
|
||||
const access = async (t) => {
|
||||
const res = await fetch(`${baseUrl}/api/auth/preview-access?subdomain=kunde-a`, { headers: { Cookie: `preview_session=${t}` } })
|
||||
return res.json()
|
||||
}
|
||||
|
||||
test('Route: eigener Kunde, hasPreview false, deployed => allowed', async () => {
|
||||
assert.equal((await access(token.c1)).allowed, true)
|
||||
})
|
||||
test('Route: anderer Kunde bleibt ausgesperrt', async () => {
|
||||
const r = await access(token.c2)
|
||||
assert.equal(r.allowed, false)
|
||||
assert.equal(r.reason, 'no_project')
|
||||
})
|
||||
Reference in new issue
Block a user