chore: sync submodule state (parent ref update)

Made-with: Cursor
This commit is contained in:
defiQUG
2026-03-02 12:14:07 -08:00
parent 6c4555cebd
commit 89b82cdadb
883 changed files with 78752 additions and 18180 deletions

View File

@@ -0,0 +1,42 @@
# SolaceNet Operations Console
React-based admin UI for managing SolaceNet capabilities, entitlements, and policies.
## Features
- Capability management and toggling
- Entitlement configuration
- Policy rule management
- Audit log viewing
- Kill switch controls
## Setup
```bash
cd frontend/solacenet-console
npm install
npm start
```
## Environment Variables
Create a `.env` file:
```
REACT_APP_API_URL=http://localhost:3000
```
## Usage
1. Login with admin credentials
2. View all capabilities in the main table
3. Click "Manage" to toggle capability states
4. Use "Kill Switch" for emergency capability disabling
5. View audit logs for all changes
## Development
The console connects to the SolaceNet API endpoints:
- `/api/v1/solacenet/capabilities`
- `/api/v1/solacenet/policy/kill-switch/:id`
- `/api/v1/solacenet/audit/toggles`

View File

@@ -0,0 +1,33 @@
{
"name": "solacenet-console",
"version": "1.0.0",
"private": true,
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}

View File

