refactor: full-stack restructure with multi-tenancy, workspace management, and K8s diagnostics

- Add Workspace domain (entity, repository, service, handler, DTO)
- Add multi-tenant K8s client with tenant binding and quota management
- Add K8s diagnostics client (instance diagnostics)
- Add authorization middleware (authz package)
- Restructure frontend to feature-based architecture (features/)
- Add User Management page in configuration
- Add AccessDenied page and route guards
- Refactor shared components (form inputs, layout, UI)
- Update Tailwind config for new design system
- Add comprehensive documentation (docs/, tasks/, plans)
- Improve cluster service with better kubeconfig handling
- Add tests for crypto, config, helm client, tenant binding
This commit is contained in:
Ivan087
2026-05-12 16:15:14 +08:00
parent c5e51ed069
commit 7f238a3168
172 changed files with 15703 additions and 3162 deletions

View File

@ -20,6 +20,7 @@ import { InstanceErrors, SuccessMessages, formatApiError } from "@/shared/utils"
import { InstanceCard } from "../components/InstanceCard";
import { ModifyModal } from "../components/ModifyModal";
import { EntriesModal } from "../components/EntriesModal";
import { DiagnosticsModal } from "../components/DiagnosticsModal";
import { globalCache } from "@/shared/services/artifact-cache";
const AUTO_REFRESH_INTERVAL_MS = 30000;
@ -47,6 +48,7 @@ const InstancesManagementPage: React.FC = () => {
// Modals
const [modifyInstance, setModifyInstance] = useState<Instance | null>(null);
const [entriesInstance, setEntriesInstance] = useState<Instance | null>(null);
const [diagnosticsInstance, setDiagnosticsInstance] = useState<Instance | null>(null);
// 核心数据加载函数 - 使用全局缓存
const loadDataCore = useCallback(async (options: LoadDataOptions = {}) => {
@ -225,6 +227,10 @@ const InstancesManagementPage: React.FC = () => {
setEntriesInstance(instance);
}, []);
const handleViewDiagnostics = useCallback((instance: Instance) => {
setDiagnosticsInstance(instance);
}, []);
const handleModifyConfirm = useCallback(async (
clusterId: string,
instanceId: string,
@ -333,43 +339,40 @@ const InstancesManagementPage: React.FC = () => {
<div className={`grid grid-cols-1 gap-5 mb-8 ${
clusters.length > 1 ? 'md:grid-cols-3' : 'md:grid-cols-2'
}`}>
<div className="relative group overflow-hidden bg-gradient-to-br from-blue-900/40 via-blue-800/30 to-blue-900/40 border border-blue-500/30 rounded-xl p-6 hover:border-blue-400/50 hover:shadow-xl hover:shadow-blue-500/20 transition-all duration-300">
<div className="absolute top-0 right-0 w-32 h-32 bg-blue-500/10 rounded-full blur-3xl group-hover:bg-blue-500/20 transition-all"></div>
<div className="relative group overflow-hidden bg-white border border-blue-100 rounded-lg p-6 hover:border-blue-200 hover:shadow-md transition-all duration-300">
<div className="relative flex items-center justify-between">
<div>
<p className="text-sm font-semibold text-blue-300 uppercase tracking-wider mb-2">Total Instances</p>
<p className="text-4xl font-bold text-white">{totalInstances}</p>
<p className="text-sm font-semibold text-blue-700 uppercase tracking-wider mb-2">Total Instances</p>
<p className="text-4xl font-bold text-slate-900">{totalInstances}</p>
</div>
<div className="p-4 bg-blue-500/20 rounded-xl border border-blue-400/30 shadow-lg shadow-blue-500/30">
<Package className="w-8 h-8 text-blue-400" />
<div className="p-4 bg-blue-50 rounded-lg border border-blue-100">
<Package className="w-8 h-8 text-blue-600" />
</div>
</div>
</div>
<div className="relative group overflow-hidden bg-gradient-to-br from-emerald-900/40 via-emerald-800/30 to-green-900/40 border border-emerald-500/30 rounded-xl p-6 hover:border-emerald-400/50 hover:shadow-xl hover:shadow-emerald-500/20 transition-all duration-300">
<div className="absolute top-0 right-0 w-32 h-32 bg-emerald-500/10 rounded-full blur-3xl group-hover:bg-emerald-500/20 transition-all"></div>
<div className="relative group overflow-hidden bg-white border border-emerald-100 rounded-lg p-6 hover:border-emerald-200 hover:shadow-md transition-all duration-300">
<div className="relative flex items-center justify-between">
<div>
<p className="text-sm font-semibold text-emerald-300 uppercase tracking-wider mb-2">Clusters</p>
<p className="text-4xl font-bold text-white">{clusters.length}</p>
<p className="text-sm font-semibold text-emerald-700 uppercase tracking-wider mb-2">Clusters</p>
<p className="text-4xl font-bold text-slate-900">{clusters.length}</p>
</div>
<div className="p-4 bg-emerald-500/20 rounded-xl border border-emerald-400/30 shadow-lg shadow-emerald-500/30">
<Server className="w-8 h-8 text-emerald-400" />
<div className="p-4 bg-emerald-50 rounded-lg border border-emerald-100">
<Server className="w-8 h-8 text-emerald-600" />
</div>
</div>
</div>
{/* Only show Filtered when there are multiple clusters */}
{clusters.length > 1 && (
<div className="relative group overflow-hidden bg-gradient-to-br from-purple-900/40 via-purple-800/30 to-purple-900/40 border border-purple-500/30 rounded-xl p-6 hover:border-purple-400/50 hover:shadow-xl hover:shadow-purple-500/20 transition-all duration-300">
<div className="absolute top-0 right-0 w-32 h-32 bg-purple-500/10 rounded-full blur-3xl group-hover:bg-purple-500/20 transition-all"></div>
<div className="relative group overflow-hidden bg-white border border-violet-100 rounded-lg p-6 hover:border-violet-200 hover:shadow-md transition-all duration-300">
<div className="relative flex items-center justify-between">
<div>
<p className="text-sm font-semibold text-purple-300 uppercase tracking-wider mb-2">Showing</p>
<p className="text-4xl font-bold text-white">{filteredInstances.length}</p>
<p className="text-sm font-semibold text-violet-700 uppercase tracking-wider mb-2">Showing</p>
<p className="text-4xl font-bold text-slate-900">{filteredInstances.length}</p>
</div>
<div className="p-4 bg-purple-500/20 rounded-xl border border-purple-400/30 shadow-lg shadow-purple-500/30">
<Boxes className="w-8 h-8 text-purple-400" />
<div className="p-4 bg-violet-50 rounded-lg border border-violet-100">
<Boxes className="w-8 h-8 text-violet-600" />
</div>
</div>
</div>
@ -379,13 +382,13 @@ const InstancesManagementPage: React.FC = () => {
{/* Enhanced Filters */}
{clusters.length > 1 && (
<div className="mb-6 p-5 bg-gradient-to-r from-slate-800/50 via-slate-800/30 to-slate-800/50 border border-slate-700/50 rounded-xl">
<div className="mb-6 p-5 bg-white border border-slate-200 rounded-lg shadow-sm">
<div className="flex items-center gap-4">
<div className="flex items-center gap-3">
<div className="p-2 bg-gradient-to-br from-cyan-500/20 to-blue-500/20 rounded-lg border border-cyan-500/30">
<Server className="w-5 h-5 text-cyan-400" />
</div>
<label className="text-sm font-semibold text-slate-300">
<label className="text-sm font-semibold text-slate-700">
Filter by Cluster:
</label>
</div>
@ -441,10 +444,10 @@ const InstancesManagementPage: React.FC = () => {
<Server className="w-5 h-5 text-emerald-400" />
</div>
<div>
<h2 className="text-xl font-bold text-white">
<h2 className="text-xl font-bold text-slate-900">
{cluster.name || "Unnamed Cluster"}
</h2>
<p className="text-sm text-slate-400 mt-0.5">
<p className="text-sm text-slate-500 mt-0.5">
{instances.length} {instances.length === 1 ? 'instance' : 'instances'} running
</p>
</div>
@ -458,6 +461,7 @@ const InstancesManagementPage: React.FC = () => {
onTerminate={handleTerminate}
onRefresh={handleRefresh}
onViewEntries={handleViewEntries}
onViewDiagnostics={handleViewDiagnostics}
/>
))}
</div>
@ -474,6 +478,7 @@ const InstancesManagementPage: React.FC = () => {
onTerminate={handleTerminate}
onRefresh={handleRefresh}
onViewEntries={handleViewEntries}
onViewDiagnostics={handleViewDiagnostics}
/>
))}
</div>
@ -497,6 +502,13 @@ const InstancesManagementPage: React.FC = () => {
onClose={() => setEntriesInstance(null)}
/>
)}
{diagnosticsInstance && (
<DiagnosticsModal
instance={diagnosticsInstance}
onClose={() => setDiagnosticsInstance(null)}
/>
)}
</div>
);
};