mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-12 15:28:12 +00:00
feat: improve Pro feature UI gating and error handling
- AISettings: Add Pro badge & disabled state for Autonomous Mode toggle - APIClient: Parse JSON error responses for better user-facing messages - Supports new 402 Payment Required responses from license-gated endpoints
This commit is contained in:
@@ -1177,17 +1177,33 @@ export const AISettings: Component = () => {
|
||||
ENABLED
|
||||
</span>
|
||||
</Show>
|
||||
<Show when={autoFixLocked()}>
|
||||
<span class="px-1.5 py-0.5 text-[10px] font-semibold bg-amber-100 dark:bg-amber-900 text-amber-700 dark:text-amber-300 rounded">Pro</span>
|
||||
</Show>
|
||||
</label>
|
||||
<p class="text-xs text-gray-600 dark:text-gray-400 mt-1">
|
||||
{form.autonomousMode
|
||||
? 'AI will execute all commands without asking for approval. Only enable if you trust your configured model.'
|
||||
: 'AI will ask for approval before running commands that modify your system. Read-only commands (like df, ps, docker stats) run automatically.'}
|
||||
</p>
|
||||
<Show when={autoFixLocked()}>
|
||||
<p class="text-[10px] text-amber-600 dark:text-amber-400 mt-1">
|
||||
Pulse Pro required for autonomous mode.{' '}
|
||||
<a
|
||||
class="underline decoration-dotted"
|
||||
href="https://pulserelay.pro"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Upgrade
|
||||
</a>
|
||||
</p>
|
||||
</Show>
|
||||
</div>
|
||||
<Toggle
|
||||
checked={form.autonomousMode}
|
||||
onChange={(event) => setForm('autonomousMode', event.currentTarget.checked)}
|
||||
disabled={saving()}
|
||||
disabled={saving() || autoFixLocked()}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -325,18 +325,31 @@ class ApiClient {
|
||||
// Try to extract just the error message without HTTP status codes
|
||||
let errorMessage = text;
|
||||
|
||||
// If it looks like an HTML error page, try to extract the message
|
||||
if (text.includes('<pre>') && text.includes('</pre>')) {
|
||||
const match = text.match(/<pre>(.*?)<\/pre>/s);
|
||||
if (match) errorMessage = match[1];
|
||||
}
|
||||
// First try to parse as JSON (our API returns structured errors like {error, message, feature, upgrade_url})
|
||||
try {
|
||||
const jsonError = JSON.parse(text);
|
||||
if (jsonError.message) {
|
||||
errorMessage = jsonError.message;
|
||||
} else if (jsonError.error && typeof jsonError.error === 'string') {
|
||||
// Some APIs return {error: "message"} format
|
||||
errorMessage = jsonError.error;
|
||||
}
|
||||
} catch {
|
||||
// Not JSON, try other formats
|
||||
|
||||
// If the backend sent a plain text error, use it directly
|
||||
if (!text.includes('<') && text.length < 200) {
|
||||
errorMessage = text;
|
||||
} else if (text.length > 200) {
|
||||
// For long responses, just use a generic message
|
||||
errorMessage = `Request failed with status ${response.status}`;
|
||||
// If it looks like an HTML error page, try to extract the message
|
||||
if (text.includes('<pre>') && text.includes('</pre>')) {
|
||||
const match = text.match(/<pre>(.*?)<\/pre>/s);
|
||||
if (match) errorMessage = match[1];
|
||||
}
|
||||
|
||||
// If the backend sent a plain text error, use it directly
|
||||
if (!text.includes('<') && text.length < 200) {
|
||||
errorMessage = text;
|
||||
} else if (text.length > 200) {
|
||||
// For long responses, just use a generic message
|
||||
errorMessage = `Request failed with status ${response.status}`;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(errorMessage || `Request failed with status ${response.status}`);
|
||||
|
||||
Reference in New Issue
Block a user