chore: push disabled local-agent code

This commit is contained in:
Aarnav Tale 2025-01-18 07:37:03 +00:00
parent de9a938da2
commit 325e9ba43d
No known key found for this signature in database
11 changed files with 147 additions and 11 deletions

View File

@ -27,7 +27,7 @@ export default [
...prefix('/settings', [ ...prefix('/settings', [
index('routes/settings/overview.tsx'), index('routes/settings/overview.tsx'),
route('/auth-keys', 'routes/settings/auth-keys.tsx'), route('/auth-keys', 'routes/settings/auth-keys.tsx'),
route('/local-agent', 'routes/settings/local-agent.tsx'), // route('/local-agent', 'routes/settings/local-agent.tsx'),
]), ]),
]), ]),
]), ]),

View File

@ -153,6 +153,7 @@ export default function MachineRow({ machine, routes, magic, users, stats }: Pro
</Menu> </Menu>
</div> </div>
</td> </td>
{/**
<td className="py-2"> <td className="py-2">
{stats !== undefined ? ( {stats !== undefined ? (
<> <>
@ -169,6 +170,7 @@ export default function MachineRow({ machine, routes, magic, users, stats }: Pro
</p> </p>
)} )}
</td> </td>
**/}
<td className="py-2"> <td className="py-2">
<span <span
className={cn( className={cn(

View File

@ -113,7 +113,7 @@ export default function Page() {
) : undefined} ) : undefined}
</div> </div>
</th> </th>
<th className="pb-2">Version</th> {/**<th className="pb-2">Version</th>**/}
<th className="pb-2">Last Seen</th> <th className="pb-2">Last Seen</th>
</tr> </tr>
</thead> </thead>

View File

@ -0,0 +1,38 @@
import Link from '~/components/Link';
import Button from '~/components/Button';
import { Link as RemixLink } from 'react-router';
import { ArrowRightIcon } from '@primer/octicons-react';
import { cn } from '~/utils/cn';
export default function AgentSection() {
return (
<>
<div className="flex flex-col w-2/3">
<h1 className="text-2xl font-medium mb-4">Local Agent</h1>
<p className="text-gray-700 dark:text-gray-300">
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 visit the{' '}
<Link
to="https://github.com/tale/headplane/blob/main/docs/Headplane-Agent.md"
name="Headplane Agent Documentation"
>
Headplane documentation
</Link>
</p>
</div>
<RemixLink to="/settings/local-agent">
<div
className={cn(
'text-lg font-medium flex items-center',
'text-gray-700 dark:text-gray-300',
)}
>
Manage Agent
<ArrowRightIcon className="w-5 h-5 ml-2" />
</div>
</RemixLink>
</>
)
}

View File

@ -0,0 +1,44 @@
import Card from '~/components/Card'
import StatusCircle from '~/components/StatusCircle'
import type { HostInfo } from '~/types';
import * as hinfo from '~/utils/host-info';
export type Props = {
reachable: boolean;
hostInfo: HostInfo;
};
export default function AgentManagement({ reachable, hostInfo }: Props) {
console.log('hostInfo:', hostInfo);
return (
<div className="flex flex-col w-2/3">
<h1 className="text-2xl font-medium mb-4">
Local Agent Configuration
</h1>
<p className="text-gray-700 dark:text-gray-300 mb-8">
A local agent has already been configured for this
Headplane instance. You can manage the agent settings here.
</p>
<Card>
<div className="flex items-center gap-2">
<StatusCircle
isOnline={reachable}
className="w-4 h-4 px-1 w-fit"
/>
<div>
<p className="text-lg font-bold">
{hostInfo.Hostname ?? 'Unknown'}
</p>
<p className="leading-snug">
{hinfo.getTSVersion(hostInfo)}
<span className="ml-2 text-sm text-gray-500 dark:text-gray-300">
{hinfo.getOSInfo(hostInfo)}
</span>
</p>
</div>
</div>
{JSON.stringify(hostInfo)}
</Card>
</div>
)
}

View File

@ -0,0 +1,44 @@
import { useMemo } from 'react';
import { useLoaderData, type LoaderFunctionArgs } from 'react-router';
import { getSession, commitSession } from '~/utils/sessions.server'
import { queryAgent } from '~/utils/ws-agent'
import AgentManagement from './components/agent/manage'
export async function loader({ request, context }: LoaderFunctionArgs) {
const { ws, wsAuthKey } = context;
const session = await getSession(request.headers.get('Cookie'));
const onboarding = session.get('agent_onboarding') ?? false;
const nodeKey = 'nodekey:542dad28354eb8d51e240aada7adf0222ba3ecc74af0bbd56123f03eefdb391b'
const stats = await queryAgent([nodeKey]);
return {
configured: wsAuthKey !== undefined,
onboarding,
stats: stats[nodeKey]
}
}
export default function Page() {
const data = useLoaderData<typeof loader>();
// Whether we show the onboarding or management UI
const management = useMemo(() => {
return data.configured && (data.onboarding === false)
}, [data.configured, data.onboarding]);
return (
<div className="flex flex-col gap-8 max-w-screen-lg">
{management ? (
<AgentManagement
reachable={true}
hostInfo={data.stats}
/>
) : (
<div>
<h1>Local Agent Coming Soon</h1>
</div>
)}
</div>
)
}

View File

@ -4,6 +4,8 @@ import { Link as RemixLink } from 'react-router';
import { ArrowRightIcon } from '@primer/octicons-react'; import { ArrowRightIcon } from '@primer/octicons-react';
import { cn } from '~/utils/cn'; import { cn } from '~/utils/cn';
import AgentSection from './components/agent';
export default function Page() { export default function Page() {
return ( return (
<div className="flex flex-col gap-8 max-w-screen-lg"> <div className="flex flex-col gap-8 max-w-screen-lg">
@ -30,16 +32,17 @@ export default function Page() {
</p> </p>
</div> </div>
<RemixLink to="/settings/auth-keys"> <RemixLink to="/settings/auth-keys">
<span <div
className={cn( className={cn(
'text-lg font-medium', 'text-lg font-medium flex items-center',
'text-gray-700 dark:text-gray-300', 'text-gray-700 dark:text-gray-300',
)} )}
> >
Manage Auth Keys Manage Auth Keys
<ArrowRightIcon className="w-5 h-5 ml-2" /> <ArrowRightIcon className="w-5 h-5 ml-2" />
</span> </div>
</RemixLink> </RemixLink>
{/**<AgentSection />**/}
</div> </div>
); );
} }

View File

@ -111,12 +111,12 @@ export async function loadContext(): Promise<HeadplaneContext> {
const cacheTTL = 300 * 1000; // 5 minutes const cacheTTL = 300 * 1000; // 5 minutes
// Load agent cache // Load agent cache
if (cacheEnabled) { // if (cacheEnabled) {
log.info('CTXT', 'Initializing Agent Cache'); // log.info('CTXT', 'Initializing Agent Cache');
log.debug('CTXT', 'Cache Path: %s', cachePath); // log.debug('CTXT', 'Cache Path: %s', cachePath);
log.debug('CTXT', 'Cache TTL: %d', cacheTTL); // log.debug('CTXT', 'Cache TTL: %d', cacheTTL);
await initAgentCache(cacheTTL, cachePath); // await initAgentCache(cacheTTL, cachePath);
} // }
context = { context = {
debug, debug,

View File

@ -5,6 +5,7 @@ export type SessionData = {
oidc_state: string; oidc_state: string;
oidc_code_verif: string; oidc_code_verif: string;
oidc_nonce: string; oidc_nonce: string;
agent_onboarding: boolean;
user: { user: {
subject: string; subject: string;
name: string; name: string;

View File

@ -97,6 +97,7 @@ export function initAgentSocket(context: LoaderFunctionArgs['context']) {
// Check the cache and then attempt the websocket query // Check the cache and then attempt the websocket query
// If we aren't connected to an agent, then debug log and return the cache // If we aren't connected to an agent, then debug log and return the cache
export async function queryAgent(nodes: string[]) { export async function queryAgent(nodes: string[]) {
return;
if (!cache) { if (!cache) {
log.error('CACH', 'Cache not initialized'); log.error('CACH', 'Cache not initialized');
return; return;

View File

@ -3,6 +3,9 @@ import log from '~server/log'
const server = new WebSocketServer({ noServer: true }); const server = new WebSocketServer({ noServer: true });
export function initWebsocket() { export function initWebsocket() {
// TODO: Finish this and make public
return;
const key = process.env.LOCAL_AGENT_AUTHKEY; const key = process.env.LOCAL_AGENT_AUTHKEY;
if (!key) { if (!key) {
return; return;