The tailnet plugin — machines, users, pre-auth keys, access policy and device invites. Moved out of officerdev/platform, where it had lived in plugins/ since the plugin system was built. Until now this code existed in exactly one place: the platform repository. That made "gitignore the plugins directory" impossible to do safely, because untracking it would have left 49 files on a single disk with no remote. This repository is what makes that move safe. Same extraction as plugins/music before it: source only, no history. The platform's history still holds every commit that shaped this, and the SHAs cited across the codebase keep resolving — replaying it here would have created a second, divergent account of the same work. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
36 lines
1.5 KiB
TypeScript
36 lines
1.5 KiB
TypeScript
// Date formatting for the Headscale views. The sidecar already turned protobuf's zero timestamp into null,
|
|
// so null genuinely means "never" here and every helper says so rather than printing a fake date.
|
|
|
|
export function timeAgo(iso: string | null): string {
|
|
if (!iso) return 'never';
|
|
const seconds = Math.round((Date.now() - new Date(iso).getTime()) / 1000);
|
|
if (!Number.isFinite(seconds)) return 'unknown';
|
|
if (seconds < 0) return 'just now';
|
|
if (seconds < 60) return 'just now';
|
|
const minutes = Math.round(seconds / 60);
|
|
if (minutes < 60) return `${minutes}m ago`;
|
|
const hours = Math.round(minutes / 60);
|
|
if (hours < 24) return `${hours}h ago`;
|
|
const days = Math.round(hours / 24);
|
|
if (days < 365) return `${days}d ago`;
|
|
return `${Math.round(days / 365)}y ago`;
|
|
}
|
|
|
|
/** "in 3d" / "5h ago" — signed, for expiry dates that may be either side of now. */
|
|
export function timeUntil(iso: string | null): string {
|
|
if (!iso) return 'never';
|
|
const seconds = Math.round((new Date(iso).getTime() - Date.now()) / 1000);
|
|
if (!Number.isFinite(seconds)) return 'unknown';
|
|
if (seconds < 0) return timeAgo(iso);
|
|
const minutes = Math.round(seconds / 60);
|
|
if (minutes < 60) return `in ${Math.max(1, minutes)}m`;
|
|
const hours = Math.round(minutes / 60);
|
|
if (hours < 24) return `in ${hours}h`;
|
|
return `in ${Math.round(hours / 24)}d`;
|
|
}
|
|
|
|
export function fullDate(iso: string | null): string {
|
|
if (!iso) return 'never';
|
|
return new Date(iso).toLocaleString();
|
|
}
|