Compare commits

...

6 Commits

Author SHA1 Message Date
Aarnav Tale b433e607e2 chore: v0.3.6 2024-11-20 18:15:19 -05:00
Aarnav Tale 8aad883c21 fix: make selects scrollable 2024-11-20 18:13:49 -05:00
Aarnav Tale 3cd28d2136 feat: make the code snippets copyable 2024-11-20 18:01:20 -05:00
Aarnav Tale 9d9cbd8e0e chore: v0.3.5 2024-11-08 12:05:33 -05:00
Aarnav Tale feb8b8bba5 chore: remove logging on expected errors 2024-11-08 12:04:24 -05:00
Aarnav Tale c304effcdf fix: externalize process.env 2024-11-08 12:03:08 -05:00
9 changed files with 58 additions and 30 deletions
+8
View File
@@ -1,3 +1,11 @@
### 0.3.6 (November 20, 2024)
- Fixed an issue where select dropdowns would not scroll (fixes [#53](https://github.com/tale/headplane/issues/53))
- Added a button to copy the machine registration command to the clipboard (fixes [#52](https://github.com/tale/headplane/issues/52))
### 0.3.5 (November 8, 2024)
- Quickfix a bug where environment variables are ignored on the server.
- Remove a nagging error about missing cookie since that happens when signed out.
### 0.3.4 (November 7, 2024)
- Clicking on the machine name in the users page now takes you to the machine overview page.
- Completely rebuilt the production server to work better outside of Docker and be lighter. More specifically, we've switched from the `@remix-run/serve` package to our own custom built server.
+40 -7
View File
@@ -1,12 +1,45 @@
import clsx from 'clsx'
import { type HTMLProps } from 'react'
import { useState, HTMLProps } from 'react'
import { CopyIcon, CheckIcon } from '@primer/octicons-react'
type Properties = HTMLProps<HTMLSpanElement>
import { cn } from '~/utils/cn'
import { toast } from '~/components/Toaster'
interface Props extends HTMLProps<HTMLSpanElement> {
isCopyable?: boolean
}
export default function Code(props: Props) {
const [isCopied, setIsCopied] = useState(false)
export default function Code(properties: Properties) {
return (
<code className={clsx('bg-gray-100 dark:bg-zinc-700 p-0.5 rounded-md', properties.className)}>
{properties.children}
</code>
<>
<code className={cn(
'bg-ui-100 dark:bg-ui-800 p-0.5 rounded-md',
props.className
)}>
{props.children}
</code>
{props.isCopyable && (
<button
className={cn(
'ml-1 p-1 rounded-md',
'bg-ui-100 dark:bg-ui-800',
'text-ui-500 dark:text-ui-400',
'inline-flex items-center justify-center'
)}
onClick={() => {
navigator.clipboard.writeText(props.children as string)
toast('Copied to clipboard')
setIsCopied(true)
setTimeout(() => setIsCopied(false), 1000)
}}
>
{isCopied ?
<CheckIcon className="h-3 w-3" /> :
<CopyIcon className="h-3 w-3" />
}
</button>
)}
</>
)
}
+2 -2
View File
@@ -46,7 +46,7 @@ function Select(props: SelectProps) {
className={cn(
'mt-2 rounded-md w-[var(--trigger-width)]',
'bg-ui-100 dark:bg-ui-800 shadow-sm',
'overflow-hidden z-50',
'z-50 overflow-y-auto',
'border border-ui-200 dark:border-ui-600',
'entering:animate-in exiting:animate-out',
'entering:fade-in entering:zoom-in-95',
@@ -54,7 +54,7 @@ function Select(props: SelectProps) {
'fill-mode-forwards origin-left-right',
)}
>
<ListBox>
<ListBox orientation="vertical">
{props.children}
</ListBox>
</Popover>
@@ -50,10 +50,8 @@ export default function New(data: NewProps) {
<Dialog.Text className='mb-4'>
The machine key is given when you run
{' '}
<Code>
<Code isCopyable>
tailscale up --login-server=
</Code>
<Code>
{data.server}
</Code>
{' '}
-12
View File
@@ -20,9 +20,6 @@ export class FatalError extends Error {
export async function pull<T>(url: string, key: string) {
if (!key || key === 'undefined' || key.length === 0) {
log.error('APIC', 'Missing API key, could this be a cookie setting issue?')
log.error('APIC', 'Check that the hp_sess cookie is being set correctly')
log.error('APIC', 'If you are running without HTTPs, make sure the Secure flag is false')
throw new Error('Missing API key, could this be a cookie setting issue?')
}
@@ -46,9 +43,6 @@ export async function pull<T>(url: string, key: string) {
export async function post<T>(url: string, key: string, body?: unknown) {
if (!key || key === 'undefined' || key.length === 0) {
log.error('APIC', 'Missing API key, could this be a cookie setting issue?')
log.error('APIC', 'Check that the hp_sess cookie is being set correctly')
log.error('APIC', 'If you are running without HTTPs, make sure the Secure flag is false')
throw new Error('Missing API key, could this be a cookie setting issue?')
}
@@ -74,9 +68,6 @@ export async function post<T>(url: string, key: string, body?: unknown) {
export async function put<T>(url: string, key: string, body?: unknown) {
if (!key || key === 'undefined' || key.length === 0) {
log.error('APIC', 'Missing API key, could this be a cookie setting issue?')
log.error('APIC', 'Check that the hp_sess cookie is being set correctly')
log.error('APIC', 'If you are running without HTTPs, make sure the Secure flag is false')
throw new Error('Missing API key, could this be a cookie setting issue?')
}
@@ -102,9 +93,6 @@ export async function put<T>(url: string, key: string, body?: unknown) {
export async function del<T>(url: string, key: string) {
if (!key || key === 'undefined' || key.length === 0) {
log.error('APIC', 'Missing API key, could this be a cookie setting issue?')
log.error('APIC', 'Check that the hp_sess cookie is being set correctly')
log.error('APIC', 'If you are running without HTTPs, make sure the Secure flag is false')
throw new Error('Missing API key, could this be a cookie setting issue?')
}
+1 -1
View File
@@ -29,7 +29,7 @@ Here is a simple Docker Compose deployment:
services:
headplane:
container_name: headplane
image: ghcr.io/tale/headplane:0.3.2
image: ghcr.io/tale/headplane:0.3.5
restart: unless-stopped
ports:
- '3000:3000'
+1 -1
View File
@@ -50,7 +50,7 @@ services:
TZ: 'America/New_York'
headplane:
container_name: headplane
image: ghcr.io/tale/headplane:0.3.2
image: ghcr.io/tale/headplane:0.3.5
restart: unless-stopped
volumes:
- './data:/var/lib/headscale'
+1 -1
View File
@@ -88,7 +88,7 @@ spec:
serviceAccountName: default
containers:
- name: headplane
image: ghcr.io/tale/headplane:0.3.2
image: ghcr.io/tale/headplane:0.3.5
env:
- name: COOKIE_SECRET
value: 'abcdefghijklmnopqrstuvwxyz'
+4 -3
View File
@@ -8,6 +8,7 @@ import { access, constants } from 'node:fs/promises'
import { createReadStream, existsSync, statSync } from 'node:fs'
import { createServer } from 'node:http'
import { join, resolve } from 'node:path'
import { env } from 'node:process'
function log(level, message) {
const date = new Date().toISOString()
@@ -42,9 +43,9 @@ const {
} = await import('@remix-run/node')
const { default: mime } = await import('mime')
const port = process.env.PORT || 3000
const host = process.env.HOST || '0.0.0.0'
const buildPath = process.env.BUILD_PATH || './build'
const port = env.PORT || 3000
const host = env.HOST || '0.0.0.0'
const buildPath = env.BUILD_PATH || './build'
// Because this is a dynamic import without an easily discernable path
// we gain the "deoptimization" we want so that Vite doesn't bundle this