mirror of
https://github.com/nuxt/nuxt.git
synced 2025-01-30 23:32:38 +00:00
fix: reverting prod error handling
This commit is contained in:
parent
786156eef5
commit
7c40b7a705
@ -19,7 +19,6 @@ import { toArray } from '../utils'
|
|||||||
import { template as defaultSpaLoadingTemplate } from '../../../ui-templates/dist/templates/spa-loading-icon'
|
import { template as defaultSpaLoadingTemplate } from '../../../ui-templates/dist/templates/spa-loading-icon'
|
||||||
import { createImportProtectionPatterns } from './plugins/import-protection'
|
import { createImportProtectionPatterns } from './plugins/import-protection'
|
||||||
import { EXTENSION_RE } from './utils'
|
import { EXTENSION_RE } from './utils'
|
||||||
import { resolveErrorHandler } from './runtime/nitro/utils'
|
|
||||||
|
|
||||||
const logLevelMapReverse = {
|
const logLevelMapReverse = {
|
||||||
silent: 0,
|
silent: 0,
|
||||||
@ -235,7 +234,7 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
|
|||||||
|
|
||||||
// add error handler
|
// add error handler
|
||||||
if (!nitroConfig.errorHandler && (nuxt.options.dev || !nuxt.options.experimental.noVueServer)) {
|
if (!nitroConfig.errorHandler && (nuxt.options.dev || !nuxt.options.experimental.noVueServer)) {
|
||||||
nitroConfig.errorHandler = resolve(distDir, resolveErrorHandler(nuxt.options.dev))
|
nitroConfig.errorHandler = resolve(distDir, 'core/runtime/nitro/error')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve user-provided paths
|
// Resolve user-provided paths
|
||||||
@ -521,7 +520,7 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
|
|||||||
if (!nuxt.options.dev && nuxt.options.experimental.noVueServer) {
|
if (!nuxt.options.dev && nuxt.options.experimental.noVueServer) {
|
||||||
nitro.hooks.hook('rollup:before', (nitro) => {
|
nitro.hooks.hook('rollup:before', (nitro) => {
|
||||||
if (nitro.options.preset === 'nitro-prerender') {
|
if (nitro.options.preset === 'nitro-prerender') {
|
||||||
nitro.options.errorHandler = resolve(distDir, resolveErrorHandler(nuxt.options.dev))
|
nitro.options.errorHandler = resolve(distDir, 'core/runtime/nitro/error')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const nuxtErrorHandler = nitro.options.handlers.findIndex(h => h.route === '/__nuxt_error')
|
const nuxtErrorHandler = nitro.options.handlers.findIndex(h => h.route === '/__nuxt_error')
|
||||||
|
@ -1,150 +0,0 @@
|
|||||||
import { readFile } from 'node:fs/promises'
|
|
||||||
import { dirname, resolve } from 'node:path'
|
|
||||||
import type { H3Event } from 'h3'
|
|
||||||
import {
|
|
||||||
getRequestHeader,
|
|
||||||
getRequestHeaders,
|
|
||||||
getRequestURL,
|
|
||||||
getResponseHeader,
|
|
||||||
send,
|
|
||||||
setResponseHeader,
|
|
||||||
setResponseStatus,
|
|
||||||
} from 'h3'
|
|
||||||
import consola from 'consola'
|
|
||||||
import { ErrorParser } from 'youch-core'
|
|
||||||
import { Youch } from 'youch'
|
|
||||||
import { SourceMapConsumer } from 'source-map'
|
|
||||||
import { defineNitroErrorHandler, setSecurityHeaders } from './utils'
|
|
||||||
|
|
||||||
export default defineNitroErrorHandler(
|
|
||||||
async function defaultNitroErrorHandler (error, _event) {
|
|
||||||
const isSensitive = error.unhandled || error.fatal
|
|
||||||
const statusCode = error.statusCode || 500
|
|
||||||
const statusMessage = error.statusMessage || 'Server Error'
|
|
||||||
// prettier-ignore
|
|
||||||
const event = _event as H3Event
|
|
||||||
const url = getRequestURL(event, { xForwardedHost: true, xForwardedProto: true }).toString()
|
|
||||||
|
|
||||||
// Load stack trace with source maps
|
|
||||||
await loadStackTrace(error).catch(consola.error)
|
|
||||||
|
|
||||||
// https://github.com/poppinss/youch
|
|
||||||
const youch = new Youch()
|
|
||||||
|
|
||||||
// Console output
|
|
||||||
if (isSensitive) {
|
|
||||||
// prettier-ignore
|
|
||||||
const tags = [error.unhandled && '[unhandled]', error.fatal && '[fatal]'].filter(Boolean).join(' ')
|
|
||||||
|
|
||||||
const columns = process.stderr.columns
|
|
||||||
if (!columns) {
|
|
||||||
process.stdout.columns = 90 // Temporary workaround for youch wrapping issue
|
|
||||||
}
|
|
||||||
const ansiError = await (
|
|
||||||
await youch.toANSI(error)
|
|
||||||
).replaceAll(process.cwd(), '.')
|
|
||||||
if (!columns) {
|
|
||||||
process.stderr.columns = columns
|
|
||||||
}
|
|
||||||
|
|
||||||
consola.error(
|
|
||||||
`[nitro] [request error] ${tags} [${event.method}] ${url}\n\n`,
|
|
||||||
ansiError,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send response
|
|
||||||
setResponseStatus(event, statusCode, statusMessage)
|
|
||||||
setSecurityHeaders(event, true /* allow js */)
|
|
||||||
if (statusCode === 404 || !getResponseHeader(event, 'cache-control')) {
|
|
||||||
setResponseHeader(event, 'cache-control', 'no-cache')
|
|
||||||
}
|
|
||||||
return getRequestHeader(event, 'accept')?.includes('text/html')
|
|
||||||
? send(
|
|
||||||
event,
|
|
||||||
await youch.toHTML(error, {
|
|
||||||
request: {
|
|
||||||
url,
|
|
||||||
method: event.method,
|
|
||||||
headers: getRequestHeaders(event),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
'text/html',
|
|
||||||
)
|
|
||||||
: send(
|
|
||||||
event,
|
|
||||||
JSON.stringify(
|
|
||||||
{
|
|
||||||
error: true,
|
|
||||||
url,
|
|
||||||
statusCode,
|
|
||||||
statusMessage,
|
|
||||||
message: error.message,
|
|
||||||
data: error.data,
|
|
||||||
stack: error.stack?.split('\n').map(line => line.trim()),
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
2,
|
|
||||||
),
|
|
||||||
'application/json',
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
// ---- Source Map support ----
|
|
||||||
|
|
||||||
export async function loadStackTrace (error: any) {
|
|
||||||
if (!(error instanceof Error)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const parsed = await new ErrorParser()
|
|
||||||
.defineSourceLoader(sourceLoader)
|
|
||||||
.parse(error)
|
|
||||||
|
|
||||||
const stack =
|
|
||||||
error.message +
|
|
||||||
'\n' +
|
|
||||||
parsed.frames.map(frame => fmtFrame(frame)).join('\n')
|
|
||||||
|
|
||||||
Object.defineProperty(error, 'stack', { value: stack })
|
|
||||||
|
|
||||||
if (error.cause) {
|
|
||||||
await loadStackTrace(error.cause).catch(consola.error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type SourceLoader = Parameters<ErrorParser['defineSourceLoader']>[0]
|
|
||||||
type StackFrame = Parameters<SourceLoader>[0]
|
|
||||||
|
|
||||||
async function sourceLoader (frame: StackFrame) {
|
|
||||||
if (!frame.fileName || frame.fileType !== 'fs' || frame.type === 'native') {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (frame.type === 'app') {
|
|
||||||
// prettier-ignore
|
|
||||||
const rawSourceMap = await readFile(`${frame.fileName}.map`, 'utf8').catch(() => {})
|
|
||||||
if (rawSourceMap) {
|
|
||||||
const consumer = await new SourceMapConsumer(rawSourceMap)
|
|
||||||
// prettier-ignore
|
|
||||||
const originalPosition = consumer.originalPositionFor({ line: frame.lineNumber!, column: frame.columnNumber! })
|
|
||||||
if (originalPosition.source && originalPosition.line) {
|
|
||||||
// prettier-ignore
|
|
||||||
frame.fileName = resolve(dirname(frame.fileName), originalPosition.source)
|
|
||||||
frame.lineNumber = originalPosition.line
|
|
||||||
frame.columnNumber = originalPosition.column || 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const contents = await readFile(frame.fileName, 'utf8').catch(() => {})
|
|
||||||
return contents ? { contents } : undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
function fmtFrame (frame: StackFrame) {
|
|
||||||
if (frame.type === 'native') {
|
|
||||||
return frame.raw
|
|
||||||
}
|
|
||||||
const src = `${frame.fileName || ''}:${frame.lineNumber}:${frame.columnNumber})`
|
|
||||||
return frame.functionName ? `at ${frame.functionName} (${src}` : `at ${src}`
|
|
||||||
}
|
|
287
packages/nuxt/src/core/runtime/nitro/error.ts
Normal file
287
packages/nuxt/src/core/runtime/nitro/error.ts
Normal file
@ -0,0 +1,287 @@
|
|||||||
|
import { readFile } from 'node:fs/promises'
|
||||||
|
import { dirname, resolve } from 'node:path'
|
||||||
|
import type { H3Event } from 'h3'
|
||||||
|
import {
|
||||||
|
getRequestHeader,
|
||||||
|
getRequestHeaders,
|
||||||
|
getRequestURL,
|
||||||
|
getResponseHeader,
|
||||||
|
send,
|
||||||
|
setResponseHeader,
|
||||||
|
setResponseStatus,
|
||||||
|
} from 'h3'
|
||||||
|
import consola from 'consola'
|
||||||
|
import { ErrorParser } from 'youch-core'
|
||||||
|
import { Youch } from 'youch'
|
||||||
|
import { SourceMapConsumer } from 'source-map'
|
||||||
|
import { useNitroApp, useRuntimeConfig } from 'nitro/runtime'
|
||||||
|
import { joinURL, withQuery } from 'ufo'
|
||||||
|
import type { NuxtPayload } from 'nuxt/app'
|
||||||
|
import { defineNitroErrorHandler, setSecurityHeaders } from './utils'
|
||||||
|
|
||||||
|
export default defineNitroErrorHandler(
|
||||||
|
async function defaultNitroErrorHandler (error, event) {
|
||||||
|
const { stack, message, isSensitive, statusCode, statusMessage } = normalizeError(error)
|
||||||
|
|
||||||
|
const url = getRequestURL(event, { xForwardedHost: true, xForwardedProto: true }).toString()
|
||||||
|
// https://github.com/poppinss/youch
|
||||||
|
const youch = new Youch()
|
||||||
|
|
||||||
|
if (import.meta.dev) {
|
||||||
|
// Load stack trace with source maps
|
||||||
|
await loadStackTrace(error).catch(consola.error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create an error object
|
||||||
|
const errorObject = {
|
||||||
|
url: event.path,
|
||||||
|
statusCode,
|
||||||
|
statusMessage,
|
||||||
|
message,
|
||||||
|
stack: import.meta.dev && statusCode !== 404
|
||||||
|
? `<pre>${stack.map(i => `<span class="stack${i.internal ? ' internal' : ''}">${i.text}</span>`).join('\n')}</pre>`
|
||||||
|
: '',
|
||||||
|
// TODO: check and validate error.data for serialisation into query
|
||||||
|
data: error.data as any,
|
||||||
|
} satisfies Partial<NuxtPayload['error']> & { url: string }
|
||||||
|
|
||||||
|
// Console output
|
||||||
|
if (isSensitive) {
|
||||||
|
let errorToLog: string = ''
|
||||||
|
|
||||||
|
const tags = [
|
||||||
|
'[nuxt]',
|
||||||
|
'[request error]',
|
||||||
|
error.unhandled && '[unhandled]',
|
||||||
|
error.fatal && '[fatal]',
|
||||||
|
Number(statusCode) !== 200 && `[${statusCode}]`,
|
||||||
|
].filter(Boolean).join(' ')
|
||||||
|
|
||||||
|
if (import.meta.dev) {
|
||||||
|
const columns = process.stderr.columns
|
||||||
|
if (!columns) {
|
||||||
|
process.stdout.columns = 90 // Temporary workaround for youch wrapping issue
|
||||||
|
}
|
||||||
|
const ansiError = (
|
||||||
|
await youch.toANSI(error)
|
||||||
|
).replaceAll(process.cwd(), '.')
|
||||||
|
if (!columns) {
|
||||||
|
process.stderr.columns = columns
|
||||||
|
}
|
||||||
|
|
||||||
|
errorToLog = ansiError
|
||||||
|
} else {
|
||||||
|
errorToLog = error.message || error.toString() || 'internal server error'
|
||||||
|
}
|
||||||
|
|
||||||
|
console.error(`${tags} [${event.method}] ${url}\n\n`, errorToLog)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.handled) { return }
|
||||||
|
|
||||||
|
// Send response
|
||||||
|
setResponseStatus(event, (statusCode !== 200 && statusCode) as any as number || 500, statusMessage)
|
||||||
|
setSecurityHeaders(event, true /* allow js */)
|
||||||
|
if (statusCode === 404 || !getResponseHeader(event, 'cache-control')) {
|
||||||
|
setResponseHeader(event, 'cache-control', 'no-cache')
|
||||||
|
}
|
||||||
|
|
||||||
|
const isHtml = getRequestHeader(event, 'accept')?.includes('text/html')
|
||||||
|
if (isHtml && import.meta.dev) {
|
||||||
|
return send(
|
||||||
|
event,
|
||||||
|
await youch.toHTML(error, {
|
||||||
|
request: {
|
||||||
|
url,
|
||||||
|
method: event.method,
|
||||||
|
headers: getRequestHeaders(event),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
'text/html',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// JSON response
|
||||||
|
if (isJsonRequest(event)) {
|
||||||
|
return send(
|
||||||
|
event,
|
||||||
|
JSON.stringify(
|
||||||
|
{
|
||||||
|
error: true,
|
||||||
|
url,
|
||||||
|
statusCode,
|
||||||
|
statusMessage,
|
||||||
|
message: error.message,
|
||||||
|
data: error.data,
|
||||||
|
stack: error.stack?.split('\n').map(line => line.trim()),
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
2,
|
||||||
|
),
|
||||||
|
'application/json',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Access request headers
|
||||||
|
const reqHeaders = getRequestHeaders(event)
|
||||||
|
|
||||||
|
// Detect to avoid recursion in SSR rendering of errors
|
||||||
|
const isRenderingError = event.path.startsWith('/__nuxt_error') || !!reqHeaders['x-nuxt-error']
|
||||||
|
|
||||||
|
// HTML response (via SSR)
|
||||||
|
const res = isRenderingError
|
||||||
|
? null
|
||||||
|
: await useNitroApp().localFetch(
|
||||||
|
withQuery(joinURL(useRuntimeConfig(event).app.baseURL, '/__nuxt_error'), errorObject),
|
||||||
|
{
|
||||||
|
headers: { ...reqHeaders, 'x-nuxt-error': 'true' },
|
||||||
|
redirect: 'manual',
|
||||||
|
},
|
||||||
|
).catch(() => null)
|
||||||
|
|
||||||
|
// Fallback to static rendered error page
|
||||||
|
if (!res) {
|
||||||
|
const { template } = import.meta.dev ? await import('./error-dev') : await import('./error-500')
|
||||||
|
if (import.meta.dev) {
|
||||||
|
// TODO: Support `message` in template
|
||||||
|
(errorObject as any).description = errorObject.message
|
||||||
|
}
|
||||||
|
if (event.handled) { return }
|
||||||
|
setResponseHeader(event, 'Content-Type', 'text/html;charset=UTF-8')
|
||||||
|
return send(event, template(errorObject))
|
||||||
|
}
|
||||||
|
|
||||||
|
const html = await res.text()
|
||||||
|
if (event.handled) { return }
|
||||||
|
|
||||||
|
for (const [header, value] of res.headers.entries()) {
|
||||||
|
setResponseHeader(event, header, value)
|
||||||
|
}
|
||||||
|
setResponseStatus(event, res.status && res.status !== 200 ? res.status : undefined, res.statusText)
|
||||||
|
|
||||||
|
return send(event, html)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// ---- Source Map support ----
|
||||||
|
|
||||||
|
export async function loadStackTrace (error: any) {
|
||||||
|
if (!(error instanceof Error)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const parsed = await new ErrorParser()
|
||||||
|
.defineSourceLoader(sourceLoader)
|
||||||
|
.parse(error)
|
||||||
|
|
||||||
|
const stack =
|
||||||
|
error.message +
|
||||||
|
'\n' +
|
||||||
|
parsed.frames.map(frame => fmtFrame(frame)).join('\n')
|
||||||
|
|
||||||
|
Object.defineProperty(error, 'stack', { value: stack })
|
||||||
|
|
||||||
|
if (error.cause) {
|
||||||
|
await loadStackTrace(error.cause).catch(consola.error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type SourceLoader = Parameters<ErrorParser['defineSourceLoader']>[0]
|
||||||
|
type StackFrame = Parameters<SourceLoader>[0]
|
||||||
|
|
||||||
|
async function sourceLoader (frame: StackFrame) {
|
||||||
|
if (!frame.fileName || frame.fileType !== 'fs' || frame.type === 'native') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (frame.type === 'app') {
|
||||||
|
// prettier-ignore
|
||||||
|
const rawSourceMap = await readFile(`${frame.fileName}.map`, 'utf8').catch(() => { })
|
||||||
|
if (rawSourceMap) {
|
||||||
|
const consumer = await new SourceMapConsumer(rawSourceMap)
|
||||||
|
// prettier-ignore
|
||||||
|
const originalPosition = consumer.originalPositionFor({ line: frame.lineNumber!, column: frame.columnNumber! })
|
||||||
|
if (originalPosition.source && originalPosition.line) {
|
||||||
|
// prettier-ignore
|
||||||
|
frame.fileName = resolve(dirname(frame.fileName), originalPosition.source)
|
||||||
|
frame.lineNumber = originalPosition.line
|
||||||
|
frame.columnNumber = originalPosition.column || 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const contents = await readFile(frame.fileName, 'utf8').catch(() => { })
|
||||||
|
return contents ? { contents } : undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
function fmtFrame (frame: StackFrame) {
|
||||||
|
if (frame.type === 'native') {
|
||||||
|
return frame.raw
|
||||||
|
}
|
||||||
|
const src = `${frame.fileName || ''}:${frame.lineNumber}:${frame.columnNumber})`
|
||||||
|
return frame.functionName ? `at ${frame.functionName} (${src}` : `at ${src}`
|
||||||
|
}
|
||||||
|
|
||||||
|
function isJsonRequest (event: H3Event) {
|
||||||
|
// If the client specifically requests HTML, then avoid classifying as JSON.
|
||||||
|
if (hasReqHeader(event, 'accept', 'text/html')) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
hasReqHeader(event, 'accept', 'application/json') ||
|
||||||
|
hasReqHeader(event, 'user-agent', 'curl/') ||
|
||||||
|
hasReqHeader(event, 'user-agent', 'httpie/') ||
|
||||||
|
hasReqHeader(event, 'sec-fetch-mode', 'cors') ||
|
||||||
|
event.path.startsWith('/api/') ||
|
||||||
|
event.path.endsWith('.json')
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasReqHeader (event: H3Event, name: string, includes: string) {
|
||||||
|
const value = getRequestHeader(event, name)
|
||||||
|
return (
|
||||||
|
value && typeof value === 'string' && value.toLowerCase().includes(includes)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeError (error: any) {
|
||||||
|
// temp fix for https://github.com/nitrojs/nitro/issues/759
|
||||||
|
// TODO: investigate vercel-edge not using unenv pollyfill
|
||||||
|
const cwd = typeof process.cwd === 'function' ? process.cwd() : '/'
|
||||||
|
|
||||||
|
// Hide details of unhandled/fatal errors in production
|
||||||
|
const hideDetails = !import.meta.dev && error.unhandled
|
||||||
|
|
||||||
|
const stack = hideDetails && !import.meta.prerender
|
||||||
|
? []
|
||||||
|
: ((error.stack as string) || '')
|
||||||
|
.split('\n')
|
||||||
|
.splice(1)
|
||||||
|
.filter(line => line.includes('at '))
|
||||||
|
.map((line) => {
|
||||||
|
const text = line
|
||||||
|
.replace(cwd + '/', './')
|
||||||
|
.replace('webpack:/', '')
|
||||||
|
.replace('file://', '')
|
||||||
|
.trim()
|
||||||
|
return {
|
||||||
|
text,
|
||||||
|
internal:
|
||||||
|
(line.includes('node_modules') && !line.includes('.cache')) ||
|
||||||
|
line.includes('internal') ||
|
||||||
|
line.includes('new Promise'),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const message = hideDetails ? 'internal server error' : (error.message || error.toString())
|
||||||
|
const isSensitive = error.unhandled || error.fatal
|
||||||
|
const statusCode = error.statusCode || 500
|
||||||
|
const statusMessage = error.statusMessage || 'Server Error'
|
||||||
|
|
||||||
|
return {
|
||||||
|
stack,
|
||||||
|
message,
|
||||||
|
isSensitive,
|
||||||
|
statusCode,
|
||||||
|
statusMessage,
|
||||||
|
}
|
||||||
|
}
|
@ -1,54 +0,0 @@
|
|||||||
import type {
|
|
||||||
H3Event } from 'h3'
|
|
||||||
import {
|
|
||||||
getRequestURL,
|
|
||||||
getResponseHeader,
|
|
||||||
send,
|
|
||||||
setResponseHeader,
|
|
||||||
setResponseStatus,
|
|
||||||
} from 'h3'
|
|
||||||
import { defineNitroErrorHandler, setSecurityHeaders } from './utils'
|
|
||||||
|
|
||||||
export default defineNitroErrorHandler(
|
|
||||||
function defaultNitroErrorHandler (error, _event) {
|
|
||||||
const isSensitive = error.unhandled || error.fatal
|
|
||||||
const statusCode = error.statusCode || 500
|
|
||||||
const statusMessage = error.statusMessage || 'Server Error'
|
|
||||||
const event = _event as H3Event
|
|
||||||
// prettier-ignore
|
|
||||||
const url = getRequestURL(event, { xForwardedHost: true, xForwardedProto: true }).toString()
|
|
||||||
|
|
||||||
// Console output
|
|
||||||
if (isSensitive) {
|
|
||||||
// prettier-ignore
|
|
||||||
const tags = [error.unhandled && '[unhandled]', error.fatal && '[fatal]'].filter(Boolean).join(' ')
|
|
||||||
console.error(
|
|
||||||
`[nitro] [request error] ${tags} [${event.method}] ${url}\n`,
|
|
||||||
error,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send response
|
|
||||||
setSecurityHeaders(event, false /* no js */)
|
|
||||||
setResponseStatus(event, statusCode, statusMessage)
|
|
||||||
if (statusCode === 404 || !getResponseHeader(event, 'cache-control')) {
|
|
||||||
setResponseHeader(event, 'cache-control', 'no-cache')
|
|
||||||
}
|
|
||||||
return send(
|
|
||||||
event,
|
|
||||||
JSON.stringify(
|
|
||||||
{
|
|
||||||
error: true,
|
|
||||||
url,
|
|
||||||
statusCode,
|
|
||||||
statusMessage,
|
|
||||||
message: isSensitive ? 'Server Error' : error.message,
|
|
||||||
data: isSensitive ? undefined : error.data,
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
2,
|
|
||||||
),
|
|
||||||
'application/json',
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
@ -1,4 +1,3 @@
|
|||||||
import { join } from 'node:path'
|
|
||||||
import { type H3Event, setResponseHeaders } from 'h3'
|
import { type H3Event, setResponseHeaders } from 'h3'
|
||||||
import type { NitroErrorHandler } from 'nitro/types'
|
import type { NitroErrorHandler } from 'nitro/types'
|
||||||
|
|
||||||
@ -22,7 +21,3 @@ export function setSecurityHeaders (event: H3Event, allowjs = false) {
|
|||||||
: 'script-src \'none\'; frame-ancestors \'none\';',
|
: 'script-src \'none\'; frame-ancestors \'none\';',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resolveErrorHandler (isDev: boolean) {
|
|
||||||
return join('core/runtime/nitro/', (isDev ? 'dev' : 'prod') + '-error')
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user