mirror of
https://github.com/bunkerity/bunkerweb
synced 2026-05-24 09:28:37 +00:00
enhance front build perf + update Dockerfile
* add vite config to avoir useless resolving * add asyncio for better perf * Dockerfile we build front and remove client folder from container
This commit is contained in:
parent
ec592a2eec
commit
7f6e6234af
7 changed files with 146 additions and 106 deletions
6
.gitignore
vendored
6
.gitignore
vendored
|
|
@ -10,4 +10,8 @@ node_modules
|
|||
.cache/
|
||||
/package*.json
|
||||
/src/client/static
|
||||
/src/client/templates
|
||||
/src/client/templates
|
||||
/src/ui/static/assets
|
||||
/src/ui/static/css
|
||||
/src/ui/static/flags
|
||||
/src/ui/static/img
|
||||
|
|
@ -21,6 +21,9 @@ RUN export MAKEFLAGS="-j$(nproc)" && \
|
|||
pip install --no-cache-dir --require-hashes --break-system-packages -r /tmp/requirements-deps.txt && \
|
||||
pip install --no-cache-dir --require-hashes --target deps/python $(for file in $(ls /tmp/req/requirements*.txt) ; do echo "-r ${file}" ; done | xargs)
|
||||
|
||||
# Install node and npm to build vite frontend
|
||||
RUN apk add --no-cache nodejs npm
|
||||
|
||||
# Copy files
|
||||
# can't exclude specific files/dir from . so we are copying everything by hand
|
||||
COPY src/common/api api
|
||||
|
|
@ -34,6 +37,13 @@ COPY src/common/templates templates
|
|||
COPY src/ui ui
|
||||
COPY src/VERSION VERSION
|
||||
|
||||
# This will build the frontend and add files to the UI folder
|
||||
WORKDIR /usr/share/bunkerweb/ui/client
|
||||
RUN python3 build.py
|
||||
|
||||
# We can delete the client folder after building the frontend
|
||||
RUN rm -rf /usr/share/bunkerweb/ui/client
|
||||
|
||||
FROM python:3.12.4-alpine3.19@sha256:ef3397d09070efd36583e83d2619cf8006158641e5b6b629d4d92a9778f5aa1c
|
||||
|
||||
# Set default umask to prevent huge recursive chmod increasing the final image size
|
||||
|
|
@ -53,7 +63,7 @@ RUN apk add --no-cache bash unzip libmagic mariadb-connector-c mariadb-client po
|
|||
mkdir -p /data/cache && ln -s /data/cache /var/cache/bunkerweb && \
|
||||
mkdir -p /data/lib && ln -s /data/lib /var/lib/bunkerweb && \
|
||||
for dir in $(echo "pro configs plugins") ; do mkdir -p "/data/${dir}" && ln -s "/data/${dir}" "/etc/bunkerweb/${dir}" ; done && \
|
||||
for dir in $(echo "pro/plugins configs/http configs/stream configs/server-http configs/server-stream configs/default-server-http configs/default-server-stream configs/modsec configs/modsec-crs configs/crs-plugins-before configs/crs-plugins-after") ; do mkdir "/data/${dir}" ; done && \
|
||||
for dir in $(echo "pro/plugins configs/http configs/stream configs/server-http configs/server-stream configs/default-server-http configs/default-server-stream configs/modsec configs/modsec-crs") ; do mkdir "/data/${dir}" ; done && \
|
||||
chown -R root:ui INTEGRATION /data /var/cache/bunkerweb /var/lib/bunkerweb /etc/bunkerweb /var/tmp/bunkerweb /var/run/bunkerweb /var/log/bunkerweb && \
|
||||
chmod -R 770 /data /var/cache/bunkerweb /var/lib/bunkerweb /etc/bunkerweb /var/tmp/bunkerweb /var/run/bunkerweb /var/log/bunkerweb && \
|
||||
chmod 750 gen/*.py ui/*.py ui/src/*.py helpers/*.sh deps/python/bin/* && \
|
||||
|
|
@ -66,7 +76,7 @@ RUN apk add --no-cache "busybox>=1.36.1-r17" "busybox-binsh>=1.36.1-r17" "ssl_cl
|
|||
RUN apk add --no-cache "libcrypto3>=3.1.6-r0" "libssl3>=3.1.6-r0" # CVE-2024-4741 CVE-2024-5535
|
||||
|
||||
LABEL maintainer="Bunkerity <contact@bunkerity.com>"
|
||||
LABEL version="1.6.0-beta"
|
||||
LABEL version="1.5.9"
|
||||
LABEL url="https://www.bunkerweb.io"
|
||||
LABEL bunkerweb.type="ui"
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ from subprocess import Popen, PIPE
|
|||
import os
|
||||
import shutil
|
||||
import re
|
||||
import asyncio
|
||||
|
||||
# get current directory
|
||||
current_directory = os.path.dirname(os.path.realpath(__file__))
|
||||
|
|
@ -19,6 +20,21 @@ ui_dir_templates = f"{current_directory}/../templates"
|
|||
statics = ("assets", "css", "flags", "img", "js")
|
||||
|
||||
|
||||
def reset():
|
||||
asyncio.run(remove_dir(opt_dir))
|
||||
asyncio.run(remove_dir(opt_dir_dashboard))
|
||||
asyncio.run(remove_dir(opt_dir_setup))
|
||||
|
||||
|
||||
def set_dashboard():
|
||||
move_template(opt_dir_dashboard_pages, ui_dir_templates)
|
||||
move_statics(opt_dir_dashboard, ui_dir_static)
|
||||
|
||||
|
||||
def set_setup():
|
||||
move_template(opt_dir_setup_page, ui_dir_templates)
|
||||
|
||||
|
||||
def run_command(command, need_wait=False):
|
||||
process = Popen(command, stdout=PIPE, stderr=PIPE, cwd=current_directory, shell=True)
|
||||
if need_wait:
|
||||
|
|
@ -26,24 +42,22 @@ def run_command(command, need_wait=False):
|
|||
|
||||
out, err = process.communicate()
|
||||
if err:
|
||||
print("Error: ", err)
|
||||
print(out)
|
||||
print(err)
|
||||
|
||||
|
||||
def remove_dir(directory):
|
||||
async def remove_dir(directory):
|
||||
if os.path.exists(directory):
|
||||
shutil.rmtree(directory)
|
||||
|
||||
|
||||
def reset():
|
||||
remove_dir(opt_dir)
|
||||
remove_dir(opt_dir_dashboard)
|
||||
remove_dir(opt_dir_setup)
|
||||
async def create_dir(directory):
|
||||
if not os.path.exists(directory):
|
||||
os.makedirs(directory, exist_ok=True)
|
||||
|
||||
|
||||
def create_base_dirs():
|
||||
os.makedirs(opt_dir, exist_ok=True)
|
||||
os.makedirs(opt_dir_templates, exist_ok=True)
|
||||
asyncio.run(create_dir(opt_dir))
|
||||
asyncio.run(create_dir(opt_dir_dashboard))
|
||||
|
||||
|
||||
def move_template(folder, target_folder):
|
||||
|
|
@ -63,67 +77,70 @@ def move_template(folder, target_folder):
|
|||
</body>
|
||||
</html>"""
|
||||
|
||||
async def move_template_file(root, file, target_folder, base_html):
|
||||
file_path = os.path.join(root, file)
|
||||
|
||||
def format_template(m):
|
||||
replace = m.group(0).replace('href="/', 'href="').replace('src="/', 'src="')
|
||||
if ".js" in replace:
|
||||
replace = ' nonce="{{ script_nonce }}" ' + replace
|
||||
return replace
|
||||
|
||||
# get file content
|
||||
content = ""
|
||||
with open(file_path, "r") as f:
|
||||
content = f.read()
|
||||
content = re.sub(r'(href|src)="\/(css|js|img|favicon|assets|js)\/[^<]*?(?=<|\/>)', format_template, content)
|
||||
# get the content before <body>
|
||||
content = content[: content.index("<body>")] + base_html
|
||||
# write the new content
|
||||
|
||||
with open(file_path, "w") as f:
|
||||
f.write(content)
|
||||
|
||||
# remove previous file if exists
|
||||
if os.path.exists(f"{target_folder}/{os.path.basename(root)}.html"):
|
||||
os.remove(f"{target_folder}/{os.path.basename(root)}.html")
|
||||
|
||||
shutil.copy(file_path, f"{target_folder}/{os.path.basename(root)}.html")
|
||||
|
||||
# I want to run this asynchronusly
|
||||
# I want to get all subfollder of a folder
|
||||
for root, dirs, files in os.walk(folder):
|
||||
for file in files:
|
||||
file_path = os.path.join(root, file)
|
||||
|
||||
# get file content
|
||||
content = ""
|
||||
with open(file_path, "r") as f:
|
||||
content = f.read()
|
||||
# I want to replace all paths using regex like this : /href="\/(css|js|img|favicon|assets|js)|src="\/(assets|js)/g
|
||||
regex_paths = """/href="\/(css|js|img|favicon|assets|js)|src="\/(assets|js)/g"""
|
||||
content = re.sub(regex_paths, "", content)
|
||||
regex_script = """/<script/g"""
|
||||
content = re.sub(regex_script, '<script nonce="{{ script_nonce }}" ', content)
|
||||
# get index of <body>
|
||||
index = content.index("<body>")
|
||||
# get the content before <body>
|
||||
content = content[:index] + base_html
|
||||
# write the new content
|
||||
with open(file_path, "w") as f:
|
||||
f.write(content)
|
||||
|
||||
# remove previous file if exists
|
||||
if os.path.exists(f"{target_folder}/{os.path.basename(root)}.html"):
|
||||
os.remove(f"{target_folder}/{os.path.basename(root)}.html")
|
||||
|
||||
shutil.copy(file_path, f"{target_folder}/{os.path.basename(root)}.html")
|
||||
asyncio.run(move_template_file(root, file, target_folder, base_html))
|
||||
|
||||
|
||||
def move_statics(folder, target_folder):
|
||||
|
||||
async def move_static_file(root, dir, target_folder):
|
||||
dir = os.path.join(root, dir)
|
||||
|
||||
# remove previous folder if exists
|
||||
if os.path.exists(f"{target_folder}/{os.path.basename(dir)}"):
|
||||
shutil.rmtree(f"{target_folder}/{os.path.basename(dir)}")
|
||||
# rename index.html by the name of the folder
|
||||
shutil.move(dir, f"{target_folder}/{os.path.basename(dir)}")
|
||||
|
||||
# I want to get all subfollder of a folder
|
||||
for root, dirs, files in os.walk(folder):
|
||||
for dir in dirs:
|
||||
if dir not in statics:
|
||||
continue
|
||||
dir = os.path.join(root, dir)
|
||||
|
||||
# remove previous folder if exists
|
||||
if os.path.exists(f"{target_folder}/{os.path.basename(dir)}"):
|
||||
shutil.rmtree(f"{target_folder}/{os.path.basename(dir)}")
|
||||
# rename index.html by the name of the folder
|
||||
shutil.move(dir, f"{target_folder}/{os.path.basename(dir)}")
|
||||
|
||||
|
||||
def set_dashboard():
|
||||
move_template(opt_dir_dashboard_pages, ui_dir_templates)
|
||||
move_statics(opt_dir_dashboard, ui_dir_static)
|
||||
|
||||
|
||||
def set_setup():
|
||||
move_template(opt_dir_setup_page, ui_dir_templates)
|
||||
asyncio.run(move_static_file(root, dir, target_folder))
|
||||
|
||||
|
||||
def build():
|
||||
reset()
|
||||
create_base_dirs()
|
||||
run_command(["npm", "install"], True)
|
||||
# Check if node modules exists
|
||||
if not os.path.exists(f"{current_directory}/node_modules"):
|
||||
run_command(["npm", "install"], True)
|
||||
# Create the build
|
||||
run_command(["npm", "run", "build-dashboard"], True)
|
||||
set_dashboard()
|
||||
# run_command(["npm", "run", "build-dashboard"])
|
||||
# run_command(["npm", "run", "build-setup"], True)
|
||||
set_dashboard()
|
||||
# set_setup()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,9 @@ export default defineConfig({
|
|||
port: 3000,
|
||||
},
|
||||
resolve: {
|
||||
// https://vitejs.dev/config/#resolve-extensions
|
||||
// Reduce the amount of extensions that Vite will try to resolv
|
||||
extensions: [".js", ".json", ".vue", ".css"],
|
||||
alias: {
|
||||
"@": resolve(__dirname, "./dashboard"),
|
||||
"@store": resolve(__dirname, "./dashboard/store"),
|
||||
|
|
@ -30,6 +33,7 @@ export default defineConfig({
|
|||
},
|
||||
},
|
||||
build: {
|
||||
minify: "esbuild",
|
||||
chunkSizeWarningLimit: 1024,
|
||||
outDir: "./opt-dashboard",
|
||||
emptyOutDir: "./opt-dashboard",
|
||||
|
|
|
|||
|
|
@ -14,6 +14,9 @@ export default defineConfig({
|
|||
}),
|
||||
],
|
||||
resolve: {
|
||||
// https://vitejs.dev/config/#resolve-extensions
|
||||
// Reduce the amount of extensions that Vite will try to resolve
|
||||
extensions: [".js", ".json", ".vue", ".css"],
|
||||
alias: {
|
||||
"@store": resolve(__dirname, "./dashboard/store"),
|
||||
"@utils": resolve(__dirname, "./dashboard/utils"),
|
||||
|
|
@ -25,6 +28,8 @@ export default defineConfig({
|
|||
},
|
||||
},
|
||||
build: {
|
||||
minify: "esbuild",
|
||||
chunkSizeWarningLimit: 1024,
|
||||
outDir: "./opt-setup",
|
||||
emptyOutDir: "./opt-setup",
|
||||
rollupOptions: {
|
||||
|
|
|
|||
52
src/ui/templates/home.html
vendored
52
src/ui/templates/home.html
vendored
|
|
@ -1,27 +1,27 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/x-icon" href="img/favicon.ico" />
|
||||
<link rel="stylesheet" href="css/style.css" />
|
||||
<link rel="stylesheet" href="css/flag-icons.min.css" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>BunkerWeb | DASHBOARD</title>
|
||||
<script nonce="{{ script_nonce }}" type="module" crossorigin src="assets/home-098d38b3.js"></script>
|
||||
<link rel="modulepreload" crossorigin href="assets/Title-9ae7a316.js">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
{% set data_server_flash = [] %}
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% for category, message in messages %}
|
||||
{% if data_server_flash.append({"type": "error" if category == "error" else "success", "title": "dashboard_error" if category == "error" else "dashboard_success", "message": message}) %}{% endif %}
|
||||
{% endfor %}
|
||||
{% endwith %}
|
||||
<div class='hidden' data-csrf-token='{{ csrf_token() }}'></div>
|
||||
<div class='hidden' data-server-global='{{data_server_global if data_server_global else {}}}'></div>
|
||||
<div class='hidden' data-server-flash='{{data_server_flash|tojson}}'></div>
|
||||
<div class='hidden' data-server-builder='{{data_server_builder[1:-1]}}'></div>
|
||||
<div id='app'></div>
|
||||
</body>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/x-icon" href="img/favicon.ico" />
|
||||
<link rel="stylesheet" href="css/style.css" />
|
||||
<link rel="stylesheet" href="css/flag-icons.min.css" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>BunkerWeb | DASHBOARD</title>
|
||||
<script type="module" crossorigin nonce="{{ script_nonce }}" src="assets/home-BnrNYFnz.js"></script>
|
||||
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/Title-B_4IDnhH.js">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
{% set data_server_flash = [] %}
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% for category, message in messages %}
|
||||
{% if data_server_flash.append({"type": "error" if category == "error" else "success", "title": "dashboard_error" if category == "error" else "dashboard_success", "message": message}) %}{% endif %}
|
||||
{% endfor %}
|
||||
{% endwith %}
|
||||
<div class='hidden' data-csrf-token='{{ csrf_token() }}'></div>
|
||||
<div class='hidden' data-server-global='{{data_server_global if data_server_global else {}}}'></div>
|
||||
<div class='hidden' data-server-flash='{{data_server_flash|tojson}}'></div>
|
||||
<div class='hidden' data-server-builder='{{data_server_builder[1:-1]}}'></div>
|
||||
<div id='app'></div>
|
||||
</body>
|
||||
</html>
|
||||
52
src/ui/templates/instances.html
vendored
52
src/ui/templates/instances.html
vendored
|
|
@ -1,27 +1,27 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/x-icon" href="img/favicon.ico" />
|
||||
<link rel="stylesheet" href="css/style.css" />
|
||||
<link rel="stylesheet" href="css/flag-icons.min.css" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>BunkerWeb | DASHBOARD</title>
|
||||
<script nonce="{{ script_nonce }}" type="module" crossorigin src="assets/instances-32f9ed21.js"></script>
|
||||
<link rel="modulepreload" crossorigin href="assets/Title-9ae7a316.js">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
{% set data_server_flash = [] %}
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% for category, message in messages %}
|
||||
{% if data_server_flash.append({"type": "error" if category == "error" else "success", "title": "dashboard_error" if category == "error" else "dashboard_success", "message": message}) %}{% endif %}
|
||||
{% endfor %}
|
||||
{% endwith %}
|
||||
<div class='hidden' data-csrf-token='{{ csrf_token() }}'></div>
|
||||
<div class='hidden' data-server-global='{{data_server_global if data_server_global else {}}}'></div>
|
||||
<div class='hidden' data-server-flash='{{data_server_flash|tojson}}'></div>
|
||||
<div class='hidden' data-server-builder='{{data_server_builder[1:-1]}}'></div>
|
||||
<div id='app'></div>
|
||||
</body>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/x-icon" href="img/favicon.ico" />
|
||||
<link rel="stylesheet" href="css/style.css" />
|
||||
<link rel="stylesheet" href="css/flag-icons.min.css" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>BunkerWeb | DASHBOARD</title>
|
||||
<script type="module" crossorigin nonce="{{ script_nonce }}" src="assets/instances-DfV5PghU.js"></script>
|
||||
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/Title-B_4IDnhH.js">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
{% set data_server_flash = [] %}
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% for category, message in messages %}
|
||||
{% if data_server_flash.append({"type": "error" if category == "error" else "success", "title": "dashboard_error" if category == "error" else "dashboard_success", "message": message}) %}{% endif %}
|
||||
{% endfor %}
|
||||
{% endwith %}
|
||||
<div class='hidden' data-csrf-token='{{ csrf_token() }}'></div>
|
||||
<div class='hidden' data-server-global='{{data_server_global if data_server_global else {}}}'></div>
|
||||
<div class='hidden' data-server-flash='{{data_server_flash|tojson}}'></div>
|
||||
<div class='hidden' data-server-builder='{{data_server_builder[1:-1]}}'></div>
|
||||
<div id='app'></div>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Reference in a new issue