Rename project to OrbitalWard
Add optional TLS certificate expiry checks for website monitors and update product, package, environment, Docker, and documentation naming.
This commit is contained in:
@@ -61,7 +61,7 @@ export function App() {
|
||||
}
|
||||
|
||||
if (auth.loading) {
|
||||
return <div className="flex min-h-screen items-center justify-center bg-[#090d13] text-sm text-slate-300">Loading InfraPulse...</div>;
|
||||
return <div className="flex min-h-screen items-center justify-center bg-[#090d13] text-sm text-slate-300">Loading OrbitalWard...</div>;
|
||||
}
|
||||
|
||||
if (!auth.user || !auth.token) {
|
||||
|
||||
@@ -47,7 +47,7 @@ export function Shell({ children, currentPage, onPageChange, onSignOut, user }:
|
||||
<Shield size={19} />
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-base font-semibold">InfraPulse</div>
|
||||
<div className="text-base font-semibold">OrbitalWard</div>
|
||||
<div className="text-xs text-slate-400">Monitoring appliance</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useEffect, useMemo, useState } from "react";
|
||||
import { api, login } from "../api/client";
|
||||
import type { User } from "../types/api";
|
||||
|
||||
const TOKEN_KEY = "infrapulse_token";
|
||||
const TOKEN_KEY = "orbitalward_token";
|
||||
|
||||
export function useAuth() {
|
||||
const [token, setToken] = useState<string | null>(() => localStorage.getItem(TOKEN_KEY));
|
||||
|
||||
@@ -9,7 +9,7 @@ interface DashboardPageProps {
|
||||
}
|
||||
|
||||
export function DashboardPage({ assets, monitors, incidents }: DashboardPageProps) {
|
||||
const downMonitors = monitors.filter((monitor) => monitor.status === "down").length;
|
||||
const attentionMonitors = monitors.filter((monitor) => monitor.status !== "up" && monitor.status !== "unknown").length;
|
||||
const activeIncidents = incidents.filter((incident) => incident.status === "open").length;
|
||||
const websites = monitors.filter((monitor) => monitor.monitor_type === "http");
|
||||
|
||||
@@ -33,7 +33,7 @@ export function DashboardPage({ assets, monitors, incidents }: DashboardPageProp
|
||||
<div className="rounded-md border border-line bg-[#0d131c]">
|
||||
<div className="flex items-center justify-between border-b border-line p-4">
|
||||
<h2 className="text-base font-semibold">Website Monitors</h2>
|
||||
<span className="text-sm text-slate-400">{downMonitors} down</span>
|
||||
<span className="text-sm text-slate-400">{attentionMonitors} need attention</span>
|
||||
</div>
|
||||
<div className="divide-y divide-line">
|
||||
{websites.length ? (
|
||||
@@ -110,6 +110,8 @@ function StatusBadge({ status }: { status: string }) {
|
||||
? "border-teal-500/40 bg-teal-950/40 text-teal-200"
|
||||
: status === "down"
|
||||
? "border-red-500/40 bg-red-950/40 text-red-200"
|
||||
: "border-slate-600 bg-slate-900 text-slate-300";
|
||||
: status === "warning"
|
||||
? "border-amber-500/40 bg-amber-950/40 text-amber-200"
|
||||
: "border-slate-600 bg-slate-900 text-slate-300";
|
||||
return <span className={`inline-flex h-7 items-center justify-center rounded-md border px-2 text-xs font-medium ${classes}`}>{status}</span>;
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ export function LoginPage({ onLogin }: LoginPageProps) {
|
||||
<div className="flex h-10 w-10 items-center justify-center rounded-md bg-pulse text-slate-950">
|
||||
<Shield size={22} />
|
||||
</div>
|
||||
<div className="text-lg font-semibold">InfraPulse</div>
|
||||
<div className="text-lg font-semibold">OrbitalWard</div>
|
||||
</div>
|
||||
<div className="max-w-3xl pb-6">
|
||||
<h1 className="max-w-2xl text-4xl font-semibold leading-tight lg:text-6xl">
|
||||
|
||||
@@ -14,7 +14,7 @@ export function NotificationsPage({ token }: NotificationsPageProps) {
|
||||
const [name, setName] = useState("");
|
||||
const [channelType, setChannelType] = useState("generic_webhook");
|
||||
const [url, setUrl] = useState("");
|
||||
const [username, setUsername] = useState("InfraPulse");
|
||||
const [username, setUsername] = useState("OrbitalWard");
|
||||
const [enabled, setEnabled] = useState(true);
|
||||
const [editingChannelId, setEditingChannelId] = useState<number | null>(null);
|
||||
const [busyId, setBusyId] = useState<number | null>(null);
|
||||
@@ -38,7 +38,7 @@ export function NotificationsPage({ token }: NotificationsPageProps) {
|
||||
await api.updateNotificationChannel(token, editingChannelId, {
|
||||
name,
|
||||
channel_type: channelType,
|
||||
settings: { username: username.trim() || "InfraPulse" },
|
||||
settings: { username: username.trim() || "OrbitalWard" },
|
||||
secret: url.trim() ? url.trim() : undefined,
|
||||
is_enabled: enabled,
|
||||
});
|
||||
@@ -46,7 +46,7 @@ export function NotificationsPage({ token }: NotificationsPageProps) {
|
||||
await api.createNotificationChannel(token, {
|
||||
name,
|
||||
channel_type: channelType,
|
||||
settings: { username: username.trim() || "InfraPulse" },
|
||||
settings: { username: username.trim() || "OrbitalWard" },
|
||||
secret: url,
|
||||
is_enabled: enabled,
|
||||
});
|
||||
@@ -65,7 +65,7 @@ export function NotificationsPage({ token }: NotificationsPageProps) {
|
||||
setName(channel.name);
|
||||
setChannelType(channel.channel_type);
|
||||
setUrl("");
|
||||
setUsername(String(channel.settings.username || "InfraPulse"));
|
||||
setUsername(String(channel.settings.username || "OrbitalWard"));
|
||||
setEnabled(channel.is_enabled);
|
||||
setMessage(null);
|
||||
}
|
||||
@@ -75,7 +75,7 @@ export function NotificationsPage({ token }: NotificationsPageProps) {
|
||||
setName("");
|
||||
setChannelType("generic_webhook");
|
||||
setUrl("");
|
||||
setUsername("InfraPulse");
|
||||
setUsername("OrbitalWard");
|
||||
setEnabled(true);
|
||||
}
|
||||
|
||||
@@ -180,7 +180,7 @@ export function NotificationsPage({ token }: NotificationsPageProps) {
|
||||
<div key={channel.id} className="grid gap-3 p-4 md:grid-cols-[1fr_140px_90px_150px] md:items-center">
|
||||
<div>
|
||||
<div className="font-medium">{channel.name}</div>
|
||||
<div className="text-sm text-slate-400">{String(channel.settings.username || "InfraPulse")}</div>
|
||||
<div className="text-sm text-slate-400">{String(channel.settings.username || "OrbitalWard")}</div>
|
||||
<div className="text-xs text-slate-500">{channel.has_secret ? "Secret stored" : "No secret"}</div>
|
||||
</div>
|
||||
<div className="text-sm text-slate-300">{channel.channel_type}</div>
|
||||
|
||||
@@ -15,8 +15,11 @@ export function WebsitesPage({ token, monitors, onCreated }: WebsitesPageProps)
|
||||
const websites = monitors.filter((monitor) => monitor.monitor_type === "http");
|
||||
const [name, setName] = useState("");
|
||||
const [url, setUrl] = useState("https://");
|
||||
const supportsTlsExpiry = url.trim().toLowerCase().startsWith("https://");
|
||||
const [expectedStatus, setExpectedStatus] = useState(200);
|
||||
const [expectedText, setExpectedText] = useState("");
|
||||
const [checkTlsExpiry, setCheckTlsExpiry] = useState(true);
|
||||
const [tlsWarningDays, setTlsWarningDays] = useState(30);
|
||||
const [intervalSeconds, setIntervalSeconds] = useState(60);
|
||||
const [failureThreshold, setFailureThreshold] = useState(3);
|
||||
const [alertEnabled, setAlertEnabled] = useState(true);
|
||||
@@ -40,6 +43,8 @@ export function WebsitesPage({ token, monitors, onCreated }: WebsitesPageProps)
|
||||
expected_text: expectedText.trim() ? expectedText.trim() : null,
|
||||
unexpected_text: null,
|
||||
timeout_seconds: 10,
|
||||
check_tls_expiry: supportsTlsExpiry && checkTlsExpiry,
|
||||
tls_warning_days: tlsWarningDays,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
@@ -50,6 +55,8 @@ export function WebsitesPage({ token, monitors, onCreated }: WebsitesPageProps)
|
||||
expected_text: expectedText.trim() ? expectedText.trim() : null,
|
||||
unexpected_text: null,
|
||||
timeout_seconds: 10,
|
||||
check_tls_expiry: supportsTlsExpiry && checkTlsExpiry,
|
||||
tls_warning_days: tlsWarningDays,
|
||||
interval_seconds: intervalSeconds,
|
||||
create_asset: true,
|
||||
alert_enabled: alertEnabled,
|
||||
@@ -72,6 +79,8 @@ export function WebsitesPage({ token, monitors, onCreated }: WebsitesPageProps)
|
||||
setUrl(monitor.target);
|
||||
setExpectedStatus(Number(monitor.config?.expected_status ?? 200));
|
||||
setExpectedText(typeof monitor.config?.expected_text === "string" ? monitor.config.expected_text : "");
|
||||
setCheckTlsExpiry(Boolean(monitor.config?.check_tls_expiry ?? false));
|
||||
setTlsWarningDays(Number(monitor.config?.tls_warning_days ?? 30));
|
||||
setIntervalSeconds(monitor.interval_seconds);
|
||||
setAlertEnabled(true);
|
||||
setFailureThreshold(3);
|
||||
@@ -84,6 +93,8 @@ export function WebsitesPage({ token, monitors, onCreated }: WebsitesPageProps)
|
||||
setUrl("https://");
|
||||
setExpectedStatus(200);
|
||||
setExpectedText("");
|
||||
setCheckTlsExpiry(true);
|
||||
setTlsWarningDays(30);
|
||||
setIntervalSeconds(60);
|
||||
setFailureThreshold(3);
|
||||
setAlertEnabled(true);
|
||||
@@ -148,6 +159,17 @@ export function WebsitesPage({ token, monitors, onCreated }: WebsitesPageProps)
|
||||
<input className="h-10 w-full rounded-md border border-line bg-slate-950 px-3 text-sm outline-none ring-pulse/40 focus:ring-2" value={expectedText} onChange={(event) => setExpectedText(event.target.value)} />
|
||||
</label>
|
||||
|
||||
<div className="grid gap-3 sm:grid-cols-[1fr_140px]">
|
||||
<div className="flex items-center justify-between rounded-md border border-line bg-slate-950 px-3 py-2">
|
||||
<span className="text-sm text-slate-300">TLS expiry check</span>
|
||||
<input className="h-5 w-5 accent-teal-400" checked={supportsTlsExpiry && checkTlsExpiry} disabled={!supportsTlsExpiry} onChange={(event) => setCheckTlsExpiry(event.target.checked)} type="checkbox" />
|
||||
</div>
|
||||
<label className="block space-y-2">
|
||||
<span className="text-sm text-slate-300">Warn Days</span>
|
||||
<input className="h-10 w-full rounded-md border border-line bg-slate-950 px-3 text-sm outline-none ring-pulse/40 focus:ring-2" disabled={!supportsTlsExpiry || !checkTlsExpiry} value={tlsWarningDays} onChange={(event) => setTlsWarningDays(Number(event.target.value))} min={1} max={365} type="number" />
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{!editingMonitorId ? (
|
||||
<div className="flex items-center justify-between rounded-md border border-line bg-slate-950 px-3 py-2">
|
||||
<span className="text-sm text-slate-300">Alert on repeated failures</span>
|
||||
@@ -189,6 +211,7 @@ export function WebsitesPage({ token, monitors, onCreated }: WebsitesPageProps)
|
||||
<div>
|
||||
<div className="font-medium">{monitor.name}</div>
|
||||
<div className="truncate text-sm text-slate-400">{monitor.target}</div>
|
||||
{monitor.config?.check_tls_expiry ? <div className="text-xs text-slate-500">TLS warning at {String(monitor.config.tls_warning_days ?? 30)} days</div> : null}
|
||||
</div>
|
||||
<Status status={monitor.status} />
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
@@ -218,6 +241,8 @@ function Status({ status }: { status: string }) {
|
||||
? "border-teal-500/40 bg-teal-950/40 text-teal-200"
|
||||
: status === "down"
|
||||
? "border-red-500/40 bg-red-950/40 text-red-200"
|
||||
: "border-slate-600 bg-slate-900 text-slate-300";
|
||||
: status === "warning"
|
||||
? "border-amber-500/40 bg-amber-950/40 text-amber-200"
|
||||
: "border-slate-600 bg-slate-900 text-slate-300";
|
||||
return <span className={`inline-flex h-7 w-24 items-center justify-center rounded-md border text-xs font-medium ${classes}`}>{status}</span>;
|
||||
}
|
||||
|
||||
@@ -86,6 +86,8 @@ export interface WebsiteMonitorCreate {
|
||||
expected_text?: string | null;
|
||||
unexpected_text?: string | null;
|
||||
timeout_seconds: number;
|
||||
check_tls_expiry: boolean;
|
||||
tls_warning_days: number;
|
||||
interval_seconds: number;
|
||||
create_asset: boolean;
|
||||
alert_enabled: boolean;
|
||||
|
||||
Reference in New Issue
Block a user