ui - automated build
This commit is contained in:
parent
4ea01bd93f
commit
559b7835d4
@ -99,7 +99,6 @@ end
|
|||||||
-- check if IP is in DNSBLs (only if not in cache)
|
-- check if IP is in DNSBLs (only if not in cache)
|
||||||
if use_dnsbl and not dnsbl.cached() then
|
if use_dnsbl and not dnsbl.cached() then
|
||||||
if dnsbl.check() then
|
if dnsbl.check() then
|
||||||
ngx.log(ngx.WARN, "[BLOCK] IP " .. ngx.var.remote_addr .. " is in DNSBL")
|
|
||||||
ngx.exit(ngx.HTTP_FORBIDDEN)
|
ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
19
ui/Dockerfile
Normal file
19
ui/Dockerfile
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
FROM alpine
|
||||||
|
|
||||||
|
RUN apk add py3-pip apache2-utils bash && \
|
||||||
|
pip3 install docker flask && \
|
||||||
|
mkdir /opt/entrypoint && \
|
||||||
|
mkdir -p /opt/confs/site
|
||||||
|
|
||||||
|
COPY confs/site/ /opt/confs/site
|
||||||
|
COPY entrypoint/* /opt/entrypoint/
|
||||||
|
COPY ui/ /opt/entrypoint/
|
||||||
|
RUN chmod +x /opt/entrypoint/*.py /opt/entrypoint/*.sh
|
||||||
|
|
||||||
|
VOLUME /etc/nginx
|
||||||
|
|
||||||
|
EXPOSE 5000
|
||||||
|
|
||||||
|
WORKDIR /opt/entrypoint
|
||||||
|
ENV FLASK_APP entrypoint.py
|
||||||
|
ENTRYPOINT ["/usr/bin/python3", "-m", "flask", "run", "--host=0.0.0.0"]
|
||||||
19
ui/Dockerfile-amd64
Normal file
19
ui/Dockerfile-amd64
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
FROM amd64/alpine
|
||||||
|
|
||||||
|
RUN apk add py3-pip apache2-utils bash && \
|
||||||
|
pip3 install docker flask && \
|
||||||
|
mkdir /opt/entrypoint && \
|
||||||
|
mkdir -p /opt/confs/site
|
||||||
|
|
||||||
|
COPY confs/site/ /opt/confs/site
|
||||||
|
COPY entrypoint/* /opt/entrypoint/
|
||||||
|
COPY ui/ /opt/entrypoint/
|
||||||
|
RUN chmod +x /opt/entrypoint/*.py /opt/entrypoint/*.sh
|
||||||
|
|
||||||
|
VOLUME /etc/nginx
|
||||||
|
|
||||||
|
EXPOSE 5000
|
||||||
|
|
||||||
|
WORKDIR /opt/entrypoint
|
||||||
|
ENV FLASK_APP entrypoint.py
|
||||||
|
ENTRYPOINT ["/usr/bin/python3", "-m", "flask", "run", "--host=0.0.0.0"]
|
||||||
26
ui/Dockerfile-arm32v7
Normal file
26
ui/Dockerfile-arm32v7
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
FROM alpine AS builder
|
||||||
|
|
||||||
|
ENV QEMU_URL https://github.com/balena-io/qemu/releases/download/v4.0.0%2Bbalena2/qemu-4.0.0.balena2-arm.tar.gz
|
||||||
|
RUN apk add curl && curl -L ${QEMU_URL} | tar zxvf - -C . --strip-components 1
|
||||||
|
|
||||||
|
FROM arm32v7/alpine
|
||||||
|
|
||||||
|
COPY --from=builder qemu-arm-static /usr/bin
|
||||||
|
|
||||||
|
RUN apk add py3-pip apache2-utils bash && \
|
||||||
|
pip3 install docker flask && \
|
||||||
|
mkdir /opt/entrypoint && \
|
||||||
|
mkdir -p /opt/confs/site
|
||||||
|
|
||||||
|
COPY confs/site/ /opt/confs/site
|
||||||
|
COPY entrypoint/* /opt/entrypoint/
|
||||||
|
COPY ui/ /opt/entrypoint/
|
||||||
|
RUN chmod +x /opt/entrypoint/*.py /opt/entrypoint/*.sh
|
||||||
|
|
||||||
|
VOLUME /etc/nginx
|
||||||
|
|
||||||
|
EXPOSE 5000
|
||||||
|
|
||||||
|
WORKDIR /opt/entrypoint
|
||||||
|
ENV FLASK_APP entrypoint.py
|
||||||
|
ENTRYPOINT ["/usr/bin/python3", "-m", "flask", "run", "--host=0.0.0.0"]
|
||||||
26
ui/Dockerfile-arm64v8
Normal file
26
ui/Dockerfile-arm64v8
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
FROM alpine AS builder
|
||||||
|
|
||||||
|
ENV QEMU_URL https://github.com/balena-io/qemu/releases/download/v4.0.0%2Bbalena2/qemu-4.0.0.balena2-aarch64.tar.gz
|
||||||
|
RUN apk add curl && curl -L ${QEMU_URL} | tar zxvf - -C . --strip-components 1
|
||||||
|
|
||||||
|
FROM arm64v8/alpine
|
||||||
|
|
||||||
|
COPY --from=builder qemu-aarch64-static /usr/bin
|
||||||
|
|
||||||
|
RUN apk add py3-pip apache2-utils bash && \
|
||||||
|
pip3 install docker flask && \
|
||||||
|
mkdir /opt/entrypoint && \
|
||||||
|
mkdir -p /opt/confs/site
|
||||||
|
|
||||||
|
COPY confs/site/ /opt/confs/site
|
||||||
|
COPY entrypoint/* /opt/entrypoint/
|
||||||
|
COPY ui/ /opt/entrypoint/
|
||||||
|
RUN chmod +x /opt/entrypoint/*.py /opt/entrypoint/*.sh
|
||||||
|
|
||||||
|
VOLUME /etc/nginx
|
||||||
|
|
||||||
|
EXPOSE 5000
|
||||||
|
|
||||||
|
WORKDIR /opt/entrypoint
|
||||||
|
ENV FLASK_APP entrypoint.py
|
||||||
|
ENTRYPOINT ["/usr/bin/python3", "-m", "flask", "run", "--host=0.0.0.0"]
|
||||||
19
ui/Dockerfile-i386
Normal file
19
ui/Dockerfile-i386
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
FROM i386/alpine
|
||||||
|
|
||||||
|
RUN apk add py3-pip apache2-utils bash && \
|
||||||
|
pip3 install docker flask && \
|
||||||
|
mkdir /opt/entrypoint && \
|
||||||
|
mkdir -p /opt/confs/site
|
||||||
|
|
||||||
|
COPY confs/site/ /opt/confs/site
|
||||||
|
COPY entrypoint/* /opt/entrypoint/
|
||||||
|
COPY ui/ /opt/entrypoint/
|
||||||
|
RUN chmod +x /opt/entrypoint/*.py /opt/entrypoint/*.sh
|
||||||
|
|
||||||
|
VOLUME /etc/nginx
|
||||||
|
|
||||||
|
EXPOSE 5000
|
||||||
|
|
||||||
|
WORKDIR /opt/entrypoint
|
||||||
|
ENV FLASK_APP entrypoint.py
|
||||||
|
ENTRYPOINT ["/usr/bin/python3", "-m", "flask", "run", "--host=0.0.0.0"]
|
||||||
85
ui/config.py
Normal file
85
ui/config.py
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
import utils
|
||||||
|
import subprocess, shutil, os, traceback
|
||||||
|
|
||||||
|
def generate(instances, vars) :
|
||||||
|
try :
|
||||||
|
# Get env vars from bunkerized-nginx instances
|
||||||
|
vars_instances = {}
|
||||||
|
for instance_id, instance in instances.items() :
|
||||||
|
for var_value in instance.attrs["Config"]["Env"] :
|
||||||
|
var = var_value.split("=")[0]
|
||||||
|
value = var_value.replace(var + "=", "", 1)
|
||||||
|
vars_instances[var] = value
|
||||||
|
vars_defaults = vars.copy()
|
||||||
|
vars_defaults.update(vars_instances)
|
||||||
|
vars_defaults.update(vars)
|
||||||
|
# Call site-config.sh to generate the config
|
||||||
|
proc = subprocess.run(["/opt/entrypoint/site-config.sh", vars["SERVER_NAME"]], env=vars_defaults, capture_output=True)
|
||||||
|
if proc.returncode == 0 :
|
||||||
|
return True
|
||||||
|
except Exception as e :
|
||||||
|
traceback.print_exc()
|
||||||
|
utils.log("[!] Error while generating config : " + str(e))
|
||||||
|
return False
|
||||||
|
|
||||||
|
def activate(instances, vars) :
|
||||||
|
try :
|
||||||
|
# Check if file exists
|
||||||
|
if not os.path.isfile("/etc/nginx/" + vars["SERVER_NAME"] + "/server.conf") :
|
||||||
|
utils.log("[!] /etc/nginx/" + vars["SERVER_NAME"] + "/server.conf doesn't exist")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Include the server conf
|
||||||
|
utils.replace_in_file("/etc/nginx/nginx.conf", "}", "include /etc/nginx/" + vars["SERVER_NAME"] + "/server.conf;\n}")
|
||||||
|
|
||||||
|
# Send SIGHUP to all running instances
|
||||||
|
for instance_id, instance in instances.items() :
|
||||||
|
if instance.status == "running" :
|
||||||
|
try :
|
||||||
|
instance.kill("SIGHUP")
|
||||||
|
utils.log("[*] Sent SIGHUP signal to bunkerized-nginx instance " + instance.name + " / " + instance.id)
|
||||||
|
except docker.errors.APIError as e :
|
||||||
|
utils.log("[!] Docker error while sending SIGHUP signal : " + str(e))
|
||||||
|
return True
|
||||||
|
except Exception as e :
|
||||||
|
utils.log("[!] Error while activating config : " + str(e))
|
||||||
|
return False
|
||||||
|
|
||||||
|
def deactivate(instances, vars) :
|
||||||
|
try :
|
||||||
|
# Check if file exists
|
||||||
|
if not os.path.isfile("/etc/nginx/" + vars["SERVER_NAME"] + "/server.conf") :
|
||||||
|
utils.log("[!] /etc/nginx/" + vars["SERVER_NAME"] + "/server.conf doesn't exist")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Remove the include
|
||||||
|
utils.replace_in_file("/etc/nginx/nginx.conf", "include /etc/nginx/" + vars["SERVER_NAME"] + "/server.conf;\n", "")
|
||||||
|
|
||||||
|
# Send SIGHUP to all running instances
|
||||||
|
for instance_id, instance in instances.items() :
|
||||||
|
if instance.status == "running" :
|
||||||
|
try :
|
||||||
|
instance.kill("SIGHUP")
|
||||||
|
utils.log("[*] Sent SIGHUP signal to bunkerized-nginx instance " + instance.name + " / " + instance.id)
|
||||||
|
except docker.errors.APIError as e :
|
||||||
|
utils.log("[!] Docker error while sending SIGHUP signal : " + str(e))
|
||||||
|
return True
|
||||||
|
except Exception as e :
|
||||||
|
utils.log("[!] Error while deactivating config : " + str(e))
|
||||||
|
return False
|
||||||
|
|
||||||
|
def remove(instances, vars) :
|
||||||
|
try :
|
||||||
|
# Check if file exists
|
||||||
|
if not os.path.isfile("/etc/nginx/" + vars["SERVER_NAME"] + "/server.conf") :
|
||||||
|
utils.log("[!] /etc/nginx/" + vars["SERVER_NAME"] + "/server.conf doesn't exist")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Remove the folder
|
||||||
|
shutil.rmtree("/etc/nginx/" + vars["SERVER_NAME"])
|
||||||
|
return True
|
||||||
|
except Exception as e :
|
||||||
|
utils.log("[!] Error while deactivating config : " + str(e))
|
||||||
|
return False
|
||||||
109
ui/entrypoint-autoconf.py
Normal file
109
ui/entrypoint-autoconf.py
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
import utils, config
|
||||||
|
import docker, os, stat, sys
|
||||||
|
|
||||||
|
def process(container, event) :
|
||||||
|
global instances, containers
|
||||||
|
|
||||||
|
# Process instance event
|
||||||
|
if "bunkerized-nginx.AUTOCONF" in container.labels :
|
||||||
|
if event == "create" :
|
||||||
|
instances[container.id] = container
|
||||||
|
utils.log("[*] bunkerized-nginx instance created : " + container.name + " / " + container.id)
|
||||||
|
elif event == "start" :
|
||||||
|
instances[container.id].reload()
|
||||||
|
utils.log("[*] bunkerized-nginx instance started : " + container.name + " / " + container.id)
|
||||||
|
elif event == "die" :
|
||||||
|
instances[container.id].reload()
|
||||||
|
utils.log("[*] bunkerized-nginx instance stopped : " + container.name + " / " + container.id)
|
||||||
|
elif event == "destroy" :
|
||||||
|
del instances[container.id]
|
||||||
|
utils.log("[*] bunkerized-nginx instance removed : " + container.name + " / " + container.id)
|
||||||
|
|
||||||
|
# Process container event
|
||||||
|
elif "bunkerized-nginx.SERVER_NAME" in container.labels :
|
||||||
|
# Convert labels to env vars
|
||||||
|
vars = { k.replace("bunkerized-nginx.", "", 1) : v for k, v in container.labels.items() if k.startswith("bunkerized-nginx.")}
|
||||||
|
if event == "create" :
|
||||||
|
if config.generate(instances, vars) :
|
||||||
|
utils.log("[*] Generated config for " + vars["SERVER_NAME"])
|
||||||
|
containers[container.id] = container
|
||||||
|
else :
|
||||||
|
utils.log("[!] Can't generate config for " + vars["SERVER_NAME"])
|
||||||
|
elif event == "start" :
|
||||||
|
if container.id in containers :
|
||||||
|
containers[container.id].reload()
|
||||||
|
if config.activate(instances, vars) :
|
||||||
|
utils.log("[*] Activated config for " + vars["SERVER_NAME"])
|
||||||
|
else :
|
||||||
|
utils.log("[!] Can't activate config for " + vars["SERVER_NAME"])
|
||||||
|
elif event == "die" :
|
||||||
|
if container.id in containers :
|
||||||
|
containers[container.id].reload()
|
||||||
|
if config.deactivate(instances, vars) :
|
||||||
|
utils.log("[*] Deactivated config for " + vars["SERVER_NAME"])
|
||||||
|
else :
|
||||||
|
utils.log("[!] Can't deactivate config for " + vars["SERVER_NAME"])
|
||||||
|
elif event == "destroy" :
|
||||||
|
if container.id in containers :
|
||||||
|
del containers[container.id]
|
||||||
|
if config.remove(vars) :
|
||||||
|
utils.log("[*] Removed config for " + vars["SERVER_NAME"])
|
||||||
|
else :
|
||||||
|
utils.log("[!] Can't remove config for " + vars["SERVER_NAME"])
|
||||||
|
|
||||||
|
# Connect to the endpoint
|
||||||
|
endpoint = "/var/run/docker.sock"
|
||||||
|
if not os.path.exists(endpoint) or not stat.S_ISSOCK(os.stat(endpoint).st_mode) :
|
||||||
|
utils.log("[!] /var/run/docker.sock not found (is it mounted ?)")
|
||||||
|
sys.exit(1)
|
||||||
|
try :
|
||||||
|
client = docker.DockerClient(base_url='unix:///var/run/docker.sock')
|
||||||
|
except Exception as e :
|
||||||
|
utils.log("[!] Can't instantiate DockerClient : " + str(e))
|
||||||
|
sys.exit(2)
|
||||||
|
|
||||||
|
# Get all bunkerized-nginx instances and web services created before
|
||||||
|
instances = {}
|
||||||
|
containers = {}
|
||||||
|
try :
|
||||||
|
before = client.containers.list(all=True, filters={"label" : "bunkerized-nginx.AUTOCONF"}) + client.containers.list(all=True, filters={"label" : "bunkerized-nginx.SERVER_NAME"})
|
||||||
|
except docker.errors.APIError as e :
|
||||||
|
utils.log("[!] Docker API error " + str(e))
|
||||||
|
sys.exit(3)
|
||||||
|
for container in before :
|
||||||
|
if container.status in ("restarting", "running", "created", "exited") :
|
||||||
|
process(container, "create")
|
||||||
|
if container.status == "running" :
|
||||||
|
process(container, "start")
|
||||||
|
|
||||||
|
# Process events received from Docker
|
||||||
|
try :
|
||||||
|
for event in client.events(decode=True) :
|
||||||
|
|
||||||
|
# Process only container events
|
||||||
|
if event["Type"] != "container" :
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Get Container object
|
||||||
|
try :
|
||||||
|
container = client.containers.get(event["id"])
|
||||||
|
except docker.errors.NotFound as e :
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Check if there is an interesting label
|
||||||
|
interesting = False
|
||||||
|
for label in container.labels :
|
||||||
|
if label in ("bunkerized-nginx.SERVER_NAME", "bunkerized-nginx.AUTOCONF") :
|
||||||
|
interesting = True
|
||||||
|
break
|
||||||
|
if not interesting :
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Process the event
|
||||||
|
process(container, event["Action"])
|
||||||
|
|
||||||
|
except docker.errors.APIError as e :
|
||||||
|
utils.log("[!] Docker API error " + str(e))
|
||||||
|
sys.exit(4)
|
||||||
9
ui/entrypoint.py
Normal file
9
ui/entrypoint.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
from flask import Flask, render_template
|
||||||
|
|
||||||
|
app = Flask(__name__, static_url_path="/", static_folder="static", template_folder="templates")
|
||||||
|
|
||||||
|
@app.route('/')
|
||||||
|
def home():
|
||||||
|
return render_template("home.html", title="Home")
|
||||||
12
ui/hooks/post_push
Normal file
12
ui/hooks/post_push
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
curl -Lo manifest-tool https://github.com/estesp/manifest-tool/releases/download/v1.0.3/manifest-tool-linux-amd64
|
||||||
|
chmod +x manifest-tool
|
||||||
|
|
||||||
|
VERSION=$(cat VERSION | tr -d '\n')
|
||||||
|
if [ "$SOURCE_BRANCH" = "dev" ] ; then
|
||||||
|
./manifest-tool push from-args --ignore-missing --platforms linux/amd64,linux/386,linux/arm/v7,linux/arm64/v8 --template bunkerity/bunkerized-nginx-ui:dev-ARCHVARIANT --target bunkerity/bunkerized-nginx-ui:dev
|
||||||
|
elif [ "$SOURCE_BRANCH" = "master" ] ; then
|
||||||
|
./manifest-tool push from-args --ignore-missing --platforms linux/amd64,linux/386,linux/arm/v7,linux/arm64/v8 --template bunkerity/bunkerized-nginx-ui:ARCHVARIANT --target bunkerity/bunkerized-nginx-ui:${VERSION}
|
||||||
|
./manifest-tool push from-args --ignore-missing --platforms linux/amd64,linux/386,linux/arm/v7,linux/arm64/v8 --template bunkerity/bunkerized-nginx-ui:ARCHVARIANT --target bunkerity/bunkerized-nginx-ui:latest
|
||||||
|
fi
|
||||||
5
ui/hooks/pre_build
Normal file
5
ui/hooks/pre_build
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Register qemu-*-static for all supported processors except the
|
||||||
|
# current one, but also remove all registered binfmt_misc before
|
||||||
|
docker run --rm --privileged multiarch/qemu-user-static:register --reset
|
||||||
7
ui/static/css/bootstrap.min.css
vendored
Normal file
7
ui/static/css/bootstrap.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
17
ui/static/css/custom.css
Normal file
17
ui/static/css/custom.css
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
.bd-placeholder-img {
|
||||||
|
font-size: 1.125rem;
|
||||||
|
text-anchor: middle;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.bd-placeholder-img-lg {
|
||||||
|
font-size: 3.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main > .container {
|
||||||
|
padding: 60px 15px 0;
|
||||||
|
}
|
||||||
7
ui/static/js/bootstrap.bundle.min.js
vendored
Normal file
7
ui/static/js/bootstrap.bundle.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
24
ui/templates/base.html
Normal file
24
ui/templates/base.html
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en" class="h-100">
|
||||||
|
|
||||||
|
{% include "head.html" %}
|
||||||
|
|
||||||
|
<body class="d-flex flex-column h-100">
|
||||||
|
|
||||||
|
{% include "menu.html" %}
|
||||||
|
|
||||||
|
<main class="flex-shrink-0">
|
||||||
|
<div class="container">
|
||||||
|
|
||||||
|
{% block content %}{% endblock %}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
{% include "footer.html" %}
|
||||||
|
|
||||||
|
{% include "end.html" %}
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
1
ui/templates/end.html
Normal file
1
ui/templates/end.html
Normal file
@ -0,0 +1 @@
|
|||||||
|
<script src="/js/bootstrap.bundle.min.js"></script>
|
||||||
7
ui/templates/footer.html
Normal file
7
ui/templates/footer.html
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<footer class="footer mt-auto py-3 bg-light">
|
||||||
|
<div class="container text-center">
|
||||||
|
<span class="text-muted">
|
||||||
|
<a href="https://github.com/bunkerity/bunkerized-nginx">bunkerized-nginx</a> user interface
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
10
ui/templates/head.html
Normal file
10
ui/templates/head.html
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta name="description" content="bunkerized-nginx user interface">
|
||||||
|
<meta name="author" content="bunkerity">
|
||||||
|
<title>{{ title }}</title>
|
||||||
|
<!-- TODO : favicon -->
|
||||||
|
<link href="/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link href="/css/custom.css" rel="stylesheet">
|
||||||
|
</head>
|
||||||
7
ui/templates/home.html
Normal file
7
ui/templates/home.html
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
Lorem ipsum.
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
20
ui/templates/menu.html
Normal file
20
ui/templates/menu.html
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<header>
|
||||||
|
<nav class="navbar navbar-expand-md navbar-dark fixed-top bg-dark">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<a class="navbar-brand" href="#">bunkerized-nginx-ui</a>
|
||||||
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarCollapse" aria-controls="navbarCollapse" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse" id="navbarCollapse">
|
||||||
|
<ul class="navbar-nav me-auto mb-2 mb-md-0">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/">Home</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="#">Link</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
13
ui/utils.py
Normal file
13
ui/utils.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
def log(event) :
|
||||||
|
print("[" + str(datetime.datetime.now().replace(microsecond=0)) + "] " + event, flush=True)
|
||||||
|
|
||||||
|
def replace_in_file(file, old_str, new_str) :
|
||||||
|
with open(file) as f :
|
||||||
|
data = f.read()
|
||||||
|
data = data[::-1].replace(old_str[::-1], new_str[::-1], 1)[::-1]
|
||||||
|
with open(file, "w") as f :
|
||||||
|
f.write(data)
|
||||||
Loading…
x
Reference in New Issue
Block a user