Reworked flow
This commit is contained in:
@ -2,49 +2,64 @@ import logging
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from flask import Blueprint, request, current_app
|
from flask import Blueprint, request, current_app
|
||||||
from werkzeug.utils import secure_filename
|
from werkzeug.utils import secure_filename
|
||||||
|
from src.py.builder.gallery_builder import update_gallery, update_hero
|
||||||
|
|
||||||
# Create a Flask blueprint for upload routes
|
# --- Create Flask blueprint for upload routes ---
|
||||||
upload_bp = Blueprint("upload", __name__)
|
upload_bp = Blueprint("upload", __name__)
|
||||||
|
|
||||||
# Allowed file extensions for uploads
|
# --- Allowed file types ---
|
||||||
ALLOWED_EXTENSIONS = {"png", "jpg", "jpeg", "webp"}
|
ALLOWED_EXTENSIONS = {"png", "jpg", "jpeg", "webp"}
|
||||||
|
|
||||||
# Function to check if a file has an allowed extension
|
|
||||||
def allowed_file(filename: str) -> bool:
|
def allowed_file(filename: str) -> bool:
|
||||||
|
"""Check if the uploaded file has an allowed extension."""
|
||||||
return "." in filename and filename.rsplit(".", 1)[1].lower() in ALLOWED_EXTENSIONS
|
return "." in filename and filename.rsplit(".", 1)[1].lower() in ALLOWED_EXTENSIONS
|
||||||
|
|
||||||
# Function to save uploaded file to a given folder
|
|
||||||
def save_uploaded_file(file, folder: Path):
|
def save_uploaded_file(file, folder: Path):
|
||||||
folder.mkdir(parents=True, exist_ok=True) # Create folder if it doesn't exist
|
"""Save an uploaded file to the specified folder."""
|
||||||
|
folder.mkdir(parents=True, exist_ok=True) # Create folder if not exists
|
||||||
filename = secure_filename(file.filename) # Sanitize filename
|
filename = secure_filename(file.filename) # Sanitize filename
|
||||||
file.save(folder / filename) # Save file to folder
|
file.save(folder / filename) # Save to disk
|
||||||
logging.info(f"[✓] Uploaded {filename} to {folder}")
|
logging.info(f"[✓] Uploaded {filename} to {folder}")
|
||||||
return filename # Return saved filename
|
return filename
|
||||||
|
|
||||||
# Route to handle photo uploads for gallery or hero
|
|
||||||
@upload_bp.route("/api/<section>/upload", methods=["POST"])
|
@upload_bp.route("/api/<section>/upload", methods=["POST"])
|
||||||
def upload_photo(section: str):
|
def upload_photo(section: str):
|
||||||
|
"""
|
||||||
|
Handle file uploads for gallery or hero section.
|
||||||
|
Accepts multiple files under 'files'.
|
||||||
|
"""
|
||||||
# Validate section
|
# Validate section
|
||||||
if section not in ["gallery", "hero"]:
|
if section not in ["gallery", "hero"]:
|
||||||
return {"error": "Invalid section"}, 400
|
return {"error": "Invalid section"}, 400
|
||||||
|
|
||||||
# Check if the request contains a file
|
# Check if files are provided
|
||||||
if "file" not in request.files:
|
if "files" not in request.files:
|
||||||
return {"error": "No file part"}, 400
|
return {"error": "No files provided"}, 400
|
||||||
file = request.files["file"]
|
|
||||||
|
files = request.files.getlist("files")
|
||||||
|
if not files:
|
||||||
|
return {"error": "No selected files"}, 400
|
||||||
|
|
||||||
# Check if a file was actually selected
|
# Get photos directory from app config
|
||||||
if file.filename == "":
|
PHOTOS_DIR = current_app.config.get("PHOTOS_DIR")
|
||||||
return {"error": "No selected file"}, 400
|
if not PHOTOS_DIR:
|
||||||
|
return {"error": "Server misconfiguration"}, 500
|
||||||
|
|
||||||
# Check file type and save it
|
folder = PHOTOS_DIR / section # Target folder
|
||||||
if file and allowed_file(file.filename):
|
uploaded = []
|
||||||
PHOTOS_DIR = current_app.config.get("PHOTOS_DIR") # Get base photos directory from config
|
|
||||||
if not PHOTOS_DIR:
|
|
||||||
return {"error": "Server misconfiguration"}, 500
|
|
||||||
folder = PHOTOS_DIR / section # Target folder (gallery or hero)
|
|
||||||
filename = save_uploaded_file(file, folder)
|
|
||||||
return {"status": "ok", "filename": filename}
|
|
||||||
|
|
||||||
# If file type is not allowed
|
# Save each valid file
|
||||||
return {"error": "File type not allowed"}, 400
|
for file in files:
|
||||||
|
if file and allowed_file(file.filename):
|
||||||
|
filename = save_uploaded_file(file, folder)
|
||||||
|
uploaded.append(filename)
|
||||||
|
|
||||||
|
# Update YAML if any files were uploaded
|
||||||
|
if uploaded:
|
||||||
|
if section == "gallery":
|
||||||
|
update_gallery()
|
||||||
|
else:
|
||||||
|
update_hero()
|
||||||
|
return {"status": "ok", "uploaded": uploaded}
|
||||||
|
|
||||||
|
return {"error": "No valid files uploaded"}, 400
|
||||||
|
@ -2,111 +2,106 @@ import logging
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from flask import Flask, jsonify, request, send_from_directory, render_template
|
from flask import Flask, jsonify, request, send_from_directory, render_template
|
||||||
from src.py.builder.gallery_builder import (
|
from src.py.builder.gallery_builder import (
|
||||||
GALLERY_YAML, GALLERY_DIR, HERO_DIR,
|
GALLERY_YAML, load_yaml, save_yaml, update_gallery, update_hero
|
||||||
load_yaml, save_yaml, get_all_image_paths, update_gallery, update_hero
|
|
||||||
)
|
)
|
||||||
from src.py.webui.upload import upload_bp
|
from src.py.webui.upload import upload_bp
|
||||||
|
|
||||||
# --- Logging configuration ---
|
# --- Logging configuration ---
|
||||||
# Logs messages to console with INFO level
|
|
||||||
logging.basicConfig(level=logging.INFO, format="%(message)s")
|
logging.basicConfig(level=logging.INFO, format="%(message)s")
|
||||||
|
|
||||||
# --- Flask app setup ---
|
# --- Flask app setup ---
|
||||||
# WEBUI_PATH points to the web UI folder (templates + static)
|
WEBUI_PATH = Path(__file__).parents[2] / "webui" # Path to static/templates
|
||||||
WEBUI_PATH = Path(__file__).parents[2] / "webui"
|
|
||||||
app = Flask(
|
app = Flask(
|
||||||
__name__,
|
__name__,
|
||||||
template_folder=WEBUI_PATH, # where Flask looks for templates
|
template_folder=WEBUI_PATH,
|
||||||
static_folder=WEBUI_PATH, # where Flask serves static files
|
static_folder=WEBUI_PATH,
|
||||||
static_url_path="" # URL path prefix for static files
|
static_url_path=""
|
||||||
)
|
)
|
||||||
|
|
||||||
# --- Absolute photos directory ---
|
# --- Photos directory (configurable) ---
|
||||||
# Used by upload.py and deletion endpoints
|
|
||||||
PHOTOS_DIR = Path(__file__).resolve().parents[3] / "config" / "photos"
|
PHOTOS_DIR = Path(__file__).resolve().parents[3] / "config" / "photos"
|
||||||
app.config["PHOTOS_DIR"] = PHOTOS_DIR
|
app.config["PHOTOS_DIR"] = PHOTOS_DIR
|
||||||
|
|
||||||
# --- Register upload blueprint ---
|
# --- Register upload blueprint ---
|
||||||
# Handles /api/<section>/upload endpoints for gallery and hero images
|
|
||||||
app.register_blueprint(upload_bp)
|
app.register_blueprint(upload_bp)
|
||||||
|
|
||||||
# --- Existing API routes ---
|
# --- Routes ---
|
||||||
|
|
||||||
# Serve main page
|
|
||||||
@app.route("/")
|
@app.route("/")
|
||||||
def index():
|
def index():
|
||||||
|
"""Serve the main HTML page."""
|
||||||
return render_template("index.html")
|
return render_template("index.html")
|
||||||
|
|
||||||
# Get gallery images (returns JSON array)
|
|
||||||
@app.route("/api/gallery", methods=["GET"])
|
@app.route("/api/gallery", methods=["GET"])
|
||||||
def get_gallery():
|
def get_gallery():
|
||||||
|
"""Return JSON list of gallery images from YAML."""
|
||||||
data = load_yaml(GALLERY_YAML)
|
data = load_yaml(GALLERY_YAML)
|
||||||
return jsonify(data.get("gallery", {}).get("images", []))
|
return jsonify(data.get("gallery", {}).get("images", []))
|
||||||
|
|
||||||
# Get hero images (returns JSON array)
|
|
||||||
@app.route("/api/hero", methods=["GET"])
|
@app.route("/api/hero", methods=["GET"])
|
||||||
def get_hero():
|
def get_hero():
|
||||||
|
"""Return JSON list of hero images from YAML."""
|
||||||
data = load_yaml(GALLERY_YAML)
|
data = load_yaml(GALLERY_YAML)
|
||||||
return jsonify(data.get("hero", {}).get("images", []))
|
return jsonify(data.get("hero", {}).get("images", []))
|
||||||
|
|
||||||
# Update gallery images with new JSON data
|
|
||||||
@app.route("/api/gallery/update", methods=["POST"])
|
@app.route("/api/gallery/update", methods=["POST"])
|
||||||
def update_gallery_api():
|
def update_gallery_api():
|
||||||
|
"""Update gallery images in YAML from frontend JSON."""
|
||||||
images = request.json
|
images = request.json
|
||||||
data = load_yaml(GALLERY_YAML)
|
data = load_yaml(GALLERY_YAML)
|
||||||
data["gallery"]["images"] = images
|
data["gallery"]["images"] = images
|
||||||
save_yaml(data, GALLERY_YAML)
|
save_yaml(data, GALLERY_YAML)
|
||||||
return jsonify({"status": "ok"})
|
return jsonify({"status": "ok"})
|
||||||
|
|
||||||
# Update hero images with new JSON data
|
|
||||||
@app.route("/api/hero/update", methods=["POST"])
|
@app.route("/api/hero/update", methods=["POST"])
|
||||||
def update_hero_api():
|
def update_hero_api():
|
||||||
|
"""Update hero images in YAML from frontend JSON."""
|
||||||
images = request.json
|
images = request.json
|
||||||
data = load_yaml(GALLERY_YAML)
|
data = load_yaml(GALLERY_YAML)
|
||||||
data["hero"]["images"] = images
|
data["hero"]["images"] = images
|
||||||
save_yaml(data, GALLERY_YAML)
|
save_yaml(data, GALLERY_YAML)
|
||||||
return jsonify({"status": "ok"})
|
return jsonify({"status": "ok"})
|
||||||
|
|
||||||
# Refresh gallery from the folder (rebuild YAML)
|
|
||||||
@app.route("/api/gallery/refresh", methods=["POST"])
|
@app.route("/api/gallery/refresh", methods=["POST"])
|
||||||
def refresh_gallery():
|
def refresh_gallery():
|
||||||
|
"""Refresh gallery YAML from photos/gallery folder."""
|
||||||
update_gallery()
|
update_gallery()
|
||||||
return jsonify({"status": "ok"})
|
return jsonify({"status": "ok"})
|
||||||
|
|
||||||
# Refresh hero images from the folder
|
|
||||||
@app.route("/api/hero/refresh", methods=["POST"])
|
@app.route("/api/hero/refresh", methods=["POST"])
|
||||||
def refresh_hero():
|
def refresh_hero():
|
||||||
|
"""Refresh hero YAML from photos/hero folder."""
|
||||||
update_hero()
|
update_hero()
|
||||||
return jsonify({"status": "ok"})
|
return jsonify({"status": "ok"})
|
||||||
|
|
||||||
# Delete a gallery image file
|
|
||||||
@app.route("/api/gallery/delete", methods=["POST"])
|
@app.route("/api/gallery/delete", methods=["POST"])
|
||||||
def delete_gallery_photo():
|
def delete_gallery_photo():
|
||||||
|
"""Delete a gallery photo from disk and return status."""
|
||||||
data = request.json
|
data = request.json
|
||||||
src = data.get("src") # filename only
|
src = data.get("src")
|
||||||
file_path = PHOTOS_DIR / "gallery" / src
|
file_path = PHOTOS_DIR / "gallery" / src
|
||||||
if file_path.exists():
|
if file_path.exists():
|
||||||
file_path.unlink() # remove the file
|
file_path.unlink()
|
||||||
return {"status": "ok"}
|
return {"status": "ok"}
|
||||||
return {"error": "File not found"}, 404
|
return {"error": "File not found"}, 404
|
||||||
|
|
||||||
# Delete a hero image file
|
|
||||||
@app.route("/api/hero/delete", methods=["POST"])
|
@app.route("/api/hero/delete", methods=["POST"])
|
||||||
def delete_hero_photo():
|
def delete_hero_photo():
|
||||||
|
"""Delete a hero photo from disk and return status."""
|
||||||
data = request.json
|
data = request.json
|
||||||
src = data.get("src") # filename only
|
src = data.get("src")
|
||||||
file_path = PHOTOS_DIR / "hero" / src
|
file_path = PHOTOS_DIR / "hero" / src
|
||||||
if file_path.exists():
|
if file_path.exists():
|
||||||
file_path.unlink() # remove the file
|
file_path.unlink()
|
||||||
return {"status": "ok"}
|
return {"status": "ok"}
|
||||||
return {"error": "File not found"}, 404
|
return {"error": "File not found"}, 404
|
||||||
|
|
||||||
# Serve photos from /photos/<section>/<filename>
|
|
||||||
@app.route("/photos/<section>/<path:filename>")
|
@app.route("/photos/<section>/<path:filename>")
|
||||||
def photos(section, filename):
|
def photos(section, filename):
|
||||||
|
"""Serve uploaded photos from disk."""
|
||||||
return send_from_directory(PHOTOS_DIR / section, filename)
|
return send_from_directory(PHOTOS_DIR / section, filename)
|
||||||
|
|
||||||
# --- Main entry point ---
|
# --- Run server ---
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
logging.info("Starting WebUI at http://127.0.0.1:5000")
|
logging.info("Starting WebUI at http://127.0.0.1:5000")
|
||||||
app.run(debug=True)
|
app.run(debug=True)
|
||||||
|
@ -3,11 +3,14 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Photo WebUI</title>
|
<title>Photo WebUI</title>
|
||||||
|
|
||||||
|
<!-- Link to your CSS in the package -->
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='style/style.css') }}">
|
<link rel="stylesheet" href="{{ url_for('static', filename='style/style.css') }}">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>Photo WebUI</h1>
|
<h1>Photo WebUI</h1>
|
||||||
|
|
||||||
|
<!-- Toolbar with refresh and save buttons -->
|
||||||
<div class="toolbar">
|
<div class="toolbar">
|
||||||
<button onclick="refreshGallery()">🔄 Refresh Gallery</button>
|
<button onclick="refreshGallery()">🔄 Refresh Gallery</button>
|
||||||
<button onclick="refreshHero()">🔄 Refresh Hero</button>
|
<button onclick="refreshHero()">🔄 Refresh Hero</button>
|
||||||
@ -19,7 +22,7 @@
|
|||||||
<h2>Gallery</h2>
|
<h2>Gallery</h2>
|
||||||
<label>
|
<label>
|
||||||
Upload Image:
|
Upload Image:
|
||||||
<input type="file" id="upload-gallery" accept=".png,.jpg,.jpeg,.webp">
|
<input type="file" id="upload-gallery" accept=".png,.jpg,.jpeg,.webp" multiple>
|
||||||
</label>
|
</label>
|
||||||
<div id="gallery"></div>
|
<div id="gallery"></div>
|
||||||
</div>
|
</div>
|
||||||
@ -29,10 +32,12 @@
|
|||||||
<h2>Hero</h2>
|
<h2>Hero</h2>
|
||||||
<label>
|
<label>
|
||||||
Upload Image:
|
Upload Image:
|
||||||
<input type="file" id="upload-hero" accept=".png,.jpg,.jpeg,.webp">
|
<input type="file" id="upload-hero" accept=".png,.jpg,.jpeg,.webp" multiple>
|
||||||
</label>
|
</label>
|
||||||
<div id="hero"></div>
|
<div id="hero"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- JS files for rendering, uploading, and actions -->
|
||||||
<script src="{{ url_for('static', filename='js/main.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/main.js') }}"></script>
|
||||||
<script src="{{ url_for('static', filename='js/upload.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/upload.js') }}"></script>
|
||||||
</body>
|
</body>
|
||||||
|
@ -1,29 +1,27 @@
|
|||||||
// Arrays to store gallery and hero images
|
// --- Arrays to store gallery and hero images ---
|
||||||
let galleryImages = [];
|
let galleryImages = [];
|
||||||
let heroImages = [];
|
let heroImages = [];
|
||||||
|
|
||||||
// Load images data from server
|
// --- Load images from server on page load ---
|
||||||
async function loadData() {
|
async function loadData() {
|
||||||
try {
|
try {
|
||||||
// Fetch gallery images from API
|
|
||||||
const galleryRes = await fetch('/api/gallery');
|
const galleryRes = await fetch('/api/gallery');
|
||||||
galleryImages = await galleryRes.json();
|
galleryImages = await galleryRes.json();
|
||||||
renderGallery(); // Render gallery images
|
renderGallery();
|
||||||
|
|
||||||
// Fetch hero images from API
|
|
||||||
const heroRes = await fetch('/api/hero');
|
const heroRes = await fetch('/api/hero');
|
||||||
heroImages = await heroRes.json();
|
heroImages = await heroRes.json();
|
||||||
renderHero(); // Render hero images
|
renderHero();
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
alert("Error while loading images!");
|
alert("Error loading images!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render gallery images in the page
|
// --- Render gallery images with tags and delete buttons ---
|
||||||
function renderGallery() {
|
function renderGallery() {
|
||||||
const container = document.getElementById('gallery');
|
const container = document.getElementById('gallery');
|
||||||
container.innerHTML = ''; // Clear current content
|
container.innerHTML = '';
|
||||||
galleryImages.forEach((img, i) => {
|
galleryImages.forEach((img, i) => {
|
||||||
const div = document.createElement('div');
|
const div = document.createElement('div');
|
||||||
div.className = 'photo';
|
div.className = 'photo';
|
||||||
@ -33,14 +31,14 @@ function renderGallery() {
|
|||||||
onchange="updateTags(${i}, this.value)">
|
onchange="updateTags(${i}, this.value)">
|
||||||
<button onclick="deleteGalleryImage(${i})">🗑 Delete</button>
|
<button onclick="deleteGalleryImage(${i})">🗑 Delete</button>
|
||||||
`;
|
`;
|
||||||
container.appendChild(div); // Add image div to container
|
container.appendChild(div);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render hero images in the page
|
// --- Render hero images with delete buttons ---
|
||||||
function renderHero() {
|
function renderHero() {
|
||||||
const container = document.getElementById('hero');
|
const container = document.getElementById('hero');
|
||||||
container.innerHTML = ''; // Clear current content
|
container.innerHTML = '';
|
||||||
heroImages.forEach((img, i) => {
|
heroImages.forEach((img, i) => {
|
||||||
const div = document.createElement('div');
|
const div = document.createElement('div');
|
||||||
div.className = 'photo';
|
div.className = 'photo';
|
||||||
@ -48,104 +46,93 @@ function renderHero() {
|
|||||||
<img src="/photos/${img.src}">
|
<img src="/photos/${img.src}">
|
||||||
<button onclick="deleteHeroImage(${i})">🗑 Delete</button>
|
<button onclick="deleteHeroImage(${i})">🗑 Delete</button>
|
||||||
`;
|
`;
|
||||||
container.appendChild(div); // Add image div to container
|
container.appendChild(div);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update tags for a gallery image
|
// --- Update tags for gallery image ---
|
||||||
function updateTags(index, value) {
|
function updateTags(index, value) {
|
||||||
// Split tags by comma, trim spaces, and remove empty values
|
|
||||||
galleryImages[index].tags = value.split(',').map(t => t.trim()).filter(t => t);
|
galleryImages[index].tags = value.split(',').map(t => t.trim()).filter(t => t);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete a gallery image
|
// --- Delete gallery image ---
|
||||||
async function deleteGalleryImage(index) {
|
async function deleteGalleryImage(index) {
|
||||||
const img = galleryImages[index];
|
const img = galleryImages[index];
|
||||||
try {
|
try {
|
||||||
// Send only the filename to the server for deletion
|
|
||||||
const res = await fetch('/api/gallery/delete', {
|
const res = await fetch('/api/gallery/delete', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: {'Content-Type': 'application/json'},
|
||||||
body: JSON.stringify({ src: img.src.split('/').pop() }) // Keep only filename
|
body: JSON.stringify({ src: img.src.split('/').pop() })
|
||||||
});
|
});
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
// Remove image from array and re-render gallery
|
|
||||||
galleryImages.splice(index, 1);
|
galleryImages.splice(index, 1);
|
||||||
renderGallery();
|
renderGallery();
|
||||||
await saveGallery(); // Save updated tags
|
await saveGallery();
|
||||||
} else {
|
} else alert("Error: " + data.error);
|
||||||
alert("Error: " + data.error);
|
|
||||||
}
|
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
console.error(err);
|
console.error(err); alert("Server error!");
|
||||||
alert("Server error!");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete a hero image
|
// --- Delete hero image ---
|
||||||
async function deleteHeroImage(index) {
|
async function deleteHeroImage(index) {
|
||||||
const img = heroImages[index];
|
const img = heroImages[index];
|
||||||
try {
|
try {
|
||||||
// Send only the filename to the server for deletion
|
|
||||||
const res = await fetch('/api/hero/delete', {
|
const res = await fetch('/api/hero/delete', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: {'Content-Type': 'application/json'},
|
||||||
body: JSON.stringify({ src: img.src.split('/').pop() }) // Keep only filename
|
body: JSON.stringify({ src: img.src.split('/').pop() })
|
||||||
});
|
});
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
// Remove image from array and re-render hero section
|
|
||||||
heroImages.splice(index, 1);
|
heroImages.splice(index, 1);
|
||||||
renderHero();
|
renderHero();
|
||||||
await saveHero(); // Save updated hero images
|
await saveHero();
|
||||||
} else {
|
} else alert("Error: " + data.error);
|
||||||
alert("Error: " + data.error);
|
|
||||||
}
|
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
console.error(err);
|
console.error(err); alert("Server error!");
|
||||||
alert("Server error!");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save gallery images (with tags) to the server
|
// --- Save gallery to server ---
|
||||||
async function saveGallery() {
|
async function saveGallery() {
|
||||||
await fetch('/api/gallery/update', {
|
await fetch('/api/gallery/update', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: {'Content-Type': 'application/json'},
|
||||||
body: JSON.stringify(galleryImages)
|
body: JSON.stringify(galleryImages)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save hero images to the server
|
// --- Save hero to server ---
|
||||||
async function saveHero() {
|
async function saveHero() {
|
||||||
await fetch('/api/hero/update', {
|
await fetch('/api/hero/update', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: {'Content-Type': 'application/json'},
|
||||||
body: JSON.stringify(heroImages)
|
body: JSON.stringify(heroImages)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save both gallery and hero changes
|
// --- Save all changes ---
|
||||||
async function saveChanges() {
|
async function saveChanges() {
|
||||||
await saveGallery();
|
await saveGallery();
|
||||||
await saveHero();
|
await saveHero();
|
||||||
alert('✅ Changes saved!');
|
alert('✅ Changes saved!');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refresh gallery images from the server folder
|
// --- Refresh gallery from folder ---
|
||||||
async function refreshGallery() {
|
async function refreshGallery() {
|
||||||
await fetch('/api/gallery/refresh', { method: 'POST' });
|
await fetch('/api/gallery/refresh', { method: 'POST' });
|
||||||
await loadData(); // Reload data after refresh
|
await loadData();
|
||||||
alert('🔄 Gallery updated from photos/gallery folder');
|
alert('🔄 Gallery updated from photos/gallery folder');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refresh hero images from the server folder
|
// --- Refresh hero from folder ---
|
||||||
async function refreshHero() {
|
async function refreshHero() {
|
||||||
await fetch('/api/hero/refresh', { method: 'POST' });
|
await fetch('/api/hero/refresh', { method: 'POST' });
|
||||||
await loadData(); // Reload data after refresh
|
await loadData();
|
||||||
alert('🔄 Hero updated from photos/hero folder');
|
alert('🔄 Hero updated from photos/hero folder');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initial load of images when page opens
|
// --- Initialize ---
|
||||||
loadData();
|
loadData();
|
||||||
|
@ -1,61 +1,39 @@
|
|||||||
// Upload handler for gallery images
|
// --- Upload gallery images ---
|
||||||
document.getElementById('upload-gallery').addEventListener('change', async (e) => {
|
document.getElementById('upload-gallery').addEventListener('change', async (e) => {
|
||||||
const file = e.target.files[0];
|
const files = e.target.files;
|
||||||
if (!file) return; // Exit if no file is selected
|
if (!files.length) return;
|
||||||
|
|
||||||
// Create a FormData object to send the file
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('file', file); // Key must match what upload.py expects
|
for (const file of files) formData.append('files', file);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Send POST request to the gallery upload endpoint
|
const res = await fetch('/api/gallery/upload', { method: 'POST', body: formData });
|
||||||
const res = await fetch('/api/gallery/upload', {
|
|
||||||
method: 'POST',
|
|
||||||
body: formData
|
|
||||||
});
|
|
||||||
|
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
alert('✅ Gallery image uploaded!');
|
alert(`✅ ${data.uploaded.length} gallery image(s) uploaded!`);
|
||||||
refreshGallery(); // Refresh the gallery list from the server
|
refreshGallery();
|
||||||
} else {
|
} else alert('Error: ' + data.error);
|
||||||
alert('Error: ' + data.error); // Show server error if upload failed
|
} catch(err) {
|
||||||
}
|
console.error(err); alert('Server error!');
|
||||||
} catch (err) {
|
} finally { e.target.value = ''; }
|
||||||
console.error(err);
|
|
||||||
alert('Server error!'); // Network or server failure
|
|
||||||
} finally {
|
|
||||||
e.target.value = ''; // Reset file input so the same file can be uploaded again if needed
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Upload handler for hero images
|
// --- Upload hero images ---
|
||||||
document.getElementById('upload-hero').addEventListener('change', async (e) => {
|
document.getElementById('upload-hero').addEventListener('change', async (e) => {
|
||||||
const file = e.target.files[0];
|
const files = e.target.files;
|
||||||
if (!file) return; // Exit if no file is selected
|
if (!files.length) return;
|
||||||
|
|
||||||
// Create a FormData object to send the file
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('file', file); // Key must match what upload.py expects
|
for (const file of files) formData.append('files', file);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Send POST request to the hero upload endpoint
|
const res = await fetch('/api/hero/upload', { method: 'POST', body: formData });
|
||||||
const res = await fetch('/api/hero/upload', {
|
|
||||||
method: 'POST',
|
|
||||||
body: formData
|
|
||||||
});
|
|
||||||
|
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
alert('✅ Hero image uploaded!');
|
alert(`✅ ${data.uploaded.length} hero image(s) uploaded!`);
|
||||||
refreshHero(); // Refresh the hero list from the server
|
refreshHero();
|
||||||
} else {
|
} else alert('Error: ' + data.error);
|
||||||
alert('Error: ' + data.error); // Show server error if upload failed
|
} catch(err) {
|
||||||
}
|
console.error(err); alert('Server error!');
|
||||||
} catch (err) {
|
} finally { e.target.value = ''; }
|
||||||
console.error(err);
|
|
||||||
alert('Server error!'); // Network or server failure
|
|
||||||
} finally {
|
|
||||||
e.target.value = ''; // Reset file input so the same file can be uploaded again if needed
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
Reference in New Issue
Block a user