web feature
This commit is contained in:
parent
091f4d53e9
commit
11cceb4c30
3 changed files with 53 additions and 22 deletions
|
|
@ -126,7 +126,7 @@ You can override which binary is used for the Linux side by setting `DOS_LINUX_S
|
||||||
| `DOS_SSH_PORT` | `2222` | Host port forwarded to container port 22. |
|
| `DOS_SSH_PORT` | `2222` | Host port forwarded to container port 22. |
|
||||||
| `DOS_TELNET_PORT` | `2323` | Host port forwarded to container port 23. |
|
| `DOS_TELNET_PORT` | `2323` | Host port forwarded to container port 23. |
|
||||||
| `DOS_HTTP_PORT` | `8080` | Host port forwarded to the web console. |
|
| `DOS_HTTP_PORT` | `8080` | Host port forwarded to the web console. |
|
||||||
| `DOS_HTTP_FONT_PATH` | `/usr/local/share/dos-httpd/WebPlus_IBM_VGA_8x16.woff` | Override the font served to the web console. |
|
| `DOS_HTTP_CRT` | `1` | Toggle the CRT styling applied to the web console (`0` to disable). |
|
||||||
| `ENABLE_TELNET` | `1` | Toggle BusyBox telnetd. |
|
| `ENABLE_TELNET` | `1` | Toggle BusyBox telnetd. |
|
||||||
| `ENABLE_HTTP_CONSOLE` | `1` | Launch the built-in web terminal. |
|
| `ENABLE_HTTP_CONSOLE` | `1` | Launch the built-in web terminal. |
|
||||||
| `TELNET_PORT` | `23` | Port inside the container where telnetd listens. |
|
| `TELNET_PORT` | `23` | Port inside the container where telnetd listens. |
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,18 @@ from aiohttp import web, WSMsgType
|
||||||
|
|
||||||
LOG = logging.getLogger("dos_httpd")
|
LOG = logging.getLogger("dos_httpd")
|
||||||
|
|
||||||
|
|
||||||
|
def _env_bool(name: str, default: bool) -> bool:
|
||||||
|
value = os.environ.get(name)
|
||||||
|
if value is None:
|
||||||
|
return default
|
||||||
|
value = value.strip().lower()
|
||||||
|
if value in {"1", "true", "yes", "on"}:
|
||||||
|
return True
|
||||||
|
if value in {"0", "false", "no", "off"}:
|
||||||
|
return False
|
||||||
|
return default
|
||||||
|
|
||||||
HTML_PAGE = """<!DOCTYPE html>
|
HTML_PAGE = """<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
|
|
@ -76,7 +88,7 @@ HTML_PAGE = """<!DOCTYPE html>
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
.crt-display {
|
.crt-display {
|
||||||
filter: saturate(1.1) contrast(1.12) brightness(0.85);
|
filter: saturate(1.05) contrast(1.08) brightness(0.95);
|
||||||
--crt-line-step: 1;
|
--crt-line-step: 1;
|
||||||
}
|
}
|
||||||
.crt-display::before {
|
.crt-display::before {
|
||||||
|
|
@ -87,15 +99,15 @@ HTML_PAGE = """<!DOCTYPE html>
|
||||||
background-image:
|
background-image:
|
||||||
repeating-linear-gradient(
|
repeating-linear-gradient(
|
||||||
to bottom,
|
to bottom,
|
||||||
rgba(255, 255, 255, 0.1) 0px,
|
rgba(255, 255, 255, 0.06) 0px,
|
||||||
rgba(255, 255, 255, 0.1) calc(var(--crt-line-step) * 1px),
|
rgba(255, 255, 255, 0.06) calc(var(--crt-line-step) * 1px),
|
||||||
rgba(0, 0, 0, 0) calc(var(--crt-line-step) * 1px),
|
rgba(0, 0, 0, 0) calc(var(--crt-line-step) * 1px),
|
||||||
rgba(0, 0, 0, 0) calc(var(--crt-line-step) * 2px)
|
rgba(0, 0, 0, 0) calc(var(--crt-line-step) * 2px)
|
||||||
),
|
),
|
||||||
repeating-linear-gradient(
|
repeating-linear-gradient(
|
||||||
to right,
|
to right,
|
||||||
rgba(255, 255, 255, 0.04) 0px,
|
rgba(255, 255, 255, 0.025) 0px,
|
||||||
rgba(255, 255, 255, 0.04) calc(var(--crt-line-step) * 1px),
|
rgba(255, 255, 255, 0.025) calc(var(--crt-line-step) * 1px),
|
||||||
rgba(0, 0, 0, 0) calc(var(--crt-line-step) * 1px),
|
rgba(0, 0, 0, 0) calc(var(--crt-line-step) * 1px),
|
||||||
rgba(0, 0, 0, 0) calc(var(--crt-line-step) * 2px)
|
rgba(0, 0, 0, 0) calc(var(--crt-line-step) * 2px)
|
||||||
);
|
);
|
||||||
|
|
@ -152,6 +164,7 @@ HTML_PAGE = """<!DOCTYPE html>
|
||||||
|
|
||||||
<script type="module">
|
<script type="module">
|
||||||
import { Terminal } from "https://cdn.jsdelivr.net/npm/xterm@5.3.0/+esm";
|
import { Terminal } from "https://cdn.jsdelivr.net/npm/xterm@5.3.0/+esm";
|
||||||
|
const CRT_ENABLED = __CRT_ENABLED__;
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const COLS = 80;
|
const COLS = 80;
|
||||||
|
|
@ -159,10 +172,15 @@ HTML_PAGE = """<!DOCTYPE html>
|
||||||
const CHAR_HEIGHT = 16;
|
const CHAR_HEIGHT = 16;
|
||||||
const VIEWPORT_WIDTH = 640;
|
const VIEWPORT_WIDTH = 640;
|
||||||
const VIEWPORT_HEIGHT = 400;
|
const VIEWPORT_HEIGHT = 400;
|
||||||
const BASE_VERTICAL_STRETCH = 1.2;
|
const BASE_VERTICAL_STRETCH = CRT_ENABLED ? 1.2 : 1;
|
||||||
const TARGET_ROW_HEIGHT = VIEWPORT_HEIGHT / ROWS;
|
const TARGET_ROW_HEIGHT = VIEWPORT_HEIGHT / ROWS;
|
||||||
|
|
||||||
const terminalHost = document.getElementById("terminal");
|
const terminalHost = document.getElementById("terminal");
|
||||||
|
if (CRT_ENABLED) {
|
||||||
|
terminalHost.classList.add("crt-display");
|
||||||
|
} else {
|
||||||
|
terminalHost.classList.remove("crt-display");
|
||||||
|
}
|
||||||
|
|
||||||
async function waitForDosvgaFont() {
|
async function waitForDosvgaFont() {
|
||||||
if (!document.fonts || typeof document.fonts.load !== "function") {
|
if (!document.fonts || typeof document.fonts.load !== "function") {
|
||||||
|
|
@ -200,7 +218,6 @@ HTML_PAGE = """<!DOCTYPE html>
|
||||||
if (screen) {
|
if (screen) {
|
||||||
screen.style.width = `${VIEWPORT_WIDTH}px`;
|
screen.style.width = `${VIEWPORT_WIDTH}px`;
|
||||||
screen.style.height = `${VIEWPORT_HEIGHT}px`;
|
screen.style.height = `${VIEWPORT_HEIGHT}px`;
|
||||||
screen.classList.add("crt-display");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const rows = terminalHost.querySelector(".xterm-rows");
|
const rows = terminalHost.querySelector(".xterm-rows");
|
||||||
|
|
@ -228,8 +245,11 @@ HTML_PAGE = """<!DOCTYPE html>
|
||||||
const scaleForWidth = maxWidth / rect.width;
|
const scaleForWidth = maxWidth / rect.width;
|
||||||
const scaleForHeight = maxHeight / rect.height;
|
const scaleForHeight = maxHeight / rect.height;
|
||||||
const uniformScale = Math.min(scaleForWidth, scaleForHeight);
|
const uniformScale = Math.min(scaleForWidth, scaleForHeight);
|
||||||
|
if (CRT_ENABLED) {
|
||||||
terminalHost.style.setProperty('--crt-line-step', (1 / uniformScale).toString());
|
terminalHost.style.setProperty('--crt-line-step', (1 / uniformScale).toString());
|
||||||
|
} else {
|
||||||
|
terminalHost.style.removeProperty('--crt-line-step');
|
||||||
|
}
|
||||||
terminalHost.style.transform = `scale(${uniformScale}) scaleY(${BASE_VERTICAL_STRETCH})`;
|
terminalHost.style.transform = `scale(${uniformScale}) scaleY(${BASE_VERTICAL_STRETCH})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -490,7 +510,9 @@ class ShellSession:
|
||||||
|
|
||||||
|
|
||||||
async def index_handler(_request: web.Request) -> web.StreamResponse:
|
async def index_handler(_request: web.Request) -> web.StreamResponse:
|
||||||
return web.Response(text=HTML_PAGE, content_type="text/html", headers={"Cache-Control": "no-store"})
|
cfg: "AppConfig" = _request.app["config"]
|
||||||
|
page = HTML_PAGE.replace("__CRT_ENABLED__", "true" if cfg.crt_effect else "false")
|
||||||
|
return web.Response(text=page, content_type="text/html", headers={"Cache-Control": "no-store"})
|
||||||
|
|
||||||
|
|
||||||
async def health_handler(_request: web.Request) -> web.Response:
|
async def health_handler(_request: web.Request) -> web.Response:
|
||||||
|
|
@ -499,7 +521,7 @@ async def health_handler(_request: web.Request) -> web.Response:
|
||||||
|
|
||||||
async def font_handler(request: web.Request) -> web.StreamResponse:
|
async def font_handler(request: web.Request) -> web.StreamResponse:
|
||||||
cfg: "AppConfig" = request.app["config"]
|
cfg: "AppConfig" = request.app["config"]
|
||||||
if not cfg.font_path or not os.path.exists(cfg.font_path):
|
if not os.path.exists(cfg.font_path):
|
||||||
raise web.HTTPNotFound()
|
raise web.HTTPNotFound()
|
||||||
return web.FileResponse(path=cfg.font_path, headers={"Cache-Control": "public, max-age=86400"})
|
return web.FileResponse(path=cfg.font_path, headers={"Cache-Control": "public, max-age=86400"})
|
||||||
|
|
||||||
|
|
@ -536,14 +558,17 @@ async def websocket_handler(request: web.Request) -> web.WebSocketResponse:
|
||||||
|
|
||||||
|
|
||||||
class AppConfig:
|
class AppConfig:
|
||||||
def __init__(self, user: str, shell_path: str, host: str, port: int, rows: int, cols: int, font_path: Optional[str]):
|
FONT_PATH = "/usr/local/share/dos-httpd/WebPlus_IBM_VGA_8x16.woff"
|
||||||
|
|
||||||
|
def __init__(self, user: str, shell_path: str, host: str, port: int, rows: int, cols: int, crt_effect: bool):
|
||||||
self.user = user
|
self.user = user
|
||||||
self.shell_path = shell_path
|
self.shell_path = shell_path
|
||||||
self.host = host
|
self.host = host
|
||||||
self.port = port
|
self.port = port
|
||||||
self.default_rows = rows
|
self.default_rows = rows
|
||||||
self.default_cols = cols
|
self.default_cols = cols
|
||||||
self.font_path = font_path
|
self.font_path = self.FONT_PATH
|
||||||
|
self.crt_effect = crt_effect
|
||||||
|
|
||||||
|
|
||||||
def build_argument_parser() -> argparse.ArgumentParser:
|
def build_argument_parser() -> argparse.ArgumentParser:
|
||||||
|
|
@ -568,11 +593,10 @@ def build_argument_parser() -> argparse.ArgumentParser:
|
||||||
choices=["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG"],
|
choices=["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG"],
|
||||||
help="Log verbosity",
|
help="Log verbosity",
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
default_crt = _env_bool("DOS_HTTP_CRT", True)
|
||||||
"--font-path",
|
parser.set_defaults(crt_effect=default_crt)
|
||||||
default=os.environ.get("DOS_HTTP_FONT_PATH", "/usr/local/share/dos-httpd/WebPlus_IBM_VGA_8x16.woff"),
|
parser.add_argument("--crt", dest="crt_effect", action="store_true", help="Enable CRT overlay")
|
||||||
help="Path to the WOFF font used by the terminal UI",
|
parser.add_argument("--no-crt", dest="crt_effect", action="store_false", help="Disable CRT overlay")
|
||||||
)
|
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -606,7 +630,7 @@ def main() -> None:
|
||||||
port=args.port,
|
port=args.port,
|
||||||
rows=args.rows,
|
rows=args.rows,
|
||||||
cols=args.cols,
|
cols=args.cols,
|
||||||
font_path=args.font_path if args.font_path else None,
|
crt_effect=args.crt_effect,
|
||||||
)
|
)
|
||||||
|
|
||||||
app = asyncio.run(create_app(cfg))
|
app = asyncio.run(create_app(cfg))
|
||||||
|
|
|
||||||
|
|
@ -113,8 +113,15 @@ start_http_console() {
|
||||||
if [ -n "${DOS_HTTP_LOG_LEVEL:-}" ]; then
|
if [ -n "${DOS_HTTP_LOG_LEVEL:-}" ]; then
|
||||||
args+=(--log-level "${DOS_HTTP_LOG_LEVEL}")
|
args+=(--log-level "${DOS_HTTP_LOG_LEVEL}")
|
||||||
fi
|
fi
|
||||||
if [ -n "${DOS_HTTP_FONT_PATH:-}" ]; then
|
if [ -n "${DOS_HTTP_CRT:-}" ]; then
|
||||||
args+=(--font-path "${DOS_HTTP_FONT_PATH}")
|
case "${DOS_HTTP_CRT,,}" in
|
||||||
|
0|false|no|off)
|
||||||
|
args+=(--no-crt)
|
||||||
|
;;
|
||||||
|
1|true|yes|on)
|
||||||
|
args+=(--crt)
|
||||||
|
;;
|
||||||
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
set +e
|
set +e
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue