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
|
||||
RUN apt update && \
|
||||
apt install -y --no-install-recommends lighttpd && \
|
||||
apt install -y --no-install-recommends lighttpd
|
||||
|
||||
# Expose the default lighttpd port
|
||||
EXPOSE 80
|
||||
|
||||
# Copy any custom config or website content if needed
|
||||
# COPY ./lighttpd.conf /etc/lighttpd/lighttpd.conf
|
||||
# COPY ./html /var/www/html
|
||||
# Copy custom config and website content
|
||||
COPY ./lighttpd_targo.conf /etc/lighttpd/lighttpd.conf
|
||||
COPY ./html /var/www/html
|
||||
|
||||
# Start lighttpd in the foreground
|
||||
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