Merge pull request 'API: remove all python dataclasses for clanModules (services)' (#2080) from hsjobeki/clan-core:hsjobeki-main into main
This commit is contained in:
@@ -1,62 +1,37 @@
|
|||||||
from clan_cli.errors import ClanError
|
# @API.register
|
||||||
from clan_cli.inventory import (
|
# def set_admin_service(
|
||||||
AdminConfig,
|
# base_url: str,
|
||||||
ServiceAdmin,
|
# allowed_keys: dict[str, str],
|
||||||
ServiceAdminRole,
|
# instance_name: str = "admin",
|
||||||
ServiceAdminRoleDefault,
|
# extra_machines: list[str] | None = None,
|
||||||
ServiceMeta,
|
# ) -> None:
|
||||||
load_inventory_eval,
|
# """
|
||||||
save_inventory,
|
# Set the admin service of a clan
|
||||||
)
|
# Every machine is by default part of the admin service via the 'all' tag
|
||||||
|
# """
|
||||||
|
# if extra_machines is None:
|
||||||
|
# extra_machines = []
|
||||||
|
# inventory = load_inventory_eval(base_url)
|
||||||
|
|
||||||
from . import API
|
# if not allowed_keys:
|
||||||
|
# msg = "At least one key must be provided to ensure access"
|
||||||
|
# raise ClanError(msg)
|
||||||
|
|
||||||
|
# instance = ServiceAdmin(
|
||||||
|
# meta=ServiceMeta(name=instance_name),
|
||||||
|
# roles=ServiceAdminRole(
|
||||||
|
# default=ServiceAdminRoleDefault(
|
||||||
|
# machines=extra_machines,
|
||||||
|
# tags=["all"],
|
||||||
|
# )
|
||||||
|
# ),
|
||||||
|
# config=AdminConfig(allowedKeys=allowed_keys),
|
||||||
|
# )
|
||||||
|
|
||||||
@API.register
|
# inventory.services.admin[instance_name] = instance
|
||||||
def get_admin_service(base_url: str) -> ServiceAdmin | None:
|
|
||||||
"""
|
|
||||||
Return the admin service of a clan.
|
|
||||||
|
|
||||||
There is only one admin service. This might be changed in the future
|
# save_inventory(
|
||||||
"""
|
# inventory,
|
||||||
inventory = load_inventory_eval(base_url)
|
# base_url,
|
||||||
return inventory.services.admin.get("admin")
|
# f"Set admin service: '{instance_name}'",
|
||||||
|
# )
|
||||||
|
|
||||||
@API.register
|
|
||||||
def set_admin_service(
|
|
||||||
base_url: str,
|
|
||||||
allowed_keys: dict[str, str],
|
|
||||||
instance_name: str = "admin",
|
|
||||||
extra_machines: list[str] | None = None,
|
|
||||||
) -> None:
|
|
||||||
"""
|
|
||||||
Set the admin service of a clan
|
|
||||||
Every machine is by default part of the admin service via the 'all' tag
|
|
||||||
"""
|
|
||||||
if extra_machines is None:
|
|
||||||
extra_machines = []
|
|
||||||
inventory = load_inventory_eval(base_url)
|
|
||||||
|
|
||||||
if not allowed_keys:
|
|
||||||
msg = "At least one key must be provided to ensure access"
|
|
||||||
raise ClanError(msg)
|
|
||||||
|
|
||||||
instance = ServiceAdmin(
|
|
||||||
meta=ServiceMeta(name=instance_name),
|
|
||||||
roles=ServiceAdminRole(
|
|
||||||
default=ServiceAdminRoleDefault(
|
|
||||||
machines=extra_machines,
|
|
||||||
tags=["all"],
|
|
||||||
)
|
|
||||||
),
|
|
||||||
config=AdminConfig(allowedKeys=allowed_keys),
|
|
||||||
)
|
|
||||||
|
|
||||||
inventory.services.admin[instance_name] = instance
|
|
||||||
|
|
||||||
save_inventory(
|
|
||||||
inventory,
|
|
||||||
base_url,
|
|
||||||
f"Set admin service: '{instance_name}'",
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -1,67 +1,34 @@
|
|||||||
from clan_cli.inventory import (
|
|
||||||
ServiceMeta,
|
|
||||||
ServiceSingleDisk,
|
|
||||||
ServiceSingleDiskRole,
|
|
||||||
ServiceSingleDiskRoleDefault,
|
|
||||||
SingleDiskConfig,
|
|
||||||
load_inventory_eval,
|
|
||||||
load_inventory_json,
|
|
||||||
save_inventory,
|
|
||||||
)
|
|
||||||
|
|
||||||
from . import API
|
|
||||||
|
|
||||||
|
|
||||||
def get_instance_name(machine_name: str) -> str:
|
def get_instance_name(machine_name: str) -> str:
|
||||||
return f"{machine_name}-single-disk"
|
return f"{machine_name}-single-disk"
|
||||||
|
|
||||||
|
|
||||||
@API.register
|
# @API.register
|
||||||
def set_single_disk_uuid(
|
# def set_single_disk_uuid(
|
||||||
base_path: str,
|
# base_path: str,
|
||||||
machine_name: str,
|
# machine_name: str,
|
||||||
disk_uuid: str,
|
# disk_uuid: str,
|
||||||
) -> None:
|
# ) -> None:
|
||||||
"""
|
# """
|
||||||
Set the disk UUID of single disk machine
|
# Set the disk UUID of single disk machine
|
||||||
"""
|
# """
|
||||||
inventory = load_inventory_json(base_path)
|
# inventory = load_inventory_json(base_path)
|
||||||
|
|
||||||
instance_name = get_instance_name(machine_name)
|
# instance_name = get_instance_name(machine_name)
|
||||||
|
|
||||||
single_disk_config: ServiceSingleDisk = ServiceSingleDisk(
|
# single_disk_config: ServiceSingleDisk = ServiceSingleDisk(
|
||||||
meta=ServiceMeta(name=instance_name),
|
# meta=ServiceMeta(name=instance_name),
|
||||||
roles=ServiceSingleDiskRole(
|
# roles=ServiceSingleDiskRole(
|
||||||
default=ServiceSingleDiskRoleDefault(
|
# default=ServiceSingleDiskRoleDefault(
|
||||||
config=SingleDiskConfig(device=f"/dev/disk/by-id/{disk_uuid}"),
|
# config=SingleDiskConfig(device=f"/dev/disk/by-id/{disk_uuid}"),
|
||||||
machines=[machine_name],
|
# machines=[machine_name],
|
||||||
)
|
# )
|
||||||
),
|
# ),
|
||||||
)
|
# )
|
||||||
|
|
||||||
inventory.services.single_disk[instance_name] = single_disk_config
|
# inventory.services.single_disk[instance_name] = single_disk_config
|
||||||
|
|
||||||
save_inventory(
|
# save_inventory(
|
||||||
inventory,
|
# inventory,
|
||||||
base_path,
|
# base_path,
|
||||||
f"Set disk UUID: '{disk_uuid}' on machine: '{machine_name}'",
|
# f"Set disk UUID: '{disk_uuid}' on machine: '{machine_name}'",
|
||||||
)
|
# )
|
||||||
|
|
||||||
|
|
||||||
@API.register
|
|
||||||
def get_single_disk_uuid(
|
|
||||||
base_path: str,
|
|
||||||
machine_name: str,
|
|
||||||
) -> str | None:
|
|
||||||
"""
|
|
||||||
Get the disk UUID of single disk machine
|
|
||||||
"""
|
|
||||||
inventory = load_inventory_eval(base_path)
|
|
||||||
|
|
||||||
instance_name = get_instance_name(machine_name)
|
|
||||||
|
|
||||||
single_disk_config: ServiceSingleDisk = inventory.services.single_disk[
|
|
||||||
instance_name
|
|
||||||
]
|
|
||||||
|
|
||||||
return single_disk_config.roles.default.config.device
|
|
||||||
|
|||||||
@@ -1,109 +1,67 @@
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from clan_cli.clan_uri import FlakeId
|
|
||||||
from clan_cli.errors import ClanError
|
|
||||||
from clan_cli.facts.generate import generate_facts
|
|
||||||
from clan_cli.inventory import (
|
|
||||||
IwdConfig,
|
|
||||||
IwdConfigNetwork,
|
|
||||||
ServiceIwd,
|
|
||||||
ServiceIwdRole,
|
|
||||||
ServiceIwdRoleDefault,
|
|
||||||
ServiceMeta,
|
|
||||||
load_inventory_eval,
|
|
||||||
save_inventory,
|
|
||||||
)
|
|
||||||
from clan_cli.machines.machines import Machine
|
|
||||||
from clan_cli.secrets.sops import (
|
|
||||||
maybe_get_public_key,
|
|
||||||
maybe_get_user_or_machine,
|
|
||||||
)
|
|
||||||
|
|
||||||
from . import API
|
|
||||||
|
|
||||||
|
|
||||||
def instance_name(machine_name: str) -> str:
|
def instance_name(machine_name: str) -> str:
|
||||||
return f"{machine_name}_wifi_0_"
|
return f"{machine_name}_wifi_0_"
|
||||||
|
|
||||||
|
|
||||||
@API.register
|
|
||||||
def get_iwd_service(base_url: str, machine_name: str) -> ServiceIwd:
|
|
||||||
"""
|
|
||||||
Return the admin service of a clan.
|
|
||||||
|
|
||||||
There is only one admin service. This might be changed in the future
|
|
||||||
"""
|
|
||||||
inventory = load_inventory_eval(base_url)
|
|
||||||
service_config = inventory.services.iwd.get(instance_name(machine_name))
|
|
||||||
if service_config:
|
|
||||||
return service_config
|
|
||||||
|
|
||||||
# Empty service
|
|
||||||
return ServiceIwd(
|
|
||||||
meta=ServiceMeta(name="wifi_0"),
|
|
||||||
roles=ServiceIwdRole(default=ServiceIwdRoleDefault(machines=[machine_name])),
|
|
||||||
config=IwdConfig(networks={}),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class NetworkConfig:
|
class NetworkConfig:
|
||||||
ssid: str
|
ssid: str
|
||||||
password: str
|
password: str
|
||||||
|
|
||||||
|
|
||||||
@API.register
|
# @API.register
|
||||||
def set_iwd_service_for_machine(
|
# def set_iwd_service_for_machine(
|
||||||
base_url: str, machine_name: str, networks: dict[str, NetworkConfig]
|
# base_url: str, machine_name: str, networks: dict[str, NetworkConfig]
|
||||||
) -> None:
|
# ) -> None:
|
||||||
"""
|
# """
|
||||||
Set the admin service of a clan
|
# Set the admin service of a clan
|
||||||
Every machine is by default part of the admin service via the 'all' tag
|
# Every machine is by default part of the admin service via the 'all' tag
|
||||||
"""
|
# """
|
||||||
_instance_name = instance_name(machine_name)
|
# _instance_name = instance_name(machine_name)
|
||||||
|
|
||||||
inventory = load_inventory_eval(base_url)
|
# inventory = load_inventory_eval(base_url)
|
||||||
|
|
||||||
instance = ServiceIwd(
|
# instance = ServiceIwd(
|
||||||
meta=ServiceMeta(name="wifi_0"),
|
# meta=ServiceMeta(name="wifi_0"),
|
||||||
roles=ServiceIwdRole(
|
# roles=ServiceIwdRole(
|
||||||
default=ServiceIwdRoleDefault(
|
# default=ServiceIwdRoleDefault(
|
||||||
machines=[machine_name],
|
# machines=[machine_name],
|
||||||
)
|
# )
|
||||||
),
|
# ),
|
||||||
config=IwdConfig(
|
# config=IwdConfig(
|
||||||
networks={k: IwdConfigNetwork(v.ssid) for k, v in networks.items()}
|
# networks={k: IwdConfigNetwork(v.ssid) for k, v in networks.items()}
|
||||||
),
|
# ),
|
||||||
)
|
# )
|
||||||
|
|
||||||
inventory.services.iwd[_instance_name] = instance
|
# inventory.services.iwd[_instance_name] = instance
|
||||||
|
|
||||||
save_inventory(
|
# save_inventory(
|
||||||
inventory,
|
# inventory,
|
||||||
base_url,
|
# base_url,
|
||||||
f"Set iwd service: '{_instance_name}'",
|
# f"Set iwd service: '{_instance_name}'",
|
||||||
)
|
# )
|
||||||
|
|
||||||
pubkey = maybe_get_public_key()
|
# pubkey = maybe_get_public_key()
|
||||||
if not pubkey:
|
# if not pubkey:
|
||||||
# TODO: do this automatically
|
# # TODO: do this automatically
|
||||||
# pubkey = generate_key()
|
# # pubkey = generate_key()
|
||||||
raise ClanError(msg="No public key found. Please initialize your key.")
|
# raise ClanError(msg="No public key found. Please initialize your key.")
|
||||||
|
|
||||||
registered_key = maybe_get_user_or_machine(Path(base_url), pubkey)
|
# registered_key = maybe_get_user_or_machine(Path(base_url), pubkey)
|
||||||
if not registered_key:
|
# if not registered_key:
|
||||||
# TODO: do this automatically
|
# # TODO: do this automatically
|
||||||
# username = os.getlogin()
|
# # username = os.getlogin()
|
||||||
# add_user(Path(base_url), username, pubkey, force=False)
|
# # add_user(Path(base_url), username, pubkey, force=False)
|
||||||
raise ClanError(msg="Your public key is not registered for use with this clan.")
|
# raise ClanError(msg="Your public key is not registered for use with this clan.")
|
||||||
|
|
||||||
password_dict = {f"iwd.{net.ssid}": net.password for net in networks.values()}
|
# password_dict = {f"iwd.{net.ssid}": net.password for net in networks.values()}
|
||||||
for net in networks.values():
|
# for net in networks.values():
|
||||||
generate_facts(
|
# generate_facts(
|
||||||
service=f"iwd.{net.ssid}",
|
# service=f"iwd.{net.ssid}",
|
||||||
machines=[Machine(machine_name, FlakeId(base_url))],
|
# machines=[Machine(machine_name, FlakeId(base_url))],
|
||||||
regenerate=True,
|
# regenerate=True,
|
||||||
# Just returns the password
|
# # Just returns the password
|
||||||
prompt=lambda service, _msg: password_dict[service],
|
# prompt=lambda service, _msg: password_dict[service],
|
||||||
)
|
# )
|
||||||
|
|||||||
@@ -23,35 +23,13 @@ from clan_cli.git import commit_file
|
|||||||
from clan_cli.nix import nix_eval
|
from clan_cli.nix import nix_eval
|
||||||
|
|
||||||
from .classes import (
|
from .classes import (
|
||||||
AdminConfig,
|
|
||||||
Inventory,
|
Inventory,
|
||||||
IwdConfig,
|
|
||||||
IwdConfigNetwork,
|
|
||||||
# Machine classes
|
# Machine classes
|
||||||
Machine,
|
Machine,
|
||||||
MachineDeploy,
|
MachineDeploy,
|
||||||
# General classes
|
# General classes
|
||||||
Meta,
|
Meta,
|
||||||
Service,
|
Service,
|
||||||
# Admin service
|
|
||||||
ServiceAdmin,
|
|
||||||
ServiceAdminRole,
|
|
||||||
ServiceAdminRoleDefault,
|
|
||||||
# Borgbackup service
|
|
||||||
ServiceBorgbackup,
|
|
||||||
ServiceBorgbackupRole,
|
|
||||||
ServiceBorgbackupRoleClient,
|
|
||||||
ServiceBorgbackupRoleServer,
|
|
||||||
# IWD
|
|
||||||
ServiceIwd,
|
|
||||||
ServiceIwdRole,
|
|
||||||
ServiceIwdRoleDefault,
|
|
||||||
ServiceMeta,
|
|
||||||
# Single Disk service
|
|
||||||
ServiceSingleDisk,
|
|
||||||
ServiceSingleDiskRole,
|
|
||||||
ServiceSingleDiskRoleDefault,
|
|
||||||
SingleDiskConfig,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Re export classes here
|
# Re export classes here
|
||||||
@@ -64,27 +42,6 @@ __all__ = [
|
|||||||
"Meta",
|
"Meta",
|
||||||
"Inventory",
|
"Inventory",
|
||||||
"MachineDeploy",
|
"MachineDeploy",
|
||||||
"ServiceBorgbackup",
|
|
||||||
"ServiceMeta",
|
|
||||||
"ServiceBorgbackupRole",
|
|
||||||
"ServiceBorgbackupRoleClient",
|
|
||||||
"ServiceBorgbackupRoleServer",
|
|
||||||
# Single Disk service
|
|
||||||
"ServiceSingleDisk",
|
|
||||||
"ServiceSingleDiskRole",
|
|
||||||
"ServiceSingleDiskRoleDefault",
|
|
||||||
"SingleDiskConfig",
|
|
||||||
# Admin service
|
|
||||||
"ServiceAdmin",
|
|
||||||
"ServiceAdminRole",
|
|
||||||
"ServiceAdminRoleDefault",
|
|
||||||
"AdminConfig",
|
|
||||||
# IWD service,
|
|
||||||
"ServiceIwd",
|
|
||||||
"ServiceIwdRole",
|
|
||||||
"ServiceIwdRoleDefault",
|
|
||||||
"IwdConfig",
|
|
||||||
"IwdConfigNetwork",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -30,234 +30,7 @@ class Meta:
|
|||||||
icon: None | str = field(default = None)
|
icon: None | str = field(default = None)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
Service = dict[str, Any]
|
||||||
class AdminConfig:
|
|
||||||
allowedKeys: dict[str, str] = field(default_factory = dict)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class ServiceAdminMachine:
|
|
||||||
config: AdminConfig = field(default_factory = AdminConfig)
|
|
||||||
imports: list[str] = field(default_factory = list)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class ServiceMeta:
|
|
||||||
name: str
|
|
||||||
description: None | str = field(default = None)
|
|
||||||
icon: None | str = field(default = None)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class ServiceAdminRoleDefault:
|
|
||||||
config: AdminConfig = field(default_factory = AdminConfig)
|
|
||||||
imports: list[str] = field(default_factory = list)
|
|
||||||
machines: list[str] = field(default_factory = list)
|
|
||||||
tags: list[str] = field(default_factory = list)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class ServiceAdminRole:
|
|
||||||
default: ServiceAdminRoleDefault
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class ServiceAdmin:
|
|
||||||
meta: ServiceMeta
|
|
||||||
roles: ServiceAdminRole
|
|
||||||
config: AdminConfig = field(default_factory = AdminConfig)
|
|
||||||
machines: dict[str, ServiceAdminMachine] = field(default_factory = dict)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class BorgbackupConfigDestination:
|
|
||||||
name: str
|
|
||||||
repo: str
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class BorgbackupConfig:
|
|
||||||
destinations: dict[str, BorgbackupConfigDestination] = field(default_factory = dict)
|
|
||||||
exclude: list[str] = field(default_factory = list)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class ServiceBorgbackupMachine:
|
|
||||||
config: BorgbackupConfig = field(default_factory = BorgbackupConfig)
|
|
||||||
imports: list[str] = field(default_factory = list)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class ServiceBorgbackupRoleClient:
|
|
||||||
config: BorgbackupConfig = field(default_factory = BorgbackupConfig)
|
|
||||||
imports: list[str] = field(default_factory = list)
|
|
||||||
machines: list[str] = field(default_factory = list)
|
|
||||||
tags: list[str] = field(default_factory = list)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class ServiceBorgbackupRoleServer:
|
|
||||||
config: BorgbackupConfig = field(default_factory = BorgbackupConfig)
|
|
||||||
imports: list[str] = field(default_factory = list)
|
|
||||||
machines: list[str] = field(default_factory = list)
|
|
||||||
tags: list[str] = field(default_factory = list)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class ServiceBorgbackupRole:
|
|
||||||
client: ServiceBorgbackupRoleClient
|
|
||||||
server: ServiceBorgbackupRoleServer
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class ServiceBorgbackup:
|
|
||||||
meta: ServiceMeta
|
|
||||||
roles: ServiceBorgbackupRole
|
|
||||||
config: BorgbackupConfig = field(default_factory = BorgbackupConfig)
|
|
||||||
machines: dict[str, ServiceBorgbackupMachine] = field(default_factory = dict)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class IwdConfigNetwork:
|
|
||||||
ssid: str
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class IwdConfig:
|
|
||||||
networks: dict[str, IwdConfigNetwork] = field(default_factory = dict)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class ServiceIwdMachine:
|
|
||||||
config: IwdConfig = field(default_factory = IwdConfig)
|
|
||||||
imports: list[str] = field(default_factory = list)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class ServiceIwdRoleDefault:
|
|
||||||
config: IwdConfig = field(default_factory = IwdConfig)
|
|
||||||
imports: list[str] = field(default_factory = list)
|
|
||||||
machines: list[str] = field(default_factory = list)
|
|
||||||
tags: list[str] = field(default_factory = list)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class ServiceIwdRole:
|
|
||||||
default: ServiceIwdRoleDefault
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class ServiceIwd:
|
|
||||||
meta: ServiceMeta
|
|
||||||
roles: ServiceIwdRole
|
|
||||||
config: IwdConfig = field(default_factory = IwdConfig)
|
|
||||||
machines: dict[str, ServiceIwdMachine] = field(default_factory = dict)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class PackagesConfig:
|
|
||||||
packages: list[str] = field(default_factory = list)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class ServicePackageMachine:
|
|
||||||
config: PackagesConfig = field(default_factory = PackagesConfig)
|
|
||||||
imports: list[str] = field(default_factory = list)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class ServicePackageRoleDefault:
|
|
||||||
config: PackagesConfig = field(default_factory = PackagesConfig)
|
|
||||||
imports: list[str] = field(default_factory = list)
|
|
||||||
machines: list[str] = field(default_factory = list)
|
|
||||||
tags: list[str] = field(default_factory = list)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class ServicePackageRole:
|
|
||||||
default: ServicePackageRoleDefault
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class ServicePackage:
|
|
||||||
meta: ServiceMeta
|
|
||||||
roles: ServicePackageRole
|
|
||||||
config: PackagesConfig = field(default_factory = PackagesConfig)
|
|
||||||
machines: dict[str, ServicePackageMachine] = field(default_factory = dict)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class SingleDiskConfig:
|
|
||||||
device: None | str = field(default = None)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class ServiceSingleDiskMachine:
|
|
||||||
config: SingleDiskConfig = field(default_factory = SingleDiskConfig)
|
|
||||||
imports: list[str] = field(default_factory = list)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class ServiceSingleDiskRoleDefault:
|
|
||||||
config: SingleDiskConfig = field(default_factory = SingleDiskConfig)
|
|
||||||
imports: list[str] = field(default_factory = list)
|
|
||||||
machines: list[str] = field(default_factory = list)
|
|
||||||
tags: list[str] = field(default_factory = list)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class ServiceSingleDiskRole:
|
|
||||||
default: ServiceSingleDiskRoleDefault
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class ServiceSingleDisk:
|
|
||||||
meta: ServiceMeta
|
|
||||||
roles: ServiceSingleDiskRole
|
|
||||||
config: SingleDiskConfig = field(default_factory = SingleDiskConfig)
|
|
||||||
machines: dict[str, ServiceSingleDiskMachine] = field(default_factory = dict)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class StateVersionConfig:
|
|
||||||
pass
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class ServiceStateVersionMachine:
|
|
||||||
config: StateVersionConfig = field(default_factory = StateVersionConfig)
|
|
||||||
imports: list[str] = field(default_factory = list)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class ServiceStateVersionRoleDefault:
|
|
||||||
config: StateVersionConfig = field(default_factory = StateVersionConfig)
|
|
||||||
imports: list[str] = field(default_factory = list)
|
|
||||||
machines: list[str] = field(default_factory = list)
|
|
||||||
tags: list[str] = field(default_factory = list)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class ServiceStateVersionRole:
|
|
||||||
default: ServiceStateVersionRoleDefault
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class ServiceStateVersion:
|
|
||||||
meta: ServiceMeta
|
|
||||||
roles: ServiceStateVersionRole
|
|
||||||
config: StateVersionConfig = field(default_factory = StateVersionConfig)
|
|
||||||
machines: dict[str, ServiceStateVersionMachine] = field(default_factory = dict)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class Service:
|
|
||||||
admin: dict[str, ServiceAdmin] = field(default_factory = dict)
|
|
||||||
borgbackup: dict[str, ServiceBorgbackup] = field(default_factory = dict)
|
|
||||||
iwd: dict[str, ServiceIwd] = field(default_factory = dict)
|
|
||||||
packages: dict[str, ServicePackage] = field(default_factory = dict)
|
|
||||||
single_disk: dict[str, ServiceSingleDisk] = field(default_factory = dict, metadata = {"alias": "single-disk"})
|
|
||||||
state_version: dict[str, ServiceStateVersion] = field(default_factory = dict, metadata = {"alias": "state-version"})
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Inventory:
|
class Inventory:
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ let
|
|||||||
ln -sf ${nixpkgs'} $out/clan_cli/nixpkgs
|
ln -sf ${nixpkgs'} $out/clan_cli/nixpkgs
|
||||||
cp -r ${../../templates} $out/clan_cli/templates
|
cp -r ${../../templates} $out/clan_cli/templates
|
||||||
|
|
||||||
${classgen}/bin/classgen ${inventory-schema}/schema.json $out/clan_cli/inventory/classes.py
|
${classgen}/bin/classgen ${inventory-schema}/schema.json $out/clan_cli/inventory/classes.py --stop-at "Service"
|
||||||
'';
|
'';
|
||||||
|
|
||||||
# Create a custom nixpkgs for use within the project
|
# Create a custom nixpkgs for use within the project
|
||||||
|
|||||||
@@ -73,7 +73,7 @@
|
|||||||
];
|
];
|
||||||
|
|
||||||
installPhase = ''
|
installPhase = ''
|
||||||
${self'.packages.classgen}/bin/classgen ${self'.packages.inventory-schema}/schema.json ./clan_cli/inventory/classes.py
|
${self'.packages.classgen}/bin/classgen ${self'.packages.inventory-schema}/schema.json ./clan_cli/inventory/classes.py --stop-at "Service"
|
||||||
|
|
||||||
python docs.py reference
|
python docs.py reference
|
||||||
mkdir -p $out
|
mkdir -p $out
|
||||||
@@ -93,10 +93,12 @@
|
|||||||
];
|
];
|
||||||
|
|
||||||
installPhase = ''
|
installPhase = ''
|
||||||
${self'.packages.classgen}/bin/classgen ${self'.packages.inventory-schema}/schema.json ./clan_cli/inventory/classes.py
|
${self'.packages.classgen}/bin/classgen ${self'.packages.inventory-schema}/schema.json ./clan_cli/inventory/classes.py --stop-at "Service"
|
||||||
mkdir -p $out
|
mkdir -p $out
|
||||||
python api.py > $out/API.json
|
python api.py > $out/API.json
|
||||||
${self'.packages.json2ts}/bin/json2ts --input $out/API.json > $out/API.ts
|
${self'.packages.json2ts}/bin/json2ts --input $out/API.json > $out/API.ts
|
||||||
|
${self'.packages.json2ts}/bin/json2ts --input ${self'.packages.inventory-schema}/schema.json > $out/Inventory.ts
|
||||||
|
cp ${self'.packages.inventory-schema}/schema.json $out/inventory-schema.json
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
json2ts = pkgs.buildNpmPackage {
|
json2ts = pkgs.buildNpmPackage {
|
||||||
@@ -122,7 +124,7 @@
|
|||||||
classFile = "classes.py";
|
classFile = "classes.py";
|
||||||
};
|
};
|
||||||
installPhase = ''
|
installPhase = ''
|
||||||
${self'.packages.classgen}/bin/classgen ${self'.packages.inventory-schema}/schema.json b_classes.py
|
${self'.packages.classgen}/bin/classgen ${self'.packages.inventory-schema}/schema.json b_classes.py --stop-at "Service"
|
||||||
file1=$classFile
|
file1=$classFile
|
||||||
file2=b_classes.py
|
file2=b_classes.py
|
||||||
|
|
||||||
|
|||||||
@@ -45,6 +45,6 @@ mkShell {
|
|||||||
|
|
||||||
# Generate classes.py from inventory schema
|
# Generate classes.py from inventory schema
|
||||||
# This file is in .gitignore
|
# This file is in .gitignore
|
||||||
${self'.packages.classgen}/bin/classgen ${self'.packages.inventory-schema}/schema.json $PKG_ROOT/clan_cli/inventory/classes.py
|
${self'.packages.classgen}/bin/classgen ${self'.packages.inventory-schema}/schema.json $PKG_ROOT/clan_cli/inventory/classes.py --stop-at "Service"
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,18 +7,6 @@ import pytest
|
|||||||
# Functions to test
|
# Functions to test
|
||||||
from clan_cli.api import dataclass_to_dict, from_dict
|
from clan_cli.api import dataclass_to_dict, from_dict
|
||||||
from clan_cli.errors import ClanError
|
from clan_cli.errors import ClanError
|
||||||
from clan_cli.inventory import (
|
|
||||||
Inventory,
|
|
||||||
Machine,
|
|
||||||
MachineDeploy,
|
|
||||||
Meta,
|
|
||||||
Service,
|
|
||||||
ServiceBorgbackup,
|
|
||||||
ServiceBorgbackupRole,
|
|
||||||
ServiceBorgbackupRoleClient,
|
|
||||||
ServiceBorgbackupRoleServer,
|
|
||||||
ServiceMeta,
|
|
||||||
)
|
|
||||||
from clan_cli.machines import machines
|
from clan_cli.machines import machines
|
||||||
|
|
||||||
|
|
||||||
@@ -172,43 +160,6 @@ def test_list() -> None:
|
|||||||
assert result == [Name("John"), Name("Sarah")]
|
assert result == [Name("John"), Name("Sarah")]
|
||||||
|
|
||||||
|
|
||||||
def test_deserialize_extensive_inventory() -> None:
|
|
||||||
# TODO: Make this an abstract test, so it doesn't break the test if the inventory changes
|
|
||||||
data = {
|
|
||||||
"meta": {"name": "superclan", "description": "nice clan"},
|
|
||||||
"services": {
|
|
||||||
"borgbackup": {
|
|
||||||
"instance1": {
|
|
||||||
"meta": {
|
|
||||||
"name": "borg1",
|
|
||||||
},
|
|
||||||
"roles": {
|
|
||||||
"client": {},
|
|
||||||
"server": {},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"machines": {"foo": {"name": "foo", "deploy": {}}},
|
|
||||||
}
|
|
||||||
expected = Inventory(
|
|
||||||
meta=Meta(name="superclan", description="nice clan"),
|
|
||||||
services=Service(
|
|
||||||
borgbackup={
|
|
||||||
"instance1": ServiceBorgbackup(
|
|
||||||
meta=ServiceMeta(name="borg1"),
|
|
||||||
roles=ServiceBorgbackupRole(
|
|
||||||
client=ServiceBorgbackupRoleClient(),
|
|
||||||
server=ServiceBorgbackupRoleServer(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
),
|
|
||||||
machines={"foo": Machine(deploy=MachineDeploy(), name="foo")},
|
|
||||||
)
|
|
||||||
assert from_dict(Inventory, data) == expected
|
|
||||||
|
|
||||||
|
|
||||||
def test_alias_field() -> None:
|
def test_alias_field() -> None:
|
||||||
@dataclass
|
@dataclass
|
||||||
class Person:
|
class Person:
|
||||||
|
|||||||
@@ -7,11 +7,6 @@ from clan_cli.clan_uri import FlakeId
|
|||||||
from clan_cli.inventory import (
|
from clan_cli.inventory import (
|
||||||
Machine,
|
Machine,
|
||||||
MachineDeploy,
|
MachineDeploy,
|
||||||
ServiceBorgbackup,
|
|
||||||
ServiceBorgbackupRole,
|
|
||||||
ServiceBorgbackupRoleClient,
|
|
||||||
ServiceBorgbackupRoleServer,
|
|
||||||
ServiceMeta,
|
|
||||||
load_inventory_json,
|
load_inventory_json,
|
||||||
save_inventory,
|
save_inventory,
|
||||||
)
|
)
|
||||||
@@ -67,18 +62,16 @@ def test_add_module_to_inventory(
|
|||||||
|
|
||||||
inventory = load_inventory_json(base_path)
|
inventory = load_inventory_json(base_path)
|
||||||
|
|
||||||
inventory.services.borgbackup = {
|
inventory.services = {
|
||||||
"borg1": ServiceBorgbackup(
|
"borgbackup": {
|
||||||
meta=ServiceMeta(name="borg1"),
|
"borg1": {
|
||||||
roles=ServiceBorgbackupRole(
|
"meta": {"name": "borg1"},
|
||||||
client=ServiceBorgbackupRoleClient(
|
"roles": {
|
||||||
machines=["machine1"],
|
"client": {"machines": ["machine1"]},
|
||||||
),
|
"server": {"machines": ["machine1"]},
|
||||||
server=ServiceBorgbackupRoleServer(
|
},
|
||||||
machines=["machine1"],
|
}
|
||||||
),
|
}
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
save_inventory(inventory, base_path, "Add borgbackup service")
|
save_inventory(inventory, base_path, "Add borgbackup service")
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ def map_json_type(
|
|||||||
|
|
||||||
known_classes = set()
|
known_classes = set()
|
||||||
root_class = "Inventory"
|
root_class = "Inventory"
|
||||||
|
stop_at = None
|
||||||
|
|
||||||
|
|
||||||
def field_def_from_default_type(
|
def field_def_from_default_type(
|
||||||
@@ -198,6 +199,9 @@ def generate_dataclass(schema: dict[str, Any], class_name: str = root_class) ->
|
|||||||
required_fields = []
|
required_fields = []
|
||||||
fields_with_default = []
|
fields_with_default = []
|
||||||
nested_classes: list[str] = []
|
nested_classes: list[str] = []
|
||||||
|
if stop_at and class_name == stop_at:
|
||||||
|
# Skip generating classes below the stop_at property
|
||||||
|
return f"{class_name} = dict[str, Any]"
|
||||||
|
|
||||||
for prop, prop_info in properties.items():
|
for prop, prop_info in properties.items():
|
||||||
field_name = prop.replace("-", "_")
|
field_name = prop.replace("-", "_")
|
||||||
@@ -272,9 +276,6 @@ def generate_dataclass(schema: dict[str, Any], class_name: str = root_class) ->
|
|||||||
field_meta = f"""{{"alias": "{prop}"}}"""
|
field_meta = f"""{{"alias": "{prop}"}}"""
|
||||||
|
|
||||||
finalize_field = partial(get_field_def, field_name, field_meta)
|
finalize_field = partial(get_field_def, field_name, field_meta)
|
||||||
# if class_name == "DyndnsConfig":
|
|
||||||
# if class_name == "ServiceDyndnMachine":
|
|
||||||
# breakpoint()
|
|
||||||
|
|
||||||
if "default" in prop_info or field_name not in prop_info.get("required", []):
|
if "default" in prop_info or field_name not in prop_info.get("required", []):
|
||||||
if "default" in prop_info:
|
if "default" in prop_info:
|
||||||
@@ -334,6 +335,10 @@ def generate_dataclass(schema: dict[str, Any], class_name: str = root_class) ->
|
|||||||
|
|
||||||
def run_gen(args: argparse.Namespace) -> None:
|
def run_gen(args: argparse.Namespace) -> None:
|
||||||
print(f"Converting {args.input} to {args.output}")
|
print(f"Converting {args.input} to {args.output}")
|
||||||
|
if args.stop_at:
|
||||||
|
global stop_at
|
||||||
|
stop_at = args.stop_at
|
||||||
|
|
||||||
dataclass_code = ""
|
dataclass_code = ""
|
||||||
with args.input.open() as f:
|
with args.input.open() as f:
|
||||||
schema = json.load(f)
|
schema = json.load(f)
|
||||||
@@ -358,6 +363,12 @@ def main() -> None:
|
|||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument("input", help="Input JSON schema file", type=Path)
|
parser.add_argument("input", help="Input JSON schema file", type=Path)
|
||||||
parser.add_argument("output", help="Output Python file", type=Path)
|
parser.add_argument("output", help="Output Python file", type=Path)
|
||||||
|
parser.add_argument(
|
||||||
|
"--stop-at",
|
||||||
|
type=str,
|
||||||
|
help="Property name to stop generating classes for. Other classes below that property will be generated",
|
||||||
|
default=None,
|
||||||
|
)
|
||||||
parser.set_defaults(func=run_gen)
|
parser.set_defaults(func=run_gen)
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|||||||
2
pkgs/webview-ui/.gitignore
vendored
2
pkgs/webview-ui/.gitignore
vendored
@@ -1,3 +1,3 @@
|
|||||||
api
|
app/api
|
||||||
|
|
||||||
.vite
|
.vite
|
||||||
31
pkgs/webview-ui/app/src/api/disk.ts
Normal file
31
pkgs/webview-ui/app/src/api/disk.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { get_inventory } from "./inventory";
|
||||||
|
|
||||||
|
export const instance_name = (machine_name: string) =>
|
||||||
|
`${machine_name}-single-disk` as const;
|
||||||
|
|
||||||
|
export async function set_single_disk_id(
|
||||||
|
base_path: string,
|
||||||
|
machine_name: string,
|
||||||
|
disk_id: string,
|
||||||
|
) {
|
||||||
|
const inventory = await get_inventory(base_path);
|
||||||
|
if (!inventory.services) {
|
||||||
|
return new Error("No services found in inventory");
|
||||||
|
}
|
||||||
|
if (!inventory.services["single-disk"]) {
|
||||||
|
inventory.services["single-disk"] = {};
|
||||||
|
}
|
||||||
|
inventory.services["single-disk"][instance_name(machine_name)] = {
|
||||||
|
meta: {
|
||||||
|
name: instance_name(machine_name),
|
||||||
|
},
|
||||||
|
roles: {
|
||||||
|
default: {
|
||||||
|
machines: [machine_name],
|
||||||
|
config: {
|
||||||
|
device: `/dev/disk/by-id/${disk_id}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,11 +1,19 @@
|
|||||||
import schema from "@/api/API.json" assert { type: "json" };
|
import schema from "@/api/API.json" assert { type: "json" };
|
||||||
import { API } from "@/api/API";
|
import { API } from "@/api/API";
|
||||||
import { nanoid } from "nanoid";
|
import { nanoid } from "nanoid";
|
||||||
|
import { Schema as Inventory } from "@/api/Inventory";
|
||||||
|
|
||||||
export type OperationNames = keyof API;
|
export type OperationNames = keyof API;
|
||||||
export type OperationArgs<T extends OperationNames> = API[T]["arguments"];
|
export type OperationArgs<T extends OperationNames> = API[T]["arguments"];
|
||||||
export type OperationResponse<T extends OperationNames> = API[T]["return"];
|
export type OperationResponse<T extends OperationNames> = API[T]["return"];
|
||||||
|
|
||||||
|
export type Services = NonNullable<Inventory["services"]>;
|
||||||
|
export type ServiceNames = keyof Services;
|
||||||
|
export type ClanService<T extends ServiceNames> = Services[T];
|
||||||
|
export type ClanServiceInstance<T extends ServiceNames> = NonNullable<
|
||||||
|
Services[T]
|
||||||
|
>[string];
|
||||||
|
|
||||||
export type SuccessQuery<T extends OperationNames> = Extract<
|
export type SuccessQuery<T extends OperationNames> = Extract<
|
||||||
OperationResponse<T>,
|
OperationResponse<T>,
|
||||||
{ status: "success" }
|
{ status: "success" }
|
||||||
40
pkgs/webview-ui/app/src/api/inventory.ts
Normal file
40
pkgs/webview-ui/app/src/api/inventory.ts
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import { callApi, ClanService, ServiceNames, Services } from ".";
|
||||||
|
import { Schema as Inventory } from "@/api/Inventory";
|
||||||
|
|
||||||
|
export async function get_inventory(base_path: string) {
|
||||||
|
const r = await callApi("get_inventory", {
|
||||||
|
base_path,
|
||||||
|
});
|
||||||
|
if (r.status == "error") {
|
||||||
|
throw new Error("Failed to get inventory");
|
||||||
|
}
|
||||||
|
const inventory: Inventory = r.data;
|
||||||
|
return inventory;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const single_instance_name = <T extends keyof Services>(
|
||||||
|
machine_name: string,
|
||||||
|
service_name: T,
|
||||||
|
) => `${machine_name}_${service_name}_0` as const;
|
||||||
|
|
||||||
|
function get_service<T extends ServiceNames>(base_path: string, service: T) {
|
||||||
|
return callApi("get_inventory", { base_path }).then((r) => {
|
||||||
|
if (r.status == "error") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const inventory: Inventory = r.data;
|
||||||
|
|
||||||
|
const serviceInstance = inventory.services?.[service];
|
||||||
|
return serviceInstance;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function get_single_service<T extends keyof Services>(
|
||||||
|
base_path: string,
|
||||||
|
machine_name: string,
|
||||||
|
service_name: T,
|
||||||
|
) {
|
||||||
|
const instance_key = single_instance_name(machine_name, service_name);
|
||||||
|
const service = await get_service(base_path, "admin");
|
||||||
|
return service?.[instance_key];
|
||||||
|
}
|
||||||
18
pkgs/webview-ui/app/src/api/wifi.ts
Normal file
18
pkgs/webview-ui/app/src/api/wifi.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { callApi } from ".";
|
||||||
|
import { Schema as Inventory } from "@/api/Inventory";
|
||||||
|
|
||||||
|
export const instance_name = (machine_name: string) =>
|
||||||
|
`${machine_name}_wifi_0` as const;
|
||||||
|
|
||||||
|
export async function get_iwd_service(base_path: string, machine_name: string) {
|
||||||
|
const r = await callApi("get_inventory", {
|
||||||
|
base_path,
|
||||||
|
});
|
||||||
|
if (r.status == "error") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const inventory: Inventory = r.data;
|
||||||
|
|
||||||
|
const instance_key = instance_name(machine_name);
|
||||||
|
return inventory.services?.iwd?.[instance_key] || null;
|
||||||
|
}
|
||||||
@@ -1,4 +1,9 @@
|
|||||||
import { callApi, SuccessQuery } from "@/src/api";
|
import {
|
||||||
|
callApi,
|
||||||
|
ClanService,
|
||||||
|
ClanServiceInstance,
|
||||||
|
SuccessQuery,
|
||||||
|
} from "@/src/api";
|
||||||
import { BackButton } from "@/src/components/BackButton";
|
import { BackButton } from "@/src/components/BackButton";
|
||||||
import { useParams } from "@solidjs/router";
|
import { useParams } from "@solidjs/router";
|
||||||
import {
|
import {
|
||||||
@@ -19,6 +24,7 @@ import {
|
|||||||
} from "@modular-forms/solid";
|
} from "@modular-forms/solid";
|
||||||
import { TextInput } from "@/src/components/TextInput";
|
import { TextInput } from "@/src/components/TextInput";
|
||||||
import toast from "solid-toast";
|
import toast from "solid-toast";
|
||||||
|
import { get_single_service } from "@/src/api/inventory";
|
||||||
|
|
||||||
interface AdminModuleFormProps {
|
interface AdminModuleFormProps {
|
||||||
admin: AdminData;
|
admin: AdminData;
|
||||||
@@ -184,19 +190,20 @@ const AdminModuleForm = (props: AdminModuleFormProps) => {
|
|||||||
const handleSubmit = async (values: AdminSettings) => {
|
const handleSubmit = async (values: AdminSettings) => {
|
||||||
console.log("submitting", values, getValues(formStore));
|
console.log("submitting", values, getValues(formStore));
|
||||||
|
|
||||||
const r = await callApi("set_admin_service", {
|
// const r = await callApi("set_admin_service", {
|
||||||
base_url: props.base_url,
|
// base_url: props.base_url,
|
||||||
allowed_keys: values.allowedKeys.reduce(
|
// allowed_keys: values.allowedKeys.reduce(
|
||||||
(acc, curr) => ({ ...acc, [curr.name]: curr.value }),
|
// (acc, curr) => ({ ...acc, [curr.name]: curr.value }),
|
||||||
{},
|
// {}
|
||||||
),
|
// ),
|
||||||
});
|
// });
|
||||||
if (r.status === "success") {
|
// if (r.status === "success") {
|
||||||
toast.success("Successfully updated admin settings");
|
// toast.success("Successfully updated admin settings");
|
||||||
}
|
// }
|
||||||
if (r.status === "error") {
|
// if (r.status === "error") {
|
||||||
toast.error(`Failed to update admin settings: ${r.errors[0].message}`);
|
// toast.error(`Failed to update admin settings: ${r.errors[0].message}`);
|
||||||
}
|
toast.error(`Failed to update admin settings: feature disabled`);
|
||||||
|
// }
|
||||||
queryClient.invalidateQueries({
|
queryClient.invalidateQueries({
|
||||||
queryKey: [props.base_url, "get_admin_service"],
|
queryKey: [props.base_url, "get_admin_service"],
|
||||||
});
|
});
|
||||||
@@ -329,7 +336,7 @@ const AdminModuleForm = (props: AdminModuleFormProps) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type GeneralData = SuccessQuery<"show_clan_meta">["data"];
|
type GeneralData = SuccessQuery<"show_clan_meta">["data"];
|
||||||
type AdminData = SuccessQuery<"get_admin_service">["data"];
|
type AdminData = ClanServiceInstance<"admin">;
|
||||||
|
|
||||||
export const ClanDetails = () => {
|
export const ClanDetails = () => {
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
@@ -347,11 +354,9 @@ export const ClanDetails = () => {
|
|||||||
const adminQuery = createQuery(() => ({
|
const adminQuery = createQuery(() => ({
|
||||||
queryKey: [clan_dir, "get_admin_service"],
|
queryKey: [clan_dir, "get_admin_service"],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const result = await callApi("get_admin_service", {
|
const result = await get_single_service(clan_dir, "", "admin");
|
||||||
base_url: clan_dir,
|
if (!result) throw new Error("Failed to fetch data");
|
||||||
});
|
return result || null;
|
||||||
if (result.status === "error") throw new Error("Failed to fetch data");
|
|
||||||
return result.data || null;
|
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,12 @@
|
|||||||
import { callApi, SuccessData, SuccessQuery } from "@/src/api";
|
import {
|
||||||
|
callApi,
|
||||||
|
ClanService,
|
||||||
|
Services,
|
||||||
|
SuccessData,
|
||||||
|
SuccessQuery,
|
||||||
|
} from "@/src/api";
|
||||||
|
import { set_single_disk_id } from "@/src/api/disk";
|
||||||
|
import { get_iwd_service } from "@/src/api/wifi";
|
||||||
import { activeURI } from "@/src/App";
|
import { activeURI } from "@/src/App";
|
||||||
import { BackButton } from "@/src/components/BackButton";
|
import { BackButton } from "@/src/components/BackButton";
|
||||||
import { FileInput } from "@/src/components/FileInput";
|
import { FileInput } from "@/src/components/FileInput";
|
||||||
@@ -117,17 +125,12 @@ const InstallMachine = (props: InstallMachineProps) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const r = await callApi("set_single_disk_uuid", {
|
const r = await set_single_disk_id(curr_uri, props.name, disk_id);
|
||||||
base_path: curr_uri,
|
if (!r) {
|
||||||
machine_name: props.name,
|
|
||||||
disk_uuid: disk_id,
|
|
||||||
});
|
|
||||||
if (r.status === "error") {
|
|
||||||
toast.error("Failed to set disk");
|
|
||||||
}
|
|
||||||
if (r.status === "success") {
|
|
||||||
toast.success("Disk set successfully");
|
toast.success("Disk set successfully");
|
||||||
setConfirmDisk(true);
|
setConfirmDisk(true);
|
||||||
|
} else {
|
||||||
|
toast.error("Failed to set disk");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -600,7 +603,7 @@ const MachineForm = (props: MachineDetailsProps) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
type WifiData = SuccessData<"get_iwd_service">;
|
type WifiData = ClanService<"iwd">;
|
||||||
|
|
||||||
export const MachineDetails = () => {
|
export const MachineDetails = () => {
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
@@ -629,12 +632,9 @@ export const MachineDetails = () => {
|
|||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const curr = activeURI();
|
const curr = activeURI();
|
||||||
if (curr) {
|
if (curr) {
|
||||||
const result = await callApi("get_iwd_service", {
|
const result = await get_iwd_service(curr, params.id);
|
||||||
base_url: curr,
|
if (!result) throw new Error("Failed to fetch data");
|
||||||
machine_name: params.id,
|
return Object.entries(result?.config?.networks || {}).map(
|
||||||
});
|
|
||||||
if (result.status === "error") throw new Error("Failed to fetch data");
|
|
||||||
return Object.entries(result.data?.config?.networks || {}).map(
|
|
||||||
([name, value]) => ({ name, ssid: value.ssid }),
|
([name, value]) => ({ name, ssid: value.ssid }),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -728,17 +728,17 @@ function WifiModule(props: MachineWifiProps) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
console.log("submitting", values, networks);
|
console.log("submitting", values, networks);
|
||||||
const r = await callApi("set_iwd_service_for_machine", {
|
// const r = await callApi("set_iwd_service_for_machine", {
|
||||||
base_url: props.base_url,
|
// base_url: props.base_url,
|
||||||
machine_name: props.machine_name,
|
// machine_name: props.machine_name,
|
||||||
networks: networks,
|
// networks: networks,
|
||||||
});
|
// });
|
||||||
if (r.status === "error") {
|
// if (r.status === "error") {
|
||||||
toast.error("Failed to set wifi");
|
toast.error("Failed to set wifi. Feature disabled temporarily");
|
||||||
}
|
// }
|
||||||
if (r.status === "success") {
|
// if (r.status === "success") {
|
||||||
toast.success("Wifi set successfully");
|
// toast.success("Wifi set successfully");
|
||||||
}
|
// }
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
Reference in New Issue
Block a user