big update: refactored to add UI and communicate with parser through flask
This commit is contained in:
parent
6e11a9cf38
commit
417b6e9650
|
@ -6,14 +6,14 @@ ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
# Install lighttpd and clean up apt cache
|
# Install lighttpd and clean up apt cache
|
||||||
RUN apt update && \
|
RUN apt update && \
|
||||||
apt install -y --no-install-recommends lighttpd && \
|
apt install -y --no-install-recommends lighttpd
|
||||||
|
|
||||||
# Expose the default lighttpd port
|
# Expose the default lighttpd port
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
|
|
||||||
# Copy any custom config or website content if needed
|
# Copy custom config and website content
|
||||||
# COPY ./lighttpd.conf /etc/lighttpd/lighttpd.conf
|
COPY ./lighttpd_targo.conf /etc/lighttpd/lighttpd.conf
|
||||||
# COPY ./html /var/www/html
|
COPY ./html /var/www/html
|
||||||
|
|
||||||
# Start lighttpd in the foreground
|
# Start lighttpd in the foreground
|
||||||
CMD ["lighttpd", "-D", "-f", "/etc/lighttpd/lighttpd.conf"]
|
CMD ["lighttpd", "-D", "-f", "/etc/lighttpd/lighttpd.conf"]
|
||||||
|
|
200
html/index.html
Normal file
200
html/index.html
Normal file
|
@ -0,0 +1,200 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>TV Channel ID Manager</title>
|
||||||
|
|
||||||
|
<!-- Bootstrap 5 via CDN -->
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.7/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-LN+7fdVzj6u52u30Kp6M/trliBMCMKTyK833zpbD+pXdCLuTusPj697FH4R/5mcr" crossorigin="anonymous">
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.body-container {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
form {
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
.id-input {
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
}
|
||||||
|
.id-input input {
|
||||||
|
flex-grow: 1;
|
||||||
|
padding: 0.5em;
|
||||||
|
}
|
||||||
|
.controls {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.controls button {
|
||||||
|
padding: 0.5em 1em;
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
.bg-targo-green{
|
||||||
|
background-color: #019547;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<nav class="navbar bg-targo-green">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<a class="navbar-brand" href="#">
|
||||||
|
<img src="https://www.targo.ca/wp-content/uploads/2022/03/logo-1.png" alt="Bootstrap" height="32" class="d-inline">
|
||||||
|
<div class="d-inline p-3 text-light">CHANNEL ID MANAGER</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="body-container">
|
||||||
|
<ul id="channelList" class="list-group mt-5 text-center">Loading...</ul>
|
||||||
|
|
||||||
|
<form id="addForm">
|
||||||
|
<div id="inputs">
|
||||||
|
<!-- Inputs will be dynamically added here -->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="controls">
|
||||||
|
<button type="button" id="addField" class="btn btn-outline-primary">+ Add Field</button>
|
||||||
|
<button type="submit" class="btn btn-primary" id="liveToastBtn">Submit All</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="toast-container position-fixed bottom-0 start-50 translate-middle p-3">
|
||||||
|
<div class="toast" role="alert" id="liveToast">
|
||||||
|
<div class="toast-header">
|
||||||
|
<img id="toast-image" src="" alt="" height="48">
|
||||||
|
<strong class="me-auto" id="toast-header"></strong>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="toast-body" id="toast-body"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="importmap">
|
||||||
|
{
|
||||||
|
"imports": {
|
||||||
|
"@popperjs/core": "https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/esm/popper.min.js",
|
||||||
|
"bootstrap": "https://cdn.jsdelivr.net/npm/bootstrap@5.3.7/dist/js/bootstrap.esm.min.js"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="module">
|
||||||
|
import * as bootstrap from 'bootstrap'
|
||||||
|
|
||||||
|
const inputsContainer = document.getElementById("inputs");
|
||||||
|
const addForm = document.getElementById("addForm");
|
||||||
|
const toastHeader = document.getElementById("toast-header");
|
||||||
|
const toastBody = document.getElementById("toast-body");
|
||||||
|
const toastTrigger = document.getElementById('liveToastBtn')
|
||||||
|
const toastLiveExample = document.getElementById('liveToast')
|
||||||
|
const toastImage = document.getElementById('toast-image')
|
||||||
|
|
||||||
|
function createInputField(value = "") {
|
||||||
|
const div = document.createElement("div");
|
||||||
|
div.className = "input-group mb-3";
|
||||||
|
|
||||||
|
const prependSpan = document.createElement("span");
|
||||||
|
prependSpan.textContent = "Channel ID";
|
||||||
|
prependSpan.className = "input-group-text";
|
||||||
|
prependSpan.id = "basic-addon1";
|
||||||
|
|
||||||
|
const input = document.createElement("input");
|
||||||
|
input.type = "text";
|
||||||
|
input.placeholder = "Example: meteomedia.ca";
|
||||||
|
input.className = "form-control";
|
||||||
|
input.value = value;
|
||||||
|
|
||||||
|
const removeBtn = document.createElement("button");
|
||||||
|
removeBtn.type = "button";
|
||||||
|
removeBtn.textContent = "Remove";
|
||||||
|
removeBtn.className = "btn btn-outline-danger";
|
||||||
|
removeBtn.onclick = () => div.remove();
|
||||||
|
|
||||||
|
div.appendChild(prependSpan);
|
||||||
|
div.appendChild(input);
|
||||||
|
div.appendChild(removeBtn);
|
||||||
|
return div;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addInputField(value = "") {
|
||||||
|
inputsContainer.appendChild(createInputField(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById("addField").addEventListener("click", () => {
|
||||||
|
addInputField();
|
||||||
|
});
|
||||||
|
|
||||||
|
addForm.addEventListener("submit", async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const ids = Array.from(inputsContainer.querySelectorAll("input"))
|
||||||
|
.map(input => input.value.trim())
|
||||||
|
.filter(id => id.length > 0);
|
||||||
|
console.log(ids)
|
||||||
|
|
||||||
|
if (ids.length === 0) {
|
||||||
|
toastImage.src = "https://media.tenor.com/SB7g0zs9y6YAAAAM/jurassic-park.gif"
|
||||||
|
toastHeader.textContent = "Whoops!"
|
||||||
|
toastBody.textContent = "Please enter at least one ID.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const res = await fetch("/add-id", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ ids })
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.ok) {
|
||||||
|
inputsContainer.innerHTML = "";
|
||||||
|
for (let i = 0; i < 3; i++) addInputField();
|
||||||
|
loadIDs();
|
||||||
|
toastImage.src = ""
|
||||||
|
toastHeader.textContent = "Success!"
|
||||||
|
toastBody.textContent = "ID list successfully updated.";
|
||||||
|
} else {
|
||||||
|
toastImage.src = "https://media.tenor.com/SB7g0zs9y6YAAAAM/jurassic-park.gif"
|
||||||
|
toastHeader.textContent = "Whoops!"
|
||||||
|
toastBody.textContent = "Failed to submit IDs.";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
async function loadIDs() {
|
||||||
|
const res = await fetch("/data/xml_tv_ids.txt");
|
||||||
|
const text = await res.text();
|
||||||
|
const ids = text.trim().split('\n');
|
||||||
|
const list = document.getElementById("channelList");
|
||||||
|
list.innerHTML = '';
|
||||||
|
ids.forEach(id => {
|
||||||
|
const li = document.createElement("li");
|
||||||
|
li.textContent = id;
|
||||||
|
li.className = "list-group-item";
|
||||||
|
list.appendChild(li);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toastTrigger) {
|
||||||
|
const toastBootstrap = bootstrap.Toast.getOrCreateInstance(toastLiveExample)
|
||||||
|
toastTrigger.addEventListener('click', () => {
|
||||||
|
toastBootstrap.show()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start with 3 fields
|
||||||
|
for (let i = 0; i < 3; i++) addInputField();
|
||||||
|
loadIDs();
|
||||||
|
</script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.7/dist/js/bootstrap.bundle.min.js" integrity="sha384-ndDqU0Gzau9qJ1lfW4pNLlhNTkCfHzAVBReH9diLvGRem5+R9g2FzA8ZGN954O5Q" crossorigin="anonymous"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js" integrity="sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r" crossorigin="anonymous"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.7/dist/js/bootstrap.min.js" integrity="sha384-7qAoOXltbVP82dhxHAUje59V5r2YsVfBafyUDxEdApLPmcdhBPg1DKg1ERo0BZlK" crossorigin="anonymous"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
59
lighttpd_targo.conf
Normal file
59
lighttpd_targo.conf
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
server.modules = (
|
||||||
|
"mod_indexfile",
|
||||||
|
"mod_access",
|
||||||
|
"mod_alias",
|
||||||
|
"mod_redirect",
|
||||||
|
"mod_setenv",
|
||||||
|
)
|
||||||
|
|
||||||
|
server.document-root = "/var/www/html"
|
||||||
|
server.upload-dirs = ( "/var/cache/lighttpd/uploads" )
|
||||||
|
server.errorlog = "/var/log/lighttpd/error.log"
|
||||||
|
server.pid-file = "/run/lighttpd.pid"
|
||||||
|
server.username = "www-data"
|
||||||
|
server.groupname = "www-data"
|
||||||
|
server.port = 80
|
||||||
|
|
||||||
|
# features
|
||||||
|
#https://redmine.lighttpd.net/projects/lighttpd/wiki/Server_feature-flagsDetails
|
||||||
|
server.feature-flags += ("server.h2proto" => "enable")
|
||||||
|
server.feature-flags += ("server.h2c" => "enable")
|
||||||
|
server.feature-flags += ("server.graceful-shutdown-timeout" => 5)
|
||||||
|
#server.feature-flags += ("server.graceful-restart-bg" => "enable")
|
||||||
|
|
||||||
|
# strict parsing and normalization of URL for consistency and security
|
||||||
|
# https://redmine.lighttpd.net/projects/lighttpd/wiki/Server_http-parseoptsDetails
|
||||||
|
# (might need to explicitly set "url-path-2f-decode" = "disable"
|
||||||
|
# if a specific application is encoding URLs inside url-path)
|
||||||
|
server.http-parseopts = (
|
||||||
|
"header-strict" => "enable",# default
|
||||||
|
"host-strict" => "enable",# default
|
||||||
|
"host-normalize" => "enable",# default
|
||||||
|
"url-normalize-unreserved"=> "enable",# recommended highly
|
||||||
|
"url-normalize-required" => "enable",# recommended
|
||||||
|
"url-ctrls-reject" => "enable",# recommended
|
||||||
|
"url-path-2f-decode" => "enable",# recommended highly (unless breaks app)
|
||||||
|
#"url-path-2f-reject" => "enable",
|
||||||
|
"url-path-dotseg-remove" => "enable",# recommended highly (unless breaks app)
|
||||||
|
#"url-path-dotseg-reject" => "enable",
|
||||||
|
#"url-query-20-plus" => "enable",# consistency in query string
|
||||||
|
)
|
||||||
|
|
||||||
|
index-file.names = ( "index.php", "index.html" )
|
||||||
|
url.access-deny = ( "~", ".inc" )
|
||||||
|
static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" )
|
||||||
|
|
||||||
|
# default listening port for IPv6 falls back to the IPv4 port
|
||||||
|
include_shell "/usr/share/lighttpd/use-ipv6.pl " + server.port
|
||||||
|
include_shell "/usr/share/lighttpd/create-mime.conf.pl"
|
||||||
|
include "/etc/lighttpd/conf-enabled/*.conf"
|
||||||
|
|
||||||
|
#server.compat-module-load = "disable"
|
||||||
|
server.modules += (
|
||||||
|
"mod_dirlisting",
|
||||||
|
"mod_staticfile",
|
||||||
|
)
|
||||||
|
|
||||||
|
$HTTP["url"] =~ "clean_CANADA_USA\.xml$" {
|
||||||
|
setenv.add-response-header = ( "Content-Disposition" => "attachment" )
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user