mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-04-28 11:30:15 +00:00
Clarify first-run unlock guidance
This commit is contained in:
parent
cefcddd14b
commit
a17987691c
5 changed files with 277 additions and 60 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import { Component, createSignal, Show } from 'solid-js';
|
||||
import { Component, createMemo, createSignal, onMount, Show } from 'solid-js';
|
||||
import { showError, showSuccess } from '@/utils/toast';
|
||||
import { apiFetch, apiFetchJSON } from '@/utils/apiClient';
|
||||
import { logger } from '@/utils/logger';
|
||||
|
|
@ -19,6 +19,7 @@ export const WelcomeStep: Component<WelcomeStepProps> = (props) => {
|
|||
const [isDocker, setIsDocker] = createSignal(false);
|
||||
const [inContainer, setInContainer] = createSignal(false);
|
||||
const [lxcCtid, setLxcCtid] = createSignal('');
|
||||
const [dockerContainerName, setDockerContainerName] = createSignal('');
|
||||
const [copied, setCopied] = createSignal(false);
|
||||
|
||||
const copyCommand = async () => {
|
||||
|
|
@ -40,20 +41,23 @@ export const WelcomeStep: Component<WelcomeStepProps> = (props) => {
|
|||
isDocker?: boolean;
|
||||
inContainer?: boolean;
|
||||
lxcCtid?: string;
|
||||
dockerContainerName?: string;
|
||||
}>('/api/security/status');
|
||||
if (data?.bootstrapTokenPath) {
|
||||
setTokenPath(data.bootstrapTokenPath);
|
||||
setIsDocker(data.isDocker || false);
|
||||
setInContainer(data.inContainer || false);
|
||||
setLxcCtid(data.lxcCtid || '');
|
||||
setDockerContainerName(data.dockerContainerName || '');
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to fetch bootstrap info:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// Call on component load
|
||||
fetchBootstrapInfo();
|
||||
onMount(() => {
|
||||
void fetchBootstrapInfo();
|
||||
});
|
||||
|
||||
const handleUnlock = async () => {
|
||||
if (!props.bootstrapToken.trim()) {
|
||||
|
|
@ -85,7 +89,7 @@ export const WelcomeStep: Component<WelcomeStepProps> = (props) => {
|
|||
const getTokenCommand = () => {
|
||||
const path = tokenPath() || '/etc/pulse/.bootstrap_token';
|
||||
if (isDocker()) {
|
||||
return `docker exec <container> cat ${path}`;
|
||||
return `docker exec ${dockerContainerName() || '<pulse-container>'} cat ${path}`;
|
||||
}
|
||||
if (inContainer() && lxcCtid()) {
|
||||
return `pct exec ${lxcCtid()} -- cat ${path}`;
|
||||
|
|
@ -96,6 +100,42 @@ export const WelcomeStep: Component<WelcomeStepProps> = (props) => {
|
|||
return `cat ${path}`;
|
||||
};
|
||||
|
||||
const deploymentLabel = createMemo(() => {
|
||||
if (isDocker()) {
|
||||
return 'Docker deployment';
|
||||
}
|
||||
if (inContainer() && lxcCtid()) {
|
||||
return 'LXC container';
|
||||
}
|
||||
if (inContainer()) {
|
||||
return 'Containerized deployment';
|
||||
}
|
||||
return 'Direct host install';
|
||||
});
|
||||
|
||||
const deploymentHint = createMemo(() => {
|
||||
const path = tokenPath() || '/etc/pulse/.bootstrap_token';
|
||||
if (isDocker()) {
|
||||
return dockerContainerName()
|
||||
? `Pulse appears to be running in Docker as container "${dockerContainerName()}". Run the command on the Docker host so you can read ${path} from that container.`
|
||||
: `Pulse appears to be running in Docker. Run the command on the Docker host and replace <pulse-container> with the running Pulse container name.`;
|
||||
}
|
||||
if (inContainer() && lxcCtid()) {
|
||||
return `Pulse appears to be running in LXC container ${lxcCtid()}. Run the command on the Proxmox host so you can execute into that container and read ${path}.`;
|
||||
}
|
||||
if (inContainer()) {
|
||||
return `Pulse appears to be running in a containerized environment. Run the command from the host that manages the container so you can read ${path}.`;
|
||||
}
|
||||
return `Run the command directly in a shell on the Pulse server to read ${path}.`;
|
||||
});
|
||||
|
||||
const unlockHelp = createMemo(() => {
|
||||
if (isDocker()) {
|
||||
return 'This one-time bootstrap token only unlocks first-run setup. After verification, you will create the admin account and Pulse will generate the long-lived API token separately.';
|
||||
}
|
||||
return 'This one-time bootstrap token only unlocks first-run setup on this Pulse server. It is not your admin password and it is not the API token you will use after setup.';
|
||||
});
|
||||
|
||||
return (
|
||||
<div class="text-center relative">
|
||||
{/* Logo */}
|
||||
|
|
@ -117,6 +157,36 @@ export const WelcomeStep: Component<WelcomeStepProps> = (props) => {
|
|||
</p>
|
||||
</div>
|
||||
|
||||
<div class="mb-8 grid gap-3 text-left sm:grid-cols-3">
|
||||
<div class="rounded-md border border-border bg-surface px-4 py-3">
|
||||
<div class="text-[11px] font-semibold uppercase tracking-wide text-blue-700 dark:text-blue-300">
|
||||
Step 1
|
||||
</div>
|
||||
<div class="mt-1 text-sm font-semibold text-base-content">Unlock this Pulse server</div>
|
||||
<p class="mt-1 text-xs text-muted">
|
||||
Read the one-time bootstrap token from the system where Pulse is installed.
|
||||
</p>
|
||||
</div>
|
||||
<div class="rounded-md border border-border bg-surface px-4 py-3">
|
||||
<div class="text-[11px] font-semibold uppercase tracking-wide text-blue-700 dark:text-blue-300">
|
||||
Step 2
|
||||
</div>
|
||||
<div class="mt-1 text-sm font-semibold text-base-content">Create the admin account</div>
|
||||
<p class="mt-1 text-xs text-muted">
|
||||
Set the first login and let Pulse generate the credentials you need to save.
|
||||
</p>
|
||||
</div>
|
||||
<div class="rounded-md border border-border bg-surface px-4 py-3">
|
||||
<div class="text-[11px] font-semibold uppercase tracking-wide text-blue-700 dark:text-blue-300">
|
||||
Step 3
|
||||
</div>
|
||||
<div class="mt-1 text-sm font-semibold text-base-content">Install the first host</div>
|
||||
<p class="mt-1 text-xs text-muted">
|
||||
Open Infrastructure Install and connect the first system you want Pulse to monitor.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bootstrap token unlock */}
|
||||
<Show when={!props.isUnlocked}>
|
||||
<div class="p-8 max-w-lg mx-auto bg-surface border border-border rounded-md text-left animate-slide-up delay-300 relative group">
|
||||
|
|
@ -125,10 +195,22 @@ export const WelcomeStep: Component<WelcomeStepProps> = (props) => {
|
|||
Unlock Setup
|
||||
</h3>
|
||||
<p class="text-sm text-muted mb-6">
|
||||
Run the following command on the machine where Pulse is installed to retrieve the
|
||||
secure bootstrap token:
|
||||
Run the following command on the Pulse server to retrieve the one-time bootstrap
|
||||
token that unlocks this wizard:
|
||||
</p>
|
||||
|
||||
<div class="mb-4 rounded-md border border-blue-200 bg-blue-50 px-4 py-3 dark:border-blue-800 dark:bg-blue-950/40">
|
||||
<div class="text-[11px] font-semibold uppercase tracking-wide text-blue-700 dark:text-blue-300">
|
||||
Where to run it
|
||||
</div>
|
||||
<div class="mt-1 flex items-center gap-2">
|
||||
<span class="inline-flex items-center rounded-full bg-blue-100 px-2 py-0.5 text-[11px] font-semibold text-blue-700 dark:bg-blue-900 dark:text-blue-200">
|
||||
{deploymentLabel()}
|
||||
</span>
|
||||
</div>
|
||||
<p class="mt-2 text-sm text-blue-900 dark:text-blue-100">{deploymentHint()}</p>
|
||||
</div>
|
||||
|
||||
<div class="mb-8">
|
||||
<div class="bg-base rounded-md p-4 font-mono text-sm text-emerald-400 border border-border-subtle flex items-center justify-between">
|
||||
<div class="flex items-center space-x-3 overflow-x-auto scrollbar-hide">
|
||||
|
|
@ -147,6 +229,13 @@ export const WelcomeStep: Component<WelcomeStepProps> = (props) => {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-6 rounded-md border border-emerald-200 bg-emerald-50 px-4 py-3 dark:border-emerald-800 dark:bg-emerald-950/40">
|
||||
<div class="text-[11px] font-semibold uppercase tracking-wide text-emerald-700 dark:text-emerald-300">
|
||||
What this token does
|
||||
</div>
|
||||
<p class="mt-2 text-sm text-emerald-900 dark:text-emerald-100">{unlockHelp()}</p>
|
||||
</div>
|
||||
|
||||
<div class="space-y-4">
|
||||
<input
|
||||
type="text"
|
||||
|
|
@ -163,12 +252,17 @@ export const WelcomeStep: Component<WelcomeStepProps> = (props) => {
|
|||
}, 400);
|
||||
}
|
||||
}}
|
||||
onKeyPress={(e) => e.key === 'Enter' && handleUnlock()}
|
||||
onKeyDown={(e) => e.key === 'Enter' && void handleUnlock()}
|
||||
class="w-full px-5 py-3.5 bg-surface border border-border rounded-md text-base-content placeholder-slate-400 focus:outline-none focus:ring-0 focus:border-blue-500 transition-colors font-mono"
|
||||
placeholder="Paste your bootstrap token"
|
||||
autofocus
|
||||
/>
|
||||
|
||||
<p class="text-xs text-muted">
|
||||
After Pulse verifies this token, the next step is creating the admin account for
|
||||
this server.
|
||||
</p>
|
||||
|
||||
<button
|
||||
onClick={handleUnlock}
|
||||
disabled={isValidating() || !props.bootstrapToken.trim()}
|
||||
|
|
@ -195,10 +289,10 @@ export const WelcomeStep: Component<WelcomeStepProps> = (props) => {
|
|||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||
></path>
|
||||
</svg>
|
||||
Validating...
|
||||
Verifying bootstrap token...
|
||||
</>
|
||||
) : (
|
||||
'Continue to Setup →'
|
||||
'Verify bootstrap token →'
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -212,7 +306,7 @@ export const WelcomeStep: Component<WelcomeStepProps> = (props) => {
|
|||
onClick={props.onNext}
|
||||
class="py-4 px-10 bg-blue-600 hover:bg-blue-700 text-white text-lg font-medium rounded-md transition-colors duration-200"
|
||||
>
|
||||
Get Started →
|
||||
Continue to Security →
|
||||
</button>
|
||||
</div>
|
||||
</Show>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue