feat: add hosts and params files, fix rules SECTION NEW header
All checks were successful
Build containers when image tags change / build-if-image-changed (backend, shorefront-backend, shorefront backend, backend/Dockerfile, git.baumann.gr/adebaumann/shorefront-backend, .backend.image) (push) Successful in 44s
Build containers when image tags change / build-if-image-changed (frontend, shorefront-frontend, shorefront frontend, frontend/Dockerfile, git.baumann.gr/adebaumann/shorefront-frontend, .frontend.image) (push) Successful in 1m32s
All checks were successful
Build containers when image tags change / build-if-image-changed (backend, shorefront-backend, shorefront backend, backend/Dockerfile, git.baumann.gr/adebaumann/shorefront-backend, .backend.image) (push) Successful in 44s
Build containers when image tags change / build-if-image-changed (frontend, shorefront-frontend, shorefront frontend, frontend/Dockerfile, git.baumann.gr/adebaumann/shorefront-frontend, .frontend.image) (push) Successful in 1m32s
This commit is contained in:
37
backend/alembic/versions/0004_add_hosts_and_params.py
Normal file
37
backend/alembic/versions/0004_add_hosts_and_params.py
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
"""add hosts and params tables
|
||||||
|
|
||||||
|
Revision ID: 0004
|
||||||
|
Revises: 0003
|
||||||
|
Create Date: 2026-03-01
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
revision = "0004"
|
||||||
|
down_revision = "0003"
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
op.create_table(
|
||||||
|
"hosts",
|
||||||
|
sa.Column("id", sa.Integer, primary_key=True),
|
||||||
|
sa.Column("config_id", sa.Integer, sa.ForeignKey("configs.id"), nullable=False),
|
||||||
|
sa.Column("zone_id", sa.Integer, sa.ForeignKey("zones.id"), nullable=False),
|
||||||
|
sa.Column("interface", sa.String(32), nullable=False),
|
||||||
|
sa.Column("subnet", sa.String(64), nullable=False),
|
||||||
|
sa.Column("options", sa.Text, server_default="''"),
|
||||||
|
)
|
||||||
|
op.create_table(
|
||||||
|
"params",
|
||||||
|
sa.Column("id", sa.Integer, primary_key=True),
|
||||||
|
sa.Column("config_id", sa.Integer, sa.ForeignKey("configs.id"), nullable=False),
|
||||||
|
sa.Column("name", sa.String(64), nullable=False),
|
||||||
|
sa.Column("value", sa.String(255), nullable=False),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
op.drop_table("params")
|
||||||
|
op.drop_table("hosts")
|
||||||
@@ -93,6 +93,8 @@ def generate_config(
|
|||||||
selectinload(models.Config.rules).selectinload(models.Rule.src_zone),
|
selectinload(models.Config.rules).selectinload(models.Rule.src_zone),
|
||||||
selectinload(models.Config.rules).selectinload(models.Rule.dst_zone),
|
selectinload(models.Config.rules).selectinload(models.Rule.dst_zone),
|
||||||
selectinload(models.Config.snat_entries),
|
selectinload(models.Config.snat_entries),
|
||||||
|
selectinload(models.Config.host_entries).selectinload(models.Host.zone),
|
||||||
|
selectinload(models.Config.params),
|
||||||
)
|
)
|
||||||
.filter(models.Config.id == config_id, models.Config.owner_id == current_user.id)
|
.filter(models.Config.id == config_id, models.Config.owner_id == current_user.id)
|
||||||
.first()
|
.first()
|
||||||
|
|||||||
64
backend/app/api/hosts.py
Normal file
64
backend/app/api/hosts.py
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
from fastapi import APIRouter, Depends, HTTPException
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
from app import models, schemas
|
||||||
|
from app.auth import get_current_user
|
||||||
|
from app.database import get_db
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
def _owner_config(config_id: int, db: Session, user: models.User) -> models.Config:
|
||||||
|
config = db.query(models.Config).filter(
|
||||||
|
models.Config.id == config_id, models.Config.owner_id == user.id
|
||||||
|
).first()
|
||||||
|
if not config:
|
||||||
|
raise HTTPException(status_code=404, detail="Config not found")
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/{config_id}/hosts", response_model=list[schemas.HostOut])
|
||||||
|
def list_hosts(config_id: int, db: Session = Depends(get_db), user: models.User = Depends(get_current_user)):
|
||||||
|
_owner_config(config_id, db, user)
|
||||||
|
return db.query(models.Host).filter(models.Host.config_id == config_id).all()
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/{config_id}/hosts", response_model=schemas.HostOut, status_code=201)
|
||||||
|
def create_host(config_id: int, body: schemas.HostCreate, db: Session = Depends(get_db), user: models.User = Depends(get_current_user)):
|
||||||
|
_owner_config(config_id, db, user)
|
||||||
|
host = models.Host(**body.model_dump(), config_id=config_id)
|
||||||
|
db.add(host)
|
||||||
|
db.commit()
|
||||||
|
db.refresh(host)
|
||||||
|
return host
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/{config_id}/hosts/{host_id}", response_model=schemas.HostOut)
|
||||||
|
def get_host(config_id: int, host_id: int, db: Session = Depends(get_db), user: models.User = Depends(get_current_user)):
|
||||||
|
_owner_config(config_id, db, user)
|
||||||
|
host = db.query(models.Host).filter(models.Host.id == host_id, models.Host.config_id == config_id).first()
|
||||||
|
if not host:
|
||||||
|
raise HTTPException(status_code=404, detail="Host entry not found")
|
||||||
|
return host
|
||||||
|
|
||||||
|
|
||||||
|
@router.put("/{config_id}/hosts/{host_id}", response_model=schemas.HostOut)
|
||||||
|
def update_host(config_id: int, host_id: int, body: schemas.HostUpdate, db: Session = Depends(get_db), user: models.User = Depends(get_current_user)):
|
||||||
|
_owner_config(config_id, db, user)
|
||||||
|
host = db.query(models.Host).filter(models.Host.id == host_id, models.Host.config_id == config_id).first()
|
||||||
|
if not host:
|
||||||
|
raise HTTPException(status_code=404, detail="Host entry not found")
|
||||||
|
for field, value in body.model_dump(exclude_none=True).items():
|
||||||
|
setattr(host, field, value)
|
||||||
|
db.commit()
|
||||||
|
db.refresh(host)
|
||||||
|
return host
|
||||||
|
|
||||||
|
|
||||||
|
@router.delete("/{config_id}/hosts/{host_id}", status_code=204)
|
||||||
|
def delete_host(config_id: int, host_id: int, db: Session = Depends(get_db), user: models.User = Depends(get_current_user)):
|
||||||
|
_owner_config(config_id, db, user)
|
||||||
|
host = db.query(models.Host).filter(models.Host.id == host_id, models.Host.config_id == config_id).first()
|
||||||
|
if not host:
|
||||||
|
raise HTTPException(status_code=404, detail="Host entry not found")
|
||||||
|
db.delete(host)
|
||||||
|
db.commit()
|
||||||
64
backend/app/api/params.py
Normal file
64
backend/app/api/params.py
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
from fastapi import APIRouter, Depends, HTTPException
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
from app import models, schemas
|
||||||
|
from app.auth import get_current_user
|
||||||
|
from app.database import get_db
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
def _owner_config(config_id: int, db: Session, user: models.User) -> models.Config:
|
||||||
|
config = db.query(models.Config).filter(
|
||||||
|
models.Config.id == config_id, models.Config.owner_id == user.id
|
||||||
|
).first()
|
||||||
|
if not config:
|
||||||
|
raise HTTPException(status_code=404, detail="Config not found")
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/{config_id}/params", response_model=list[schemas.ParamOut])
|
||||||
|
def list_params(config_id: int, db: Session = Depends(get_db), user: models.User = Depends(get_current_user)):
|
||||||
|
_owner_config(config_id, db, user)
|
||||||
|
return db.query(models.Param).filter(models.Param.config_id == config_id).all()
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/{config_id}/params", response_model=schemas.ParamOut, status_code=201)
|
||||||
|
def create_param(config_id: int, body: schemas.ParamCreate, db: Session = Depends(get_db), user: models.User = Depends(get_current_user)):
|
||||||
|
_owner_config(config_id, db, user)
|
||||||
|
param = models.Param(**body.model_dump(), config_id=config_id)
|
||||||
|
db.add(param)
|
||||||
|
db.commit()
|
||||||
|
db.refresh(param)
|
||||||
|
return param
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/{config_id}/params/{param_id}", response_model=schemas.ParamOut)
|
||||||
|
def get_param(config_id: int, param_id: int, db: Session = Depends(get_db), user: models.User = Depends(get_current_user)):
|
||||||
|
_owner_config(config_id, db, user)
|
||||||
|
param = db.query(models.Param).filter(models.Param.id == param_id, models.Param.config_id == config_id).first()
|
||||||
|
if not param:
|
||||||
|
raise HTTPException(status_code=404, detail="Param not found")
|
||||||
|
return param
|
||||||
|
|
||||||
|
|
||||||
|
@router.put("/{config_id}/params/{param_id}", response_model=schemas.ParamOut)
|
||||||
|
def update_param(config_id: int, param_id: int, body: schemas.ParamUpdate, db: Session = Depends(get_db), user: models.User = Depends(get_current_user)):
|
||||||
|
_owner_config(config_id, db, user)
|
||||||
|
param = db.query(models.Param).filter(models.Param.id == param_id, models.Param.config_id == config_id).first()
|
||||||
|
if not param:
|
||||||
|
raise HTTPException(status_code=404, detail="Param not found")
|
||||||
|
for field, value in body.model_dump(exclude_none=True).items():
|
||||||
|
setattr(param, field, value)
|
||||||
|
db.commit()
|
||||||
|
db.refresh(param)
|
||||||
|
return param
|
||||||
|
|
||||||
|
|
||||||
|
@router.delete("/{config_id}/params/{param_id}", status_code=204)
|
||||||
|
def delete_param(config_id: int, param_id: int, db: Session = Depends(get_db), user: models.User = Depends(get_current_user)):
|
||||||
|
_owner_config(config_id, db, user)
|
||||||
|
param = db.query(models.Param).filter(models.Param.id == param_id, models.Param.config_id == config_id).first()
|
||||||
|
if not param:
|
||||||
|
raise HTTPException(status_code=404, detail="Param not found")
|
||||||
|
db.delete(param)
|
||||||
|
db.commit()
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
from starlette.middleware.sessions import SessionMiddleware
|
from starlette.middleware.sessions import SessionMiddleware
|
||||||
from app.api import auth, configs, zones, interfaces, policies, rules, snat
|
from app.api import auth, configs, zones, interfaces, policies, rules, snat, hosts, params
|
||||||
from app.database import settings
|
from app.database import settings
|
||||||
|
|
||||||
app = FastAPI(title="Shorefront", version="0.1.0")
|
app = FastAPI(title="Shorefront", version="0.1.0")
|
||||||
@@ -22,6 +22,8 @@ app.include_router(interfaces.router, prefix="/configs", tags=["interfaces"])
|
|||||||
app.include_router(policies.router, prefix="/configs", tags=["policies"])
|
app.include_router(policies.router, prefix="/configs", tags=["policies"])
|
||||||
app.include_router(rules.router, prefix="/configs", tags=["rules"])
|
app.include_router(rules.router, prefix="/configs", tags=["rules"])
|
||||||
app.include_router(snat.router, prefix="/configs", tags=["snat"])
|
app.include_router(snat.router, prefix="/configs", tags=["snat"])
|
||||||
|
app.include_router(hosts.router, prefix="/configs", tags=["hosts"])
|
||||||
|
app.include_router(params.router, prefix="/configs", tags=["params"])
|
||||||
|
|
||||||
|
|
||||||
@app.get("/health")
|
@app.get("/health")
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ class Config(Base):
|
|||||||
policies: Mapped[list["Policy"]] = relationship("Policy", back_populates="config", cascade="all, delete-orphan", order_by="Policy.position")
|
policies: Mapped[list["Policy"]] = relationship("Policy", back_populates="config", cascade="all, delete-orphan", order_by="Policy.position")
|
||||||
rules: Mapped[list["Rule"]] = relationship("Rule", back_populates="config", cascade="all, delete-orphan", order_by="Rule.position")
|
rules: Mapped[list["Rule"]] = relationship("Rule", back_populates="config", cascade="all, delete-orphan", order_by="Rule.position")
|
||||||
snat_entries: Mapped[list["Snat"]] = relationship("Snat", back_populates="config", cascade="all, delete-orphan")
|
snat_entries: Mapped[list["Snat"]] = relationship("Snat", back_populates="config", cascade="all, delete-orphan")
|
||||||
|
host_entries: Mapped[list["Host"]] = relationship("Host", back_populates="config", cascade="all, delete-orphan")
|
||||||
|
params: Mapped[list["Param"]] = relationship("Param", back_populates="config", cascade="all, delete-orphan")
|
||||||
|
|
||||||
|
|
||||||
class Zone(Base):
|
class Zone(Base):
|
||||||
@@ -113,3 +115,28 @@ class Snat(Base):
|
|||||||
comment: Mapped[str] = mapped_column(Text, default="")
|
comment: Mapped[str] = mapped_column(Text, default="")
|
||||||
|
|
||||||
config: Mapped["Config"] = relationship("Config", back_populates="snat_entries")
|
config: Mapped["Config"] = relationship("Config", back_populates="snat_entries")
|
||||||
|
|
||||||
|
|
||||||
|
class Host(Base):
|
||||||
|
__tablename__ = "hosts"
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
||||||
|
config_id: Mapped[int] = mapped_column(Integer, ForeignKey("configs.id"), nullable=False)
|
||||||
|
zone_id: Mapped[int] = mapped_column(Integer, ForeignKey("zones.id"), nullable=False)
|
||||||
|
interface: Mapped[str] = mapped_column(String(32), nullable=False)
|
||||||
|
subnet: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||||
|
options: Mapped[str] = mapped_column(Text, default="")
|
||||||
|
|
||||||
|
config: Mapped["Config"] = relationship("Config", back_populates="host_entries")
|
||||||
|
zone: Mapped["Zone"] = relationship("Zone")
|
||||||
|
|
||||||
|
|
||||||
|
class Param(Base):
|
||||||
|
__tablename__ = "params"
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
||||||
|
config_id: Mapped[int] = mapped_column(Integer, ForeignKey("configs.id"), nullable=False)
|
||||||
|
name: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||||
|
value: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||||
|
|
||||||
|
config: Mapped["Config"] = relationship("Config", back_populates="params")
|
||||||
|
|||||||
@@ -186,6 +186,52 @@ class SnatOut(BaseModel):
|
|||||||
model_config = {"from_attributes": True}
|
model_config = {"from_attributes": True}
|
||||||
|
|
||||||
|
|
||||||
|
# --- Host ---
|
||||||
|
class HostCreate(BaseModel):
|
||||||
|
zone_id: int
|
||||||
|
interface: str
|
||||||
|
subnet: str
|
||||||
|
options: str = ""
|
||||||
|
|
||||||
|
|
||||||
|
class HostUpdate(BaseModel):
|
||||||
|
zone_id: Optional[int] = None
|
||||||
|
interface: Optional[str] = None
|
||||||
|
subnet: Optional[str] = None
|
||||||
|
options: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
class HostOut(BaseModel):
|
||||||
|
id: int
|
||||||
|
config_id: int
|
||||||
|
zone_id: int
|
||||||
|
interface: str
|
||||||
|
subnet: str
|
||||||
|
options: str
|
||||||
|
|
||||||
|
model_config = {"from_attributes": True}
|
||||||
|
|
||||||
|
|
||||||
|
# --- Param ---
|
||||||
|
class ParamCreate(BaseModel):
|
||||||
|
name: str
|
||||||
|
value: str
|
||||||
|
|
||||||
|
|
||||||
|
class ParamUpdate(BaseModel):
|
||||||
|
name: Optional[str] = None
|
||||||
|
value: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
class ParamOut(BaseModel):
|
||||||
|
id: int
|
||||||
|
config_id: int
|
||||||
|
name: str
|
||||||
|
value: str
|
||||||
|
|
||||||
|
model_config = {"from_attributes": True}
|
||||||
|
|
||||||
|
|
||||||
# --- Generate ---
|
# --- Generate ---
|
||||||
class GenerateOut(BaseModel):
|
class GenerateOut(BaseModel):
|
||||||
zones: str
|
zones: str
|
||||||
@@ -193,3 +239,5 @@ class GenerateOut(BaseModel):
|
|||||||
policy: str
|
policy: str
|
||||||
rules: str
|
rules: str
|
||||||
snat: str
|
snat: str
|
||||||
|
hosts: str
|
||||||
|
params: str
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ class ShorewallGenerator:
|
|||||||
lines = [
|
lines = [
|
||||||
self._header("rules"),
|
self._header("rules"),
|
||||||
"#ACTION".ljust(16) + "SOURCE".ljust(24) + "DEST".ljust(24) + "PROTO".ljust(10) + "DPORT".ljust(10) + "SPORT\n",
|
"#ACTION".ljust(16) + "SOURCE".ljust(24) + "DEST".ljust(24) + "PROTO".ljust(10) + "DPORT".ljust(10) + "SPORT\n",
|
||||||
|
"SECTION NEW\n",
|
||||||
]
|
]
|
||||||
for r in sorted(self._config.rules, key=lambda x: x.position):
|
for r in sorted(self._config.rules, key=lambda x: x.position):
|
||||||
src = (r.src_zone.name if r.src_zone else "all") + (f":{r.src_ip}" if r.src_ip else "")
|
src = (r.src_zone.name if r.src_zone else "all") + (f":{r.src_ip}" if r.src_ip else "")
|
||||||
@@ -48,6 +49,19 @@ class ShorewallGenerator:
|
|||||||
lines.append(self._col(r.action, src, dst, r.proto or "-", r.dport or "-", r.sport or "-", width=16))
|
lines.append(self._col(r.action, src, dst, r.proto or "-", r.dport or "-", r.sport or "-", width=16))
|
||||||
return "".join(lines)
|
return "".join(lines)
|
||||||
|
|
||||||
|
def hosts(self) -> str:
|
||||||
|
lines = [self._header("hosts"), "#ZONE".ljust(16) + "HOSTS\n"]
|
||||||
|
for h in self._config.host_entries:
|
||||||
|
hosts_val = f"{h.interface}:{h.subnet}"
|
||||||
|
lines.append(self._col(h.zone.name, hosts_val, h.options or "-", width=16))
|
||||||
|
return "".join(lines)
|
||||||
|
|
||||||
|
def params(self) -> str:
|
||||||
|
lines = [self._header("params")]
|
||||||
|
for p in self._config.params:
|
||||||
|
lines.append(f"{p.name}={p.value}\n")
|
||||||
|
return "".join(lines)
|
||||||
|
|
||||||
def snat(self) -> str:
|
def snat(self) -> str:
|
||||||
lines = [self._header("snat"), "#ACTION".ljust(24) + "SOURCE".ljust(24) + "DEST\n"]
|
lines = [self._header("snat"), "#ACTION".ljust(24) + "SOURCE".ljust(24) + "DEST\n"]
|
||||||
for m in self._config.snat_entries:
|
for m in self._config.snat_entries:
|
||||||
@@ -62,6 +76,8 @@ class ShorewallGenerator:
|
|||||||
"policy": self.policy(),
|
"policy": self.policy(),
|
||||||
"rules": self.rules(),
|
"rules": self.rules(),
|
||||||
"snat": self.snat(),
|
"snat": self.snat(),
|
||||||
|
"hosts": self.hosts(),
|
||||||
|
"params": self.params(),
|
||||||
}
|
}
|
||||||
|
|
||||||
def as_zip(self) -> bytes:
|
def as_zip(self) -> bytes:
|
||||||
@@ -72,4 +88,6 @@ class ShorewallGenerator:
|
|||||||
zf.writestr("policy", self.policy())
|
zf.writestr("policy", self.policy())
|
||||||
zf.writestr("rules", self.rules())
|
zf.writestr("rules", self.rules())
|
||||||
zf.writestr("snat", self.snat())
|
zf.writestr("snat", self.snat())
|
||||||
|
zf.writestr("hosts", self.hosts())
|
||||||
|
zf.writestr("params", self.params())
|
||||||
return buf.getvalue()
|
return buf.getvalue()
|
||||||
|
|||||||
@@ -55,3 +55,5 @@ export const interfacesApi = nestedApi('interfaces')
|
|||||||
export const policiesApi = nestedApi('policies')
|
export const policiesApi = nestedApi('policies')
|
||||||
export const rulesApi = nestedApi('rules')
|
export const rulesApi = nestedApi('rules')
|
||||||
export const snatApi = nestedApi('snat')
|
export const snatApi = nestedApi('snat')
|
||||||
|
export const hostsApi = nestedApi('hosts')
|
||||||
|
export const paramsApi = nestedApi('params')
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ interface GeneratedFiles {
|
|||||||
policy: string
|
policy: string
|
||||||
rules: string
|
rules: string
|
||||||
snat: string
|
snat: string
|
||||||
|
hosts: string
|
||||||
|
params: string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -28,7 +30,7 @@ interface Props {
|
|||||||
onClose: () => void
|
onClose: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const TABS = ['zones', 'interfaces', 'policy', 'rules', 'snat'] as const
|
const TABS = ['zones', 'interfaces', 'policy', 'rules', 'snat', 'hosts', 'params'] as const
|
||||||
|
|
||||||
export default function GenerateModal({ open, configId, configName, onClose }: Props) {
|
export default function GenerateModal({ open, configId, configName, onClose }: Props) {
|
||||||
const [tab, setTab] = useState(0)
|
const [tab, setTab] = useState(0)
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import Typography from '@mui/material/Typography'
|
|||||||
import Breadcrumbs from '@mui/material/Breadcrumbs'
|
import Breadcrumbs from '@mui/material/Breadcrumbs'
|
||||||
import AddIcon from '@mui/icons-material/Add'
|
import AddIcon from '@mui/icons-material/Add'
|
||||||
import BuildIcon from '@mui/icons-material/Build'
|
import BuildIcon from '@mui/icons-material/Build'
|
||||||
import { zonesApi, interfacesApi, policiesApi, rulesApi, snatApi, configsApi } from '../api'
|
import { zonesApi, interfacesApi, policiesApi, rulesApi, snatApi, hostsApi, paramsApi, configsApi } from '../api'
|
||||||
|
|
||||||
// ---- Types ----
|
// ---- Types ----
|
||||||
interface Zone { id: number; name: string; type: string; options: string }
|
interface Zone { id: number; name: string; type: string; options: string }
|
||||||
@@ -20,6 +20,8 @@ interface Iface { id: number; name: string; zone_id: number; options: string }
|
|||||||
interface Policy { id: number; src_zone_id: number; dst_zone_id: number; policy: string; log_level: string; comment: string; position: number }
|
interface Policy { id: number; src_zone_id: number; dst_zone_id: number; policy: string; log_level: string; comment: string; position: number }
|
||||||
interface Rule { id: number; action: string; src_zone_id: number | null; dst_zone_id: number | null; src_ip: string; dst_ip: string; proto: string; dport: string; sport: string; comment: string; position: number }
|
interface Rule { id: number; action: string; src_zone_id: number | null; dst_zone_id: number | null; src_ip: string; dst_ip: string; proto: string; dport: string; sport: string; comment: string; position: number }
|
||||||
interface Snat { id: number; source_network: string; out_interface: string; to_address: string; comment: string }
|
interface Snat { id: number; source_network: string; out_interface: string; to_address: string; comment: string }
|
||||||
|
interface Host { id: number; zone_id: number; interface: string; subnet: string; options: string }
|
||||||
|
interface Param { id: number; name: string; value: string }
|
||||||
|
|
||||||
type AnyEntity = { id: number } & Record<string, unknown>
|
type AnyEntity = { id: number } & Record<string, unknown>
|
||||||
|
|
||||||
@@ -34,6 +36,8 @@ export default function ConfigDetail() {
|
|||||||
const [policies, setPolicies] = useState<Policy[]>([])
|
const [policies, setPolicies] = useState<Policy[]>([])
|
||||||
const [rules, setRules] = useState<Rule[]>([])
|
const [rules, setRules] = useState<Rule[]>([])
|
||||||
const [snat, setSnat] = useState<Snat[]>([])
|
const [snat, setSnat] = useState<Snat[]>([])
|
||||||
|
const [hosts, setHosts] = useState<Host[]>([])
|
||||||
|
const [paramsList, setParamsList] = useState<Param[]>([])
|
||||||
const [formOpen, setFormOpen] = useState(false)
|
const [formOpen, setFormOpen] = useState(false)
|
||||||
const [editing, setEditing] = useState<AnyEntity | null>(null)
|
const [editing, setEditing] = useState<AnyEntity | null>(null)
|
||||||
const [generateOpen, setGenerateOpen] = useState(false)
|
const [generateOpen, setGenerateOpen] = useState(false)
|
||||||
@@ -45,6 +49,8 @@ export default function ConfigDetail() {
|
|||||||
policiesApi.list(configId).then((r) => setPolicies(r.data))
|
policiesApi.list(configId).then((r) => setPolicies(r.data))
|
||||||
rulesApi.list(configId).then((r) => setRules(r.data))
|
rulesApi.list(configId).then((r) => setRules(r.data))
|
||||||
snatApi.list(configId).then((r) => setSnat(r.data))
|
snatApi.list(configId).then((r) => setSnat(r.data))
|
||||||
|
hostsApi.list(configId).then((r) => setHosts(r.data))
|
||||||
|
paramsApi.list(configId).then((r) => setParamsList(r.data))
|
||||||
}, [configId])
|
}, [configId])
|
||||||
|
|
||||||
const zoneOptions = zones.map((z) => ({ value: z.id, label: z.name }))
|
const zoneOptions = zones.map((z) => ({ value: z.id, label: z.name }))
|
||||||
@@ -168,6 +174,38 @@ export default function ConfigDetail() {
|
|||||||
{ name: 'comment', label: 'Comment' },
|
{ name: 'comment', label: 'Comment' },
|
||||||
] as FieldDef[],
|
] as FieldDef[],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: 'Hosts',
|
||||||
|
rows: hosts as unknown as AnyEntity[],
|
||||||
|
setRows: setHosts as unknown as Dispatch<SetStateAction<AnyEntity[]>>,
|
||||||
|
api: hostsApi,
|
||||||
|
columns: [
|
||||||
|
{ key: 'zone_id' as const, label: 'Zone' },
|
||||||
|
{ key: 'interface' as const, label: 'Interface' },
|
||||||
|
{ key: 'subnet' as const, label: 'Subnet' },
|
||||||
|
{ key: 'options' as const, label: 'Options' },
|
||||||
|
] as Column<AnyEntity>[],
|
||||||
|
fields: [
|
||||||
|
{ name: 'zone_id', label: 'Zone', type: 'select' as const, options: zoneOptions, required: true },
|
||||||
|
{ name: 'interface', label: 'Interface', required: true },
|
||||||
|
{ name: 'subnet', label: 'Subnet', required: true },
|
||||||
|
{ name: 'options', label: 'Options' },
|
||||||
|
] as FieldDef[],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Params',
|
||||||
|
rows: paramsList as unknown as AnyEntity[],
|
||||||
|
setRows: setParamsList as unknown as Dispatch<SetStateAction<AnyEntity[]>>,
|
||||||
|
api: paramsApi,
|
||||||
|
columns: [
|
||||||
|
{ key: 'name' as const, label: 'Name' },
|
||||||
|
{ key: 'value' as const, label: 'Value' },
|
||||||
|
] as Column<AnyEntity>[],
|
||||||
|
fields: [
|
||||||
|
{ name: 'name', label: 'Name', required: true },
|
||||||
|
{ name: 'value', label: 'Value', required: true },
|
||||||
|
] as FieldDef[],
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
const current = tabConfig[tab]
|
const current = tabConfig[tab]
|
||||||
|
|||||||
@@ -42,4 +42,4 @@ keycloak:
|
|||||||
redirectUri: https://shorefront.baumann.gr/api/auth/oidc/callback
|
redirectUri: https://shorefront.baumann.gr/api/auth/oidc/callback
|
||||||
|
|
||||||
containers:
|
containers:
|
||||||
version: "0.007"
|
version: "0.008"
|
||||||
|
|||||||
Reference in New Issue
Block a user