mirror of https://github.com/ospab/ostp.git
fix: resolve TS errors in ostp-control for CI build
This commit is contained in:
parent
8eef85ceca
commit
fe6fdd20bd
|
|
@ -1,6 +1,7 @@
|
|||
import { HashRouter as Router, Routes, Route, Link, Navigate, useLocation } from 'react-router-dom';
|
||||
import { Activity, Users, Settings, Shield, MoreVertical, RefreshCw, BookOpen, Wrench, History, Globe, LogOut } from 'lucide-react';
|
||||
import { useState, useEffect, ReactNode } from 'react';
|
||||
import { useState, useEffect } from 'react';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
// Components
|
||||
import Dashboard from './pages/Dashboard';
|
||||
|
|
|
|||
|
|
@ -21,8 +21,6 @@ export interface ApiResponse<T> {
|
|||
error?: string;
|
||||
}
|
||||
|
||||
// Helpers for localStorage state
|
||||
const API_URL_KEY = 'ostp_api_url';
|
||||
const API_TOKEN_KEY = 'ostp_api_token';
|
||||
|
||||
export function getApiSettings() {
|
||||
|
|
|
|||
|
|
@ -1,160 +0,0 @@
|
|||
import React, { useState } from 'react';
|
||||
import { Shield, Globe, Key, CheckCircle, XCircle, RefreshCw, ChevronRight } from 'lucide-react';
|
||||
import { saveApiSettings, api } from '../lib/api';
|
||||
import { useLanguage } from '../lib/LanguageContext';
|
||||
import { addAuditLog } from '../lib/audit';
|
||||
|
||||
interface ConnectionSetupProps {
|
||||
onSetupComplete: () => void;
|
||||
}
|
||||
|
||||
export default function ConnectionSetup({ onSetupComplete }: ConnectionSetupProps) {
|
||||
const { t, language } = useLanguage();
|
||||
|
||||
const [apiUrl, setApiUrl] = useState('http://localhost:9090');
|
||||
const [apiToken, setApiToken] = useState('');
|
||||
const [isTesting, setIsTesting] = useState(false);
|
||||
const [testResult, setTestResult] = useState<{ success: boolean; message: string } | null>(null);
|
||||
|
||||
const handleTestAndSave = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (!apiUrl) return;
|
||||
|
||||
setIsTesting(true);
|
||||
setTestResult(null);
|
||||
|
||||
const formattedUrl = apiUrl.trim().replace(/\/$/, '');
|
||||
const formattedToken = apiToken.trim();
|
||||
|
||||
const oldUrl = localStorage.getItem('ostp_api_url');
|
||||
const oldToken = localStorage.getItem('ostp_api_token');
|
||||
|
||||
try {
|
||||
saveApiSettings(formattedUrl, formattedToken);
|
||||
const status = await api.getServerStatus();
|
||||
|
||||
setTestResult({
|
||||
success: true,
|
||||
message: language === 'ru'
|
||||
? `Успешно подключено! Версия сервера: v${status.version}, активных сессий: ${status.active_users}`
|
||||
: `Successfully connected! Server version: v${status.version}, active sessions: ${status.active_users}`,
|
||||
});
|
||||
|
||||
addAuditLog(
|
||||
`Initial setup connected to API at ${formattedUrl} (Version: v${status.version})`,
|
||||
`Первоначальная настройка успешно подключена к API по адресу ${formattedUrl} (Версия: v${status.version})`,
|
||||
true
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
onSetupComplete();
|
||||
}, 1000);
|
||||
} catch (err: any) {
|
||||
if (oldUrl) localStorage.setItem('ostp_api_url', oldUrl);
|
||||
else localStorage.removeItem('ostp_api_url');
|
||||
if (oldToken !== null) localStorage.setItem('ostp_api_token', oldToken);
|
||||
else localStorage.removeItem('ostp_api_token');
|
||||
|
||||
const errorMsgStr = err.message || err;
|
||||
setTestResult({
|
||||
success: false,
|
||||
message: `${t('conn_setup_error')}${errorMsgStr}`,
|
||||
});
|
||||
|
||||
addAuditLog(
|
||||
`Initial setup failed to connect to ${formattedUrl}: ${errorMsgStr}`,
|
||||
`Первоначальная настройка не смогла подключиться к ${formattedUrl}: ${errorMsgStr}`,
|
||||
false
|
||||
);
|
||||
} finally {
|
||||
setIsTesting(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-background p-4 overflow-y-auto">
|
||||
{/* Background blobs */}
|
||||
<div className="absolute top-[10%] left-[10%] w-[50%] h-[50%] rounded-full bg-primary/10 blur-[150px] pointer-events-none"></div>
|
||||
<div className="absolute bottom-[10%] right-[10%] w-[50%] h-[50%] rounded-full bg-secondary/5 blur-[150px] pointer-events-none"></div>
|
||||
|
||||
<div className="relative w-full max-w-lg glass-panel rounded-3xl p-8 space-y-6 shadow-2xl border border-white/10 my-8">
|
||||
<div className="text-center space-y-2">
|
||||
<div className="inline-flex p-4 bg-primary/10 rounded-2xl mb-2 text-primary border border-primary/20">
|
||||
<Shield className="w-12 h-12" />
|
||||
</div>
|
||||
<h1 className="text-3xl font-extrabold tracking-tight text-white">OSTP<span className="text-primary">CORE</span></h1>
|
||||
<p className="text-text-muted text-sm">{t('conn_setup_sub')}</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-white/5 border border-white/5 p-4 rounded-2xl space-y-2 text-white">
|
||||
<h2 className="text-sm font-semibold">{t('conn_setup_header')}</h2>
|
||||
<p className="text-xs text-text-muted leading-relaxed">
|
||||
{t('conn_setup_desc')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleTestAndSave} className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<label className="text-xs font-semibold text-text-muted uppercase tracking-wider flex items-center gap-1.5">
|
||||
<Globe className="w-3.5 h-3.5 text-primary" /> {t('conn_setup_url_label')}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
required
|
||||
className="w-full bg-white/5 border border-white/10 rounded-xl px-4 py-3 text-white placeholder-text-muted focus:outline-none focus:border-primary transition-colors font-mono text-sm"
|
||||
placeholder="e.g. http://localhost:9090"
|
||||
value={apiUrl}
|
||||
onChange={(e) => setApiUrl(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<label className="text-xs font-semibold text-text-muted uppercase tracking-wider flex items-center gap-1.5">
|
||||
<Key className="w-3.5 h-3.5 text-primary" /> {t('conn_setup_token_label')}
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
required
|
||||
className="w-full bg-white/5 border border-white/10 rounded-xl px-4 py-3 text-white placeholder-text-muted focus:outline-none focus:border-primary transition-colors font-mono text-sm"
|
||||
placeholder={t('conn_setup_token_placeholder')}
|
||||
value={apiToken}
|
||||
onChange={(e) => setApiToken(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isTesting || !apiUrl || !apiToken}
|
||||
className="w-full flex items-center justify-center gap-2 bg-primary hover:bg-primary/90 disabled:opacity-50 disabled:hover:bg-primary text-white py-3.5 rounded-xl font-semibold transition-colors mt-2 shadow-[0_0_20px_rgba(108,114,255,0.4)]"
|
||||
>
|
||||
{isTesting ? (
|
||||
<RefreshCw className="w-5 h-5 animate-spin" />
|
||||
) : (
|
||||
<>
|
||||
{t('conn_setup_btn')} <ChevronRight className="w-5 h-5" />
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
{testResult && (
|
||||
<div className={`p-4 rounded-xl flex items-start gap-3 border animate-in fade-in slide-in-from-bottom-2 duration-200 ${
|
||||
testResult.success
|
||||
? 'bg-secondary/10 border-secondary/20 text-secondary'
|
||||
: 'bg-red-500/10 border-red-500/20 text-red-400'
|
||||
}`}>
|
||||
{testResult.success ? (
|
||||
<CheckCircle className="w-5 h-5 shrink-0 mt-0.5" />
|
||||
) : (
|
||||
<XCircle className="w-5 h-5 shrink-0 mt-0.5" />
|
||||
)}
|
||||
<div>
|
||||
<p className="font-semibold text-sm">{testResult.success ? t('conn_setup_success') : 'Connection Error'}</p>
|
||||
<p className="text-xs mt-1 opacity-90 font-mono break-all">{testResult.message}</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,23 +1,17 @@
|
|||
import { useState, useEffect } from 'react';
|
||||
import {
|
||||
Settings as SettingsIcon, Globe, Key, CheckCircle, XCircle,
|
||||
Settings as SettingsIcon, Globe, CheckCircle, XCircle,
|
||||
RefreshCw, Save, Sliders, Code2, AlertTriangle
|
||||
} from 'lucide-react';
|
||||
import { getApiSettings, saveApiSettings, api } from '../lib/api';
|
||||
import { api } from '../lib/api';
|
||||
import { useLanguage } from '../lib/LanguageContext';
|
||||
import { addAuditLog } from '../lib/audit';
|
||||
|
||||
export default function Settings() {
|
||||
const { t, language } = useLanguage();
|
||||
|
||||
// Tabs: 'connection' | 'interactive' | 'raw'
|
||||
const [activeTab, setActiveTab] = useState<'connection' | 'interactive' | 'raw'>('interactive');
|
||||
|
||||
// Connection settings state
|
||||
const [panelApiUrl, setPanelApiUrl] = useState('');
|
||||
const [panelApiToken, setPanelApiToken] = useState('');
|
||||
const [isTestingConnection, setIsTestingConnection] = useState(false);
|
||||
const [connectionTestResult, setConnectionTestResult] = useState<{ success: boolean; message: string } | null>(null);
|
||||
// Tabs: 'interactive' | 'raw'
|
||||
const [activeTab, setActiveTab] = useState<'interactive' | 'raw'>('interactive');
|
||||
|
||||
// Full Server Config JSON state
|
||||
const [config, setConfig] = useState<any>(null);
|
||||
|
|
@ -28,9 +22,6 @@ export default function Settings() {
|
|||
|
||||
// Initial load
|
||||
useEffect(() => {
|
||||
const { url, token } = getApiSettings();
|
||||
setPanelApiUrl(url);
|
||||
setPanelApiToken(token);
|
||||
fetchServerConfig();
|
||||
}, []);
|
||||
|
||||
|
|
@ -50,53 +41,7 @@ export default function Settings() {
|
|||
}
|
||||
};
|
||||
|
||||
const handleTestConnection = async () => {
|
||||
setIsTestingConnection(true);
|
||||
setConnectionTestResult(null);
|
||||
const oldUrl = localStorage.getItem('ostp_api_url');
|
||||
const oldToken = localStorage.getItem('ostp_api_token');
|
||||
|
||||
try {
|
||||
saveApiSettings(panelApiUrl, panelApiToken);
|
||||
const status = await api.getServerStatus();
|
||||
setConnectionTestResult({
|
||||
success: true,
|
||||
message: t('st_conn_success', { version: status.version, users: status.active_users }),
|
||||
});
|
||||
addAuditLog(
|
||||
`Tested connection to Management API at ${panelApiUrl} (Success)`,
|
||||
`Успешно протестировано подключение к API по адресу ${panelApiUrl}`,
|
||||
true
|
||||
);
|
||||
} catch (err: any) {
|
||||
if (oldUrl) localStorage.setItem('ostp_api_url', oldUrl);
|
||||
if (oldToken !== null) localStorage.setItem('ostp_api_token', oldToken);
|
||||
|
||||
const errorMsgStr = err.message || err;
|
||||
setConnectionTestResult({
|
||||
success: false,
|
||||
message: t('st_conn_error', { error: errorMsgStr }),
|
||||
});
|
||||
addAuditLog(
|
||||
`Tested connection to Management API at ${panelApiUrl} (Failed: ${errorMsgStr})`,
|
||||
`Ошибка при тесте подключения к API по адресу ${panelApiUrl} (${errorMsgStr})`,
|
||||
false
|
||||
);
|
||||
} finally {
|
||||
setIsTestingConnection(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSaveConnection = () => {
|
||||
saveApiSettings(panelApiUrl, panelApiToken);
|
||||
alert(language === 'ru' ? 'Настройки подключения сохранены!' : 'Connection settings saved!');
|
||||
addAuditLog(
|
||||
`Saved API connection settings: ${panelApiUrl}`,
|
||||
`Сохранены настройки подключения к API: ${panelApiUrl}`,
|
||||
true
|
||||
);
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
// Save Config to Server
|
||||
const handleSaveConfig = async (configToSave: any) => {
|
||||
|
|
@ -205,14 +150,6 @@ export default function Settings() {
|
|||
>
|
||||
<Code2 className="w-4 h-4" /> {t('st_tab_json')}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab('connection')}
|
||||
className={`flex items-center gap-2 px-5 py-3 font-medium transition-colors border-b-2 text-sm ${
|
||||
activeTab === 'connection' ? 'border-primary text-white' : 'border-transparent text-text-muted hover:text-white'
|
||||
}`}
|
||||
>
|
||||
<Globe className="w-4 h-4" /> {t('st_tab_conn')}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Global save notifications */}
|
||||
|
|
@ -235,75 +172,7 @@ export default function Settings() {
|
|||
</div>
|
||||
)}
|
||||
|
||||
{/* ── TAB: CONNECTION SETTINGS ── */}
|
||||
{activeTab === 'connection' && (
|
||||
<div className="glass-panel rounded-2xl p-6 space-y-6">
|
||||
<div>
|
||||
<h2 className="text-xl font-semibold mb-2">{t('st_conn_title')}</h2>
|
||||
<p className="text-sm text-text-muted">
|
||||
{t('st_conn_desc')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium flex items-center gap-2">
|
||||
<Globe className="w-4 h-4 text-primary" /> {t('st_conn_url')}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
className="w-full bg-white/5 border border-white/10 rounded-xl px-4 py-3 text-white placeholder-text-muted focus:outline-none focus:border-primary transition-colors font-mono"
|
||||
placeholder="e.g. http://localhost:9090"
|
||||
value={panelApiUrl}
|
||||
onChange={(e) => setPanelApiUrl(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium flex items-center gap-2">
|
||||
<Key className="w-4 h-4 text-primary" /> {t('st_conn_token')}
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
className="w-full bg-white/5 border border-white/10 rounded-xl px-4 py-3 text-white placeholder-text-muted focus:outline-none focus:border-primary transition-colors font-mono"
|
||||
placeholder={t('st_conn_token_sub')}
|
||||
value={panelApiToken}
|
||||
onChange={(e) => setPanelApiToken(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-4 pt-4 border-t border-white/5">
|
||||
<button
|
||||
onClick={handleTestConnection}
|
||||
disabled={isTestingConnection || !panelApiUrl}
|
||||
className="flex items-center gap-2 bg-white/5 hover:bg-white/10 text-white px-5 py-2.5 rounded-xl font-medium transition-colors border border-white/10"
|
||||
>
|
||||
<RefreshCw className={`w-5 h-5 ${isTestingConnection ? 'animate-spin text-primary' : ''}`} /> {t('st_conn_test')}
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={handleSaveConnection}
|
||||
disabled={!panelApiUrl}
|
||||
className="flex items-center gap-2 bg-primary hover:bg-primary/90 text-white px-6 py-2.5 rounded-xl font-medium transition-colors shadow-[0_0_15px_rgba(108,114,255,0.3)]"
|
||||
>
|
||||
<Save className="w-5 h-5" /> {t('st_conn_save')}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{connectionTestResult && (
|
||||
<div className={`mt-4 p-4 rounded-xl flex items-start gap-3 border ${
|
||||
connectionTestResult.success ? 'bg-secondary/10 border-secondary/20 text-secondary' : 'bg-red-500/10 border-red-500/20 text-red-400'
|
||||
}`}>
|
||||
{connectionTestResult.success ? <CheckCircle className="w-5 h-5 shrink-0 mt-0.5" /> : <XCircle className="w-5 h-5 shrink-0 mt-0.5" />}
|
||||
<div>
|
||||
<p className="font-semibold text-sm">{connectionTestResult.success ? 'Success' : 'Failed'}</p>
|
||||
<p className="text-xs mt-1 opacity-90 font-mono break-all">{connectionTestResult.message}</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* ── TAB: RAW JSON EDITOR ── */}
|
||||
{activeTab === 'raw' && config && (
|
||||
|
|
|
|||
Loading…
Reference in New Issue