bugfixing :D
This commit is contained in:
+25
-2
@@ -1369,6 +1369,23 @@ function isTurnstileSessionVerified(req) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function protectedSiteSessionStatus(req) {
|
||||||
|
const turnstileVerified = isTurnstileSessionVerified(req)
|
||||||
|
const siteVerified = isSiteSessionVerified(req)
|
||||||
|
const canIssueSiteSession = turnstileVerified && !siteVerified && Boolean(SITE_SESSION_HMAC_KEY)
|
||||||
|
return {
|
||||||
|
turnstileVerified,
|
||||||
|
siteVerified,
|
||||||
|
canIssueSiteSession,
|
||||||
|
verified: turnstileVerified && siteVerified,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function protectedSiteSessionGateState(req) {
|
||||||
|
const sessionStatus = protectedSiteSessionStatus(req)
|
||||||
|
return sessionStatus.verified || sessionStatus.canIssueSiteSession ? 'verified' : 'required'
|
||||||
|
}
|
||||||
|
|
||||||
function callTurnstileSiteverify(token, remoteIp, idempotencyKey) {
|
function callTurnstileSiteverify(token, remoteIp, idempotencyKey) {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const params = new URLSearchParams()
|
const params = new URLSearchParams()
|
||||||
@@ -2776,7 +2793,7 @@ function htmlWithSeo(req, data) {
|
|||||||
.replaceAll('__SEO_PUBLISHED_TIME__', escapeHtml(seo.publishedAt || ''))
|
.replaceAll('__SEO_PUBLISHED_TIME__', escapeHtml(seo.publishedAt || ''))
|
||||||
.replaceAll('__SEO_MODIFIED_TIME__', escapeHtml(seo.publishedAt || ''))
|
.replaceAll('__SEO_MODIFIED_TIME__', escapeHtml(seo.publishedAt || ''))
|
||||||
.replaceAll('__SEO_JSON_LD__', routeStructuredData(origin, seo, canonicalUrl).replace(/</g, '\\u003c'))
|
.replaceAll('__SEO_JSON_LD__', routeStructuredData(origin, seo, canonicalUrl).replace(/</g, '\\u003c'))
|
||||||
.replaceAll('__TURNSTILE_SESSION__', isTurnstileSessionVerified(req) ? 'verified' : 'required')
|
.replaceAll('__TURNSTILE_SESSION__', protectedSiteSessionGateState(req))
|
||||||
.replace('<div id="root"></div>', `<div id="root">\n${routeFallbackHtml(seo)}\n</div>`)
|
.replace('<div id="root"></div>', `<div id="root">\n${routeFallbackHtml(seo)}\n</div>`)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2791,8 +2808,11 @@ function requestPathname(req) {
|
|||||||
function sendHtml(req, res, data, status) {
|
function sendHtml(req, res, data, status) {
|
||||||
const html = htmlWithSeo(req, data)
|
const html = htmlWithSeo(req, data)
|
||||||
const finalStatus = status ?? routeSeo(requestPathname(req)).status ?? 200
|
const finalStatus = status ?? routeSeo(requestPathname(req)).status ?? 200
|
||||||
|
const sessionStatus = protectedSiteSessionStatus(req)
|
||||||
|
const sessionCookie = sessionStatus.canIssueSiteSession ? buildSiteSessionCookie(req) : ''
|
||||||
send(res, finalStatus, html, {
|
send(res, finalStatus, html, {
|
||||||
...securityHeaders(req, { html: true }),
|
...securityHeaders(req, { html: true }),
|
||||||
|
...(sessionCookie ? { 'set-cookie': sessionCookie } : {}),
|
||||||
'content-type': mimeTypes['.html'],
|
'content-type': mimeTypes['.html'],
|
||||||
'cache-control': 'no-cache',
|
'cache-control': 'no-cache',
|
||||||
})
|
})
|
||||||
@@ -3379,7 +3399,10 @@ const server = http.createServer((req, res) => {
|
|||||||
sendJson(res, 403, { error: 'Turnstile session check is restricted to this site' })
|
sendJson(res, 403, { error: 'Turnstile session check is restricted to this site' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
sendJson(res, 200, { verified: isTurnstileSessionVerified(req) })
|
const sessionStatus = protectedSiteSessionStatus(req)
|
||||||
|
const sessionCookie = sessionStatus.canIssueSiteSession ? buildSiteSessionCookie(req) : ''
|
||||||
|
const headers = sessionCookie ? { 'set-cookie': sessionCookie } : {}
|
||||||
|
sendJson(res, 200, { verified: sessionStatus.verified || Boolean(sessionCookie) }, headers)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+17
-42
@@ -323,14 +323,8 @@ function runCapture(command, args, options = {}) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Files that, when changed, require a fresh `npm ci`. package.json is included
|
|
||||||
// because npm ci refuses to run against a package.json/lockfile that are out of
|
|
||||||
// sync, so a change to either should trigger a reinstall.
|
|
||||||
const DEPENDENCY_FILES = ['package.json', 'package-lock.json']
|
|
||||||
|
|
||||||
// Resolve the modules the build relies on. Returns the first missing module's
|
// Resolve the modules the build relies on. Returns the first missing module's
|
||||||
// name, or null if all are present. Used both to validate after an install and
|
// name, or null if all are present. Used to validate after install.
|
||||||
// to decide whether a skipped install left an incomplete node_modules.
|
|
||||||
function missingBuildDependency() {
|
function missingBuildDependency() {
|
||||||
try {
|
try {
|
||||||
require.resolve('@vitejs/plugin-react')
|
require.resolve('@vitejs/plugin-react')
|
||||||
@@ -351,35 +345,7 @@ function missingBuildDependency() {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
// True when any dependency file changed between previousHead and the current
|
async function ensureBuildDependencies() {
|
||||||
// HEAD. Defaults to true (reinstall) whenever we can't determine the range, so
|
|
||||||
// an uncertain state never skips a needed install.
|
|
||||||
async function dependencyFilesChanged(previousHead) {
|
|
||||||
if (!validGitSha(previousHead)) return true
|
|
||||||
try {
|
|
||||||
const changed = await runCapture('git', [
|
|
||||||
'diff',
|
|
||||||
'--name-only',
|
|
||||||
`${previousHead}..HEAD`,
|
|
||||||
'--',
|
|
||||||
...DEPENDENCY_FILES,
|
|
||||||
])
|
|
||||||
return changed.trim().length > 0
|
|
||||||
} catch {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function ensureBuildDependencies(previousHead) {
|
|
||||||
const depsChanged = await dependencyFilesChanged(previousHead)
|
|
||||||
// Skip the clean reinstall (which wipes node_modules and rebuilds native
|
|
||||||
// modules like better-sqlite3) only when no dependency files changed AND the
|
|
||||||
// existing node_modules already has everything the build needs.
|
|
||||||
const canSkip = !depsChanged && missingBuildDependency() === null
|
|
||||||
|
|
||||||
if (canSkip) {
|
|
||||||
console.log('deploy step skipped: npm ci (no package.json/package-lock.json changes)')
|
|
||||||
} else {
|
|
||||||
await run('npm', ['ci'], {
|
await run('npm', ['ci'], {
|
||||||
env: {
|
env: {
|
||||||
NODE_ENV: 'development',
|
NODE_ENV: 'development',
|
||||||
@@ -394,7 +360,6 @@ async function ensureBuildDependencies(previousHead) {
|
|||||||
if (missing) {
|
if (missing) {
|
||||||
throw new Error(`${missing} is missing after npm install; dependencies were not installed`)
|
throw new Error(`${missing} is missing after npm install; dependencies were not installed`)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function copyMissingFiles(fromDir, toDir) {
|
function copyMissingFiles(fromDir, toDir) {
|
||||||
@@ -643,10 +608,9 @@ async function deploy(push) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await notifyDeployStarted(push)
|
await notifyDeployStarted(push)
|
||||||
const previousHead = await runCapture('git', ['rev-parse', 'HEAD']).catch(() => null)
|
|
||||||
await run('git', ['pull', '--ff-only'])
|
await run('git', ['pull', '--ff-only'])
|
||||||
diff = await deployDiff(push)
|
diff = await deployDiff(push)
|
||||||
await ensureBuildDependencies(previousHead)
|
await ensureBuildDependencies()
|
||||||
await run('npm', ['run', 'build', '--', '--outDir', '../dist-next'])
|
await run('npm', ['run', 'build', '--', '--outDir', '../dist-next'])
|
||||||
validateBuiltDist()
|
validateBuiltDist()
|
||||||
await run('cargo', ['build', '--manifest-path', 'backend/Cargo.toml', '--release'])
|
await run('cargo', ['build', '--manifest-path', 'backend/Cargo.toml', '--release'])
|
||||||
@@ -738,43 +702,54 @@ const webhookServer = http.createServer((req, res) => {
|
|||||||
if (tooLarge) return
|
if (tooLarge) return
|
||||||
const rawBody = Buffer.concat(chunks)
|
const rawBody = Buffer.concat(chunks)
|
||||||
const push = safeJsonParse(rawBody)
|
const push = safeJsonParse(rawBody)
|
||||||
|
const event = String(req.headers['x-github-event'] || '')
|
||||||
|
const pushRef = String(push?.ref || '')
|
||||||
|
const repoFullName = String(push?.repository?.full_name || '')
|
||||||
|
const delivery = String(req.headers['x-github-delivery'] || 'unknown')
|
||||||
|
|
||||||
if (!verifySignature(rawBody, req.headers['x-hub-signature-256'])) {
|
if (!verifySignature(rawBody, req.headers['x-hub-signature-256'])) {
|
||||||
|
console.warn(`webhook delivery rejected: invalid signature delivery=${delivery} event=${event || '(missing)'}`)
|
||||||
json(res, 401, { error: 'Invalid signature' })
|
json(res, 401, { error: 'Invalid signature' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (req.headers['x-github-event'] !== 'push') {
|
console.log(`webhook delivery received: delivery=${delivery} event=${event || '(missing)'} ref=${pushRef || '(missing)'} repo=${repoFullName || '(missing)'}`)
|
||||||
|
|
||||||
|
if (event !== 'push') {
|
||||||
|
console.log(`webhook delivery skipped: delivery=${delivery} reason=non-push event=${event || '(missing)'}`)
|
||||||
json(res, 202, { skipped: true, reason: 'Only push events trigger deploys' })
|
json(res, 202, { skipped: true, reason: 'Only push events trigger deploys' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const pushRef = String(push?.ref || '')
|
|
||||||
if (!ALLOWED_REFS.has(pushRef)) {
|
if (!ALLOWED_REFS.has(pushRef)) {
|
||||||
|
console.log(`webhook delivery skipped: delivery=${delivery} reason=ref ref=${pushRef || '(missing)'} allowed=${[...ALLOWED_REFS].join(',')}`)
|
||||||
json(res, 202, { skipped: true, reason: `Ignoring ref ${pushRef || '(missing)'}` })
|
json(res, 202, { skipped: true, reason: `Ignoring ref ${pushRef || '(missing)'}` })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ALLOWED_REPOSITORY) {
|
if (ALLOWED_REPOSITORY) {
|
||||||
const repoFullName = String(push?.repository?.full_name || '')
|
|
||||||
if (repoFullName !== ALLOWED_REPOSITORY) {
|
if (repoFullName !== ALLOWED_REPOSITORY) {
|
||||||
|
console.log(`webhook delivery skipped: delivery=${delivery} reason=repository repo=${repoFullName || '(missing)'} allowed=${ALLOWED_REPOSITORY}`)
|
||||||
json(res, 202, { skipped: true, reason: `Ignoring repository ${repoFullName || '(missing)'}` })
|
json(res, 202, { skipped: true, reason: `Ignoring repository ${repoFullName || '(missing)'}` })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (push?.deleted) {
|
if (push?.deleted) {
|
||||||
|
console.log(`webhook delivery skipped: delivery=${delivery} reason=branch-delete ref=${pushRef}`)
|
||||||
json(res, 202, { skipped: true, reason: 'Ignoring branch-delete push' })
|
json(res, 202, { skipped: true, reason: 'Ignoring branch-delete push' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (deploying) {
|
if (deploying) {
|
||||||
queuedPush = push
|
queuedPush = push
|
||||||
|
console.log(`webhook deploy queued: delivery=${delivery} ref=${pushRef} repo=${repoFullName || '(missing)'}`)
|
||||||
json(res, 202, { queued: true, deploying: true })
|
json(res, 202, { queued: true, deploying: true })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
deploying = true
|
deploying = true
|
||||||
|
console.log(`webhook deploy accepted: delivery=${delivery} ref=${pushRef} repo=${repoFullName || '(missing)'} restart_targets=${RESTART_TARGETS.join(',') || 'none'}`)
|
||||||
json(res, 202, { accepted: true, restart_targets: RESTART_TARGETS })
|
json(res, 202, { accepted: true, restart_targets: RESTART_TARGETS })
|
||||||
|
|
||||||
runDeployQueue(push)
|
runDeployQueue(push)
|
||||||
|
|||||||
Reference in New Issue
Block a user