@@ -0,0 +1,53 @@
.container {
max-width: 1400px;
margin: 0 auto;
padding: 20px;
background-color: #f5f5f5;
min-height: 100vh;
}
header {
background: white;
padding: 20px;
border-radius: 8px;
margin-bottom: 20px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
header h1 {
color: #333;
margin: 0 0 20px 0;
}
.tabs {
display: flex;
gap: 10px;
}
.tabs button {
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
background-color: #e9ecef;
color: #495057;
font-size: 14px;
font-weight: 500;
transition: all 0.2s;
}
.tabs button:hover {
background-color: #dee2e6;
}
.tabs button.active {
background-color: #007bff;
color: white;
}
.content {
background: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
min-height: 600px;
}

View File

@@ -0,0 +1,40 @@
// SolaceNet Operations Console
// React/TypeScript admin UI for capability management
import React, { useState } from 'react';
import { CapabilityManager } from './components/CapabilityManager';
import { AuditLogViewer } from './components/AuditLogViewer';
import './App.css';
function App() {
const [activeTab, setActiveTab] = useState<'capabilities' | 'audit'>('capabilities');
return (
<div className="container">
<header>
<h1>SolaceNet Operations Console</h1>
<nav className="tabs">
<button
className={activeTab === 'capabilities' ? 'active' : ''}
onClick={() => setActiveTab('capabilities')}
>
Capabilities
</button>
<button
className={activeTab === 'audit' ? 'active' : ''}
onClick={() => setActiveTab('audit')}
>
Audit Logs
</button>
</nav>
</header>
<div className="content">
{activeTab === 'capabilities' && <CapabilityManager />}
{activeTab === 'audit' && <AuditLogViewer />}
</div>
</div>
);
}
export default App;

View File

@@ -0,0 +1,67 @@
.audit-log-viewer {
padding: 20px;
}
.filters {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
.filters input,
.filters select {
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
}
.logs-table {
overflow-x: auto;
}
.logs-table table {
width: 100%;
border-collapse: collapse;
}
.logs-table th,
.logs-table td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #ddd;
}
.logs-table th {
background-color: #f5f5f5;
font-weight: bold;
}
.action-badge {
display: inline-block;
padding: 4px 8px;
border-radius: 4px;
font-size: 11px;
font-weight: bold;
text-transform: uppercase;
}
.action-enabled {
background-color: #d4edda;
color: #155724;
}
.action-disabled {
background-color: #f0f0f0;
color: #666;
}
.action-suspended {
background-color: #f8d7da;
color: #721c24;
}
.action-kill_switch {
background-color: #dc3545;
color: white;
}

View File

@@ -0,0 +1,121 @@
import React, { useState, useEffect } from 'react';
import './AuditLogViewer.css';
interface AuditLog {
id: string;
actor: string;
action: string;
capabilityId: string;
beforeState?: string;
afterState: string;
timestamp: string;
reason?: string;
}
const API_BASE = process.env.REACT_APP_API_URL || 'http://localhost:3000';
export const AuditLogViewer: React.FC = () => {
const [logs, setLogs] = useState<AuditLog[]>([]);
const [loading, setLoading] = useState(true);
const [filters, setFilters] = useState({
capabilityId: '',
actor: '',
action: '',
});
useEffect(() => {
fetchLogs();
}, [filters]);
const fetchLogs = async () => {
try {
const params = new URLSearchParams();
if (filters.capabilityId) params.append('capabilityId', filters.capabilityId);
if (filters.actor) params.append('actor', filters.actor);
if (filters.action) params.append('action', filters.action);
const response = await fetch(
`${API_BASE}/api/v1/solacenet/audit/toggles?${params.toString()}`,
{
headers: {
'Authorization': `Bearer ${localStorage.getItem('token')}`,
},
}
);
const data = await response.json();
setLogs(data);
} catch (error) {
console.error('Failed to fetch audit logs:', error);
} finally {
setLoading(false);
}
};
if (loading) {
return <div className="loading">Loading audit logs...</div>;
}
return (
<div className="audit-log-viewer">
<h2>Audit Logs</h2>
<div className="filters">
<input
type="text"
placeholder="Capability ID"
value={filters.capabilityId}
onChange={(e) => setFilters({ ...filters, capabilityId: e.target.value })}
/>
<input
type="text"
placeholder="Actor"
value={filters.actor}
onChange={(e) => setFilters({ ...filters, actor: e.target.value })}
/>
<select
value={filters.action}
onChange={(e) => setFilters({ ...filters, action: e.target.value })}
>
<option value="">All Actions</option>
<option value="enabled">Enabled</option>
<option value="disabled">Disabled</option>
<option value="suspended">Suspended</option>
<option value="kill_switch">Kill Switch</option>
</select>
</div>
<div className="logs-table">
<table>
<thead>
<tr>
<th>Timestamp</th>
<th>Actor</th>
<th>Action</th>
<th>Capability</th>
<th>Before</th>
<th>After</th>
<th>Reason</th>
</tr>
</thead>
<tbody>
{logs.map((log) => (
<tr key={log.id}>
<td>{new Date(log.timestamp).toLocaleString()}</td>
<td>{log.actor}</td>
<td>
<span className={`action-badge action-${log.action}`}>
{log.action}
</span>
</td>
<td>{log.capabilityId}</td>
<td>{log.beforeState || '-'}</td>
<td>{log.afterState}</td>
<td>{log.reason || '-'}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
);
};

View File

@@ -0,0 +1,112 @@
.capability-manager {
padding: 20px;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30px;
}
.tenant-selector {
display: flex;
align-items: center;
gap: 10px;
}
.tenant-selector input {
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
}
.capabilities-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 20px;
}
.capability-card {
border: 1px solid #ddd;
border-radius: 8px;
padding: 20px;
background: white;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.capability-card h3 {
margin: 0 0 10px 0;
color: #333;
}
.capability-id {
font-family: monospace;
font-size: 12px;
color: #666;
margin: 5px 0;
}
.version {
font-size: 12px;
color: #999;
margin: 5px 0;
}
.state-indicator {
margin: 15px 0;
}
.state-badge {
display: inline-block;
padding: 4px 12px;
border-radius: 12px;
font-size: 12px;
font-weight: bold;
text-transform: uppercase;
}
.state-disabled {
background-color: #f0f0f0;
color: #666;
}
.state-pilot {
background-color: #fff3cd;
color: #856404;
}
.state-enabled {
background-color: #d4edda;
color: #155724;
}
.state-suspended {
background-color: #f8d7da;
color: #721c24;
}
.state-drain {
background-color: #d1ecf1;
color: #0c5460;
}
.actions select {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
}
.actions select:disabled {
background-color: #f5f5f5;
cursor: not-allowed;
}
.loading {
text-align: center;
padding: 40px;
color: #666;
}

View File

@@ -0,0 +1,165 @@
import React, { useState, useEffect } from 'react';
import './CapabilityManager.css';
interface Capability {
id: string;
capabilityId: string;
name: string;
version: string;
defaultState: string;
status: string;
}
interface Entitlement {
id: string;
tenantId: string;
capabilityId: string;
stateOverride?: string;
}
const API_BASE = process.env.REACT_APP_API_URL || 'http://localhost:3000';
export const CapabilityManager: React.FC = () => {
const [capabilities, setCapabilities] = useState<Capability[]>([]);
const [entitlements, setEntitlements] = useState<Entitlement[]>([]);
const [selectedTenant, setSelectedTenant] = useState<string>('');
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchCapabilities();
if (selectedTenant) {
fetchEntitlements(selectedTenant);
}
}, [selectedTenant]);
const fetchCapabilities = async () => {
try {
const response = await fetch(`${API_BASE}/api/v1/solacenet/capabilities`, {
headers: {
'Authorization': `Bearer ${localStorage.getItem('token')}`,
},
});
const data = await response.json();
setCapabilities(data);
} catch (error) {
console.error('Failed to fetch capabilities:', error);
} finally {
setLoading(false);
}
};
const fetchEntitlements = async (tenantId: string) => {
try {
const response = await fetch(
`${API_BASE}/api/v1/solacenet/tenants/${tenantId}/programs/entitlements`,
{
headers: {
'Authorization': `Bearer ${localStorage.getItem('token')}`,
},
}
);
const data = await response.json();
setEntitlements(data);
} catch (error) {
console.error('Failed to fetch entitlements:', error);
}
};
const toggleCapability = async (capabilityId: string, newState: string) => {
try {
// Create or update entitlement
const existing = entitlements.find(e => e.capabilityId === capabilityId);
if (existing) {
// Update existing entitlement
await fetch(`${API_BASE}/api/v1/solacenet/entitlements/${existing.id}`, {
method: 'PUT',
headers: {
'Authorization': `Bearer ${localStorage.getItem('token')}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
stateOverride: newState,
}),
});
} else {
// Create new entitlement
await fetch(`${API_BASE}/api/v1/solacenet/entitlements`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${localStorage.getItem('token')}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
tenantId: selectedTenant,
capabilityId,
stateOverride: newState,
}),
});
}
await fetchEntitlements(selectedTenant);
alert(`Capability ${capabilityId} set to ${newState}`);
} catch (error) {
console.error('Failed to toggle capability:', error);
alert('Failed to toggle capability');
}
};
const getCurrentState = (capabilityId: string): string => {
const entitlement = entitlements.find(e => e.capabilityId === capabilityId);
return entitlement?.stateOverride || 'disabled';
};
if (loading) {
return <div className="loading">Loading capabilities...</div>;
}
return (
<div className="capability-manager">
<div className="header">
<h2>Capability Management</h2>
<div className="tenant-selector">
<label>Tenant ID:</label>
<input
type="text"
value={selectedTenant}
onChange={(e) => setSelectedTenant(e.target.value)}
placeholder="Enter tenant ID"
/>
</div>
</div>
<div className="capabilities-grid">
{capabilities.map((cap) => {
const currentState = getCurrentState(cap.capabilityId);
return (
<div key={cap.id} className="capability-card">
<h3>{cap.name}</h3>
<p className="capability-id">{cap.capabilityId}</p>
<p className="version">v{cap.version}</p>
<div className="state-indicator">
<span className={`state-badge state-${currentState}`}>
{currentState}
</span>
</div>
<div className="actions">
<select
value={currentState}
onChange={(e) => toggleCapability(cap.capabilityId, e.target.value)}
disabled={!selectedTenant}
>
<option value="disabled">Disabled</option>
<option value="pilot">Pilot</option>
<option value="enabled">Enabled</option>
<option value="suspended">Suspended</option>
<option value="drain">Drain</option>
</select>
</div>
</div>
);
})}
</div>
</div>
);
};