Replaces LICENSE (GPLv3 -> AGPLv3) and prepends `SPDX-License-Identifier: AGPL-3.0-or-later` to every source file across decnet/, decnet_web/, tests/, scripts/, and tools/. Rationale: closes the GPLv3 ASP loophole so any party operating a modified DECNET as a network service must offer their modified source. Personal copyright (Samuel Paschuan) + inbound=outbound contributions make a future unilateral relicense infeasible. - LICENSE: full AGPL-3.0 text (gnu.org/licenses/agpl-3.0.txt) - COPYRIGHT: project copyright notice - tools/add_spdx_headers.py: idempotent header injector (shebang- and PEP 263-aware) Touches 1565 source files (.py, .ts, .tsx, .js, .jsx, .css, .sh). No behavior change; comments only.
141 lines
4.3 KiB
TypeScript
141 lines
4.3 KiB
TypeScript
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
import React, { useEffect, useState } from 'react';
|
|
import {
|
|
Settings, Users, Sliders, Shield, Palette, Activity, Cpu,
|
|
} from '../icons';
|
|
import { useToast } from './Toasts/useToast';
|
|
import RuleStateControls from './RuleStateControls';
|
|
import './DeckyFleet.css';
|
|
import './Config.css';
|
|
import type { ConfigTab } from './Config/types';
|
|
import { useConfig } from './Config/useConfig';
|
|
import { WorkersPanel } from './Config/WorkersPanel';
|
|
import { LimitsTab } from './Config/tabs/LimitsTab';
|
|
import { UsersTab } from './Config/tabs/UsersTab';
|
|
import { GlobalsTab } from './Config/tabs/GlobalsTab';
|
|
import { AppearanceTab } from './Config/tabs/AppearanceTab';
|
|
import { LLMTab } from './Config/tabs/LLMTab';
|
|
|
|
const Config: React.FC = () => {
|
|
const {
|
|
config, loading, isAdmin,
|
|
setDeploymentLimit, setGlobalMutationInterval,
|
|
addUser, deleteUser, setUserRole, resetUserPassword,
|
|
reinit,
|
|
} = useConfig();
|
|
const { push: pushToast } = useToast();
|
|
|
|
const [activeTab, setActiveTab] = useState<ConfigTab>('limits');
|
|
|
|
// If server didn't send users, force tab away from users.
|
|
useEffect(() => {
|
|
if (config && !config.users && activeTab === 'users') {
|
|
setActiveTab('limits');
|
|
}
|
|
}, [config, activeTab]);
|
|
|
|
if (loading) {
|
|
return (
|
|
<div className="logs-section">
|
|
<div className="loader">LOADING CONFIGURATION...</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (!config) {
|
|
return (
|
|
<div className="logs-section">
|
|
<div style={{ padding: '40px', textAlign: 'center', opacity: 0.5 }}>
|
|
<p>FAILED TO LOAD CONFIGURATION</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const tabs: { key: ConfigTab; label: string; icon: React.ReactNode }[] = [
|
|
{ key: 'limits', label: 'DEPLOYMENT LIMITS', icon: <Sliders size={14} /> },
|
|
...(config.users
|
|
? [{ key: 'users' as const, label: 'USER MANAGEMENT', icon: <Users size={14} /> }]
|
|
: []),
|
|
{ key: 'globals', label: 'GLOBAL VALUES', icon: <Settings size={14} /> },
|
|
{ key: 'appearance', label: 'APPEARANCE', icon: <Palette size={14} /> },
|
|
...(isAdmin
|
|
? [{ key: 'workers' as const, label: 'WORKERS', icon: <Activity size={14} /> }]
|
|
: []),
|
|
...(isAdmin
|
|
? [{ key: 'ttp' as const, label: 'TTP RULES', icon: <Shield size={14} /> }]
|
|
: []),
|
|
...(isAdmin
|
|
? [{ key: 'llm' as const, label: 'LLM PROVIDER', icon: <Cpu size={14} /> }]
|
|
: []),
|
|
];
|
|
|
|
return (
|
|
<div className="fleet-root config-page">
|
|
<div className="page-header">
|
|
<div className="page-title-group">
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
|
|
<Settings size={22} className="violet-accent" />
|
|
<h1>SYSTEM CONFIGURATION</h1>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="config-tabs">
|
|
{tabs.map((tab) => (
|
|
<button
|
|
key={tab.key}
|
|
className={`config-tab ${activeTab === tab.key ? 'active' : ''}`}
|
|
onClick={() => setActiveTab(tab.key)}
|
|
>
|
|
{tab.icon}
|
|
{tab.label}
|
|
</button>
|
|
))}
|
|
</div>
|
|
|
|
{activeTab === 'limits' && (
|
|
<LimitsTab
|
|
isAdmin={isAdmin}
|
|
initialValue={config.deployment_limit}
|
|
onSave={setDeploymentLimit}
|
|
/>
|
|
)}
|
|
|
|
{activeTab === 'users' && config.users && (
|
|
<UsersTab
|
|
users={config.users}
|
|
onDeleteUser={deleteUser}
|
|
onSetUserRole={setUserRole}
|
|
onResetUserPassword={resetUserPassword}
|
|
onAddUser={addUser}
|
|
/>
|
|
)}
|
|
|
|
{activeTab === 'globals' && (
|
|
<GlobalsTab
|
|
isAdmin={isAdmin}
|
|
developerMode={config.developer_mode === true}
|
|
initialInterval={config.global_mutation_interval}
|
|
onSaveInterval={setGlobalMutationInterval}
|
|
onReinit={reinit}
|
|
/>
|
|
)}
|
|
|
|
{activeTab === 'appearance' && <AppearanceTab />}
|
|
|
|
{activeTab === 'workers' && isAdmin && (
|
|
<WorkersPanel pushToast={pushToast} />
|
|
)}
|
|
|
|
{/* RuleStateControls also self-gates on /config?.role so a state
|
|
leak can't render it. */}
|
|
{activeTab === 'ttp' && isAdmin && <RuleStateControls />}
|
|
|
|
{activeTab === 'llm' && isAdmin && <LLMTab isAdmin={isAdmin} />}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default Config;
|