marriedtermiteblyi commited on
Commit
3a4b37f
·
verified ·
1 Parent(s): 36ce73b

Upload 7 files

Browse files
Files changed (2) hide show
  1. app.py +45 -43
  2. config.py +40 -33
app.py CHANGED
@@ -1,43 +1,45 @@
1
- """
2
- HugPanel — Multi-zone workspace for HuggingFace Spaces.
3
-
4
- Open/Closed Principle: routers are auto-discovered from the routers/ package.
5
- Adding a new feature = adding a new file in routers/ — no changes here.
6
- """
7
-
8
- from contextlib import asynccontextmanager
9
-
10
- import uvicorn
11
- from fastapi import FastAPI
12
- from fastapi.staticfiles import StaticFiles
13
- from fastapi.responses import FileResponse
14
-
15
- from routers import discover_routers
16
- from routers.terminal import active_terminals, kill_terminal
17
-
18
-
19
- @asynccontextmanager
20
- async def lifespan(app: FastAPI):
21
- yield
22
- for zone_name in list(active_terminals.keys()):
23
- kill_terminal(zone_name)
24
-
25
-
26
- app = FastAPI(title="HugPanel", lifespan=lifespan)
27
-
28
- # Auto-register all routers (Open/Closed — new router files are picked up automatically)
29
- for router in discover_routers():
30
- app.include_router(router)
31
-
32
- # ── Static Files & SPA ──────────────────────────────────
33
-
34
- app.mount("/static", StaticFiles(directory="static"), name="static")
35
-
36
-
37
- @app.get("/")
38
- def index():
39
- return FileResponse("static/index.html")
40
-
41
-
42
- if __name__ == "__main__":
43
- uvicorn.run(app, host="0.0.0.0", port=7860)
 
 
 
1
+ """
2
+ HugPanel — Multi-zone workspace for HuggingFace Spaces.
3
+
4
+ Open/Closed Principle: routers are auto-discovered from the routers/ package.
5
+ Adding a new feature = adding a new file in routers/ — no changes here.
6
+ """
7
+
8
+ from contextlib import asynccontextmanager
9
+
10
+ import uvicorn
11
+ from fastapi import FastAPI
12
+ from fastapi.staticfiles import StaticFiles
13
+ from fastapi.responses import FileResponse
14
+
15
+ from routers import discover_routers
16
+ from routers.terminal import active_terminals, kill_terminal
17
+ from routers.proxy import close_proxy_client
18
+
19
+
20
+ @asynccontextmanager
21
+ async def lifespan(app: FastAPI):
22
+ yield
23
+ for zone_name in list(active_terminals.keys()):
24
+ kill_terminal(zone_name)
25
+ await close_proxy_client()
26
+
27
+
28
+ app = FastAPI(title="HugPanel", lifespan=lifespan)
29
+
30
+ # Auto-register all routers (Open/Closed — new router files are picked up automatically)
31
+ for router in discover_routers():
32
+ app.include_router(router)
33
+
34
+ # ── Static Files & SPA ──────────────────────────────────
35
+
36
+ app.mount("/static", StaticFiles(directory="static"), name="static")
37
+
38
+
39
+ @app.get("/")
40
+ def index():
41
+ return FileResponse("static/index.html")
42
+
43
+
44
+ if __name__ == "__main__":
45
+ uvicorn.run(app, host="0.0.0.0", port=7860)
config.py CHANGED
@@ -1,33 +1,40 @@
1
- """
2
- Centralized configuration for HugPanel.
3
-
4
- Single Responsibility: all constants and paths live here.
5
- Dependency Inversion: other modules depend on this abstraction, not on each other.
6
- """
7
-
8
- import os
9
- import re
10
- from pathlib import Path
11
-
12
- # ── Paths ──────────────────────────────────────
13
- DATA_DIR = Path(os.environ.get("DATA_DIR", "/data/zones"))
14
- ZONES_META = DATA_DIR.parent / "zones_meta.json"
15
-
16
- DATA_DIR.mkdir(parents=True, exist_ok=True)
17
-
18
- # ── Validation ─────────────────────────────────
19
- ZONE_NAME_PATTERN = re.compile(r"^[a-zA-Z0-9_-]{1,50}$")
20
-
21
- # ── Port Limits ────────────────────────────────
22
- MIN_PORT = 1024
23
- MAX_PORT = 65535
24
-
25
- # ── Terminal ───────────────────────────────────
26
- SCROLLBACK_SIZE = 128 * 1024 # 128 KB
27
-
28
- # ── Admin API (Cloudflare Worker) ──────────────
29
- ADMIN_API_URL = "https://hugpanel-admin.lab70018.workers.dev"
30
-
31
- # ── Backup (local temp dir) ────────────────────
32
- BACKUP_DIR = DATA_DIR.parent / "backups"
33
- BACKUP_DIR.mkdir(parents=True, exist_ok=True)
 
 
 
 
 
 
 
 
1
+ """
2
+ Centralized configuration for HugPanel.
3
+
4
+ Single Responsibility: all constants and paths live here.
5
+ Dependency Inversion: other modules depend on this abstraction, not on each other.
6
+ """
7
+
8
+ import os
9
+ import re
10
+ from pathlib import Path
11
+
12
+
13
+ def _clean_url(value: str) -> str:
14
+ return value.rstrip("/") if value else ""
15
+
16
+
17
+ # ── Paths ──────────────────────────────────────
18
+ DATA_DIR = Path(os.environ.get("DATA_DIR", "/data/zones"))
19
+ ZONES_META = DATA_DIR.parent / "zones_meta.json"
20
+
21
+ DATA_DIR.mkdir(parents=True, exist_ok=True)
22
+
23
+ # ── Validation ─────────────────────────────────
24
+ ZONE_NAME_PATTERN = re.compile(r"^[a-zA-Z0-9_-]{1,50}$")
25
+
26
+ # ── Port Limits ────────────────────────────────
27
+ MIN_PORT = 1024
28
+ MAX_PORT = 65535
29
+
30
+ # ── Terminal ───────────────────────────────────
31
+ SCROLLBACK_SIZE = 128 * 1024 # 128 KB
32
+
33
+ # ── Admin API (Cloudflare Worker) ──────────────
34
+ ADMIN_API_URL = _clean_url(
35
+ os.environ.get("ADMIN_API_URL", "https://hugpanel-admin.lab70018.workers.dev")
36
+ )
37
+
38
+ # ── Backup (local temp dir) ────────────────────
39
+ BACKUP_DIR = DATA_DIR.parent / "backups"
40
+ BACKUP_DIR.mkdir(parents=True, exist_ok=True)