doorin-cli 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- doorin_cli-0.1.0/.gitignore +162 -0
- doorin_cli-0.1.0/PKG-INFO +8 -0
- doorin_cli-0.1.0/README.md +0 -0
- doorin_cli-0.1.0/doorin_cli/__init__.py +1 -0
- doorin_cli-0.1.0/doorin_cli/commands/__init__.py +0 -0
- doorin_cli-0.1.0/doorin_cli/commands/config.py +24 -0
- doorin_cli-0.1.0/doorin_cli/commands/routes.py +40 -0
- doorin_cli-0.1.0/doorin_cli/commands/services.py +38 -0
- doorin_cli-0.1.0/doorin_cli/commands/system.py +44 -0
- doorin_cli-0.1.0/doorin_cli/common.py +17 -0
- doorin_cli-0.1.0/doorin_cli/config_utils.py +20 -0
- doorin_cli-0.1.0/doorin_cli/main.py +25 -0
- doorin_cli-0.1.0/pyproject.toml +18 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
*.http
|
|
9
|
+
|
|
10
|
+
# Distribution / packaging
|
|
11
|
+
.Python
|
|
12
|
+
build/
|
|
13
|
+
develop-eggs/
|
|
14
|
+
dist/
|
|
15
|
+
downloads/
|
|
16
|
+
eggs/
|
|
17
|
+
.eggs/
|
|
18
|
+
lib/
|
|
19
|
+
lib64/
|
|
20
|
+
parts/
|
|
21
|
+
sdist/
|
|
22
|
+
var/
|
|
23
|
+
wheels/
|
|
24
|
+
pip-wheel-metadata/
|
|
25
|
+
share/python-wheels/
|
|
26
|
+
*.egg-info/
|
|
27
|
+
.installed.cfg
|
|
28
|
+
*.egg
|
|
29
|
+
MANIFEST
|
|
30
|
+
|
|
31
|
+
# PyInstaller
|
|
32
|
+
# Usually these files are written by a python script from a template
|
|
33
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
34
|
+
*.manifest
|
|
35
|
+
*.spec
|
|
36
|
+
|
|
37
|
+
# Installer logs
|
|
38
|
+
pip-log.txt
|
|
39
|
+
pip-delete-this-directory.txt
|
|
40
|
+
|
|
41
|
+
# Unit test / coverage reports
|
|
42
|
+
htmlcov/
|
|
43
|
+
.tox/
|
|
44
|
+
.nox/
|
|
45
|
+
.coverage
|
|
46
|
+
.coverage.*
|
|
47
|
+
.cache
|
|
48
|
+
nosetests.xml
|
|
49
|
+
coverage.xml
|
|
50
|
+
*.cover
|
|
51
|
+
*.py,cover
|
|
52
|
+
.hypothesis/
|
|
53
|
+
.pytest_cache/
|
|
54
|
+
|
|
55
|
+
# Translations
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
*.sqlite3
|
|
64
|
+
|
|
65
|
+
# Flask stuff:
|
|
66
|
+
instance/
|
|
67
|
+
.webassets-cache
|
|
68
|
+
|
|
69
|
+
# Scrapy stuff:
|
|
70
|
+
.scrapy
|
|
71
|
+
|
|
72
|
+
# Sphinx documentation
|
|
73
|
+
docs/_build/
|
|
74
|
+
|
|
75
|
+
# PyBuilder
|
|
76
|
+
target/
|
|
77
|
+
|
|
78
|
+
# Jupyter Notebook
|
|
79
|
+
.ipynb_checkpoints
|
|
80
|
+
|
|
81
|
+
# IPython
|
|
82
|
+
profile_default/
|
|
83
|
+
ipython_config.py
|
|
84
|
+
|
|
85
|
+
# pyenv
|
|
86
|
+
.python-version
|
|
87
|
+
|
|
88
|
+
# pipenv
|
|
89
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
90
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
91
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
92
|
+
# install all needed dependencies.
|
|
93
|
+
#Pipfile.lock
|
|
94
|
+
|
|
95
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
|
96
|
+
__pypackages__/
|
|
97
|
+
|
|
98
|
+
# SageMath parsed files
|
|
99
|
+
*.sage.py
|
|
100
|
+
|
|
101
|
+
# Environments
|
|
102
|
+
.env
|
|
103
|
+
.venv
|
|
104
|
+
.venv-speech/
|
|
105
|
+
.venv-cli/
|
|
106
|
+
.venv-toolbox/
|
|
107
|
+
.venv-rag/
|
|
108
|
+
.venv-agents/
|
|
109
|
+
.venv-sdk/
|
|
110
|
+
env/
|
|
111
|
+
venv/
|
|
112
|
+
ENV/
|
|
113
|
+
env.bak/
|
|
114
|
+
venv.bak/
|
|
115
|
+
staticfiles/
|
|
116
|
+
mediafiles/
|
|
117
|
+
|
|
118
|
+
# Spyder project settings
|
|
119
|
+
.spyderproject
|
|
120
|
+
.spyproject
|
|
121
|
+
|
|
122
|
+
# Rope project settings
|
|
123
|
+
.ropeproject
|
|
124
|
+
|
|
125
|
+
# mkdocs documentation
|
|
126
|
+
/site
|
|
127
|
+
|
|
128
|
+
# mypy
|
|
129
|
+
.mypy_cache/
|
|
130
|
+
.dmypy.json
|
|
131
|
+
dmypy.json
|
|
132
|
+
|
|
133
|
+
# Pyre type checker
|
|
134
|
+
.pyre/
|
|
135
|
+
|
|
136
|
+
# EDITOR
|
|
137
|
+
.idea
|
|
138
|
+
|
|
139
|
+
# NODEJS
|
|
140
|
+
node_modules/
|
|
141
|
+
|
|
142
|
+
# LOCAL ONLY
|
|
143
|
+
localonly/
|
|
144
|
+
|
|
145
|
+
# Whoosh index
|
|
146
|
+
whoosh/
|
|
147
|
+
whoosh_index/
|
|
148
|
+
.whoosh/
|
|
149
|
+
.whoosh_index/
|
|
150
|
+
|
|
151
|
+
export/
|
|
152
|
+
.export/
|
|
153
|
+
|
|
154
|
+
*.identifier
|
|
155
|
+
staticfiles/
|
|
156
|
+
redis/
|
|
157
|
+
minio/
|
|
158
|
+
postgresql/
|
|
159
|
+
notebooks/
|
|
160
|
+
volumes/
|
|
161
|
+
.artifacts/
|
|
162
|
+
.env
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Doorin Gateway Standalone CLI
|
|
File without changes
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import typer
|
|
2
|
+
from doorin_cli.common import console, DEFAULT_URL, DEFAULT_KEY
|
|
3
|
+
from doorin_cli.config_utils import save_config
|
|
4
|
+
|
|
5
|
+
app = typer.Typer(help="Manage CLI configuration", rich_markup_mode="rich")
|
|
6
|
+
|
|
7
|
+
@app.callback(invoke_without_command=True)
|
|
8
|
+
def config_main(ctx: typer.Context):
|
|
9
|
+
"""
|
|
10
|
+
[bold green]Configure Doorin CLI settings.[/bold green]
|
|
11
|
+
Run without arguments to start interactive setup.
|
|
12
|
+
"""
|
|
13
|
+
if ctx.invoked_subcommand is None:
|
|
14
|
+
url = typer.prompt("Doorin URL", default=DEFAULT_URL)
|
|
15
|
+
key = typer.prompt("Admin API Key", default=DEFAULT_KEY, hide_input=True)
|
|
16
|
+
|
|
17
|
+
save_config(url, key)
|
|
18
|
+
console.print(f"[green]Configuration saved to ~/.doorin_cli.json[/green]")
|
|
19
|
+
|
|
20
|
+
@app.command()
|
|
21
|
+
def show():
|
|
22
|
+
"""[bold blue]Show current CLI configuration.[/bold blue]"""
|
|
23
|
+
console.print(f"[bold]URL:[/bold] {DEFAULT_URL}")
|
|
24
|
+
console.print(f"[bold]Key:[/bold] {'*' * len(DEFAULT_KEY) if DEFAULT_KEY else 'Not set'}")
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import typer
|
|
2
|
+
from rich.table import Table
|
|
3
|
+
from doorin_cli.common import get_client, console, DEFAULT_URL, DEFAULT_KEY
|
|
4
|
+
|
|
5
|
+
app = typer.Typer(help="Manage Doorin Gateway routes", rich_markup_mode="rich")
|
|
6
|
+
|
|
7
|
+
@app.command("list")
|
|
8
|
+
def list_routes(
|
|
9
|
+
url: str = typer.Option(DEFAULT_URL, "--url", "-u", help="Doorin Gateway URL"),
|
|
10
|
+
key: str = typer.Option(DEFAULT_KEY, "--key", "-k", help="Admin API Key")
|
|
11
|
+
):
|
|
12
|
+
"""
|
|
13
|
+
[bold cyan]List all routes registered in the gateway.[/bold cyan]
|
|
14
|
+
Displays path, service, methods, and target URL for all active mappings.
|
|
15
|
+
"""
|
|
16
|
+
try:
|
|
17
|
+
with get_client(url, key) as client:
|
|
18
|
+
resp = client.get("/admin/routes")
|
|
19
|
+
if resp.status_code == 403:
|
|
20
|
+
console.print("[red]Error: Unauthorized. Please check your admin API key.[/red]")
|
|
21
|
+
return
|
|
22
|
+
|
|
23
|
+
routes = resp.json()
|
|
24
|
+
table = Table(title="Doorin Gateway Routes")
|
|
25
|
+
table.add_column("Path", style="cyan")
|
|
26
|
+
table.add_column("Service ID", style="magenta")
|
|
27
|
+
table.add_column("Methods", style="green")
|
|
28
|
+
table.add_column("Target URL", style="blue")
|
|
29
|
+
|
|
30
|
+
for r in routes:
|
|
31
|
+
methods = ", ".join(r["config"].get("methods", []))
|
|
32
|
+
table.add_row(
|
|
33
|
+
r["path"],
|
|
34
|
+
r["service_id"] or "N/A",
|
|
35
|
+
methods,
|
|
36
|
+
r.get("target_url", "N/A")
|
|
37
|
+
)
|
|
38
|
+
console.print(table)
|
|
39
|
+
except Exception as e:
|
|
40
|
+
console.print(f"[red]Error:[/red] {e}")
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import typer
|
|
2
|
+
from rich.table import Table
|
|
3
|
+
from doorin_cli.common import get_client, console, DEFAULT_URL, DEFAULT_KEY
|
|
4
|
+
|
|
5
|
+
app = typer.Typer(help="Manage Doorin Gateway services", rich_markup_mode="rich")
|
|
6
|
+
|
|
7
|
+
@app.command("list")
|
|
8
|
+
def list_services(
|
|
9
|
+
url: str = typer.Option(DEFAULT_URL, "--url", "-u", help="Doorin Gateway URL"),
|
|
10
|
+
key: str = typer.Option(DEFAULT_KEY, "--key", "-k", help="Admin API Key")
|
|
11
|
+
):
|
|
12
|
+
"""
|
|
13
|
+
[bold cyan]List all backend services configured in the gateway.[/bold cyan]
|
|
14
|
+
Displays service ID, base URL, and the default auth provider.
|
|
15
|
+
"""
|
|
16
|
+
try:
|
|
17
|
+
with get_client(url, key) as client:
|
|
18
|
+
resp = client.get("/admin/services")
|
|
19
|
+
if resp.status_code == 403:
|
|
20
|
+
console.print("[red]Error: Unauthorized.[/red]")
|
|
21
|
+
return
|
|
22
|
+
|
|
23
|
+
services = resp.json()
|
|
24
|
+
table = Table(title="Doorin Gateway Services")
|
|
25
|
+
table.add_column("Service ID", style="cyan")
|
|
26
|
+
table.add_column("Base URL", style="green")
|
|
27
|
+
table.add_column("Auth Provider", style="magenta")
|
|
28
|
+
|
|
29
|
+
for s in services:
|
|
30
|
+
conf = s["config"]
|
|
31
|
+
table.add_row(
|
|
32
|
+
s["id"],
|
|
33
|
+
conf.get("base_url", "N/A"),
|
|
34
|
+
conf.get("default_auth_provider", "api_key")
|
|
35
|
+
)
|
|
36
|
+
console.print(table)
|
|
37
|
+
except Exception as e:
|
|
38
|
+
console.print(f"[red]Error:[/red] {e}")
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import typer
|
|
2
|
+
from rich import print as rprint
|
|
3
|
+
from doorin_cli.common import get_client, DEFAULT_URL, DEFAULT_KEY
|
|
4
|
+
|
|
5
|
+
app = typer.Typer(help="Manage Doorin Gateway system", rich_markup_mode="rich")
|
|
6
|
+
|
|
7
|
+
@app.command()
|
|
8
|
+
def status(
|
|
9
|
+
url: str = typer.Option(DEFAULT_URL, "--url", "-u", help="Doorin Gateway URL"),
|
|
10
|
+
key: str = typer.Option(DEFAULT_KEY, "--key", "-k", help="Admin API Key")
|
|
11
|
+
):
|
|
12
|
+
"""
|
|
13
|
+
[bold green]Check gateway health status.[/bold green]
|
|
14
|
+
Connects to the /health endpoint to verify the gateway is responding.
|
|
15
|
+
"""
|
|
16
|
+
try:
|
|
17
|
+
with get_client(url, key) as client:
|
|
18
|
+
resp = client.get("/health")
|
|
19
|
+
if resp.status_code == 200:
|
|
20
|
+
rprint(f"[bold green]✓[/bold green] Gateway at {url} is [green]healthy[/green]")
|
|
21
|
+
rprint(f"Response: {resp.json()}")
|
|
22
|
+
else:
|
|
23
|
+
rprint(f"[bold red]✗[/bold red] Gateway at {url} returned {resp.status_code}")
|
|
24
|
+
except Exception as e:
|
|
25
|
+
rprint(f"[bold red]Error connecting to {url}:[/bold red] {e}")
|
|
26
|
+
|
|
27
|
+
@app.command()
|
|
28
|
+
def reload(
|
|
29
|
+
url: str = typer.Option(DEFAULT_URL, "--url", "-u", help="Doorin Gateway URL"),
|
|
30
|
+
key: str = typer.Option(DEFAULT_KEY, "--key", "-k", help="Admin API Key")
|
|
31
|
+
):
|
|
32
|
+
"""
|
|
33
|
+
[bold yellow]Trigger a full configuration reload.[/bold yellow]
|
|
34
|
+
Tells the gateway to re-scan its YAML files and refresh the storage.
|
|
35
|
+
"""
|
|
36
|
+
try:
|
|
37
|
+
with get_client(url, key) as client:
|
|
38
|
+
resp = client.post("/admin/reload")
|
|
39
|
+
if resp.status_code == 200:
|
|
40
|
+
rprint("[bold green]✓ Configuration reloaded successfully![/bold green]")
|
|
41
|
+
else:
|
|
42
|
+
rprint(f"[bold red]✗ Failed to reload:[/bold red] {resp.text}")
|
|
43
|
+
except Exception as e:
|
|
44
|
+
rprint(f"[red]Error:[/red] {e}")
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import httpx
|
|
3
|
+
from rich.console import Console
|
|
4
|
+
|
|
5
|
+
from doorin_cli.config_utils import load_config
|
|
6
|
+
|
|
7
|
+
console = Console()
|
|
8
|
+
|
|
9
|
+
# Load configuration (Priority: Env > Config File > Default)
|
|
10
|
+
_config = load_config()
|
|
11
|
+
DEFAULT_URL = os.getenv("DOORIN_URL", _config.get("url", "http://localhost:8000"))
|
|
12
|
+
DEFAULT_KEY = os.getenv("DOORIN_ADMIN_KEY", _config.get("key", ""))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def get_client(url: str, key: str):
|
|
16
|
+
headers = {"X-Admin-Key": key}
|
|
17
|
+
return httpx.Client(base_url=url, headers=headers, timeout=10.0)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
CONFIG_PATH = Path.home() / ".doorin_cli.json"
|
|
6
|
+
|
|
7
|
+
def save_config(url: str, key: str):
|
|
8
|
+
config = {
|
|
9
|
+
"url": url,
|
|
10
|
+
"key": key
|
|
11
|
+
}
|
|
12
|
+
CONFIG_PATH.write_text(json.dumps(config, indent=2))
|
|
13
|
+
|
|
14
|
+
def load_config():
|
|
15
|
+
if CONFIG_PATH.exists():
|
|
16
|
+
try:
|
|
17
|
+
return json.loads(CONFIG_PATH.read_text())
|
|
18
|
+
except Exception:
|
|
19
|
+
return {}
|
|
20
|
+
return {}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import typer
|
|
2
|
+
from doorin_cli.commands import routes, services, system, config
|
|
3
|
+
|
|
4
|
+
app = typer.Typer(
|
|
5
|
+
name="doorin-cli",
|
|
6
|
+
help="[bold cyan]Doorin Gateway Admin CLI[/bold cyan] 🚪",
|
|
7
|
+
add_completion=False,
|
|
8
|
+
rich_markup_mode="rich"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
# Register sub-commands
|
|
12
|
+
app.add_typer(routes.app, name="routes", help="🔍 Inspect registered routes")
|
|
13
|
+
app.add_typer(services.app, name="services", help="🛠️ Manage backend services")
|
|
14
|
+
app.add_typer(system.app, name="system", help="⚙️ Gateway system operations")
|
|
15
|
+
app.add_typer(config.app, name="config", help="📝 Configure CLI settings (URL, API Key)")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# Also keep system commands at top level for convenience if desired,
|
|
20
|
+
# or just use the grouped ones. Let's keep status and reload at top level too.
|
|
21
|
+
app.command()(system.status)
|
|
22
|
+
app.command()(system.reload)
|
|
23
|
+
|
|
24
|
+
if __name__ == "__main__":
|
|
25
|
+
app()
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "doorin-cli"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Standalone Doorin Gateway Admin CLI"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.12"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"httpx>=0.27.0",
|
|
9
|
+
"typer>=0.26.7",
|
|
10
|
+
"rich>=15.0.0",
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
[project.scripts]
|
|
14
|
+
doorin-cli = "doorin_cli.main:app"
|
|
15
|
+
|
|
16
|
+
[build-system]
|
|
17
|
+
requires = ["hatchling"]
|
|
18
|
+
build-backend = "hatchling.build"
|