remove outdated builder + add builder utils

* remove advanced, easy and raw mode and merge in one file
* remove outdated files
* add save_builder utils to easily create json from build or directly update the dev html page
* update new builders
This commit is contained in:
Jordan Blasenhauer 2024-08-12 22:20:07 +02:00
parent acb8ad605f
commit 8c3e2f1ce3
12 changed files with 68 additions and 8790 deletions

File diff suppressed because it is too large Load diff

View file

@ -1 +0,0 @@
W3sidHlwZSI6ICJjYXJkIiwgImxpbmsiOiAiaHR0cHM6Ly9wYW5lbC5idW5rZXJ3ZWIuaW8vP3V0bV9jYW1wYWlnbj1zZWxmJnV0bV9zb3VyY2U9dWkjcHJvIiwgImNvbnRhaW5lckNvbHVtbnMiOiB7InBjIjogNCwgInRhYmxldCI6IDYsICJtb2JpbGUiOiAxMn0sICJ3aWRnZXRzIjogW3sidHlwZSI6ICJTdGF0IiwgImRhdGEiOiB7InRpdGxlIjogImhvbWVfdmVyc2lvbiIsICJzdGF0IjogImhvbWVfZnJlZSIsICJzdWJ0aXRsZSI6ICJob21lX3VwZ3JhZGVfdG9fcHJvIiwgImljb25OYW1lIjogImtleSIsICJzdWJ0aXRsZUNvbG9yIjogIndhcm5pbmcifX1dfSwgeyJ0eXBlIjogImNhcmQiLCAibGluayI6ICJodHRwczovL2dpdGh1Yi5jb20vYnVua2VyaXR5L2J1bmtlcndlYiIsICJjb250YWluZXJDb2x1bW5zIjogeyJwYyI6IDQsICJ0YWJsZXQiOiA2LCAibW9iaWxlIjogMTJ9LCAid2lkZ2V0cyI6IFt7InR5cGUiOiAiU3RhdCIsICJkYXRhIjogeyJ0aXRsZSI6ICJob21lX3ZlcnNpb25fbnVtYmVyIiwgInN0YXQiOiAiMS42LjAtYmV0YSIsICJzdWJ0aXRsZSI6ICJob21lX3VwZGF0ZV9hdmFpbGFibGUiLCAiaWNvbk5hbWUiOiAid2lyZSIsICJzdWJ0aXRsZUNvbG9yIjogIndhcm5pbmcifX1dfSwgeyJ0eXBlIjogImNhcmQiLCAibGluayI6ICJpbnN0YW5jZXMiLCAiY29udGFpbmVyQ29sdW1ucyI6IHsicGMiOiA0LCAidGFibGV0IjogNiwgIm1vYmlsZSI6IDEyfSwgIndpZGdldHMiOiBbeyJ0eXBlIjogIlN0YXQiLCAiZGF0YSI6IHsidGl0bGUiOiAiaG9tZV9pbnN0YW5jZXMiLCAic3RhdCI6IDEsICJzdWJ0aXRsZSI6ICJob21lX3RvdGFsX251bWJlciIsICJpY29uTmFtZSI6ICJib3gifX1dfSwgeyJ0eXBlIjogImNhcmQiLCAibGluayI6ICJzZXJ2aWNlcyIsICJjb250YWluZXJDb2x1bW5zIjogeyJwYyI6IDQsICJ0YWJsZXQiOiA2LCAibW9iaWxlIjogMTJ9LCAid2lkZ2V0cyI6IFt7InR5cGUiOiAiU3RhdCIsICJkYXRhIjogeyJ0aXRsZSI6ICJob21lX3NlcnZpY2VzIiwgInN0YXQiOiAyLCAic3VidGl0bGUiOiAiaG9tZV9hbGxfbWV0aG9kc19pbmNsdWRlZCIsICJpY29uTmFtZSI6ICJkaXNrIn19XX0sIHsidHlwZSI6ICJjYXJkIiwgImxpbmsiOiAicGx1Z2lucyIsICJjb250YWluZXJDb2x1bW5zIjogeyJwYyI6IDQsICJ0YWJsZXQiOiA2LCAibW9iaWxlIjogMTJ9LCAid2lkZ2V0cyI6IFt7InR5cGUiOiAiU3RhdCIsICJkYXRhIjogeyJ0aXRsZSI6ICJob21lX3BsdWdpbnMiLCAic3RhdCI6IDM4LCAic3VidGl0bGUiOiAiaG9tZV9ub19lcnJvciIsICJpY29uTmFtZSI6ICJwdXp6bGUiLCAic3VidGl0bGVDb2xvciI6ICJzdWNjZXNzIn19XX1d

