Refactor HTML templates to separate header files

- Created html_pages/ directory for HTML templates
- Moved index page HTML template to html_pages/index_page.h
- Moved terminal page HTML template to html_pages/terminal_page.h
- Updated web.c to use templates from header files instead of embedded strings
- Updated configure.sh to include new header file dependencies in generated Makefile
parent c8be3561
# Makefile for wssshd C implementation # Makefile for wssshd2 - Generated by configure.sh
# Do not edit manually, run ./configure.sh instead
CC = gcc CC = gcc
CFLAGS = -Wall -Wextra -O2 -I. -pthread CFLAGS = -Wall -Wextra -O2 -I. -pthread
LDFLAGS = -lssl -lcrypto -lm -luuid LDFLAGS = -lssl -lcrypto -lm -luuid
PREFIX = /usr/local
BINDIR = $(PREFIX)/bin
MANDIR = $(PREFIX)/share/man
CONFIGDIR = /etc
# Source files # Source files
SRCS = main.c config.c tunnel.c terminal.c websocket.c websocket_protocol.c web.c assets.c ssl.c SRCS = main.c config.c tunnel.c terminal.c websocket.c websocket_protocol.c web.c assets.c ssl.c
OBJS = $(SRCS:.c=.o) OBJS = $(SRCS:.c=.o)
# Target
TARGET = wssshd TARGET = wssshd
# Default target .PHONY: all clean install uninstall
all: $(TARGET) all: $(TARGET)
# Link the executable
$(TARGET): $(OBJS) $(TARGET): $(OBJS)
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
# Compile source files
%.o: %.c %.o: %.c
$(CC) $(CFLAGS) -c $< -o $@ $(CC) $(CFLAGS) -c $< -o $@
# Clean build artifacts
clean:
rm -f $(OBJS) $(TARGET)
# Install (optional)
install: $(TARGET)
install -m 755 $(TARGET) /usr/local/bin/
# Uninstall (optional)
uninstall:
rm -f /usr/local/bin/$(TARGET)
# Dependencies # Dependencies
main.o: main.c config.h websocket.h web.h main.o: main.c config.h websocket.h web.h
config.o: config.c config.h config.o: config.c config.h
tunnel.o: tunnel.c tunnel.h tunnel.o: tunnel.c tunnel.h websocket.h
terminal.o: terminal.c terminal.h config.h terminal.o: terminal.c terminal.h config.h
websocket.o: websocket.c websocket.h config.h tunnel.h websocket.o: websocket.c websocket.h websocket_protocol.h config.h
web.o: web.c web.h config.h websocket.h assets.h websocket_protocol.o: websocket_protocol.c websocket_protocol.h
web.o: web.c web.h terminal.h assets.h websocket.h html_pages/index_page.h html_pages/terminal_page.h
assets.o: assets.c assets.h assets.o: assets.c assets.h
ssl.o: ssl.c ssl.h ssl.o: ssl.c ssl.h
.PHONY: all clean install uninstall # Asset embedding (run before compilation)
\ No newline at end of file assets.o: image_data.h
image_data.h: embed_assets.sh
./embed_assets.sh
clean:
rm -f $(OBJS) $(TARGET) image_data.h
install: $(TARGET)
install -d $(DESTDIR)$(BINDIR)
install -m 755 $(TARGET) $(DESTDIR)$(BINDIR)/
install -d $(DESTDIR)$(CONFIGDIR)
[ -f $(DESTDIR)$(CONFIGDIR)/wssshd.conf ] || install -m 644 wssshd.conf.example $(DESTDIR)$(CONFIGDIR)/wssshd.conf
install -d $(DESTDIR)$(MANDIR)/man8
install -m 644 wssshd.8 $(DESTDIR)$(MANDIR)/man8/
uninstall:
rm -f $(DESTDIR)$(BINDIR)/$(TARGET)
rm -f $(DESTDIR)$(MANDIR)/man8/wssshd.8
# Development targets
debug: CFLAGS += -g -DDEBUG
debug: clean all
test: $(TARGET)
@echo "Running basic functionality test..."
./$(TARGET) --help || true
distclean: clean
rm -f Makefile
# Help target
help:
@echo "Available targets:"
@echo " all - Build wssshd2 (default)"
@echo " clean - Remove build artifacts"
@echo " install - Install wssshd2 to system"
@echo " uninstall - Remove wssshd2 from system"
@echo " debug - Build with debug symbols"
@echo " test - Run basic tests"
@echo " distclean - Remove all generated files"
@echo " help - Show this help"
#!/bin/bash
# configure.sh - Configuration script for wssshd2
# Generates Makefile based on system detection
#
# Copyright (C) 2024 Stefy Lanza <stefy@nexlab.net> and SexHack.me
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
set -e
echo "Configuring wssshd2..."
# Detect system information
OS=$(uname -s)
ARCH=$(uname -m)
echo "Detected OS: $OS"
echo "Detected architecture: $ARCH"
# Check for required tools
check_command() {
if ! command -v "$1" &> /dev/null; then
echo "Error: $1 is required but not found"
exit 1
fi
}
check_command gcc
check_command make
check_command pkg-config
# Check for required libraries
if ! pkg-config --exists libssl libcrypto uuid; then
echo "Error: Required libraries (libssl, libcrypto, uuid) not found"
echo "Please install development packages:"
echo " Ubuntu/Debian: sudo apt-get install libssl-dev uuid-dev"
echo " CentOS/RHEL: sudo yum install openssl-devel libuuid-devel"
echo " macOS: brew install openssl ossp-uuid"
exit 1
fi
# Generate Makefile
cat > Makefile << 'EOF'
# Makefile for wssshd2 - Generated by configure.sh
# Do not edit manually, run ./configure.sh instead
CC = gcc
CFLAGS = -Wall -Wextra -O2 -I. -pthread
LDFLAGS = -lssl -lcrypto -lm -luuid
PREFIX = /usr/local
BINDIR = $(PREFIX)/bin
MANDIR = $(PREFIX)/share/man
CONFIGDIR = /etc
# Source files
SRCS = main.c config.c tunnel.c terminal.c websocket.c websocket_protocol.c web.c assets.c ssl.c
OBJS = $(SRCS:.c=.o)
# Target
TARGET = wssshd
.PHONY: all clean install uninstall
all: $(TARGET)
$(TARGET): $(OBJS)
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
# Dependencies
main.o: main.c config.h websocket.h web.h
config.o: config.c config.h
tunnel.o: tunnel.c tunnel.h websocket.h
terminal.o: terminal.c terminal.h config.h
websocket.o: websocket.c websocket.h websocket_protocol.h config.h
websocket_protocol.o: websocket_protocol.c websocket_protocol.h
web.o: web.c web.h terminal.h assets.h websocket.h html_pages/index_page.h html_pages/terminal_page.h
assets.o: assets.c assets.h
ssl.o: ssl.c ssl.h
# Asset embedding (run before compilation)
assets.o: image_data.h
image_data.h: embed_assets.sh
./embed_assets.sh
clean:
rm -f $(OBJS) $(TARGET) image_data.h
install: $(TARGET)
install -d $(DESTDIR)$(BINDIR)
install -m 755 $(TARGET) $(DESTDIR)$(BINDIR)/
install -d $(DESTDIR)$(CONFIGDIR)
[ -f $(DESTDIR)$(CONFIGDIR)/wssshd.conf ] || install -m 644 wssshd.conf.example $(DESTDIR)$(CONFIGDIR)/wssshd.conf
install -d $(DESTDIR)$(MANDIR)/man8
install -m 644 wssshd.8 $(DESTDIR)$(MANDIR)/man8/
uninstall:
rm -f $(DESTDIR)$(BINDIR)/$(TARGET)
rm -f $(DESTDIR)$(MANDIR)/man8/wssshd.8
# Development targets
debug: CFLAGS += -g -DDEBUG
debug: clean all
test: $(TARGET)
@echo "Running basic functionality test..."
./$(TARGET) --help || true
distclean: clean
rm -f Makefile
# Help target
help:
@echo "Available targets:"
@echo " all - Build wssshd2 (default)"
@echo " clean - Remove build artifacts"
@echo " install - Install wssshd2 to system"
@echo " uninstall - Remove wssshd2 from system"
@echo " debug - Build with debug symbols"
@echo " test - Run basic tests"
@echo " distclean - Remove all generated files"
@echo " help - Show this help"
EOF
echo "Makefile generated successfully"
echo "Run 'make' to build wssshd2"
echo "Run 'make install' to install system-wide"
\ No newline at end of file
/**
* Index/Dashboard page HTML template for wssshd
*
* Copyright (C) 2024 Stefy Lanza <stefy@nexlab.net> and SexHack.me
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef INDEX_PAGE_H
#define INDEX_PAGE_H
// Index page HTML template with placeholders:
// %s - username
// %s - client_list HTML
// %s - admin_actions HTML
// %d - websocket port
// %s - domain
// %zu - client count
static const char *index_page_html =
"<!DOCTYPE html>"
"<html lang=\"en\">"
"<head>"
"<meta charset=\"UTF-8\">"
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"
"<title>Dashboard - WebSocket SSH Daemon</title>"
"<link href=\"https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css\" rel=\"stylesheet\">"
"<link href=\"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css\" rel=\"stylesheet\">"
"</head>"
"<body>"
"<nav class=\"navbar navbar-expand-lg navbar-dark bg-primary\">"
"<div class=\"container\">"
"<a class=\"navbar-brand\" href=\"/\">"
"<i class=\"fas fa-terminal\"></i> WebSocket SSH Daemon</a>"
"<div class=\"navbar-nav ms-auto\">"
"<span class=\"navbar-text me-3\">Welcome, %s</span>"
"<a class=\"nav-link\" href=\"/logout\">Logout</a>"
"</div></div></nav>"
"<div class=\"container mt-4\">"
"<div class=\"row\">"
"<div class=\"col-md-8\">"
"<div class=\"card\">"
"<div class=\"card-header\">"
"<h3 class=\"card-title mb-0\">"
"<i class=\"fas fa-server\"></i> Connected Clients</h3></div>"
"<div class=\"card-body\"><div class=\"row\">%s</div></div></div></div>"
"<div class=\"col-md-4\">"
"<div class=\"card\">"
"<div class=\"card-header\">"
"<h3 class=\"card-title mb-0\">"
"<i class=\"fas fa-cogs\"></i> Quick Actions</h3></div>"
"<div class=\"card-body\">%s"
"<button class=\"btn btn-outline-secondary btn-sm w-100\" onclick=\"location.reload()\">"
"<i class=\"fas fa-sync\"></i> Refresh Status</button></div></div>"
"<div class=\"card mt-3\">"
"<div class=\"card-header\">"
"<h3 class=\"card-title mb-0\">"
"<i class=\"fas fa-info-circle\"></i> System Info</h3></div>"
"<div class=\"card-body\">"
"<p class=\"mb-1\"><strong>WebSocket Port:</strong> <span id=\"websocket-port\">%d</span></p>"
"<p class=\"mb-1\"><strong>Domain:</strong> <span id=\"domain\">%s</span></p>"
"<p class=\"mb-0\"><strong>Connected Clients:</strong> <span id=\"client-count\">%zu</span></p>"
"</div></div></div></div></div>"
"<script src=\"https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js\"></script>"
"</body></html>";
#endif /* INDEX_PAGE_H */
\ No newline at end of file
/**
* Terminal page HTML template for wssshd
*
* Copyright (C) 2024 Stefy Lanza <stefy@nexlab.net> and SexHack.me
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef TERMINAL_PAGE_H
#define TERMINAL_PAGE_H
// Terminal page HTML template with placeholders:
// %s - client_id (appears multiple times)
static const char *terminal_page_html =
"<!DOCTYPE html>"
"<html lang=\"en\">"
"<head>"
"<meta charset=\"UTF-8\">"
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"
"<title>Terminal - %s</title>"
"<link href=\"https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css\" rel=\"stylesheet\">"
"<link rel=\"stylesheet\" href=\"https://unpkg.com/xterm@5.3.0/css/xterm.css\">"
"</head>"
"<body>"
"<div class=\"container mt-4\">"
"<div class=\"row\">"
"<div class=\"col-12\">"
"<div class=\"card\">"
"<div class=\"card-header d-flex justify-content-between align-items-center\">"
"<div class=\"d-flex align-items-center\">"
"<a href=\"/\" class=\"btn btn-outline-secondary btn-sm me-3\">"
"<i class=\"fas fa-arrow-left\"></i> Back to Dashboard</a>"
"<h3 class=\"card-title mb-0\">"
"<i class=\"fas fa-terminal\"></i> SSH Terminal - %s</h3></div>"
"<div>"
"<input type=\"text\" id=\"sshUsername\" class=\"form-control form-control-sm d-inline-block w-auto me-2\" placeholder=\"Username\" value=\"root\">"
"<button id=\"connectBtn\" class=\"btn btn-success btn-sm\">"
"<i class=\"fas fa-play\"></i> Connect</button>"
"<button id=\"disconnectBtn\" class=\"btn btn-danger btn-sm\" disabled>"
"<i class=\"fas fa-stop\"></i> Disconnect</button>"
"</div></div>"
"<div class=\"card-body p-2\">"
"<div id=\"terminal\" class=\"terminal-container w-100\"></div>"
"</div></div></div></div></div>"
"<script src=\"https://unpkg.com/xterm@5.3.0/lib/xterm.js\"></script>"
"<script src=\"https://unpkg.com/xterm-addon-fit@0.8.0/lib/xterm-addon-fit.js\"></script>"
"<script>"
"let term = null; let fitAddon = null; let connected = false; let requestId = null; let pollInterval = null;"
"document.getElementById('connectBtn').addEventListener('click', connect);"
"document.getElementById('disconnectBtn').addEventListener('click', disconnect);"
"function connect() {"
"const username = document.getElementById('sshUsername').value;"
"if (!username) { alert('Please enter a username'); return; }"
"if (!term) {"
"term = new Terminal({cursorBlink: true, fontSize: 14, theme: {background: '#1e1e1e', foreground: '#f8f8f2'}});"
"term.open(document.getElementById('terminal'));"
"fitAddon = new FitAddon.FitAddon(); term.loadAddon(fitAddon); fitAddon.fit();"
"window.addEventListener('resize', () => { if (fitAddon) fitAddon.fit(); });"
"term.onData(data => {"
"if (!connected || !requestId) return;"
"fetch('/terminal/%s/data', {"
"method: 'POST',"
"headers: {'Content-Type': 'application/x-www-form-urlencoded'},"
"body: 'request_id=' + encodeURIComponent(requestId) + '&data=' + encodeURIComponent(data)"
"});"
"});"
"}"
"term.write('Connecting...\\r\\n');"
"connected = true;"
"document.getElementById('connectBtn').disabled = true;"
"document.getElementById('disconnectBtn').disabled = false;"
"document.getElementById('sshUsername').disabled = true;"
"let cols = 80, rows = 24;"
"if (fitAddon) { const d = fitAddon.proposeDimensions(); cols = d.cols || 80; rows = d.rows || 24; }"
"fetch('/terminal/%s/connect', {"
"method: 'POST',"
"headers: {'Content-Type': 'application/x-www-form-urlencoded'},"
"body: 'username=' + encodeURIComponent(username) + '&cols=' + cols + '&rows=' + rows"
"}).then(r => r.json()).then(data => {"
"if (data.request_id) {"
"requestId = data.request_id;"
"term.write('Connected!\\r\\n');"
"pollInterval = setInterval(pollData, 100);"
"} else {"
"term.write('Error: ' + (data.error || 'Unknown') + '\\r\\n');"
"disconnect();"
"}"
"}).catch(e => { term.write('Connection failed\\r\\n'); disconnect(); });"
"}"
"function disconnect() {"
"connected = false;"
"document.getElementById('connectBtn').disabled = false;"
"document.getElementById('disconnectBtn').disabled = true;"
"document.getElementById('sshUsername').disabled = false;"
"if (pollInterval) { clearInterval(pollInterval); pollInterval = null; }"
"if (requestId) {"
"fetch('/terminal/%s/disconnect', {"
"method: 'POST',"
"headers: {'Content-Type': 'application/x-www-form-urlencoded'},"
"body: 'request_id=' + encodeURIComponent(requestId)"
"});"
"requestId = null;"
"}"
"if (term) term.write('\\r\\nDisconnected.\\r\\n');"
"}"
"function pollData() {"
"if (!requestId) return;"
"fetch('/terminal/%s/data?request_id=' + encodeURIComponent(requestId))"
".then(r => r.text()).then(data => {"
"if (data) term.write(data.replace(/\\n/g, '\\r\\n'));"
"});"
"}"
"</script>"
"</body></html>";
#endif /* TERMINAL_PAGE_H */
\ No newline at end of file
...@@ -33,6 +33,8 @@ ...@@ -33,6 +33,8 @@
#include "terminal.h" #include "terminal.h"
#include "assets.h" #include "assets.h"
#include "websocket.h" #include "websocket.h"
#include "html_pages/index_page.h"
#include "html_pages/terminal_page.h"
// Embedded web assets are defined in assets.c // Embedded web assets are defined in assets.c
...@@ -341,52 +343,7 @@ static char *generate_index_html(const char *username, int is_admin) { ...@@ -341,52 +343,7 @@ static char *generate_index_html(const char *username, int is_admin) {
"<i class=\"fas fa-users\"></i> Manage Users</a>"); "<i class=\"fas fa-users\"></i> Manage Users</a>");
} }
snprintf(html, sizeof(html), snprintf(html, sizeof(html), index_page_html,
"<!DOCTYPE html>"
"<html lang=\"en\">"
"<head>"
"<meta charset=\"UTF-8\">"
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"
"<title>Dashboard - WebSocket SSH Daemon</title>"
"<link href=\"https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css\" rel=\"stylesheet\">"
"<link href=\"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css\" rel=\"stylesheet\">"
"</head>"
"<body>"
"<nav class=\"navbar navbar-expand-lg navbar-dark bg-primary\">"
"<div class=\"container\">"
"<a class=\"navbar-brand\" href=\"/\">"
"<i class=\"fas fa-terminal\"></i> WebSocket SSH Daemon</a>"
"<div class=\"navbar-nav ms-auto\">"
"<span class=\"navbar-text me-3\">Welcome, %s</span>"
"<a class=\"nav-link\" href=\"/logout\">Logout</a>"
"</div></div></nav>"
"<div class=\"container mt-4\">"
"<div class=\"row\">"
"<div class=\"col-md-8\">"
"<div class=\"card\">"
"<div class=\"card-header\">"
"<h3 class=\"card-title mb-0\">"
"<i class=\"fas fa-server\"></i> Connected Clients</h3></div>"
"<div class=\"card-body\"><div class=\"row\">%s</div></div></div></div>"
"<div class=\"col-md-4\">"
"<div class=\"card\">"
"<div class=\"card-header\">"
"<h3 class=\"card-title mb-0\">"
"<i class=\"fas fa-cogs\"></i> Quick Actions</h3></div>"
"<div class=\"card-body\">%s"
"<button class=\"btn btn-outline-secondary btn-sm w-100\" onclick=\"location.reload()\">"
"<i class=\"fas fa-sync\"></i> Refresh Status</button></div></div>"
"<div class=\"card mt-3\">"
"<div class=\"card-header\">"
"<h3 class=\"card-title mb-0\">"
"<i class=\"fas fa-info-circle\"></i> System Info</h3></div>"
"<div class=\"card-body\">"
"<p class=\"mb-1\"><strong>WebSocket Port:</strong> <span id=\"websocket-port\">%d</span></p>"
"<p class=\"mb-1\"><strong>Domain:</strong> <span id=\"domain\">%s</span></p>"
"<p class=\"mb-0\"><strong>Connected Clients:</strong> <span id=\"client-count\">%zu</span></p>"
"</div></div></div></div></div>"
"<script src=\"https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js\"></script>"
"</body></html>",
username, client_list, admin_actions, global_config->port, username, client_list, admin_actions, global_config->port,
global_config->domain ? global_config->domain : "N/A", global_state->clients_count); global_config->domain ? global_config->domain : "N/A", global_state->clients_count);
...@@ -453,107 +410,7 @@ static void handle_request(int client_fd, const http_request_t *req) { ...@@ -453,107 +410,7 @@ static void handle_request(int client_fd, const http_request_t *req) {
} }
// Generate terminal HTML with client_id // Generate terminal HTML with client_id
char html[16384]; char html[16384];
int len = snprintf(html, sizeof(html), int len = snprintf(html, sizeof(html), terminal_page_html,
"<!DOCTYPE html>"
"<html lang=\"en\">"
"<head>"
"<meta charset=\"UTF-8\">"
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"
"<title>Terminal - %s</title>"
"<link href=\"https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css\" rel=\"stylesheet\">"
"<link rel=\"stylesheet\" href=\"https://unpkg.com/xterm@5.3.0/css/xterm.css\">"
"</head>"
"<body>"
"<div class=\"container mt-4\">"
"<div class=\"row\">"
"<div class=\"col-12\">"
"<div class=\"card\">"
"<div class=\"card-header d-flex justify-content-between align-items-center\">"
"<div class=\"d-flex align-items-center\">"
"<a href=\"/\" class=\"btn btn-outline-secondary btn-sm me-3\">"
"<i class=\"fas fa-arrow-left\"></i> Back to Dashboard</a>"
"<h3 class=\"card-title mb-0\">"
"<i class=\"fas fa-terminal\"></i> SSH Terminal - %s</h3></div>"
"<div>"
"<input type=\"text\" id=\"sshUsername\" class=\"form-control form-control-sm d-inline-block w-auto me-2\" placeholder=\"Username\" value=\"root\">"
"<button id=\"connectBtn\" class=\"btn btn-success btn-sm\">"
"<i class=\"fas fa-play\"></i> Connect</button>"
"<button id=\"disconnectBtn\" class=\"btn btn-danger btn-sm\" disabled>"
"<i class=\"fas fa-stop\"></i> Disconnect</button>"
"</div></div>"
"<div class=\"card-body p-2\">"
"<div id=\"terminal\" class=\"terminal-container w-100\"></div>"
"</div></div></div></div></div>"
"<script src=\"https://unpkg.com/xterm@5.3.0/lib/xterm.js\"></script>"
"<script src=\"https://unpkg.com/xterm-addon-fit@0.8.0/lib/xterm-addon-fit.js\"></script>"
"<script>"
"let term = null; let fitAddon = null; let connected = false; let requestId = null; let pollInterval = null;"
"document.getElementById('connectBtn').addEventListener('click', connect);"
"document.getElementById('disconnectBtn').addEventListener('click', disconnect);"
"function connect() {"
"const username = document.getElementById('sshUsername').value;"
"if (!username) { alert('Please enter a username'); return; }"
"if (!term) {"
"term = new Terminal({cursorBlink: true, fontSize: 14, theme: {background: '#1e1e1e', foreground: '#f8f8f2'}});"
"term.open(document.getElementById('terminal'));"
"fitAddon = new FitAddon.FitAddon(); term.loadAddon(fitAddon); fitAddon.fit();"
"window.addEventListener('resize', () => { if (fitAddon) fitAddon.fit(); });"
"term.onData(data => {"
"if (!connected || !requestId) return;"
"fetch('/terminal/%s/data', {"
"method: 'POST',"
"headers: {'Content-Type': 'application/x-www-form-urlencoded'},"
"body: 'request_id=' + encodeURIComponent(requestId) + '&data=' + encodeURIComponent(data)"
"});"
"});"
"}"
"term.write('Connecting...\\r\\n');"
"connected = true;"
"document.getElementById('connectBtn').disabled = true;"
"document.getElementById('disconnectBtn').disabled = false;"
"document.getElementById('sshUsername').disabled = true;"
"let cols = 80, rows = 24;"
"if (fitAddon) { const d = fitAddon.proposeDimensions(); cols = d.cols || 80; rows = d.rows || 24; }"
"fetch('/terminal/%s/connect', {"
"method: 'POST',"
"headers: {'Content-Type': 'application/x-www-form-urlencoded'},"
"body: 'username=' + encodeURIComponent(username) + '&cols=' + cols + '&rows=' + rows"
"}).then(r => r.json()).then(data => {"
"if (data.request_id) {"
"requestId = data.request_id;"
"term.write('Connected!\\r\\n');"
"pollInterval = setInterval(pollData, 100);"
"} else {"
"term.write('Error: ' + (data.error || 'Unknown') + '\\r\\n');"
"disconnect();"
"}"
"}).catch(e => { term.write('Connection failed\\r\\n'); disconnect(); });"
"}"
"function disconnect() {"
"connected = false;"
"document.getElementById('connectBtn').disabled = false;"
"document.getElementById('disconnectBtn').disabled = true;"
"document.getElementById('sshUsername').disabled = false;"
"if (pollInterval) { clearInterval(pollInterval); pollInterval = null; }"
"if (requestId) {"
"fetch('/terminal/%s/disconnect', {"
"method: 'POST',"
"headers: {'Content-Type': 'application/x-www-form-urlencoded'},"
"body: 'request_id=' + encodeURIComponent(requestId)"
"});"
"requestId = null;"
"}"
"if (term) term.write('\\r\\nDisconnected.\\r\\n');"
"}"
"function pollData() {"
"if (!requestId) return;"
"fetch('/terminal/%s/data?request_id=' + encodeURIComponent(requestId))"
".then(r => r.text()).then(data => {"
"if (data) term.write(data.replace(/\\n/g, '\\r\\n'));"
"});"
"}"
"</script>"
"</body></html>",
client_id, client_id, client_id, client_id, client_id, client_id); client_id, client_id, client_id, client_id, client_id, client_id);
send_response(client_fd, 200, "OK", "text/html", html, len, NULL, NULL); send_response(client_fd, 200, "OK", "text/html", html, len, NULL, NULL);
} else if (strcmp(req->path, "/api/clients") == 0) { } else if (strcmp(req->path, "/api/clients") == 0) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment