Standardize Prism Console service metadata and health endpoints
This commit is contained in:
13
.env.example
Normal file
13
.env.example
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# BlackRoad OS – Prism Console
|
||||||
|
OS_ROOT=https://blackroad.systems
|
||||||
|
SERVICE_BASE_URL=https://console.blackroad.systems
|
||||||
|
NEXT_PUBLIC_OS_ROOT=https://blackroad.systems
|
||||||
|
NEXT_PUBLIC_SERVICE_ID=console
|
||||||
|
NEXT_PUBLIC_SERVICE_NAME="BlackRoad OS – Prism Console"
|
||||||
|
|
||||||
|
# Optional upstream services
|
||||||
|
CORE_API_URL=
|
||||||
|
AGENTS_API_URL=
|
||||||
|
PUBLIC_CONSOLE_URL=
|
||||||
|
NEXT_PUBLIC_CORE_API_URL=
|
||||||
|
NEXT_PUBLIC_AGENTS_API_URL=
|
||||||
14
Dockerfile
Normal file
14
Dockerfile
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
FROM node:18-alpine AS base
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY package*.json ./
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
ENV PORT=8080
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
CMD ["npm", "start"]
|
||||||
73
README.md
73
README.md
@@ -1,47 +1,48 @@
|
|||||||
# BlackRoad OS — Prism Console
|
# BlackRoad OS – Prism Console
|
||||||
|
|
||||||
Operator console UI for BlackRoad OS built with Next.js (App Router + TypeScript).
|
Operator / admin console for BlackRoad OS services. This frontend surfaces system health, environment metadata, and operator workflows for the broader BlackRoad OS platform.
|
||||||
|
|
||||||
## Running locally
|
## Tech Stack
|
||||||
|
- Next.js (App Router)
|
||||||
|
- React
|
||||||
|
- TypeScript
|
||||||
|
|
||||||
1. Install dependencies: `npm ci`
|
## Getting Started
|
||||||
2. Set environment variables (see below). You can use a `.env.local` file for local development.
|
Install dependencies and run the development server:
|
||||||
3. Start the dev server: `npm run dev`
|
|
||||||
4. Visit `http://localhost:3000` to load the console.
|
|
||||||
|
|
||||||
## Key routes
|
```bash
|
||||||
|
npm install
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
- `/` — Overview of configured upstream endpoints.
|
Visit http://localhost:3000 by default.
|
||||||
- `/status` — Live health table that polls each configured `/health` endpoint.
|
|
||||||
- `/health` — UI that consumes the `/api/health` JSON endpoint and shows service badges.
|
|
||||||
- `/agents` — Lists agents via `AGENTS_API_URL/agents` and triggers `AGENTS_API_URL/agents/run` through internal API proxies.
|
|
||||||
- `/api/health` and `/api/version` — Machine-friendly health and version metadata.
|
|
||||||
|
|
||||||
## Configuration
|
## Build & Start
|
||||||
|
Production build and runtime (default port 8080 for deployment targets like Railway):
|
||||||
|
|
||||||
Environment variables are centralized in `src/lib/config.ts`. Server-side values stay private; `NEXT_PUBLIC_*` values are exposed to the browser.
|
```bash
|
||||||
|
npm run build
|
||||||
|
npm start
|
||||||
|
```
|
||||||
|
|
||||||
| Variable | Purpose |
|
## Environment Variables
|
||||||
| --- | --- |
|
See `.env.example` for available variables. Key values:
|
||||||
| `NODE_ENV` | Runtime environment selector (`development` \| `staging` \| `production`). |
|
- `OS_ROOT` – base BlackRoad OS root URL
|
||||||
| `CORE_API_URL` | Server-side Core API base URL used for backend calls. |
|
- `SERVICE_BASE_URL` – public URL for this console
|
||||||
| `AGENTS_API_URL` | Server-side Agents API base URL for listing/running agents. |
|
- `CORE_API_URL`, `AGENTS_API_URL` – optional upstream APIs
|
||||||
| `PUBLIC_CONSOLE_URL` | Public URL for this console; also used for the operator badge. |
|
|
||||||
| `NEXT_PUBLIC_CORE_API_URL` | Browser-safe Core API base URL (if direct browser calls are allowed). |
|
|
||||||
| `NEXT_PUBLIC_AGENTS_API_URL` | Browser-safe Agents API base URL (if direct browser calls are allowed). |
|
|
||||||
| `SKIP_ENV_VALIDATION` | Optional; set to `true` in dev to bypass strict env checks. |
|
|
||||||
|
|
||||||
> Keep sensitive URLs in server-side vars (`CORE_API_URL`, `AGENTS_API_URL`). Only expose endpoints in `NEXT_PUBLIC_*` if they are safe for the browser.
|
## Health & Info
|
||||||
|
- `/api/health` – health payload including service id and readiness
|
||||||
|
- `/api/info` – static metadata about the Prism Console service
|
||||||
|
- `/api/version` – version and environment snapshot
|
||||||
|
- `/api/debug-env` – safe environment surface for troubleshooting
|
||||||
|
|
||||||
## Wiring to backend services
|
## Deployment (Railway)
|
||||||
|
- Build command: `npm install && npm run build`
|
||||||
|
- Start command: `npm start`
|
||||||
|
- Port: `8080`
|
||||||
|
- Healthcheck: `/api/health`
|
||||||
|
|
||||||
- Health polling: `/api/health` calls each configured service and appends `/health` to the base URL. `/status` renders the live results.
|
## Additional Notes
|
||||||
- Version metadata: `/api/version` (and `/version`) returns the build version, environment, and timestamp.
|
- Base URL: https://console.blackroad.systems
|
||||||
- Agents: `/api/agents` proxies `GET {AGENTS_API_URL}/agents`; `/api/agents/run` proxies `POST {AGENTS_API_URL}/agents/run`. The `/agents` page consumes these endpoints.
|
- OS Root: https://blackroad.systems
|
||||||
|
|
||||||
## Deployment
|
|
||||||
|
|
||||||
- Railway service: `prism-console-web` defined in `railway.json` (build `npm run build`, start `npm run start`, health check `/api/health`).
|
|
||||||
- GitHub Actions: `.github/workflows/console-deploy.yaml` installs dependencies, runs lint+build, deploys to the correct Railway environment (`dev`/`staging`/`prod`), and performs a post-deploy health check.
|
|
||||||
|
|
||||||
Production and staging URLs remain `https://console.blackroad.systems` and `https://staging.console.blackroad.systems`; dev uses the Railway-provided URL (or `DEV_CONSOLE_URL` in CI for health checks).
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
"build": "SKIP_ENV_VALIDATION=true next build",
|
"build": "SKIP_ENV_VALIDATION=true next build",
|
||||||
"start": "next start",
|
"start": "next start -p 8080",
|
||||||
"lint": "eslint . --ext .ts,.tsx"
|
"lint": "eslint . --ext .ts,.tsx"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
|||||||
24
railway.json
24
railway.json
@@ -1,22 +1,10 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://railway.app/railway.schema.json",
|
"build": "npm install && npm run build",
|
||||||
"project": "blackroad-prism-console",
|
"start": "npm start",
|
||||||
"services": [
|
"service": {
|
||||||
{
|
"port": 8080,
|
||||||
"name": "prism-console-web",
|
"healthcheck": {
|
||||||
"source": "./",
|
"path": "/api/health"
|
||||||
"port": 3000,
|
|
||||||
"buildCommand": "npm run build",
|
|
||||||
"startCommand": "npm run start",
|
|
||||||
"healthcheckPath": "/api/health",
|
|
||||||
"envVariables": {
|
|
||||||
"NODE_ENV": "production",
|
|
||||||
"CORE_API_URL": "",
|
|
||||||
"AGENTS_API_URL": "",
|
|
||||||
"PUBLIC_CONSOLE_URL": "",
|
|
||||||
"NEXT_PUBLIC_CORE_API_URL": "",
|
|
||||||
"NEXT_PUBLIC_AGENTS_API_URL": ""
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|||||||
20
src/app/api/debug-env/route.ts
Normal file
20
src/app/api/debug-env/route.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { NextResponse } from 'next/server';
|
||||||
|
import { publicConfig, serverConfig } from '@/lib/config';
|
||||||
|
import { serviceConfig } from '@/config/serviceConfig';
|
||||||
|
|
||||||
|
export async function GET() {
|
||||||
|
const safeEnv = {
|
||||||
|
nodeEnv: process.env.NODE_ENV,
|
||||||
|
nextRuntime: process.env.NEXT_RUNTIME,
|
||||||
|
environment: serverConfig.environment,
|
||||||
|
serviceId: serviceConfig.SERVICE_ID,
|
||||||
|
serviceName: serviceConfig.SERVICE_NAME,
|
||||||
|
serviceBaseUrl: serviceConfig.SERVICE_BASE_URL,
|
||||||
|
osRoot: serviceConfig.OS_ROOT,
|
||||||
|
publicCoreApiUrl: publicConfig.coreApiUrl,
|
||||||
|
publicAgentsApiUrl: publicConfig.agentsApiUrl,
|
||||||
|
publicConsoleUrl: publicConfig.consoleUrl
|
||||||
|
};
|
||||||
|
|
||||||
|
return NextResponse.json(safeEnv);
|
||||||
|
}
|
||||||
@@ -1,13 +1,16 @@
|
|||||||
import { NextResponse } from 'next/server';
|
import { NextResponse } from 'next/server';
|
||||||
import { pollServiceHealth, serverConfig } from '@/lib/config';
|
import { pollServiceHealth, serverConfig } from '@/lib/config';
|
||||||
|
import { SERVICE_ID } from '@/config/serviceConfig';
|
||||||
|
|
||||||
export async function GET() {
|
export async function GET() {
|
||||||
const services = await pollServiceHealth();
|
const services = await pollServiceHealth();
|
||||||
const healthy = services.every((service) => service.status === 'healthy' || service.status === 'not_configured');
|
const healthy = services.every((service) => service.status === 'healthy' || service.status === 'not_configured');
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
|
ok: healthy,
|
||||||
|
service: SERVICE_ID,
|
||||||
|
ts: new Date().toISOString(),
|
||||||
status: healthy ? 'ok' : 'degraded',
|
status: healthy ? 'ok' : 'degraded',
|
||||||
timestamp: new Date().toISOString(),
|
|
||||||
environment: serverConfig.environment,
|
environment: serverConfig.environment,
|
||||||
version: process.env.npm_package_version ?? 'unknown',
|
version: process.env.npm_package_version ?? 'unknown',
|
||||||
services
|
services
|
||||||
|
|||||||
14
src/app/api/info/route.ts
Normal file
14
src/app/api/info/route.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { NextResponse } from 'next/server';
|
||||||
|
import { serviceConfig } from '@/config/serviceConfig';
|
||||||
|
|
||||||
|
export async function GET() {
|
||||||
|
const payload = {
|
||||||
|
name: serviceConfig.SERVICE_NAME,
|
||||||
|
id: serviceConfig.SERVICE_ID,
|
||||||
|
baseUrl: serviceConfig.SERVICE_BASE_URL,
|
||||||
|
osRoot: serviceConfig.OS_ROOT,
|
||||||
|
ts: new Date().toISOString()
|
||||||
|
};
|
||||||
|
|
||||||
|
return NextResponse.json(payload);
|
||||||
|
}
|
||||||
@@ -1,9 +1,12 @@
|
|||||||
import { NextResponse } from 'next/server';
|
import { NextResponse } from 'next/server';
|
||||||
import { serverConfig } from '@/lib/config';
|
import { serverConfig } from '@/lib/config';
|
||||||
|
import { serviceConfig } from '@/config/serviceConfig';
|
||||||
|
|
||||||
export async function GET() {
|
export async function GET() {
|
||||||
const version = process.env.npm_package_version ?? 'unknown';
|
const version = process.env.npm_package_version ?? 'unknown';
|
||||||
return NextResponse.json({
|
return NextResponse.json({
|
||||||
|
service: serviceConfig.SERVICE_ID,
|
||||||
|
name: serviceConfig.SERVICE_NAME,
|
||||||
version,
|
version,
|
||||||
environment: serverConfig.environment,
|
environment: serverConfig.environment,
|
||||||
timestamp: new Date().toISOString()
|
timestamp: new Date().toISOString()
|
||||||
|
|||||||
@@ -2,10 +2,12 @@
|
|||||||
|
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import type { ServiceStatus } from '@/lib/config';
|
import type { ServiceStatus } from '@/lib/config';
|
||||||
|
import { serviceConfig } from '@/config/serviceConfig';
|
||||||
|
|
||||||
type HealthResponse = {
|
type HealthResponse = {
|
||||||
|
ok: boolean;
|
||||||
status: string;
|
status: string;
|
||||||
timestamp: string;
|
ts: string;
|
||||||
environment: string;
|
environment: string;
|
||||||
version?: string;
|
version?: string;
|
||||||
services: ServiceStatus[];
|
services: ServiceStatus[];
|
||||||
@@ -53,15 +55,13 @@ export default function HealthPage() {
|
|||||||
<div className="grid">
|
<div className="grid">
|
||||||
<div className="card">
|
<div className="card">
|
||||||
<h1>Health Check</h1>
|
<h1>Health Check</h1>
|
||||||
<p className="muted">Live status from <code>/api/health</code>.</p>
|
<p className="muted">Live status from <code>/api/health</code> for {serviceConfig.SERVICE_NAME}.</p>
|
||||||
{health && (
|
{health && (
|
||||||
<div style={{ display: 'flex', gap: 12, alignItems: 'center' }}>
|
<div style={{ display: 'flex', gap: 12, alignItems: 'center' }}>
|
||||||
<span className={health.status === 'ok' ? 'badge' : 'status-bad'}>
|
<span className={health.ok ? 'badge' : 'status-bad'}>{health.ok ? 'OK' : 'Degraded'}</span>
|
||||||
{health.status === 'ok' ? 'OK' : 'Degraded'}
|
|
||||||
</span>
|
|
||||||
<span className="muted">Env: {health.environment}</span>
|
<span className="muted">Env: {health.environment}</span>
|
||||||
<span className="muted">Version: {health.version}</span>
|
<span className="muted">Version: {health.version}</span>
|
||||||
<span className="muted">Updated: {new Date(health.timestamp).toLocaleString()}</span>
|
<span className="muted">Updated: {new Date(health.ts).toLocaleString()}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{error && <p className="status-bad">{error}</p>}
|
{error && <p className="status-bad">{error}</p>}
|
||||||
|
|||||||
@@ -1,43 +1,74 @@
|
|||||||
|
import { LiveHealthCard } from '@/components/status/LiveHealthCard';
|
||||||
import { getStaticServiceHealth, publicConfig, serverConfig } from '@/lib/config';
|
import { getStaticServiceHealth, publicConfig, serverConfig } from '@/lib/config';
|
||||||
|
import { serviceConfig } from '@/config/serviceConfig';
|
||||||
|
|
||||||
const cards = [
|
const serviceLinks = [
|
||||||
{ title: 'Core API', key: 'coreApiUrl', description: 'Primary backend for Prism data.' },
|
{ name: 'Core', url: 'https://core.blackroad.systems' },
|
||||||
{ title: 'Agents API', key: 'agentsApiUrl', description: 'Agent runtime surface area.' },
|
{ name: 'API', url: 'https://api.blackroad.systems' },
|
||||||
{ title: 'Console URL', key: 'consoleUrl', description: 'Public entrypoint for this console.' }
|
{ name: 'Operator', url: 'https://operator.blackroad.systems' },
|
||||||
] as const;
|
{ name: 'Web', url: 'https://blackroad.systems' },
|
||||||
|
{ name: 'Docs', url: 'https://docs.blackroad.systems' }
|
||||||
|
];
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
const serviceHealth = getStaticServiceHealth();
|
const serviceHealth = getStaticServiceHealth();
|
||||||
const resolvedValues = {
|
|
||||||
coreApiUrl: publicConfig.coreApiUrl || serverConfig.coreApiUrl,
|
|
||||||
agentsApiUrl: publicConfig.agentsApiUrl || serverConfig.agentsApiUrl,
|
|
||||||
consoleUrl: publicConfig.consoleUrl || serverConfig.consoleUrl
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="grid">
|
<div className="grid">
|
||||||
|
<div className="card" style={{ gridColumn: 'span 2' }}>
|
||||||
|
<h1>{serviceConfig.SERVICE_NAME}</h1>
|
||||||
|
<p className="muted">Operator-facing control panel for BlackRoad OS</p>
|
||||||
|
<div style={{ display: 'flex', gap: 12, alignItems: 'center', flexWrap: 'wrap' }}>
|
||||||
|
<span className="badge">Service ID: {serviceConfig.SERVICE_ID}</span>
|
||||||
|
<span className="badge">Environment: {serverConfig.environment}</span>
|
||||||
|
</div>
|
||||||
|
<div style={{ marginTop: 12 }}>
|
||||||
|
<div className="muted">Base URL: {serviceConfig.SERVICE_BASE_URL}</div>
|
||||||
|
<div className="muted">OS Root: {serviceConfig.OS_ROOT}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<LiveHealthCard />
|
||||||
|
|
||||||
<div className="card">
|
<div className="card">
|
||||||
<h3>Environment</h3>
|
<h3>Configuration Snapshot</h3>
|
||||||
<p className="muted">Aligns with Railway and Cloudflare mapping.</p>
|
<p className="muted">Resolved URLs from server/public configuration.</p>
|
||||||
<div className="badge">{serverConfig.environment}</div>
|
|
||||||
<table className="table">
|
<table className="table">
|
||||||
<tbody>
|
<tbody>
|
||||||
{cards.map((card) => (
|
<tr>
|
||||||
<tr key={card.key}>
|
<td>Core API</td>
|
||||||
<td>{card.title}</td>
|
<td className="muted">{publicConfig.coreApiUrl || serverConfig.coreApiUrl || 'not set'}</td>
|
||||||
<td className="muted">{resolvedValues[card.key] || 'not set'}</td>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Agents API</td>
|
||||||
|
<td className="muted">{publicConfig.agentsApiUrl || serverConfig.agentsApiUrl || 'not set'}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Console URL</td>
|
||||||
|
<td className="muted">{publicConfig.consoleUrl || serverConfig.consoleUrl || 'not set'}</td>
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<p className="muted" style={{ marginTop: 12 }}>
|
</div>
|
||||||
Add authentication here later — centralize auth checks before rendering protected pages.
|
|
||||||
</p>
|
<div className="card" style={{ gridColumn: 'span 2' }}>
|
||||||
|
<h3>Services</h3>
|
||||||
|
<p className="muted">Static references for connected BlackRoad OS surfaces.</p>
|
||||||
|
<ul>
|
||||||
|
{serviceLinks.map((svc) => (
|
||||||
|
<li key={svc.name}>
|
||||||
|
<a href={svc.url} target="_blank" rel="noreferrer">
|
||||||
|
{svc.name}
|
||||||
|
</a>{' '}
|
||||||
|
<span className="muted">{svc.url}</span>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="card">
|
<div className="card">
|
||||||
<h3>Connectivity</h3>
|
<h3>System Status</h3>
|
||||||
<p className="muted">Configuration-based readiness of upstream services.</p>
|
<p className="muted">Configuration readiness across Prism Console dependencies.</p>
|
||||||
<ul>
|
<ul>
|
||||||
{serviceHealth.map((service) => (
|
{serviceHealth.map((service) => (
|
||||||
<li key={service.key}>
|
<li key={service.key}>
|
||||||
@@ -52,11 +83,12 @@ export default function Home() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="card">
|
<div className="card">
|
||||||
<h3>Notes</h3>
|
<h3>Operator Queue</h3>
|
||||||
|
<p className="muted">Placeholder for pending operator tasks, incidents, or approvals.</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Use /status for a focused service status board.</li>
|
<li>Integrate authentication for console routes.</li>
|
||||||
<li>Centralized fetch helper lives in <code>src/lib/api.ts</code>.</li>
|
<li>Connect deployment events stream.</li>
|
||||||
<li>Health endpoint: <code>/api/health</code>.</li>
|
<li>Surface observability snapshots from core services.</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
70
src/components/status/LiveHealthCard.tsx
Normal file
70
src/components/status/LiveHealthCard.tsx
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import type { ServiceStatus } from '@/lib/config';
|
||||||
|
import { serviceConfig } from '@/config/serviceConfig';
|
||||||
|
|
||||||
|
type HealthResponse = {
|
||||||
|
ok: boolean;
|
||||||
|
service: string;
|
||||||
|
ts: string;
|
||||||
|
status: string;
|
||||||
|
environment: string;
|
||||||
|
version?: string;
|
||||||
|
services: ServiceStatus[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export function LiveHealthCard() {
|
||||||
|
const [health, setHealth] = useState<HealthResponse | null>(null);
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let cancelled = false;
|
||||||
|
|
||||||
|
const load = async () => {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/health', { cache: 'no-store' });
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Health endpoint returned ${response.status}`);
|
||||||
|
}
|
||||||
|
const payload = (await response.json()) as HealthResponse;
|
||||||
|
if (!cancelled) {
|
||||||
|
setHealth(payload);
|
||||||
|
setError(null);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
if (!cancelled) {
|
||||||
|
setError(err instanceof Error ? err.message : 'Unable to load health');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
load();
|
||||||
|
const interval = setInterval(load, 10000);
|
||||||
|
return () => {
|
||||||
|
cancelled = true;
|
||||||
|
clearInterval(interval);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const statusText = health?.ok ? 'ONLINE' : 'OFFLINE';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="card">
|
||||||
|
<h3>System Status</h3>
|
||||||
|
<p className="muted">Live ping against <code>/api/health</code> for {serviceConfig.SERVICE_NAME}.</p>
|
||||||
|
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
|
||||||
|
<span className={health?.ok ? 'badge' : 'status-bad'}>Status: {statusText}</span>
|
||||||
|
<span className="muted">Service: {serviceConfig.SERVICE_ID}</span>
|
||||||
|
</div>
|
||||||
|
{health && (
|
||||||
|
<div style={{ marginTop: 12, display: 'grid', gap: 6 }}>
|
||||||
|
<div className="muted">Environment: {health.environment}</div>
|
||||||
|
<div className="muted">Version: {health.version || 'unknown'}</div>
|
||||||
|
<div className="muted">Last checked: {new Date(health.ts).toLocaleString()}</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{error && <p className="status-bad">{error}</p>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
11
src/config/serviceConfig.ts
Normal file
11
src/config/serviceConfig.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
export const SERVICE_ID = 'console';
|
||||||
|
export const SERVICE_NAME = 'BlackRoad OS – Prism Console';
|
||||||
|
export const SERVICE_BASE_URL = process.env.SERVICE_BASE_URL || 'https://console.blackroad.systems';
|
||||||
|
export const OS_ROOT = process.env.OS_ROOT || 'https://blackroad.systems';
|
||||||
|
|
||||||
|
export const serviceConfig = {
|
||||||
|
SERVICE_ID,
|
||||||
|
SERVICE_NAME,
|
||||||
|
SERVICE_BASE_URL,
|
||||||
|
OS_ROOT
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user