import { Switch } from '@headlessui/react' import { type ActionFunctionArgs } from '@remix-run/node' import { json, useFetcher, useLoaderData } from '@remix-run/react' import clsx from 'clsx' import { useState } from 'react' import Button from '~/components/Button' import Code from '~/components/Code' import Input from '~/components/Input' import Notice from '~/components/Notice' import Spinner from '~/components/Spinner' import TableList from '~/components/TableList' import { getConfig, getContext, patchConfig } from '~/utils/config' import { restartHeadscale } from '~/utils/docker' import { getSession } from '~/utils/sessions' import { useLiveData } from '~/utils/useLiveData' import Domains from './domains' import MagicModal from './magic' import RenameModal from './rename' // We do not want to expose every config value export async function loader() { const context = await getContext() if (!context.hasConfig) { throw new Error('No configuration is available') } const config = await getConfig() const dns = { prefixes: config.prefixes, magicDns: config.dns_config.magic_dns, baseDomain: config.dns_config.base_domain, overrideLocal: config.dns_config.override_local_dns, nameservers: config.dns_config.nameservers, splitDns: config.dns_config.restricted_nameservers, searchDomains: config.dns_config.domains, extraRecords: config.dns_config.extra_records } return { ...dns, ...context } } export async function action({ request }: ActionFunctionArgs) { const session = await getSession(request.headers.get('Cookie')) if (!session.has('hsApiKey')) { return json({ success: false }, { status: 401 }) } const context = await getContext() if (!context.hasConfigWrite) { return json({ success: false }, { status: 403 }) } const data = await request.json() as Record await patchConfig(data) await restartHeadscale() return json({ success: true }) } export default function Page() { useLiveData({ interval: 5000 }) const data = useLoaderData() const fetcher = useFetcher() const [localOverride, setLocalOverride] = useState(data.overrideLocal) const [ns, setNs] = useState('') return (
{data.hasConfigWrite ? undefined : ( The Headscale configuration is read-only. You cannot make changes to the configuration )}

Nameservers

Set the nameservers used by devices on the Tailnet to resolve DNS queries.

Global Nameservers

Override local DNS { fetcher.submit({ // eslint-disable-next-line @typescript-eslint/naming-convention 'dns_config.override_local_dns': !localOverride }, { method: 'PATCH', encType: 'application/json' }) setLocalOverride(!localOverride) }} > Override local DNS
{data.nameservers.map((ns, index) => ( // eslint-disable-next-line react/no-array-index-key

{ns}

))} {data.hasConfigWrite ? ( { setNs(event.target.value) }} /> {fetcher.state === 'idle' ? ( ) : ( )} ) : undefined}
{/* TODO: Split DNS and Custom A Records */}

Magic DNS

Automaticall register domain names for each device on the tailnet. Devices will be accessible at {' '} [device].[user].{data.baseDomain} {' '} when Magic DNS is enabled.

) }