fix: handle configs better and propagate errors
This commit is contained in:
parent
e19dbda5ed
commit
ed50c48965
62
app/entry.server.tsx
Normal file
62
app/entry.server.tsx
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import { PassThrough } from 'node:stream'
|
||||||
|
|
||||||
|
import type { AppLoadContext, EntryContext } from '@remix-run/node'
|
||||||
|
import { createReadableStreamFromReadable } from '@remix-run/node'
|
||||||
|
import { RemixServer } from '@remix-run/react'
|
||||||
|
import { isbot } from 'isbot'
|
||||||
|
import { renderToPipeableStream } from 'react-dom/server'
|
||||||
|
|
||||||
|
import { loadContext } from './utils/config/headplane'
|
||||||
|
|
||||||
|
await loadContext()
|
||||||
|
|
||||||
|
export default function handleRequest(
|
||||||
|
request: Request,
|
||||||
|
responseStatusCode: number,
|
||||||
|
responseHeaders: Headers,
|
||||||
|
remixContext: EntryContext,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
_loadContext: AppLoadContext,
|
||||||
|
) {
|
||||||
|
const ua = request.headers.get('user-agent')
|
||||||
|
const isBot = ua ? isbot(ua) : false
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let shellRendered = false
|
||||||
|
const { pipe, abort } = renderToPipeableStream(
|
||||||
|
<RemixServer
|
||||||
|
context={remixContext}
|
||||||
|
url={request.url}
|
||||||
|
abortDelay={5000}
|
||||||
|
/>,
|
||||||
|
{
|
||||||
|
[isBot ? 'onAllReady' : 'onShellReady']() {
|
||||||
|
shellRendered = true
|
||||||
|
const body = new PassThrough()
|
||||||
|
const stream = createReadableStreamFromReadable(body)
|
||||||
|
responseHeaders.set('Content-Type', 'text/html')
|
||||||
|
|
||||||
|
resolve(
|
||||||
|
new Response(stream, {
|
||||||
|
headers: responseHeaders,
|
||||||
|
status: responseStatusCode,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
pipe(body)
|
||||||
|
},
|
||||||
|
onShellError(error: unknown) {
|
||||||
|
reject(error as Error)
|
||||||
|
},
|
||||||
|
onError(error: unknown) {
|
||||||
|
responseStatusCode = 500
|
||||||
|
if (shellRendered) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
setTimeout(abort, 5000)
|
||||||
|
})
|
||||||
|
}
|
||||||
@ -43,12 +43,8 @@ export async function loadContext(): Promise<HeadplaneContext> {
|
|||||||
return context
|
return context
|
||||||
}
|
}
|
||||||
|
|
||||||
let config: HeadscaleConfig | undefined
|
|
||||||
try {
|
|
||||||
config = await loadConfig()
|
|
||||||
} catch {}
|
|
||||||
|
|
||||||
const path = resolve(process.env.CONFIG_FILE ?? '/etc/headscale/config.yaml')
|
const path = resolve(process.env.CONFIG_FILE ?? '/etc/headscale/config.yaml')
|
||||||
|
const { config, contextData } = await checkConfig(path)
|
||||||
|
|
||||||
let headscaleUrl = process.env.HEADSCALE_URL
|
let headscaleUrl = process.env.HEADSCALE_URL
|
||||||
if (!headscaleUrl && !config) {
|
if (!headscaleUrl && !config) {
|
||||||
@ -72,7 +68,7 @@ export async function loadContext(): Promise<HeadplaneContext> {
|
|||||||
headscaleUrl,
|
headscaleUrl,
|
||||||
cookieSecret,
|
cookieSecret,
|
||||||
integration: await checkIntegration(),
|
integration: await checkIntegration(),
|
||||||
config: await checkConfig(path, config),
|
config: contextData,
|
||||||
acl: await checkAcl(config),
|
acl: await checkAcl(config),
|
||||||
oidc: await checkOidc(config),
|
oidc: await checkOidc(config),
|
||||||
}
|
}
|
||||||
@ -121,7 +117,20 @@ export async function patchAcl(data: string) {
|
|||||||
await writeFile(path, data, 'utf8')
|
await writeFile(path, data, 'utf8')
|
||||||
}
|
}
|
||||||
|
|
||||||
async function checkConfig(path: string, config?: HeadscaleConfig) {
|
async function checkConfig(path: string) {
|
||||||
|
let config: HeadscaleConfig | undefined
|
||||||
|
try {
|
||||||
|
config = await loadConfig(path)
|
||||||
|
} catch {
|
||||||
|
return {
|
||||||
|
config: undefined,
|
||||||
|
contextData: {
|
||||||
|
read: false,
|
||||||
|
write: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let write = false
|
let write = false
|
||||||
try {
|
try {
|
||||||
await access(path, constants.W_OK)
|
await access(path, constants.W_OK)
|
||||||
@ -129,8 +138,11 @@ async function checkConfig(path: string, config?: HeadscaleConfig) {
|
|||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
read: config ? true : false,
|
config,
|
||||||
write,
|
contextData: {
|
||||||
|
read: true,
|
||||||
|
write,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -158,16 +158,40 @@ export type HeadscaleConfig = z.infer<typeof HeadscaleConfig>
|
|||||||
export let configYaml: Document | undefined
|
export let configYaml: Document | undefined
|
||||||
export let config: HeadscaleConfig | undefined
|
export let config: HeadscaleConfig | undefined
|
||||||
|
|
||||||
export async function loadConfig() {
|
export async function loadConfig(path?: string) {
|
||||||
if (config) {
|
if (config) {
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
const path = resolve(process.env.CONFIG_FILE ?? '/etc/headscale/config.yaml')
|
if (!path) {
|
||||||
const data = await readFile(path, 'utf8')
|
throw new Error('Path is required to lazy load config')
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await readFile(path, 'utf8')
|
||||||
configYaml = parseDocument(data)
|
configYaml = parseDocument(data)
|
||||||
config = await HeadscaleConfig.parseAsync(configYaml.toJSON())
|
|
||||||
|
try {
|
||||||
|
config = await HeadscaleConfig.parseAsync(configYaml.toJSON())
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof z.ZodError) {
|
||||||
|
console.log('Failed to parse the Headscale configuration file!')
|
||||||
|
console.log('The following schema issues were found:')
|
||||||
|
for (const issue of error.issues) {
|
||||||
|
const path = issue.path.map(String).join('.')
|
||||||
|
const message = issue.message
|
||||||
|
|
||||||
|
console.log(`- '${path}': ${message}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('')
|
||||||
|
console.log('Please fix the configuration file and try again.')
|
||||||
|
console.log('Headplane will operate as if no config is present.')
|
||||||
|
console.log('')
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user