better-auth
Version:
The most comprehensive authentication framework for TypeScript.
1 lines • 17 kB
Source Map (JSON)
{"version":3,"file":"error.mjs","names":[],"sources":["../../../src/api/routes/error.ts"],"sourcesContent":["import type { BetterAuthOptions } from \"@better-auth/core\";\nimport { createAuthEndpoint } from \"@better-auth/core/api\";\nimport { isProduction } from \"@better-auth/core/env\";\nimport { HIDE_METADATA } from \"../../utils/hide-metadata\";\n\nfunction sanitize(input: string): string {\n\t// Replace & last to avoid double-encoding existing HTML entities\n\t// Match & only when it's not already part of an HTML entity\n\treturn input\n\t\t.replace(/</g, \"<\")\n\t\t.replace(/>/g, \">\")\n\t\t.replace(/\"/g, \""\")\n\t\t.replace(/'/g, \"'\")\n\t\t.replace(/&(?!amp;|lt;|gt;|quot;|#39;|#x[0-9a-fA-F]+;|#[0-9]+;)/g, \"&\");\n}\n\nconst html = (\n\toptions: BetterAuthOptions,\n\tcode: string = \"Unknown\",\n\tdescription: string | null = null,\n) => {\n\tconst custom = options.onAPIError?.customizeDefaultErrorPage;\n\treturn `<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>Error</title>\n <style>\n * {\n box-sizing: border-box;\n }\n body {\n font-family: ${custom?.font?.defaultFamily || \"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif\"};\n background: ${custom?.colors?.background || \"var(--background)\"};\n color: var(--foreground);\n margin: 0;\n }\n :root,\n :host {\n --spacing: 0.25rem;\n --container-md: 28rem;\n --text-sm: ${custom?.size?.textSm || \"0.875rem\"};\n --text-sm--line-height: calc(1.25 / 0.875);\n --text-2xl: ${custom?.size?.text2xl || \"1.5rem\"};\n --text-2xl--line-height: calc(2 / 1.5);\n --text-4xl: ${custom?.size?.text4xl || \"2.25rem\"};\n --text-4xl--line-height: calc(2.5 / 2.25);\n --text-6xl: ${custom?.size?.text6xl || \"3rem\"};\n --text-6xl--line-height: 1;\n --font-weight-medium: 500;\n --font-weight-semibold: 600;\n --font-weight-bold: 700;\n --default-transition-duration: 150ms;\n --default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n --radius: ${custom?.size?.radiusSm || \"0.625rem\"};\n --default-mono-font-family: ${custom?.font?.monoFamily || \"var(--font-geist-mono)\"};\n --primary: ${custom?.colors?.primary || \"black\"};\n --primary-foreground: ${custom?.colors?.primaryForeground || \"white\"};\n --background: ${custom?.colors?.background || \"white\"};\n --foreground: ${custom?.colors?.foreground || \"oklch(0.271 0 0)\"};\n --border: ${custom?.colors?.border || \"oklch(0.89 0 0)\"};\n --destructive: ${custom?.colors?.destructive || \"oklch(0.55 0.15 25.723)\"};\n --muted-foreground: ${custom?.colors?.mutedForeground || \"oklch(0.545 0 0)\"};\n --corner-border: ${custom?.colors?.cornerBorder || \"#404040\"};\n }\n\n button, .btn {\n cursor: pointer;\n background: none;\n border: none;\n color: inherit;\n font: inherit;\n transition: all var(--default-transition-duration)\n var(--default-transition-timing-function);\n }\n button:hover, .btn:hover {\n opacity: 0.8;\n }\n\n @media (prefers-color-scheme: dark) {\n :root,\n :host {\n --primary: ${custom?.colors?.primary || \"white\"};\n --primary-foreground: ${custom?.colors?.primaryForeground || \"black\"};\n --background: ${custom?.colors?.background || \"oklch(0.15 0 0)\"};\n --foreground: ${custom?.colors?.foreground || \"oklch(0.98 0 0)\"};\n --border: ${custom?.colors?.border || \"oklch(0.27 0 0)\"};\n --destructive: ${custom?.colors?.destructive || \"oklch(0.65 0.15 25.723)\"};\n --muted-foreground: ${custom?.colors?.mutedForeground || \"oklch(0.65 0 0)\"};\n --corner-border: ${custom?.colors?.cornerBorder || \"#a0a0a0\"};\n }\n }\n @media (max-width: 640px) {\n :root, :host {\n --text-6xl: 2.5rem;\n --text-2xl: 1.25rem;\n --text-sm: 0.8125rem;\n }\n }\n @media (max-width: 480px) {\n :root, :host {\n --text-6xl: 2rem;\n --text-2xl: 1.125rem;\n }\n }\n </style>\n </head>\n <body style=\"width: 100vw; min-height: 100vh; overflow-x: hidden; overflow-y: auto;\">\n <div\n style=\"\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 1.5rem;\n position: relative;\n width: 100%;\n min-height: 100vh;\n padding: 1rem;\n \"\n >\n${\n\tcustom?.disableBackgroundGrid\n\t\t? \"\"\n\t\t: `\n <div\n style=\"\n position: absolute;\n inset: 0;\n background-image: linear-gradient(to right, ${custom?.colors?.gridColor || \"var(--border)\"} 1px, transparent 1px),\n linear-gradient(to bottom, ${custom?.colors?.gridColor || \"var(--border)\"} 1px, transparent 1px);\n background-size: 40px 40px;\n opacity: 0.6;\n pointer-events: none;\n width: 100vw;\n height: 100vh;\n \"\n ></div>\n <div\n style=\"\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: ${custom?.colors?.background || \"var(--background)\"};\n mask-image: radial-gradient(ellipse at center, transparent 20%, black);\n -webkit-mask-image: radial-gradient(ellipse at center, transparent 20%, black);\n pointer-events: none;\n \"\n ></div>\n`\n}\n\n<div\n style=\"\n position: relative;\n z-index: 10;\n border: 2px solid var(--border);\n background: ${custom?.colors?.cardBackground || \"var(--background)\"};\n padding: 1.5rem;\n max-width: 42rem;\n width: 100%;\n \"\n>\n ${\n\t\t\tcustom?.disableCornerDecorations\n\t\t\t\t? \"\"\n\t\t\t\t: `\n <!-- Corner decorations -->\n <div\n style=\"\n position: absolute;\n top: -2px;\n left: -2px;\n width: 2rem;\n height: 2rem;\n border-top: 4px solid var(--corner-border);\n border-left: 4px solid var(--corner-border);\n \"\n ></div>\n <div\n style=\"\n position: absolute;\n top: -2px;\n right: -2px;\n width: 2rem;\n height: 2rem;\n border-top: 4px solid var(--corner-border);\n border-right: 4px solid var(--corner-border);\n \"\n ></div>\n \n <div\n style=\"\n position: absolute;\n bottom: -2px;\n left: -2px;\n width: 2rem;\n height: 2rem;\n border-bottom: 4px solid var(--corner-border);\n border-left: 4px solid var(--corner-border);\n \"\n ></div>\n <div\n style=\"\n position: absolute;\n bottom: -2px;\n right: -2px;\n width: 2rem;\n height: 2rem;\n border-bottom: 4px solid var(--corner-border);\n border-right: 4px solid var(--corner-border);\n \"\n ></div>`\n\t\t}\n\n <div style=\"text-align: center; margin-bottom: 1.5rem;\">\n <div style=\"margin-bottom: 1.5rem;\">\n <div\n style=\"\n display: inline-block;\n border: 2px solid ${custom?.disableTitleBorder ? \"transparent\" : custom?.colors?.titleBorder || \"var(--destructive)\"};\n padding: 0.375rem 1rem;\n \"\n >\n <h1\n style=\"\n font-size: var(--text-6xl);\n font-weight: var(--font-weight-semibold);\n color: ${custom?.colors?.titleColor || \"var(--foreground)\"};\n letter-spacing: -0.02em;\n margin: 0;\n \"\n >\n ERROR\n </h1>\n </div>\n <div\n style=\"\n height: 2px;\n background-color: var(--border);\n width: calc(100% + 3rem);\n margin-left: -1.5rem;\n margin-top: 1.5rem;\n \"\n ></div>\n </div>\n\n <h2\n style=\"\n font-size: var(--text-2xl);\n font-weight: var(--font-weight-semibold);\n color: var(--foreground);\n margin: 0 0 1rem;\n \"\n >\n Something went wrong\n </h2>\n\n <div\n style=\"\n display: inline-flex;\n align-items: center;\n gap: 0.5rem;\n border: 2px solid var(--border);\n background-color: var(--muted);\n padding: 0.375rem 0.75rem;\n margin: 0 0 1rem;\n flex-wrap: wrap;\n justify-content: center;\n \"\n >\n <span\n style=\"\n font-size: 0.75rem;\n color: var(--muted-foreground);\n font-weight: var(--font-weight-semibold);\n \"\n >\n CODE:\n </span>\n <span\n style=\"\n font-size: var(--text-sm);\n font-family: var(--default-mono-font-family, monospace);\n color: var(--foreground);\n word-break: break-all;\n \"\n >\n ${sanitize(code)}\n </span>\n </div>\n\n <p\n style=\"\n color: var(--muted-foreground);\n max-width: 28rem;\n margin: 0 auto;\n font-size: var(--text-sm);\n line-height: 1.5;\n text-wrap: pretty;\n \"\n >\n ${\n\t\t\t\t\t\t\t!description\n\t\t\t\t\t\t\t\t? \"We encountered an unexpected error. Please try again or return to the home page. If you're a developer, you can find more information about the error \" +\n\t\t\t\t\t\t\t\t\t`<a href='https://better-auth.com/docs/errors/${encodeURIComponent(code)}' target='_blank' rel=\"noopener noreferrer\" style='color: var(--foreground); text-decoration: underline;'>here</a>.`\n\t\t\t\t\t\t\t\t: description\n\t\t\t\t\t\t}\n </p>\n </div>\n\n <div\n style=\"\n display: flex;\n gap: 0.75rem;\n margin-top: 1.5rem;\n justify-content: center;\n flex-wrap: wrap;\n \"\n >\n <a\n href=\"/\"\n style=\"\n text-decoration: none;\n \"\n >\n <div\n style=\"\n border: 2px solid var(--border);\n background: var(--primary);\n color: var(--primary-foreground);\n padding: 0.5rem 1rem;\n border-radius: 0;\n white-space: nowrap;\n \"\n class=\"btn\"\n >\n Go Home\n </div>\n </a>\n <a\n href=\"https://better-auth.com/docs/errors/${encodeURIComponent(code)}?askai=${encodeURIComponent(`What does the error code ${code} mean?`)}\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n style=\"\n text-decoration: none;\n \"\n >\n <div\n style=\"\n border: 2px solid var(--border);\n background: transparent;\n color: var(--foreground);\n padding: 0.5rem 1rem;\n border-radius: 0;\n white-space: nowrap;\n \"\n class=\"btn\"\n >\n Ask AI\n </div>\n </a>\n </div>\n </div>\n </div>\n </body>\n</html>`;\n};\n\nexport const error = createAuthEndpoint(\n\t\"/error\",\n\t{\n\t\tmethod: \"GET\",\n\t\tmetadata: {\n\t\t\t...HIDE_METADATA,\n\t\t\topenapi: {\n\t\t\t\tdescription: \"Displays an error page\",\n\t\t\t\tresponses: {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\tdescription: \"Success\",\n\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\"text/html\": {\n\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\tdescription: \"The HTML content of the error page\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t},\n\tasync (c) => {\n\t\tconst url = new URL(c.request?.url || \"\");\n\t\tconst unsanitizedCode = url.searchParams.get(\"error\") || \"UNKNOWN\";\n\t\tconst unsanitizedDescription =\n\t\t\turl.searchParams.get(\"error_description\") || null;\n\n\t\tconst isValid = /^[\\'A-Za-z0-9_-]+$/.test(unsanitizedCode || \"\");\n\t\tconst safeCode = isValid ? unsanitizedCode : \"UNKNOWN\";\n\t\tconst safeDescription = unsanitizedDescription\n\t\t\t? sanitize(unsanitizedDescription)\n\t\t\t: null;\n\n\t\tconst queryParams = new URLSearchParams();\n\t\tqueryParams.set(\"error\", safeCode);\n\t\tif (unsanitizedDescription) {\n\t\t\tqueryParams.set(\"error_description\", unsanitizedDescription);\n\t\t}\n\n\t\tconst options = c.context.options;\n\t\tconst errorURL = options.onAPIError?.errorURL;\n\n\t\tif (errorURL) {\n\t\t\treturn new Response(null, {\n\t\t\t\tstatus: 302,\n\t\t\t\theaders: {\n\t\t\t\t\tLocation: `${errorURL}${errorURL.includes(\"?\") ? \"&\" : \"?\"}${queryParams.toString()}`,\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\n\t\tif (isProduction && !options.onAPIError?.customizeDefaultErrorPage) {\n\t\t\treturn new Response(null, {\n\t\t\t\tstatus: 302,\n\t\t\t\theaders: {\n\t\t\t\t\tLocation: `/?${queryParams.toString()}`,\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\n\t\treturn new Response(html(c.context.options, safeCode, safeDescription), {\n\t\t\theaders: {\n\t\t\t\t\"Content-Type\": \"text/html\",\n\t\t\t},\n\t\t});\n\t},\n);\n"],"mappings":"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"}