@@ -138,7 +140,6 @@ export default function Domains({
onPress={() => {
fetcher.submit(
{
- // eslint-disable-next-line @typescript-eslint/naming-convention
'dns.search_domains': [...localDomains, newDomain],
},
{
@@ -204,11 +205,6 @@ function Domain({
diff --git a/app/routes/dns/components/nameservers.tsx b/app/routes/dns/components/nameservers.tsx
index 92e3a28..6ee86bf 100644
--- a/app/routes/dns/components/nameservers.tsx
+++ b/app/routes/dns/components/nameservers.tsx
@@ -14,7 +14,7 @@ export default function Nameservers({ nameservers, isDisabled }: Props) {
return (
Nameservers
-
+
Set the nameservers used by devices on the Tailnet to resolve DNS
queries.{' '}
Magic DNS
-
+
Automatically register domain names for each device on the tailnet.
Devices will be accessible at{' '}
diff --git a/app/routes/machines/components/machine.tsx b/app/routes/machines/components/machine.tsx
index 8378ddb..fa1af58 100644
--- a/app/routes/machines/components/machine.tsx
+++ b/app/routes/machines/components/machine.tsx
@@ -90,7 +90,7 @@ export default function MachineRow({
return (
|
@@ -103,9 +103,7 @@ export default function MachineRow({
>
{machine.givenName}
-
- {machine.name}
-
+ {machine.name}
{tags.map((tag) => (
@@ -152,12 +150,12 @@ export default function MachineRow({
{hinfo.getTSVersion(stats)}
-
+
{hinfo.getOSInfo(stats)}
>
) : (
-
+
Unknown
)}
@@ -167,7 +165,7 @@ export default function MachineRow({
Edit machine name for {machine.givenName}
-
+
This name is shown in the admin panel, in Tailscale clients, and used
when generating MagicDNS names.
@@ -38,7 +38,7 @@ export default function Rename({
/>
{magic ? (
name.length > 0 && name !== machine.givenName ? (
-
+
This machine will be accessible by the hostname{' '}
{name.toLowerCase().replaceAll(/\s+/g, '-')}
@@ -48,7 +48,7 @@ export default function Rename({
will no longer point to this machine.
) : (
-
+
This machine is accessible by the hostname{' '}
{machine.givenName}.
diff --git a/app/routes/machines/dialogs/routes.tsx b/app/routes/machines/dialogs/routes.tsx
index ae9c3cc..149c6ff 100644
--- a/app/routes/machines/dialogs/routes.tsx
+++ b/app/routes/machines/dialogs/routes.tsx
@@ -1,8 +1,10 @@
+import { GlobeLock, RouteOff } from 'lucide-react';
import { useMemo } from 'react';
import { useFetcher } from 'react-router';
import Dialog from '~/components/Dialog';
import Link from '~/components/Link';
import Switch from '~/components/Switch';
+import TableList from '~/components/TableList';
import type { Machine, Route } from '~/types';
import cn from '~/utils/cn';
@@ -59,32 +61,17 @@ export default function Routes({
Learn More
-
+
{subnet.length === 0 ? (
-
- No routes are advertised on this machine.
-
+
+
+
+ No routes are advertised by this machine
+
+
) : undefined}
{subnet.map((route) => (
-
+
))}
-
+
Exit nodes
Allow your network to route internet traffic through this machine.{' '}
@@ -114,30 +101,14 @@ export default function Routes({
Learn More
-
+
{exit.length === 0 ? (
-
- This machine is not an exit node.
-
+
+
+ This machine is not an exit node
+
) : (
-
+
)}
-
+
);
diff --git a/app/routes/machines/dialogs/tags.tsx b/app/routes/machines/dialogs/tags.tsx
index 02f3240..286630e 100644
--- a/app/routes/machines/dialogs/tags.tsx
+++ b/app/routes/machines/dialogs/tags.tsx
@@ -1,4 +1,4 @@
-import { Plus, X } from 'lucide-react';
+import { Plus, TagsIcon, X } from 'lucide-react';
import { useState } from 'react';
import Button from '~/components/Button';
import Dialog from '~/components/Dialog';
@@ -38,15 +38,10 @@ export default function Tags({ machine, isOpen, setIsOpen }: TagsProps) {
{tags.length === 0 ? (
-
- No tags are set on this machine.
-
+
+
+ No tags are set on this machine
+
) : (
tags.map((item) => (
diff --git a/app/routes/machines/machine.tsx b/app/routes/machines/machine.tsx
index 7b2755e..04a81a6 100644
--- a/app/routes/machines/machine.tsx
+++ b/app/routes/machines/machine.tsx
@@ -1,20 +1,12 @@
-import {
- CheckCircleIcon,
- GearIcon,
- InfoIcon,
- PersonIcon,
- SkipIcon,
-} from '@primer/octicons-react';
+import { CheckCircle, CircleSlash, Info, UserCircle } from 'lucide-react';
import { useMemo, useState } from 'react';
import type { ActionFunctionArgs, LoaderFunctionArgs } from 'react-router';
import { Link as RemixLink, useLoaderData } from 'react-router';
-
import Attribute from '~/components/Attribute';
import Button from '~/components/Button';
import Card from '~/components/Card';
import Chip from '~/components/Chip';
import Link from '~/components/Link';
-import Menu from '~/components/Menu';
import StatusCircle from '~/components/StatusCircle';
import Tooltip from '~/components/Tooltip';
import type { Machine, Route, User } from '~/types';
@@ -23,7 +15,6 @@ import { loadContext } from '~/utils/config/headplane';
import { loadConfig } from '~/utils/config/headscale';
import { pull } from '~/utils/headscale';
import { getSession } from '~/utils/sessions.server';
-
import { menuAction } from './action';
import MenuOptions from './components/menu';
import Routes from './dialogs/routes';
@@ -126,11 +117,11 @@ export default function Page() {
-
+
{machine.givenName}
@@ -144,30 +135,25 @@ export default function Page() {
/>
-
-
+
+
Managed by
-
+
By default, a machine’s permissions match its creator’s.
- Status
+
+ Status
+
{tags.map((tag) => (
@@ -202,10 +188,10 @@ export default function Page() {
)}
>
-
+
Approved
-
+
Traffic to these routes are being routed through this machine.
@@ -213,7 +199,7 @@ export default function Page() {
{subnetApproved.length === 0 ? (
- —
+ —
) : (
{subnetApproved.map((route) => (
@@ -233,10 +219,10 @@ export default function Page() {
-
+
Awaiting Approval
-
+
This machine is advertising these routes, but they must be
approved before traffic will be routed to them.
@@ -245,7 +231,7 @@ export default function Page() {
{subnet.length === 0 ? (
- —
+ —
) : (
{subnet.map((route) => (
@@ -265,10 +251,10 @@ export default function Page() {
-
+
Exit Node
-
+
Whether this machine can act as an exit node for your tailnet.
@@ -276,15 +262,15 @@ export default function Page() {
{exit.length === 0 ? (
- —
+ —
) : exitEnabled ? (
-
+
Allowed
) : (
-
+
Awaiting Approval
)}
diff --git a/app/routes/machines/overview.tsx b/app/routes/machines/overview.tsx
index 3974de6..bc40b56 100644
--- a/app/routes/machines/overview.tsx
+++ b/app/routes/machines/overview.tsx
@@ -59,10 +59,10 @@ export default function Page() {
return (
<>
-
+
- Machines
-
+ Machines
+
Manage the devices connected to your Tailnet.{' '}
-
+
- | Name |
+ Name |
Addresses
@@ -99,14 +99,14 @@ export default function Page() {
) : undefined}
|
- {/**Version | **/}
- Last Seen |
+ {/**Version | **/}
+ Last Seen |
{data.nodes.map((machine) => (
diff --git a/app/routes/settings/auth-keys.tsx b/app/routes/settings/auth-keys.tsx
index 8f489b7..f9c6bed 100644
--- a/app/routes/settings/auth-keys.tsx
+++ b/app/routes/settings/auth-keys.tsx
@@ -119,31 +119,31 @@ export async function loader({ request }: LoaderFunctionArgs) {
export default function Page() {
const { keys, users, server } = useLoaderData();
- const [user, setUser] = useState('All');
- const [status, setStatus] = useState('Active');
+ const [user, setUser] = useState('__headplane_all');
+ const [status, setStatus] = useState('active');
const filteredKeys = keys.filter((key) => {
- if (user !== 'All' && key.user !== user) {
+ if (user !== '__headplane_all' && key.user !== user) {
return false;
}
- if (status !== 'All') {
+ if (status !== 'all') {
const now = new Date();
const expiry = new Date(key.expiration);
- if (status === 'Active') {
+ if (status === 'active') {
return !(expiry < now) && (!key.used || key.reusable);
}
- if (status === 'Used/Expired') {
+ if (status === 'expired') {
return key.used || expiry < now;
}
- if (status === 'Reusable') {
+ if (status === 'reusable') {
return key.reusable;
}
- if (status === 'Ephemeral') {
+ if (status === 'ephemeral') {
return key.ephemeral;
}
}
@@ -160,8 +160,8 @@ export default function Page() {
/ Pre-Auth Keys
- Pre-Auth Keys
-
+ Pre-Auth Keys
+
Headscale fully supports pre-authentication keys in order to easily add
devices to your Tailnet. To learn more about using pre-authentication
keys, visit the{' '}
@@ -173,40 +173,34 @@ export default function Page() {
-
-
-
- Filter by user
-
-
-
-
-
- Filter by status
-
-
-
+
+
+
{filteredKeys.length === 0 ? (
diff --git a/app/routes/settings/components/agent.tsx b/app/routes/settings/components/agent.tsx
index 4412cb0..70fc7df 100644
--- a/app/routes/settings/components/agent.tsx
+++ b/app/routes/settings/components/agent.tsx
@@ -9,7 +9,7 @@ export default function AgentSection() {
<>
Local Agent
-
+
Headplane provides a local agent that can be installed on a server to
provide additional features including viewing device information and
SSH access via the web interface (soon). To learn more about the agent
@@ -23,12 +23,7 @@ export default function AgentSection() {
-
+
diff --git a/app/routes/settings/components/agent/manage.tsx b/app/routes/settings/components/agent/manage.tsx
index a2ff409..3414f04 100644
--- a/app/routes/settings/components/agent/manage.tsx
+++ b/app/routes/settings/components/agent/manage.tsx
@@ -1,5 +1,5 @@
-import Card from '~/components/Card'
-import StatusCircle from '~/components/StatusCircle'
+import Card from '~/components/Card';
+import StatusCircle from '~/components/StatusCircle';
import type { HostInfo } from '~/types';
import * as hinfo from '~/utils/host-info';
@@ -12,26 +12,21 @@ export default function AgentManagement({ reachable, hostInfo }: Props) {
console.log('hostInfo:', hostInfo);
return (
-
- Local Agent Configuration
-
-
- A local agent has already been configured for this
- Headplane instance. You can manage the agent settings here.
+ Local Agent Configuration
+
+ A local agent has already been configured for this Headplane instance.
+ You can manage the agent settings here.
-
+
{hostInfo.Hostname ?? 'Unknown'}
{hinfo.getTSVersion(hostInfo)}
-
+
{hinfo.getOSInfo(hostInfo)}
@@ -40,5 +35,5 @@ export default function AgentManagement({ reachable, hostInfo }: Props) {
{JSON.stringify(hostInfo)}
- )
+ );
}
diff --git a/app/routes/settings/overview.tsx b/app/routes/settings/overview.tsx
index efc4112..04529a5 100644
--- a/app/routes/settings/overview.tsx
+++ b/app/routes/settings/overview.tsx
@@ -11,7 +11,7 @@ export default function Page() {
Settings
-
+
The settings page is still under construction. As I'm able to add more
features, I'll be adding them here. If you require any features, feel
free to open an issue on the GitHub repository.
@@ -19,7 +19,7 @@ export default function Page() {
Pre-Auth Keys
-
+
Headscale fully supports pre-authentication keys in order to easily
add devices to your Tailnet. To learn more about using
pre-authentication keys, visit the{' '}
@@ -32,12 +32,7 @@ export default function Page() {
-
+
diff --git a/app/routes/users/components/auth.tsx b/app/routes/users/components/auth.tsx
index a7c4c37..187e1a9 100644
--- a/app/routes/users/components/auth.tsx
+++ b/app/routes/users/components/auth.tsx
@@ -13,10 +13,10 @@ export default function Auth({ magic }: Props) {
return (
-
+
Basic Authentication
-
+
Users are not managed externally. Using OpenID Connect can create a
better experience when using Headscale.{' '}
-
+
User Management
-
+
You can add, remove, and rename users here.
diff --git a/app/routes/users/components/oidc.tsx b/app/routes/users/components/oidc.tsx
index 4e5175d..4c034da 100644
--- a/app/routes/users/components/oidc.tsx
+++ b/app/routes/users/components/oidc.tsx
@@ -15,10 +15,10 @@ export default function Oidc({ oidc, magic }: Props) {
return (
-
+
OpenID Connect
-
+
Users are managed through your{' '}
OpenID Connect provider
@@ -33,10 +33,10 @@ export default function Oidc({ oidc, magic }: Props) {
-
+
User Management
-
+
You can still add users manually, however it is recommended that you
manage users through your OIDC provider.
diff --git a/app/routes/users/overview.tsx b/app/routes/users/overview.tsx
index 4c31d62..e0e920d 100644
--- a/app/routes/users/overview.tsx
+++ b/app/routes/users/overview.tsx
@@ -253,7 +253,7 @@ function MachineChip({ machine }: { readonly machine: Machine }) {
ref={setNodeRef}
className={cn(
'flex items-center w-full gap-2 py-1',
- 'hover:bg-ui-100 dark:hover:bg-ui-800 rounded-lg',
+ 'hover:bg-headplane-50 dark:hover:bg-headplane-950 rounded-xl',
)}
style={{
transform: transform
@@ -263,7 +263,7 @@ function MachineChip({ machine }: { readonly machine: Machine }) {
{...listeners}
{...attributes}
>
-
+
diff --git a/app/utils/config/headplane.ts b/app/utils/config/headplane.ts
index b0e08bc..df39948 100644
--- a/app/utils/config/headplane.ts
+++ b/app/utils/config/headplane.ts
@@ -2,18 +2,17 @@
// Functionally only used for all sorts of sanity checks across headplane.
//
// Around the codebase, this is referred to as the context
+// TODO: Fix the TRASH that is this env var mess
+// - Zod needs to be used for the config
+// - Switch to YAML for the config file
-import { access, constants, readFile, writeFile } from 'node:fs/promises';
+import { constants, access, readFile, writeFile } from 'node:fs/promises';
import { resolve } from 'node:path';
-
-import { parse } from 'yaml';
-
import { IntegrationFactory, loadIntegration } from '~/integration';
import { HeadscaleConfig, loadConfig } from '~/utils/config/headscale';
-import { testOidc } from '~/utils/oidc';
import log from '~/utils/log';
+import { testOidc } from '~/utils/oidc';
import { initSessionManager } from '~/utils/sessions.server';
-import { initAgentCache } from '~/utils/ws-agent';
export interface HeadplaneContext {
debug: boolean;
@@ -26,7 +25,7 @@ export interface HeadplaneContext {
enabled: boolean;
path: string;
defaultTTL: number;
- }
+ };
config: {
read: boolean;
@@ -107,7 +106,8 @@ export async function loadContext(): Promise {
initSessionManager();
const cacheEnabled = process.env.AGENT_CACHE_DISABLED !== 'true';
- const cachePath = process.env.AGENT_CACHE_PATH ?? '/etc/headplane/agent.cache';
+ const cachePath =
+ process.env.AGENT_CACHE_PATH ?? '/etc/headplane/agent.cache';
const cacheTTL = 300 * 1000; // 5 minutes
// Load agent cache
@@ -237,9 +237,9 @@ async function checkOidc(config?: HeadscaleConfig) {
clientId: client,
clientSecret: secret,
tokenEndpointAuthMethod: method,
- }
+ };
- const result = await testOidc(oidcConfig)
+ const result = await testOidc(oidcConfig);
if (!result) {
return;
}
@@ -301,9 +301,9 @@ async function checkOidc(config?: HeadscaleConfig) {
clientId: client,
clientSecret: secret,
tokenEndpointAuthMethod: method,
- }
+ };
- const result = await testOidc(oidcConfig)
+ const result = await testOidc(oidcConfig);
if (!result) {
return;
}
|