chore: initial project setup with backend, frontend, CI/CD, and documentation
CI / backend (push) Failing after 15s
CI / frontend (push) Failing after 39s

Add complete NexaFabric project structure including:
- FastAPI backend with SQLAlchemy models, JWT auth, RBAC, audit logging, and provider interfaces
- React + TypeScript frontend with Vite, Tailwind CSS, TanStack Query, and Zustand
- Docker Compose configuration for PostgreSQL, Redis, API, worker, frontend, and nginx
- GitHub Actions and GitLab CI workflows for testing, linting, building, and security scanning
- Environment
This commit is contained in:
2026-07-09 12:10:35 +02:00
commit 14e7710120
83 changed files with 2887 additions and 0 deletions
+50
View File
@@ -0,0 +1,50 @@
from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import Any
@dataclass(frozen=True)
class ProviderConnection:
api_url: str
token: str
verify_tls: bool = True
read_only: bool = True
class HypervisorProvider(ABC):
@abstractmethod
async def test_connection(self, connection: ProviderConnection) -> dict[str, Any]:
raise NotImplementedError
class InventoryProvider(ABC):
@abstractmethod
async def sync_inventory(self, connection: ProviderConnection) -> dict[str, list[dict[str, Any]]]:
raise NotImplementedError
class NetworkProvider(ABC):
@abstractmethod
async def list_networks(self, connection: ProviderConnection) -> list[dict[str, Any]]:
raise NotImplementedError
class FirewallProvider(ABC):
@abstractmethod
async def preview_rules(self, connection: ProviderConnection, rules: list[dict[str, Any]]) -> dict[str, Any]:
raise NotImplementedError
@abstractmethod
async def apply_rules(self, connection: ProviderConnection, rules: list[dict[str, Any]]) -> dict[str, Any]:
raise NotImplementedError
class Provider(
HypervisorProvider,
InventoryProvider,
NetworkProvider,
FirewallProvider,
ABC,
):
name: str
+35
View File
@@ -0,0 +1,35 @@
from typing import Any
from app.services.providers.base import Provider, ProviderConnection
class DemoProvider(Provider):
name = "demo"
async def test_connection(self, connection: ProviderConnection) -> dict[str, Any]:
return {"version": "demo", "api_url": connection.api_url, "mode": "offline"}
async def sync_inventory(self, connection: ProviderConnection) -> dict[str, list[dict[str, Any]]]:
return {
"nodes": [
{"node": "demo-pve-01", "status": "online", "maxcpu": 16, "maxmem": 68719476736},
{"node": "demo-pve-02", "status": "online", "maxcpu": 16, "maxmem": 68719476736},
],
"workloads": [
{"vmid": 201, "name": "demo-web-01", "type": "qemu", "status": "running", "node": "demo-pve-01"},
{"vmid": 202, "name": "demo-db-01", "type": "qemu", "status": "running", "node": "demo-pve-02"},
],
"networks": [
{"name": "vmbr0", "type": "bridge", "vlan": 10},
{"name": "vnet-prod", "type": "vnet", "vlan": 120},
],
}
async def list_networks(self, connection: ProviderConnection) -> list[dict[str, Any]]:
return (await self.sync_inventory(connection))["networks"]
async def preview_rules(self, connection: ProviderConnection, rules: list[dict[str, Any]]) -> dict[str, Any]:
return {"provider": self.name, "generated": rules, "warnings": ["Demo provider preview."]}
async def apply_rules(self, connection: ProviderConnection, rules: list[dict[str, Any]]) -> dict[str, Any]:
return {"applied": False, "reason": "Demo provider never applies firewall changes.", "rules": rules}
+56
View File
@@ -0,0 +1,56 @@
from typing import Any
import httpx
from app.services.providers.base import Provider, ProviderConnection
class ProxmoxProvider(Provider):
name = "proxmox"
async def test_connection(self, connection: ProviderConnection) -> dict[str, Any]:
async with httpx.AsyncClient(verify=connection.verify_tls, timeout=10) as client:
response = await client.get(
f"{connection.api_url.rstrip('/')}/api2/json/version",
headers={"Authorization": connection.token},
)
response.raise_for_status()
return response.json().get("data", {})
async def sync_inventory(self, connection: ProviderConnection) -> dict[str, list[dict[str, Any]]]:
async with httpx.AsyncClient(verify=connection.verify_tls, timeout=20) as client:
resources = await client.get(
f"{connection.api_url.rstrip('/')}/api2/json/cluster/resources",
headers={"Authorization": connection.token},
)
resources.raise_for_status()
data = resources.json().get("data", [])
nodes = [item for item in data if item.get("type") == "node"]
workloads = [item for item in data if item.get("type") in {"qemu", "lxc"}]
networks = await self.list_networks(connection)
return {"nodes": nodes, "workloads": workloads, "networks": networks}
async def list_networks(self, connection: ProviderConnection) -> list[dict[str, Any]]:
async with httpx.AsyncClient(verify=connection.verify_tls, timeout=20) as client:
resources = await client.get(
f"{connection.api_url.rstrip('/')}/api2/json/cluster/resources",
headers={"Authorization": connection.token},
)
resources.raise_for_status()
data = resources.json().get("data", [])
return [item for item in data if item.get("type") in {"network", "sdn"}]
async def preview_rules(self, connection: ProviderConnection, rules: list[dict[str, Any]]) -> dict[str, Any]:
return {
"provider": self.name,
"read_only": connection.read_only,
"generated": rules,
"warnings": ["Preview only. No Proxmox firewall changes were sent."],
}
async def apply_rules(self, connection: ProviderConnection, rules: list[dict[str, Any]]) -> dict[str, Any]:
if connection.read_only:
return {"applied": False, "reason": "Cluster is read-only", "rules": rules}
return {"applied": False, "reason": "Apply adapter intentionally requires explicit implementation", "rules": rules}
@@ -0,0 +1,10 @@
from app.services.providers.base import Provider
from app.services.providers.demo import DemoProvider
from app.services.providers.proxmox import ProxmoxProvider
_providers: dict[str, Provider] = {"proxmox": ProxmoxProvider(), "demo": DemoProvider()}
def get_provider(name: str) -> Provider:
return _providers[name]