Skip to content

Commit

Permalink
new: profile command and some cleaning
Browse files Browse the repository at this point in the history
  • Loading branch information
evilsocket committed Oct 16, 2024
1 parent 10f6d3e commit 43c522e
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 41 deletions.
2 changes: 1 addition & 1 deletion dreadnode_cli/agent/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from dreadnode_cli.agent.config import AgentConfig
from dreadnode_cli.utils import copy_template

cli = typer.Typer()
cli = typer.Typer(no_args_is_help=True, help="TODO")


class Template(str, enum.Enum):
Expand Down
2 changes: 1 addition & 1 deletion dreadnode_cli/challenge/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import dreadnode_cli.api as api
from dreadnode_cli.config import UserConfig

cli = typer.Typer()
cli = typer.Typer(no_args_is_help=True, help="Visualize and interact with the Crucible challenges")


def format_difficulty(difficulty: str) -> str:
Expand Down
2 changes: 0 additions & 2 deletions dreadnode_cli/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,6 @@ def read(cls) -> "UserConfig":
def write(self) -> None:
"""Write the user configuration to the file system."""

self._update_active()

if not USER_CONFIG_PATH.parent.exists():
print(f":rocket: creating user configuration directory: {USER_CONFIG_PATH.parent}")
USER_CONFIG_PATH.parent.mkdir(parents=True)
Expand Down
86 changes: 49 additions & 37 deletions dreadnode_cli/profile/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,60 +4,72 @@
from rich import box, print
from rich.table import Table

from dreadnode_cli import utils
from dreadnode_cli.api import Token
from dreadnode_cli.config import UserConfig

cli = typer.Typer()
cli = typer.Typer(no_args_is_help=True, help="Manage server profiles")


@cli.command(help="List all server profiles")
def list() -> None:
config = UserConfig.read()
try:
config = UserConfig.read()

table = Table(box=box.ROUNDED)
table.add_column("Profile")
table.add_column("URL")
table.add_column("Username")
table.add_column("Created At")
table = Table(box=box.ROUNDED)
table.add_column("Profile")
table.add_column("URL")
table.add_column("Expires At")

current = config.active_server
current = config.active_server

for profile, server in config.servers.items():
active_profile = server == current
table.add_row(
f"[bold]{profile}*[/]" if active_profile else profile,
server.url,
server.username,
server.created_at.strftime("%Y-%m-%d %H:%M:%S"),
style="cyan" if active_profile else None,
)
for profile, server in config.servers.items():
active_profile = server == current
refresh_token = Token(server.refresh_token)

print(table)
table.add_row(
f"[bold]{profile}*[/]" if active_profile else profile,
server.url,
"[red]expired[/]"
if refresh_token.is_expired()
else f'{refresh_token.expires_at.strftime("%Y-%m-%d %H:%M:%S")} ({utils.time_to(refresh_token.expires_at)})',
style="cyan" if active_profile else None,
)

print(table)
except Exception as e:
print(f":cross_mark: {e}")


@cli.command(help="Set the active server profile")
def switch(profile: t.Annotated[str, typer.Argument(help="Profile to switch to")]) -> None:
config = UserConfig.read()
if profile not in config.servers:
print(f":exclamation: Profile [bold]{profile}[/] is not configured")
return
try:
config = UserConfig.read()
if profile not in config.servers:
print(f":exclamation: Profile [bold]{profile}[/] is not configured")
return

config.active = profile
config.write()
config.active = profile
config.write()

print()
print(f":white_check_mark: Switched to [bold]{profile}[/]")
print(f"|- user: [bold]{config.servers[profile].username}[/]")
print(f"|- url: [bold]{config.servers[profile].url}[/]")
print()
print(f":white_check_mark: Switched to [bold]{profile}[/]")
print(f"|- url: [bold]{config.servers[profile].url}[/]")
except Exception as e:
print(f":cross_mark: {e}")


@cli.command(help="Remove a server profile")
def forget(profile: t.Annotated[str, typer.Argument(help="Profile of the server to remove")]) -> None:
config = UserConfig.read()
if profile not in config.servers:
print(f":exclamation: Profile [bold]{profile}[/] is not configured")
return

config.remove_profile_config(profile).write()

print()
print(f":axe: Forgot about [bold]{profile}[/]")
try:
config = UserConfig.read()
if profile not in config.servers:
print(f":exclamation: Profile [bold]{profile}[/] is not configured")
return

config.remove_profile_config(profile).write()

print()
print(f":axe: Forgot about [bold]{profile}[/]")
except Exception as e:
print(f":cross_mark: {e}")
26 changes: 26 additions & 0 deletions dreadnode_cli/utils.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,36 @@
import pathlib
import typing as t
from datetime import datetime

from jinja2 import Environment, FileSystemLoader
from rich.prompt import Prompt


def time_to(future_datetime: datetime) -> str:
"""Get a string describing the time difference between a future datetime and now."""

now = datetime.now()
time_difference = future_datetime - now

days = time_difference.days
seconds = time_difference.seconds
hours = seconds // 3600
minutes = (seconds % 3600) // 60
seconds = seconds % 60

result = []
if days > 0:
result.append(f"{days} days")
if hours > 0:
result.append(f"{hours} hours")
if minutes > 0:
result.append(f"{minutes} minutes")
if seconds > 0:
result.append(f"{seconds} seconds")

return ", ".join(result) if result else "Just now"


def copy_template(src: pathlib.Path, dest: pathlib.Path, context: dict[str, t.Any]) -> None:
env = Environment(loader=FileSystemLoader(src))

Expand Down

0 comments on commit 43c522e

Please sign in to comment.