Skip to content

Commit

Permalink
enhance front build perf + update Dockerfile
Browse files Browse the repository at this point in the history
* add vite config to avoir useless resolving
* add asyncio for better perf
* Dockerfile we build front and remove client folder from container
  • Loading branch information
syrk4web committed Jul 22, 2024
1 parent ec592a2 commit 7f6e623
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 106 deletions.
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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
14 changes: 12 additions & 2 deletions src/ui/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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/* && \
Expand All @@ -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 <[email protected]>"
LABEL version="1.6.0-beta"
LABEL version="1.5.9"
LABEL url="https://www.bunkerweb.io"
LABEL bunkerweb.type="ui"

Expand Down
119 changes: 68 additions & 51 deletions src/ui/client/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import os
import shutil
import re
import asyncio

# get current directory
current_directory = os.path.dirname(os.path.realpath(__file__))
Expand All @@ -19,31 +20,44 @@
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:
process.wait()

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):
Expand All @@ -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()


Expand Down
4 changes: 4 additions & 0 deletions src/ui/client/vite.config.dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
Expand All @@ -30,6 +33,7 @@ export default defineConfig({
},
},
build: {
minify: "esbuild",
chunkSizeWarningLimit: 1024,
outDir: "./opt-dashboard",
emptyOutDir: "./opt-dashboard",
Expand Down
5 changes: 5 additions & 0 deletions src/ui/client/vite.config.setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
Expand All @@ -25,6 +28,8 @@ export default defineConfig({
},
},
build: {
minify: "esbuild",
chunkSizeWarningLimit: 1024,
outDir: "./opt-setup",
emptyOutDir: "./opt-setup",
rollupOptions: {
Expand Down
52 changes: 26 additions & 26 deletions src/ui/templates/home.html
Original file line number Diff line number Diff line change
@@ -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 changes: 26 additions & 26 deletions src/ui/templates/instances.html
Original file line number Diff line number Diff line change
@@ -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>

0 comments on commit 7f6e623

Please sign in to comment.