View file

@ -1,7 +1,8 @@
import json
import base64
from utils import save_builder
from builder.advanced_mode import advanced_mode_builder
from pages.advanced_mode import advanced_mode_builder
from pages.easy_mode import easy_mode_builder
from pages.raw_mode import raw_mode_builder
# Default plugins from docker-compose.ui.yml
plugins = [
@ -4303,11 +4304,10 @@ templates_ui = {
}
output = advanced_mode_builder(templates_ui, plugins, global_config, total_config, service_name)
with open("advanced.json", "w") as f:
json.dump(output, f, indent=4)
output_advanced = advanced_mode_builder(templates_ui, plugins, global_config, total_config, service_name)
output_easy = easy_mode_builder(templates_ui, plugins, global_config, total_config, service_name)
output_raw = raw_mode_builder(templates_ui, plugins, global_config, total_config, service_name)
output_base64_bytes = base64.b64encode(bytes(json.dumps(output), "utf-8"))
output_base64_string = output_base64_bytes.decode("ascii")
with open("advanced.txt", "w") as f:
f.write(output_base64_string)
save_builder("advanced", output_advanced, update_page=False)
save_builder("easy", output_easy, update_page=False)
save_builder("raw", output_raw, update_page=False)

View file

@ -1,14 +1,6 @@
from .utils.widgets import button_widget, button_group_widget, title_widget, text_widget, tabulator_widget, input_widget, icons_widget
from .utils.table import add_column
from enum import Enum
class Healths(Enum):
UP = "up"
DOWN = "down"
LOADING = "loading"
columns = [
add_column(title="Name", field="name", formatter="text"),

File diff suppressed because it is too large Load diff

View file

@ -1,94 +0,0 @@
import json
import base64
services = [
{
"USE_REVERSE_PROXY": {"value": "yes", "method": "scheduler", "global": False},
"IS_DRAFT": {"value": "no", "method": "default", "global": False},
"SERVE_FILES": {"value": "no", "method": "scheduler", "global": True},
"REMOTE_PHP": {"value": "", "method": "default", "global": True},
"AUTO_LETS_ENCRYPT": {"value": "no", "method": "default", "global": True},
"USE_CUSTOM_SSL": {"value": "no", "method": "default", "global": True},
"USE_MODSECURITY": {"value": "yes", "method": "default", "global": True},
"USE_BAD_BEHAVIOR": {"value": "yes", "method": "default", "global": True},
"USE_LIMIT_REQ": {"value": "yes", "method": "default", "global": True},
"USE_DNSBL": {"value": "yes", "method": "default", "global": True},
"SERVER_NAME": {"value": "app1.example.com", "method": "scheduler", "global": False},
},
{
"USE_REVERSE_PROXY": {"value": "yes", "method": "scheduler", "global": False},
"IS_DRAFT": {"value": "no", "method": "default", "global": False},
"SERVE_FILES": {"value": "no", "method": "scheduler", "global": True},
"REMOTE_PHP": {"value": "", "method": "default", "global": True},
"AUTO_LETS_ENCRYPT": {"value": "no", "method": "default", "global": True},
"USE_CUSTOM_SSL": {"value": "no", "method": "default", "global": True},
"USE_MODSECURITY": {"value": "yes", "method": "default", "global": True},
"USE_BAD_BEHAVIOR": {"value": "yes", "method": "default", "global": True},
"USE_LIMIT_REQ": {"value": "yes", "method": "default", "global": True},
"USE_DNSBL": {"value": "yes", "method": "default", "global": True},
"SERVER_NAME": {"value": "www.example.com", "method": "scheduler", "global": False},
},
]
data = []
for index, service in enumerate(services):
server_name = service["SERVER_NAME"]["value"]
server_method = service["SERVER_NAME"]["method"]
is_draft = True if service["IS_DRAFT"]["value"] == "yes" else False
is_deletable = False if server_method in ("autoconf", "scheduler") else True
item = []
# Get name
item.append({"name": server_name, "type": "Text", "data": {"text": server_name}})
item.append({"method": server_method, "type": "Text", "data": {"text": server_method}})
item.append(
{
"type": "Button",
"data": {"id": f"open-modal-settings-{index}", "text": "settings", "hideText": False, " color": "info", "size": "normal", "iconName": "settings"},
}
)
item.append(
{
"type": "Button",
"data": {
"attrs": {"data-server-name": server_name},
"id": f"open-modal-manage-{index}",
"text": "manage",
"hideText": False,
" color": "green",
"size": "normal",
"iconName": "manage",
},
}
)
item.append(
{
"type": "Button",
"data": {
"attrs": {"data-server-name": server_name, "data-is-draft": "yes" if is_draft else "no"},
"id": f"open-modal-draft-{index}",
"text": "draft" if is_draft else "online",
"hideText": False,
" color": "cyan",
"size": "normal",
"iconName": "draft" if is_draft else "online",
},
}
)
item.append(
{
"type": "Button",
"data": {
"attrs": {"data-server-name": server_name},
"id": f"open-modal-delete-{index}",
"text": "delete",
"disabled": not is_deletable,
"hideText": False,
" color": "red",
"size": "normal",
"iconName": "trash",
},
}
)
data.append(item)

View file

@ -1,6 +1,4 @@
import json
import base64
from utils import save_builder
from pages.bans2 import bans_builder
bans = [
@ -31,14 +29,5 @@ reasons = ["all", "antibot", "test"]
remains = ["all", "hour(s)", "day(s)"]
builder = bans_builder(bans, reasons, remains)
print("builder", builder)
with open("test_bans2.json", "w") as f:
json.dump(builder, f, indent=4)
output_base64_bytes = base64.b64encode(bytes(json.dumps(builder), "utf-8"))
output_base64_string = output_base64_bytes.decode("ascii")
with open("test_bans2.txt", "w") as f:
f.write(output_base64_string)
save_builder("bans2", builder)

View file

@ -1,5 +1,4 @@
import json
import base64
from utils import save_builder
from pages.configs2 import configs_builder
@ -28,13 +27,5 @@ configs = [
config_types = ["http", "https", "socks4", "socks5"]
builder = configs_builder(configs, config_types)
print("builder", builder)
with open("test_configs2.json", "w") as f:
json.dump(builder, f, indent=4)
output_base64_bytes = base64.b64encode(bytes(json.dumps(builder), "utf-8"))
output_base64_string = output_base64_bytes.decode("ascii")
with open("test_configs2.txt", "w") as f:
f.write(output_base64_string)
save_builder("configs2", builder, update_page=False)

View file

@ -1,5 +1,4 @@
import json
import base64
from utils import save_builder
from pages.instances2 import instances_builder
@ -26,12 +25,4 @@ healths = ["up", "down", "loading"]
builder = instances_builder(instances)
# store on a file
with open("test_instances2.json", "w") as f:
json.dump(builder, f, indent=4)
output_base64_bytes = base64.b64encode(bytes(json.dumps(builder), "utf-8"))
output_base64_string = output_base64_bytes.decode("ascii")
with open("test_instances2.txt", "w") as f:
f.write(output_base64_string)
save_builder("instances2", builder, update_page=False)

View file

@ -1,5 +1,4 @@
import json
import base64
from utils import save_builder
from pages.reports2 import reports_builder
@ -47,15 +46,4 @@ reasons = list(reasons)
builder = reports_builder(reports, reasons, countries, methods, codes)
print("builder", builder)
with open("test_reports2.json", "w") as f:
json.dump(builder, f, indent=4)
output_base64_bytes = base64.b64encode(bytes(json.dumps(builder), "utf-8"))
output_base64_string = output_base64_bytes.decode("ascii")
with open("test_reports2.txt", "w") as f:
f.write(output_base64_string)
save_builder("reports2", builder, update_page=False)

View file

@ -0,0 +1,47 @@
from pathlib import Path
import json
import base64
def save_builder(page_name: str, output: str, store_json: bool = True, update_page: bool = True):
if store_json:
with open(f"test_{page_name.lower()}.json", "w") as f:
json.dump(output, f, indent=4)
data = base64.b64encode(bytes(json.dumps(output), "utf-8"))
data = data.decode("ascii")
# get current directory
current_directory = Path.cwd()
# needed dirs
opt_dir_templates = current_directory.parent.joinpath("dashboard", "pages", page_name)
new_content = f"""
<!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 | {page_name.lower().capitalize()}</title>
</head>
<body>
<div
class="hidden"
data-server-global='{{"username" : "admin", "plugins_page": [{{"id" : "antibot", "name": "Antibot"}}, {{"id": "backup", "name" : "backup"}} ]}}'
></div>
<div
class="hidden"
data-server-flash='[{{"type" : "success", "title" : "title", "message" : "Success feedback"}}, {{"type" : "error", "title" : "title", "message" : "Error feedback"}}, {{"type" : "warning", "title" : "title", "message" : "Warning feedback"}}, {{"type" : "info", "title" : "title", "message" : "Info feedback"}}]'
></div>
<div
class="hidden"
data-server-builder='{data}'
></div>
<div id="app"></div>
<script type="module" src="{page_name.lower()}.js"></script>
</body>
</html>
"""
for file in opt_dir_templates.glob("index.html"):
file.write_text(new_content)

View file

@ -1,3 +1,4 @@
<!doctype html>
<html lang="en">
<head>
@ -11,7 +12,7 @@
<body>
<div
class="hidden"
data-server-global='{"username" : "admin", "plugins_page": [{"id" : "antibot", "name": "Antibot"}, {"id": "backup", "name" : "backup"}]}'
data-server-global='{"username" : "admin", "plugins_page": [{"id" : "antibot", "name": "Antibot"}, {"id": "backup", "name" : "backup"} ]}'
></div>
<div
class="hidden"
@ -19,7 +20,7 @@
></div>
<div
class="hidden"
data-server-builder='[{"type":"card","containerColumns":{"pc":6,"tablet":6,"mobile":12},"widgets":[{"type":"Instance","data":{"details":[{"key":"instances_hostname","value":"bunkerweb"},{"key":"instances_type","value":"manual"},{"key":"instances_status","value":"instances_active"}],"status":"success","title":"bunkerweb","buttons":[{"attrs":{"data-form-INSTANCE_ID":"bunkerweb","data-form-operation":"reload","data-submit-form":"true"},"text":"action_reload","color":"warning","size":"normal"},{"attrs":{"data-form-INSTANCE_ID":"bunkerweb","data-form-operation":"stop","data-submit-form":"true"},"text":"action_stop","color":"error","size":"normal"}]}}]}]'
data-server-builder='W3sidHlwZSI6ICJ0YWJzIiwgIndpZGdldHMiOiBbeyJ0eXBlIjogIkJ1dHRvbmdyb3VwIiwgImRhdGEiOiB7ImJ1dHRvbnMiOiBbeyJ0eXBlIjogIkJ1dHRvbiIsICJkYXRhIjogeyJ0ZXh0IjogImJhbnNfdGFiX2xpc3QiLCAiZGlzcGxheSI6IFsibWFpbiIsIDFdLCAiaWNvbkNvbG9yIjogIndoaXRlIiwgInNpemUiOiAidGFiIiwgImljb25OYW1lIjogImxpc3QifX0sIHsidHlwZSI6ICJCdXR0b24iLCAiZGF0YSI6IHsidGV4dCI6ICJiYW5zX3RhYl9hZGQiLCAiZGlzcGxheSI6IFsibWFpbiIsIDJdLCAiaWNvbkNvbG9yIjogIndoaXRlIiwgInNpemUiOiAidGFiIiwgImljb25OYW1lIjogInBsdXMifX1dfX1dfSwgeyJ0eXBlIjogImNhcmQiLCAiZGlzcGxheSI6IFsibWFpbiIsIDFdLCAid2lkZ2V0cyI6IFt7InR5cGUiOiAiVGFidWxhdG9yIiwgImRhdGEiOiB7ImlkIjogInRhYmxlLWJhbnMtbGlzdCIsICJjb2x1bW5zIjogW3sidGl0bGUiOiAiSVAiLCAiZmllbGQiOiAiaXAiLCAiZm9ybWF0dGVyIjogInRleHQifSwgeyJ0aXRsZSI6ICJSZWFzb24iLCAiZmllbGQiOiAicmVhc29uIiwgImZvcm1hdHRlciI6ICJ0ZXh0In0sIHsidGl0bGUiOiAiQmFuIHN0YXJ0IGRhdGUiLCAiZmllbGQiOiAiYmFuX3N0YXJ0X2RhdGUiLCAiZm9ybWF0dGVyIjogImZpZWxkcyJ9LCB7InRpdGxlIjogIkJhbiBlbmQgZGF0ZSIsICJmaWVsZCI6ICJiYW5fZW5kX2RhdGUiLCAiZm9ybWF0dGVyIjogImZpZWxkcyJ9LCB7InRpdGxlIjogIlJlbWFpbiIsICJmaWVsZCI6ICJyZW1haW4iLCAiZm9ybWF0dGVyIjogInRleHQifV0sICJpdGVtcyI6IFtdLCAiZmlsdGVycyI6IFt7InR5cGUiOiAibGlrZSIsICJmaWVsZHMiOiBbImlwIl0sICJzZXR0aW5nIjogeyJpZCI6ICJpbnB1dC1zZWFyY2gtaXAiLCAibmFtZSI6ICJpbnB1dC1zZWFyY2gtaXAiLCAibGFiZWwiOiAiYmFuc19zZWFyY2hfaXAiLCAidmFsdWUiOiAiIiwgImlucFR5cGUiOiAiaW5wdXQiLCAiY29sdW1ucyI6IHsicGMiOiAzLCAidGFibGV0IjogNCwgIiBtb2JpbGUiOiAxMn19fSwgeyJ0eXBlIjogIj0iLCAiZmllbGRzIjogWyJyZWFzb24iXSwgInNldHRpbmciOiB7ImlkIjogInNlbGVjdC1iYW4tcmVhc29uIiwgIm5hbWUiOiAic2VsZWN0LWJhbi1yZWFzb24iLCAibGFiZWwiOiAiYmFuc19zZWxlY3RfcmVhc29uIiwgInZhbHVlIjogImFsbCIsICJ2YWx1ZXMiOiBbImFsbCIsICJhbGwiLCAiYW50aWJvdCIsICJ0ZXN0Il0sICJpbnBUeXBlIjogInNlbGVjdCIsICJvbmx5RG93biI6IHRydWUsICJjb2x1bW5zIjogeyJwYyI6IDMsICJ0YWJsZXQiOiA0LCAiIG1vYmlsZSI6IDEyfX19LCB7InR5cGUiOiAiPSIsICJmaWVsZHMiOiBbInJlbWFpbiJdLCAic2V0dGluZyI6IHsiaWQiOiAic2VsZWN0LWJhbi1yZW1haW4iLCAibmFtZSI6ICJzZWxlY3QtYmFuLXJlbWFpbiIsICJsYWJlbCI6ICJiYW5zX3NlbGVjdF9yZW1haW4iLCAidmFsdWUiOiAiYWxsIiwgInZhbHVlcyI6IFsiYWxsIiwgImFsbCIsICJob3VyKHMpIiwgImRheShzKSJdLCAiaW5wVHlwZSI6ICJzZWxlY3QiLCAib25seURvd24iOiB0cnVlLCAiY29sdW1ucyI6IHsicGMiOiAzLCAidGFibGV0IjogNCwgIiBtb2JpbGUiOiAxMn19fV19fSwgW3sidHlwZSI6ICJCdXR0b24iLCAiZGF0YSI6IHsidGV4dCI6ICJhY3Rpb25fdW5iYW4iLCAiaWQiOiAidW5iYW4tYnRuIiwgImNvbG9yIjogInN1Y2Nlc3MiLCAibW9kYWwiOiB7IndpZGdldHMiOiBbeyJ0eXBlIjogIlRpdGxlIiwgImRhdGEiOiB7InRpdGxlIjogImJhbnNfdW5iYW5fdGl0bGUifX0sIHsidHlwZSI6ICJUZXh0IiwgImRhdGEiOiB7InRleHQiOiAiYmFuc191bmJhbl9zdWJ0aXRsZSJ9fSwgeyJ0eXBlIjogIkJ1dHRvbmdyb3VwIiwgImRhdGEiOiB7ImJ1dHRvbnMiOiBbeyJ0ZXh0IjogImFjdGlvbl9jbG9zZSIsICJpZCI6ICJjbG9zZS11bmJhbi1idG4iLCAiY29sb3IiOiAiY2xvc2UiLCAiYXR0cnMiOiB7ImRhdGEtY2xvc2UtbW9kYWwiOiAiIn19LCB7InRleHQiOiAiYWN0aW9uX3VuYmFuIiwgImlkIjogInVuYmFuLWJ0bi1jb25maXJtIiwgImNvbG9yIjogInN1Y2Nlc3MifV19fV19fX1dXX0sIHsidHlwZSI6ICJjYXJkIiwgImRpc3BsYXkiOiBbIm1haW4iLCAyXSwgIndpZGdldHMiOiBbeyJ0eXBlIjogIkJ1dHRvbmdyb3VwIiwgImRhdGEiOiB7ImJ1dHRvbnMiOiBbeyJ0eXBlIjogIkJ1dHRvbiIsICJkYXRhIjogeyJ0ZXh0IjogImFjdGlvbl9lbnRyeSIsICJpZCI6ICJhZGQtYmFucy1lbnRyeS1idG4iLCAiY29sb3IiOiAic3VjY2VzcyIsICJpY29uQ29sb3IiOiAid2hpdGUiLCAiaWNvbk5hbWUiOiAicGx1cyIsICJhdHRycyI6IHsiZGF0YS1hZGQtcm93IjogIiJ9fX0sIHsidHlwZSI6ICJCdXR0b24iLCAiZGF0YSI6IHsidGV4dCI6ICJhY3Rpb25fZGVsZXRlX2FsbCIsICJpZCI6ICJhZGQtYmFucy1kZWxldGUtYWxsLWJ0biIsICJjb2xvciI6ICJlcnJvciIsICJpY29uQ29sb3IiOiAid2hpdGUiLCAiaWNvbk5hbWUiOiAidHJhc2giLCAiYXR0cnMiOiB7ImRhdGEtZGVsZXRlLWFsbCI6ICIifX19XX19LCB7InR5cGUiOiAiVGFidWxhdG9yIiwgImRhdGEiOiB7ImlkIjogInRhYmxlLXJlZ2lzdGVyLXBsdWdpbnMiLCAiY29sdW1ucyI6IFt7InRpdGxlIjogIklQIiwgImZpZWxkIjogImlwIiwgImZvcm1hdHRlciI6ICJmaWVsZHMifSwgeyJ0aXRsZSI6ICJCYW4gZW5kIiwgImZpZWxkIjogImJhbl9lbmQiLCAiZm9ybWF0dGVyIjogImZpZWxkcyJ9XSwgIml0ZW1zIjogW3siaWQiOiAxLCAiaXAiOiB7InNldHRpbmciOiB7ImxhYmVsIjogImJhbnNfYWRkX2Jhbl9pcCIsICJuYW1lIjogImRhdGVwaWNrZXItYWRkLWJhbi1pcC0xIiwgImlkIjogImRhdGVwaWNrZXItYWRkLWJhbi1pcC0xIiwgImhpZGVMYWJlbCI6IHRydWUsICJjb2x1bW5zIjogeyJwYyI6IDEyLCAidGFibGV0IjogMTIsICIgbW9iaWxlIjogMTJ9fX0sICJiYW5fZW5kIjogeyJzZXR0aW5nIjogeyJsYWJlbCI6ICJiYW5zX2FkZF9lbmRfZGF0ZSIsICJuYW1lIjogImRhdGVwaWNrZXItYWRkLWJhbi1lbmQtMSIsICJpZCI6ICJkYXRlcGlja2VyLWFkZC1iYW4tZW5kLTEiLCAiaGlkZUxhYmVsIjogdHJ1ZX19LCAiZGVsZXRlIjogeyJ0eXBlIjogIkJ1dHRvbmdyb3VwIiwgImRhdGEiOiB7ImJ1dHRvbnMiOiBbeyJ0eXBlIjogIkJ1dHRvbiIsICJkYXRhIjogeyJ0ZXh0IjogImFjdGlvbl9kZWxldGUiLCAiaWQiOiAiZGVsZXRlLWJhbi0xIiwgImhpZGVUZXh0IjogdHJ1ZSwgImNvbG9yIjogImVycm9yIiwgImljb25Db2xvciI6ICJ3aGl0ZSIsICJpY29uTmFtZSI6ICJ0cmFzaCIsICJhdHRycyI6IHsiZGF0YS1kZWxldGUtcm93IjogIjEifX19XX19fV19fSwgW3sidHlwZSI6ICJCdXR0b24iLCAiZGF0YSI6IHsidGV4dCI6ICJhY3Rpb25fYWRkX2JhbnMiLCAiaWQiOiAiYWRkLWJhbnMtYnRuIiwgImNvbG9yIjogInN1Y2Nlc3MifX1dXX1d'
></div>
<div id="app"></div>
<script type="module" src="bans.js"></script>