front: автоматическая генерация скриншотов скриптом
2
.gitignore
vendored
@@ -7,7 +7,7 @@ dh.pem
|
|||||||
/web-action
|
/web-action
|
||||||
|
|
||||||
# эти файлы после генерации должны быть перемещены в `/static`
|
# эти файлы после генерации должны быть перемещены в `/static`
|
||||||
front-generator/main-*.html
|
front-generator/out
|
||||||
|
|
||||||
# логи сервера в релизной версии
|
# логи сервера в релизной версии
|
||||||
http_server_*.log
|
http_server_*.log
|
||||||
|
|||||||
@@ -2,6 +2,14 @@
|
|||||||
|
|
||||||
Сервис, запускаемый на терминале как веб-морда.
|
Сервис, запускаемый на терминале как веб-морда.
|
||||||
|
|
||||||
|
# Превью (NEW)
|
||||||
|
|
||||||
|
По многочисленным просьбам работающих, была создана новая фича - автоматическая генерация скриншотов для всех типов веб-сервера.
|
||||||
|
Вот готовые страницы со скриншотами:
|
||||||
|
* [SCPC](preview/scpc.md)
|
||||||
|
* [ШПС](preview/shps.md)
|
||||||
|
* [TDMA](preview/tdma.md)
|
||||||
|
|
||||||
# Зависимости
|
# Зависимости
|
||||||
|
|
||||||
По идее только `boost` и `ssl`
|
По идее только `boost` и `ssl`
|
||||||
|
|||||||
9
front-generator/README.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# Terminal web server - front generator
|
||||||
|
|
||||||
|
Эта часть является подпроектом для веб-сервера и позволяет "на коленке" портировать/изменять веб-сервер для абонентских модемов, и терминалов.
|
||||||
|
|
||||||
|
Скрипт [render.py](render.py) нужен для генерации фронтенда, скрипт [generate-screens.py](generate-screens.py) для генерации скриншотов.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
148
front-generator/generate-screens.py
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
import subprocess
|
||||||
|
import pathlib
|
||||||
|
import time
|
||||||
|
import socket
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
|
||||||
|
from selenium import webdriver
|
||||||
|
from selenium.webdriver.common.by import By
|
||||||
|
|
||||||
|
BASE = pathlib.Path(__file__).parent
|
||||||
|
|
||||||
|
CONFIG_DIR = BASE / "config"
|
||||||
|
MODEM_TYPES = {}
|
||||||
|
|
||||||
|
for _fname in os.listdir(CONFIG_DIR):
|
||||||
|
if not _fname.endswith(".json"):
|
||||||
|
continue
|
||||||
|
path = os.path.join(CONFIG_DIR, _fname)
|
||||||
|
try:
|
||||||
|
with open(path, "r", encoding="utf-8") as f:
|
||||||
|
data = json.load(f)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[WARN] Failed to read {path}: {e}")
|
||||||
|
continue
|
||||||
|
modem_type = data.get("modem_type")
|
||||||
|
if modem_type is None:
|
||||||
|
print(f"[WARN] {_fname} ignored: no 'modem_type' field")
|
||||||
|
continue
|
||||||
|
MODEM_TYPES[modem_type] = data
|
||||||
|
|
||||||
|
BASE_URL = "http://localhost:8080/"
|
||||||
|
|
||||||
|
SCREEN_WIDTH = 1200
|
||||||
|
|
||||||
|
LOGIN = {
|
||||||
|
"username": "admin",
|
||||||
|
"password": "admin",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def wait_port(port=8080, host="127.0.0.1"):
|
||||||
|
for _ in range(100):
|
||||||
|
try:
|
||||||
|
socket.create_connection((host, port), timeout=1)
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
time.sleep(0.1)
|
||||||
|
raise RuntimeError("TCP port 8080 did not open in time")
|
||||||
|
|
||||||
|
|
||||||
|
def run_server(modem, build):
|
||||||
|
binary = BASE / f"../cmake-build-{build}-{modem}/terminal-web-server"
|
||||||
|
proc = subprocess.Popen([
|
||||||
|
binary,
|
||||||
|
"nossl",
|
||||||
|
"0.0.0.0",
|
||||||
|
"8080",
|
||||||
|
"../static"
|
||||||
|
])
|
||||||
|
return proc
|
||||||
|
|
||||||
|
|
||||||
|
def create_driver():
|
||||||
|
opts = webdriver.FirefoxOptions()
|
||||||
|
serv = webdriver.FirefoxService(executable_path='/snap/bin/geckodriver')
|
||||||
|
driver = webdriver.Firefox(options=opts, service=serv)
|
||||||
|
|
||||||
|
driver.set_window_size(SCREEN_WIDTH, 1000)
|
||||||
|
driver.set_page_load_timeout(15)
|
||||||
|
return driver
|
||||||
|
|
||||||
|
|
||||||
|
def login(driver):
|
||||||
|
driver.get("http://localhost:8080/#")
|
||||||
|
|
||||||
|
# Проверяем, редиректнуло ли на /login
|
||||||
|
if "/login" in driver.current_url:
|
||||||
|
# Ищем поля
|
||||||
|
user = driver.find_element(By.NAME, "username")
|
||||||
|
pwd = driver.find_element(By.NAME, "password")
|
||||||
|
|
||||||
|
user.clear()
|
||||||
|
pwd.clear()
|
||||||
|
user.send_keys(LOGIN["username"])
|
||||||
|
pwd.send_keys(LOGIN["password"])
|
||||||
|
|
||||||
|
# submit
|
||||||
|
btn = driver.find_element(By.ID, "submit")
|
||||||
|
btn.click()
|
||||||
|
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
# Проверка успешного входа
|
||||||
|
if "/login" in driver.current_url:
|
||||||
|
raise RuntimeError("Login failed: still on /login after submit")
|
||||||
|
|
||||||
|
|
||||||
|
def make_screenshots_or_checks(modem):
|
||||||
|
out_dir = BASE / "out" / modem
|
||||||
|
out_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
driver = create_driver()
|
||||||
|
try:
|
||||||
|
driver.get("http://localhost:8080/login")
|
||||||
|
driver.save_screenshot(str(out_dir / f"login.png"))
|
||||||
|
|
||||||
|
login(driver)
|
||||||
|
|
||||||
|
time.sleep(2)
|
||||||
|
|
||||||
|
for tab in MODEM_TYPES[modem]["tabs"]:
|
||||||
|
tab_name = tab["name"]
|
||||||
|
driver.find_element(By.CSS_SELECTOR, f'a[href="#{tab_name}"]').click()
|
||||||
|
|
||||||
|
# Проверка, что body загрузилось
|
||||||
|
time.sleep(1)
|
||||||
|
page_height = driver.execute_script("return document.body.scrollHeight")
|
||||||
|
driver.set_window_size(SCREEN_WIDTH, page_height + 200)
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
driver.find_element(By.TAG_NAME, "body") # гарантирует что DOM есть
|
||||||
|
driver.save_screenshot(str(out_dir / f"{tab_name}.png"))
|
||||||
|
|
||||||
|
finally:
|
||||||
|
driver.quit()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
for mt in MODEM_TYPES:
|
||||||
|
build = "debug"
|
||||||
|
print(f"\n=== {mt} ({build}) ===")
|
||||||
|
|
||||||
|
proc = run_server(mt, build)
|
||||||
|
try:
|
||||||
|
wait_port()
|
||||||
|
make_screenshots_or_checks(mt)
|
||||||
|
finally:
|
||||||
|
proc.terminate()
|
||||||
|
proc.wait()
|
||||||
|
|
||||||
|
print("Done.")
|
||||||
|
|
||||||
|
print("\nAll configurations processed.")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -123,16 +123,27 @@ def render_modem(modem):
|
|||||||
loader = FileSystemLoader('template')
|
loader = FileSystemLoader('template')
|
||||||
env = Environment(loader=loader, trim_blocks=True, lstrip_blocks=True)
|
env = Environment(loader=loader, trim_blocks=True, lstrip_blocks=True)
|
||||||
template = env.get_template('main.html')
|
template = env.get_template('main.html')
|
||||||
|
|
||||||
context = build_modem_env(modem)
|
context = build_modem_env(modem)
|
||||||
|
|
||||||
with open(f"main-{modem}.html", "w") as f:
|
with open(f"out/main-{modem}.html", "w") as f:
|
||||||
|
f.write(template.render(context))
|
||||||
|
|
||||||
|
def render_modem_preview(modem):
|
||||||
|
loader = FileSystemLoader('template')
|
||||||
|
env = Environment(loader=loader, trim_blocks=True, lstrip_blocks=True)
|
||||||
|
template = env.get_template('modem-preview.md')
|
||||||
|
context = build_modem_env(modem)
|
||||||
|
|
||||||
|
with open(f"out/{modem}.md", "w") as f:
|
||||||
f.write(template.render(context))
|
f.write(template.render(context))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
os.makedirs('out', exist_ok=True)
|
||||||
for mt in MODEM_TYPES:
|
for mt in MODEM_TYPES:
|
||||||
print(f'Generating {mt} modem...')
|
print(f'Generating {mt} modem...')
|
||||||
render_modem(mt)
|
render_modem(mt)
|
||||||
os.system(f'cp -u main-{mt}.html ../static')
|
render_modem_preview(mt)
|
||||||
|
os.system(f'cp -u out/main-{mt}.html ../static')
|
||||||
|
os.system(f'cp -u out/{mt}.md ../preview')
|
||||||
|
|
||||||
|
|||||||
5
front-generator/requirements.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
jinja2
|
||||||
|
selenium
|
||||||
|
selenium-firefox
|
||||||
|
requests
|
||||||
|
brotli
|
||||||
13
front-generator/template/modem-preview.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# {{ modem_name }}
|
||||||
|
|
||||||
|
## Страница входа
|
||||||
|
|
||||||
|
На эту страницу пользователь попадет при попытке зайти в веб-интерфейс модема
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
{% for tab in header_tabs %}
|
||||||
|
## Страница `{{ tab.desc }}`
|
||||||
|
|
||||||
|

|
||||||
|
{% endfor %}
|
||||||
BIN
preview/images/login.png
Normal file
|
After Width: | Height: | Size: 99 KiB |
BIN
preview/images/scpc/admin.png
Normal file
|
After Width: | Height: | Size: 464 KiB |
BIN
preview/images/scpc/monitoring.png
Normal file
|
After Width: | Height: | Size: 589 KiB |
BIN
preview/images/scpc/qos.png
Normal file
|
After Width: | Height: | Size: 206 KiB |
BIN
preview/images/scpc/setup.png
Normal file
|
After Width: | Height: | Size: 602 KiB |
BIN
preview/images/shps/admin.png
Normal file
|
After Width: | Height: | Size: 463 KiB |
BIN
preview/images/shps/monitoring.png
Normal file
|
After Width: | Height: | Size: 454 KiB |
BIN
preview/images/shps/setup.png
Normal file
|
After Width: | Height: | Size: 508 KiB |
BIN
preview/images/tdma/admin.png
Normal file
|
After Width: | Height: | Size: 428 KiB |
BIN
preview/images/tdma/logs.png
Normal file
|
After Width: | Height: | Size: 214 KiB |
BIN
preview/images/tdma/monitoring.png
Normal file
|
After Width: | Height: | Size: 634 KiB |
BIN
preview/images/tdma/setup.png
Normal file
|
After Width: | Height: | Size: 513 KiB |
20
preview/scpc.md
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# RSCM-201
|
||||||
|
|
||||||
|
## Страница входа
|
||||||
|
|
||||||
|
На эту страницу пользователь попадет при попытке зайти в веб-интерфейс модема
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Страница `Мониторинг`
|
||||||
|
|
||||||
|

|
||||||
|
## Страница `Настройки`
|
||||||
|
|
||||||
|

|
||||||
|
## Страница `QoS`
|
||||||
|
|
||||||
|

|
||||||
|
## Страница `Администрирование`
|
||||||
|
|
||||||
|

|
||||||
17
preview/shps.md
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# ШПС Модем
|
||||||
|
|
||||||
|
## Страница входа
|
||||||
|
|
||||||
|
На эту страницу пользователь попадет при попытке зайти в веб-интерфейс модема
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Страница `Мониторинг`
|
||||||
|
|
||||||
|

|
||||||
|
## Страница `Настройки`
|
||||||
|
|
||||||
|

|
||||||
|
## Страница `Администрирование`
|
||||||
|
|
||||||
|

|
||||||
20
preview/tdma.md
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# VSAT Модем
|
||||||
|
|
||||||
|
## Страница входа
|
||||||
|
|
||||||
|
На эту страницу пользователь попадет при попытке зайти в веб-интерфейс модема
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Страница `Мониторинг`
|
||||||
|
|
||||||
|

|
||||||
|
## Страница `Настройки`
|
||||||
|
|
||||||
|

|
||||||
|
## Страница `Администрирование`
|
||||||
|
|
||||||
|

|
||||||
|
## Страница `Журнал`
|
||||||
|
|
||||||
|

|
||||||