All fields OK
This commit is contained in:
@ -63,3 +63,4 @@ def upload_photo(section: str):
|
|||||||
return {"status": "ok", "uploaded": uploaded}
|
return {"status": "ok", "uploaded": uploaded}
|
||||||
|
|
||||||
return {"error": "No valid files uploaded"}, 400
|
return {"error": "No valid files uploaded"}, 400
|
||||||
|
|
||||||
|
@ -136,6 +136,10 @@ def photos(section, filename):
|
|||||||
"""Serve uploaded photos from disk."""
|
"""Serve uploaded photos from disk."""
|
||||||
return send_from_directory(PHOTOS_DIR / section, filename)
|
return send_from_directory(PHOTOS_DIR / section, filename)
|
||||||
|
|
||||||
|
@app.route("/photos/<path:filename>")
|
||||||
|
def serve_photo(filename):
|
||||||
|
photos_dir = Path(__file__).resolve().parents[3] / "config" / "photos"
|
||||||
|
return send_from_directory(photos_dir, filename)
|
||||||
|
|
||||||
@app.route("/site-info")
|
@app.route("/site-info")
|
||||||
def site_info():
|
def site_info():
|
||||||
@ -154,6 +158,28 @@ def update_site_info():
|
|||||||
yaml.safe_dump(data, f, sort_keys=False, allow_unicode=True)
|
yaml.safe_dump(data, f, sort_keys=False, allow_unicode=True)
|
||||||
return jsonify({"status": "ok"})
|
return jsonify({"status": "ok"})
|
||||||
|
|
||||||
|
@app.route("/api/themes")
|
||||||
|
def list_themes():
|
||||||
|
themes_dir = Path(__file__).resolve().parents[3] / "config" / "themes"
|
||||||
|
themes = [d.name for d in themes_dir.iterdir() if d.is_dir()]
|
||||||
|
return jsonify(themes)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/api/thumbnail/upload", methods=["POST"])
|
||||||
|
def upload_thumbnail():
|
||||||
|
PHOTOS_DIR = app.config["PHOTOS_DIR"]
|
||||||
|
file = request.files.get("file")
|
||||||
|
if not file:
|
||||||
|
return {"error": "No file provided"}, 400
|
||||||
|
filename = "thumbnail.png"
|
||||||
|
file.save(PHOTOS_DIR / filename)
|
||||||
|
# Update site.yaml
|
||||||
|
with open(SITE_YAML, "r") as f:
|
||||||
|
data = yaml.safe_load(f)
|
||||||
|
data.setdefault("social", {})["thumbnail"] = filename
|
||||||
|
with open(SITE_YAML, "w") as f:
|
||||||
|
yaml.safe_dump(data, f, sort_keys=False, allow_unicode=True)
|
||||||
|
return jsonify({"status": "ok", "filename": filename})
|
||||||
|
|
||||||
# --- Run server ---
|
# --- Run server ---
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
@ -63,6 +63,58 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
const convertImagesCheckbox = document.getElementById("convert-images-checkbox");
|
const convertImagesCheckbox = document.getElementById("convert-images-checkbox");
|
||||||
const resizeImagesCheckbox = document.getElementById("resize-images-checkbox");
|
const resizeImagesCheckbox = document.getElementById("resize-images-checkbox");
|
||||||
|
|
||||||
|
// --- Theme select ---
|
||||||
|
const themeSelect = document.getElementById("theme-select");
|
||||||
|
|
||||||
|
// --- Thumbnail upload ---
|
||||||
|
const thumbnailInput = form?.elements["social.thumbnail"];
|
||||||
|
const thumbnailUpload = document.getElementById("thumbnail-upload");
|
||||||
|
const thumbnailPreview = document.getElementById("thumbnail-preview");
|
||||||
|
|
||||||
|
if (thumbnailUpload) {
|
||||||
|
thumbnailUpload.addEventListener("change", async (e) => {
|
||||||
|
const file = e.target.files[0];
|
||||||
|
if (!file) return;
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append("file", file);
|
||||||
|
const res = await fetch("/api/thumbnail/upload", { method: "POST", body: formData });
|
||||||
|
const result = await res.json();
|
||||||
|
if (result.status === "ok") {
|
||||||
|
if (thumbnailInput) thumbnailInput.value = result.filename;
|
||||||
|
if (thumbnailPreview) {
|
||||||
|
thumbnailPreview.src = `/photos/${result.filename}`;
|
||||||
|
thumbnailPreview.style.display = "block";
|
||||||
|
}
|
||||||
|
status.textContent = "✅ Thumbnail uploaded!";
|
||||||
|
setTimeout(() => status.textContent = "", 2000);
|
||||||
|
} else {
|
||||||
|
status.textContent = "❌ Error uploading thumbnail";
|
||||||
|
setTimeout(() => status.textContent = "", 2000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch theme list and populate select
|
||||||
|
if (themeSelect) {
|
||||||
|
fetch("/api/themes")
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(themes => {
|
||||||
|
themeSelect.innerHTML = "";
|
||||||
|
themes.forEach(theme => {
|
||||||
|
const option = document.createElement("option");
|
||||||
|
option.value = theme;
|
||||||
|
option.textContent = theme;
|
||||||
|
themeSelect.appendChild(option);
|
||||||
|
});
|
||||||
|
// Set selected value after loading config
|
||||||
|
fetch("/api/site-info")
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => {
|
||||||
|
themeSelect.value = data.build?.theme || "";
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Load config
|
// Load config
|
||||||
if (form) {
|
if (form) {
|
||||||
fetch("/api/site-info")
|
fetch("/api/site-info")
|
||||||
@ -81,10 +133,16 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
form.elements["info.keywords"].value = Array.isArray(data.info?.keywords) ? data.info.keywords.join(", ") : (data.info?.keywords || "");
|
form.elements["info.keywords"].value = Array.isArray(data.info?.keywords) ? data.info.keywords.join(", ") : (data.info?.keywords || "");
|
||||||
form.elements["info.author"].value = data.info?.author || "";
|
form.elements["info.author"].value = data.info?.author || "";
|
||||||
form.elements["social.instagram_url"].value = data.social?.instagram_url || "";
|
form.elements["social.instagram_url"].value = data.social?.instagram_url || "";
|
||||||
form.elements["social.thumbnail"].value = data.social?.thumbnail || "";
|
if (thumbnailInput) thumbnailInput.value = data.social?.thumbnail || "";
|
||||||
|
if (thumbnailPreview && data.social?.thumbnail) {
|
||||||
|
thumbnailPreview.src = `/photos/${data.social.thumbnail}`;
|
||||||
|
thumbnailPreview.style.display = "block";
|
||||||
|
}
|
||||||
form.elements["footer.copyright"].value = data.footer?.copyright || "";
|
form.elements["footer.copyright"].value = data.footer?.copyright || "";
|
||||||
form.elements["footer.legal_label"].value = data.footer?.legal_label || "";
|
form.elements["footer.legal_label"].value = data.footer?.legal_label || "";
|
||||||
form.elements["build.theme"].value = data.build?.theme || "";
|
if (themeSelect) {
|
||||||
|
themeSelect.value = data.build?.theme || "";
|
||||||
|
}
|
||||||
form.elements["legals.hoster_name"].value = data.legals?.hoster_name || "";
|
form.elements["legals.hoster_name"].value = data.legals?.hoster_name || "";
|
||||||
form.elements["legals.hoster_adress"].value = data.legals?.hoster_adress || "";
|
form.elements["legals.hoster_adress"].value = data.legals?.hoster_adress || "";
|
||||||
form.elements["legals.hoster_contact"].value = data.legals?.hoster_contact || "";
|
form.elements["legals.hoster_contact"].value = data.legals?.hoster_contact || "";
|
||||||
@ -149,9 +207,9 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
updateMenuItemsFromInputs();
|
updateMenuItemsFromInputs();
|
||||||
updateIpParagraphsFromInputs();
|
updateIpParagraphsFromInputs();
|
||||||
|
|
||||||
// --- Build object with checkboxes ---
|
// --- Build object with checkboxes and theme select ---
|
||||||
const build = {
|
const build = {
|
||||||
theme: form.elements["build.theme"].value,
|
theme: themeSelect ? themeSelect.value : "",
|
||||||
convert_images: !!(convertImagesCheckbox && convertImagesCheckbox.checked),
|
convert_images: !!(convertImagesCheckbox && convertImagesCheckbox.checked),
|
||||||
resize_images: !!(resizeImagesCheckbox && resizeImagesCheckbox.checked)
|
resize_images: !!(resizeImagesCheckbox && resizeImagesCheckbox.checked)
|
||||||
};
|
};
|
||||||
@ -167,7 +225,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
},
|
},
|
||||||
social: {
|
social: {
|
||||||
instagram_url: form.elements["social.instagram_url"].value,
|
instagram_url: form.elements["social.instagram_url"].value,
|
||||||
thumbnail: form.elements["social.thumbnail"].value
|
thumbnail: thumbnailInput ? thumbnailInput.value : ""
|
||||||
},
|
},
|
||||||
menu: {
|
menu: {
|
||||||
items: menuItems
|
items: menuItems
|
||||||
|
@ -55,7 +55,9 @@
|
|||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Social</legend>
|
<legend>Social</legend>
|
||||||
<label>Instagram URL: <input type="text" name="social.instagram_url"></label><br>
|
<label>Instagram URL: <input type="text" name="social.instagram_url"></label><br>
|
||||||
<label>Thumbnail: <input type="text" name="social.thumbnail"></label><br>
|
<label>Thumbnail: <input type="text" name="social.thumbnail" readonly></label>
|
||||||
|
<input type="file" id="thumbnail-upload" accept="image/png,image/jpeg,image/webp">
|
||||||
|
<img id="thumbnail-preview" src="" alt="Thumbnail preview" style="max-width:100px;display:none;">
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Menu</legend>
|
<legend>Menu</legend>
|
||||||
@ -69,7 +71,9 @@
|
|||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Build</legend>
|
<legend>Build</legend>
|
||||||
<label>Theme: <input type="text" name="build.theme"></label><br>
|
<label>Theme:
|
||||||
|
<select name="build.theme" id="theme-select"></select>
|
||||||
|
</label>
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" name="build.convert_images" id="convert-images-checkbox">
|
<input type="checkbox" name="build.convert_images" id="convert-images-checkbox">
|
||||||
Convert images
|
Convert images
|
||||||
|
Reference in New Issue
Block a user